| /* 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 "client.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 host_cfg *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 (match_config(cc->cfg, name, MATCH_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 = host_cfg_alloc(cc, name, host, port); |
| if (!cc->cfg) { |
| talloc_free(cc); |
| return NULL; |
| } |
| |
| 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, 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, 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; |
| } |