osmo-sysmon/src/osysmon_ctrl.c

305 lines
7.7 KiB
C

/* Simple Osmocom System Monitor (osysmon): Support for CTRL monitoring */
/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <string.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
#include "osysmon.h"
#include "simple_ctrl.h"
#include "value_node.h"
/***********************************************************************
* Data Model
***********************************************************************/
/* a single CTRL client */
struct ctrl_client {
/* links to osysmon.ctrl_clients */
struct llist_head list;
struct {
/* name of this CTRL client */
const char *name;
/* remote host/IP */
const char *remote_host;
/* remote CTRL port */
uint16_t remote_port;
} cfg;
struct simple_ctrl_handle *sch;
/* list of ctrl_client_get_var objects */
struct llist_head get_vars;
};
/* a variable we are GETing via a ctrl_client */
struct ctrl_client_get_var {
/* links to ctrl_client.get_vars */
struct llist_head list;
/* back-link to ctrl_client */
struct ctrl_client *cc;
struct {
/* CTRL variable name */
const char *name;
/* display name, if any */
const char *display_name;
} cfg;
};
static struct ctrl_client *ctrl_client_find(struct osysmon_state *os, const char *name)
{
struct ctrl_client *cc;
llist_for_each_entry(cc, &os->ctrl_clients, list) {
if (!strcmp(name, cc->cfg.name))
return cc;
}
return NULL;
}
static struct ctrl_client *ctrl_client_create(struct osysmon_state *os, const char *name,
const char *host, uint16_t port)
{
struct ctrl_client *cc;
if (ctrl_client_find(os, name))
return NULL;
cc = talloc_zero(os, struct ctrl_client);
if (!cc)
return NULL;
cc->cfg.name = talloc_strdup(cc, name);
cc->cfg.remote_host = talloc_strdup(cc, host);
cc->cfg.remote_port = port;
INIT_LLIST_HEAD(&cc->get_vars);
llist_add_tail(&cc->list, &os->ctrl_clients);
/* FIXME */
return cc;
}
static void ctrl_client_destroy(struct ctrl_client *cc)
{
/* FIXME */
llist_del(&cc->list);
talloc_free(cc);
}
static struct ctrl_client_get_var *
ctrl_client_get_var_find_or_create(struct ctrl_client *cc, const char *name)
{
struct ctrl_client_get_var *gv;
llist_for_each_entry(gv, &cc->get_vars, list) {
if (!strcmp(name, gv->cfg.name))
return gv;
}
gv = talloc_zero(cc, struct ctrl_client_get_var);
if (!gv)
return NULL;
gv->cc = cc;
gv->cfg.name = talloc_strdup(gv, name);
llist_add_tail(&gv->list, &cc->get_vars);
return gv;
}
/***********************************************************************
* VTY
***********************************************************************/
static struct cmd_node ctrl_client_node = {
CTRL_CLIENT_NODE,
"%s(config-ctrlclient)# ",
1,
};
static struct cmd_node ctrl_client_getvar_node = {
CTRL_CLIENT_GETVAR_NODE,
"%s(config-ctrlclient-getvar)# ",
1,
};
int osysmon_ctrl_go_parent(struct vty *vty)
{
switch (vty->node) {
case CTRL_CLIENT_NODE:
vty->node = CONFIG_NODE;
vty->index = NULL;
break;
case CTRL_CLIENT_GETVAR_NODE:
vty->node = CTRL_CLIENT_NODE;
{
struct ctrl_client_get_var *gv = vty->index;
vty->index = gv->cc;
}
break;
default:
break;
}
return vty->node;
}
DEFUN(cfg_ctrl_client, cfg_ctrl_client_cmd,
"ctrl-client NAME A.B.C.D <1-65535>",
"")
{
struct ctrl_client *cc;
cc = ctrl_client_find(g_oss, argv[0]);
if (cc) {
if ((strcmp(cc->cfg.remote_host, argv[1])) ||
(cc->cfg.remote_port != atoi(argv[2]))) {
vty_out(vty, "Client %s has different IP/port, please remove it first%s",
cc->cfg.name, VTY_NEWLINE);
return CMD_WARNING;
}
} else
cc = ctrl_client_create(g_oss, argv[0], argv[1], atoi(argv[2]));
OSMO_ASSERT(cc);
vty->node = CTRL_CLIENT_NODE;
vty->index = cc;
return CMD_SUCCESS;
}
DEFUN(cfg_no_ctrl_client, cfg_no_ctrl_client_cmd,
"no ctrl-client NAME",
NO_STR "")
{
struct ctrl_client *cc;
cc = ctrl_client_find(g_oss, argv[0]);
if (!cc) {
vty_out(vty, "Client %s doesn't exist%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
ctrl_client_destroy(cc);
return CMD_SUCCESS;
}
DEFUN(cfg_ctrlc_get_var, cfg_ctrlc_get_var_cmd,
"get-variable NAME",
"")
{
struct ctrl_client *cc = vty->index;
struct ctrl_client_get_var *ccgv;
ccgv = ctrl_client_get_var_find_or_create(cc, argv[0]);
OSMO_ASSERT(ccgv);
vty->node = CTRL_CLIENT_GETVAR_NODE;
vty->index = ccgv;
return CMD_SUCCESS;
}
DEFUN(cfg_ctrlc_no_get_var, cfg_ctrlc_no_get_var_cmd,
"no get-variable NAME",
NO_STR "")
{
struct ctrl_client *cc = vty->index;
struct ctrl_client_get_var *ccgv;
ccgv = ctrl_client_get_var_find_or_create(cc, argv[0]);
talloc_free(ccgv);
return CMD_SUCCESS;
}
static void write_one_ctrl_client(struct vty *vty, struct ctrl_client *cc)
{
struct ctrl_client_get_var *ccgv;
vty_out(vty, "ctrl-client %s %s %u%s", cc->cfg.name,
cc->cfg.remote_host, cc->cfg.remote_port, VTY_NEWLINE);
llist_for_each_entry(ccgv, &cc->get_vars, list) {
vty_out(vty, " get-variable %s%s", ccgv->cfg.name, VTY_NEWLINE);
if (ccgv->cfg.display_name)
vty_out(vty, " display-name %s%s", ccgv->cfg.display_name, VTY_NEWLINE);
}
}
static int config_write_ctrl_client(struct vty *vty)
{
struct ctrl_client *cc;
llist_for_each_entry(cc, &g_oss->ctrl_clients, list)
write_one_ctrl_client(vty, cc);
return CMD_SUCCESS;
}
static void osysmon_ctrl_vty_init(void)
{
install_element(CONFIG_NODE, &cfg_ctrl_client_cmd);
install_element(CONFIG_NODE, &cfg_no_ctrl_client_cmd);
install_node(&ctrl_client_node, config_write_ctrl_client);
install_element(CTRL_CLIENT_NODE, &cfg_ctrlc_get_var_cmd);
install_element(CTRL_CLIENT_NODE, &cfg_ctrlc_no_get_var_cmd);
install_node(&ctrl_client_getvar_node, NULL);
//install_element(CTRL_CLIENT_GETVAR_NODE, &cfg_getvar_disp_name_cmd);
}
/***********************************************************************
* Runtime Code
***********************************************************************/
/* called once on startup before config file parsing */
int osysmon_ctrl_init()
{
osysmon_ctrl_vty_init();
return 0;
}
static int ctrl_client_poll(struct ctrl_client *cc, struct value_node *parent)
{
struct ctrl_client_get_var *ccgv;
struct value_node *vn_clnt = value_node_add(parent, parent, cc->cfg.name, NULL);
/* attempt to re-connect */
if (!cc->sch)
cc->sch = simple_ctrl_open(cc, cc->cfg.remote_host, cc->cfg.remote_port, 1000);
/* abort, if that failed */
if (!cc->sch) {
return -1;
}
llist_for_each_entry(ccgv, &cc->get_vars, list) {
char *value = simple_ctrl_get(cc->sch, ccgv->cfg.name);
/* FIXME: Distinguish between ERROR reply and
* connection issues */
/* Close connection on error */
if (!value) {
simple_ctrl_close(cc->sch);
cc->sch = NULL;
return 0;
}
value_node_add(vn_clnt, vn_clnt, ccgv->cfg.name, value);
free(value); /* no talloc, this is from sscanf() */
}
return 0;
}
/* called periodically */
int osysmon_ctrl_poll(struct value_node *parent)
{
struct ctrl_client *cc;
llist_for_each_entry(cc, &g_oss->ctrl_clients, list)
ctrl_client_poll(cc, parent);
return 0;
}