blob: b13b13b823cb19dff4d81adf275b8e787cf2489d [file] [log] [blame]
Harald Welte1a36f332018-06-04 17:36:33 +02001/* Simple Osmocom System Monitor (osysmon): Support for CTRL monitoring */
2
3/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved.
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 */
23
24#include <string.h>
25
26#include <osmocom/vty/vty.h>
27#include <osmocom/vty/command.h>
28
29#include "osysmon.h"
30#include "simple_ctrl.h"
31#include "value_node.h"
32
33/***********************************************************************
34 * Data Model
35 ***********************************************************************/
36
37/* a single CTRL client */
38struct ctrl_client {
39 /* links to osysmon.ctrl_clients */
40 struct llist_head list;
41 struct {
42 /* name of this CTRL client */
43 const char *name;
44 /* remote host/IP */
45 const char *remote_host;
46 /* remote CTRL port */
47 uint16_t remote_port;
48 } cfg;
49 struct simple_ctrl_handle *sch;
50 /* list of ctrl_client_get_var objects */
51 struct llist_head get_vars;
52};
53
54/* a variable we are GETing via a ctrl_client */
55struct ctrl_client_get_var {
56 /* links to ctrl_client.get_vars */
57 struct llist_head list;
58 /* back-link to ctrl_client */
59 struct ctrl_client *cc;
60 struct {
61 /* CTRL variable name */
62 const char *name;
63 /* display name, if any */
64 const char *display_name;
65 } cfg;
66 /* most recent value we received for this */
67 char *last_value;
68};
69
70static struct ctrl_client *ctrl_client_find(struct osysmon_state *os, const char *name)
71{
72 struct ctrl_client *cc;
73 llist_for_each_entry(cc, &os->ctrl_clients, list) {
74 if (!strcmp(name, cc->cfg.name))
75 return cc;
76 }
77 return NULL;
78}
79
80static struct ctrl_client *ctrl_client_create(struct osysmon_state *os, const char *name,
81 const char *host, uint16_t port)
82{
83 struct ctrl_client *cc;
84
85 if (ctrl_client_find(os, name))
86 return NULL;
87
88 cc = talloc_zero(os, struct ctrl_client);
89 if (!cc)
90 return NULL;
91 cc->cfg.name = talloc_strdup(cc, name);
92 cc->cfg.remote_host = talloc_strdup(cc, host);
93 cc->cfg.remote_port = port;
94 INIT_LLIST_HEAD(&cc->get_vars);
95 llist_add_tail(&cc->list, &os->ctrl_clients);
96 /* FIXME */
97 return cc;
98}
99
100static void ctrl_client_destroy(struct ctrl_client *cc)
101{
102 /* FIXME */
103 llist_del(&cc->list);
104 talloc_free(cc);
105}
106
107static struct ctrl_client_get_var *
108ctrl_client_get_var_find_or_create(struct ctrl_client *cc, const char *name)
109{
110 struct ctrl_client_get_var *gv;
111 llist_for_each_entry(gv, &cc->get_vars, list) {
112 if (!strcmp(name, gv->cfg.name))
113 return gv;
114 }
115 gv = talloc_zero(cc, struct ctrl_client_get_var);
116 if (!gv)
117 return NULL;
118 gv->cc = cc;
119 gv->cfg.name = talloc_strdup(gv, name);
120 llist_add_tail(&gv->list, &cc->get_vars);
121 return gv;
122}
123
124/***********************************************************************
125 * VTY
126 ***********************************************************************/
127
128static struct cmd_node ctrl_client_node = {
129 CTRL_CLIENT_NODE,
130 "%s(config-ctrlclient)# ",
131 1,
132};
133
134static struct cmd_node ctrl_client_getvar_node = {
135 CTRL_CLIENT_GETVAR_NODE,
136 "%s(config-ctrlclient-getvar)# ",
137 1,
138};
139
140int osysmon_ctrl_go_parent(struct vty *vty)
141{
142 switch (vty->node) {
143 case CTRL_CLIENT_NODE:
144 vty->node = CONFIG_NODE;
145 vty->index = NULL;
146 break;
147 case CTRL_CLIENT_GETVAR_NODE:
148 vty->node = CTRL_CLIENT_NODE;
149 {
150 struct ctrl_client_get_var *gv = vty->index;
151 vty->index = gv->cc;
152 }
153 break;
154 default:
155 break;
156 }
157 return vty->node;
158}
159
160
161DEFUN(cfg_ctrl_client, cfg_ctrl_client_cmd,
162 "ctrl-client NAME A.B.C.D <1-65535>",
163 "")
164{
165 struct ctrl_client *cc;
166 cc = ctrl_client_find(g_oss, argv[0]);
167 if (cc) {
168 if ((strcmp(cc->cfg.remote_host, argv[1])) ||
169 (cc->cfg.remote_port != atoi(argv[2]))) {
170 vty_out(vty, "Client %s has different IP/port, please remove it first%s",
171 cc->cfg.name, VTY_NEWLINE);
172 return CMD_WARNING;
173 }
174 } else
175 cc = ctrl_client_create(g_oss, argv[0], argv[1], atoi(argv[2]));
176 OSMO_ASSERT(cc);
177
178 vty->node = CTRL_CLIENT_NODE;
179 vty->index = cc;
180 return CMD_SUCCESS;
181}
182
183DEFUN(cfg_no_ctrl_client, cfg_no_ctrl_client_cmd,
184 "no ctrl-client NAME",
185 NO_STR "")
186{
187 struct ctrl_client *cc;
188 cc = ctrl_client_find(g_oss, argv[0]);
189 if (!cc) {
190 vty_out(vty, "Client %s doesn't exist%s", argv[0], VTY_NEWLINE);
191 return CMD_WARNING;
192 }
193 ctrl_client_destroy(cc);
194 return CMD_SUCCESS;
195}
196
197DEFUN(cfg_ctrlc_get_var, cfg_ctrlc_get_var_cmd,
198 "get-variable NAME",
199 "")
200{
201 struct ctrl_client *cc = vty->index;
202 struct ctrl_client_get_var *ccgv;
203
204 ccgv = ctrl_client_get_var_find_or_create(cc, argv[0]);
205 OSMO_ASSERT(ccgv);
206
207 vty->node = CTRL_CLIENT_GETVAR_NODE;
208 vty->index = ccgv;
209 return CMD_SUCCESS;
210}
211
212DEFUN(cfg_ctrlc_no_get_var, cfg_ctrlc_no_get_var_cmd,
213 "no get-variable NAME",
214 NO_STR "")
215{
216 struct ctrl_client *cc = vty->index;
217 struct ctrl_client_get_var *ccgv;
218
219 ccgv = ctrl_client_get_var_find_or_create(cc, argv[0]);
220 talloc_free(ccgv);
221 return CMD_SUCCESS;
222}
223
224static void write_one_ctrl_client(struct vty *vty, struct ctrl_client *cc)
225{
226 struct ctrl_client_get_var *ccgv;
227 vty_out(vty, "ctrl-client %s %s %u%s", cc->cfg.name,
228 cc->cfg.remote_host, cc->cfg.remote_port, VTY_NEWLINE);
229 llist_for_each_entry(ccgv, &cc->get_vars, list) {
230 vty_out(vty, " get-variable %s%s", ccgv->cfg.name, VTY_NEWLINE);
231 if (ccgv->cfg.display_name)
232 vty_out(vty, " display-name %s%s", ccgv->cfg.display_name, VTY_NEWLINE);
233 }
234}
235
236static int config_write_ctrl_client(struct vty *vty)
237{
238 struct ctrl_client *cc;
239
240 llist_for_each_entry(cc, &g_oss->ctrl_clients, list)
241 write_one_ctrl_client(vty, cc);
242 return CMD_SUCCESS;
243}
244
245static void osysmon_ctrl_vty_init(void)
246{
247 install_element(CONFIG_NODE, &cfg_ctrl_client_cmd);
248 install_element(CONFIG_NODE, &cfg_no_ctrl_client_cmd);
249 install_node(&ctrl_client_node, config_write_ctrl_client);
250 install_element(CTRL_CLIENT_NODE, &cfg_ctrlc_get_var_cmd);
251 install_element(CTRL_CLIENT_NODE, &cfg_ctrlc_no_get_var_cmd);
252 install_node(&ctrl_client_getvar_node, NULL);
253 //install_element(CTRL_CLIENT_GETVAR_NODE, &cfg_getvar_disp_name_cmd);
254}
255
256
257/***********************************************************************
258 * Runtime Code
259 ***********************************************************************/
260
261/* called once on startup before config file parsing */
262int osysmon_ctrl_init()
263{
264 osysmon_ctrl_vty_init();
265 return 0;
266}
267
268static int ctrl_client_poll(struct ctrl_client *cc, struct value_node *parent)
269{
270 struct ctrl_client_get_var *ccgv;
271 struct value_node *vn_clnt = value_node_add(parent, parent, cc->cfg.name, NULL);
272
273 /* attempt to re-connect */
274 if (!cc->sch)
275 cc->sch = simple_ctrl_open(cc, cc->cfg.remote_host, cc->cfg.remote_port, 1000);
276 /* abort, if that failed */
277 if (!cc->sch) {
278 llist_for_each_entry(ccgv, &cc->get_vars, list) {
279 if (ccgv->last_value) {
280 talloc_free(ccgv->last_value);
281 ccgv->last_value = talloc_strdup(ccgv, "<UNKNOWN>");
282 }
283 }
284 return -1;
285 }
286
287 llist_for_each_entry(ccgv, &cc->get_vars, list) {
288 char *value = simple_ctrl_get(cc->sch, ccgv->cfg.name);
289#if 0
290 if (ccgv->last_value) {
291 talloc_free(ccgv->last_value);
292 ccgv->last_value = NULL;
293 }
294 ccgv->last_value = value;
295#else
Harald Welteb3ff6412018-06-04 18:21:46 +0200296 value_node_add(vn_clnt, vn_clnt, ccgv->cfg.name, value);
Harald Welte1a36f332018-06-04 17:36:33 +0200297 free(value); /* no talloc, this is from sscanf() */
298#endif
299 }
300 return 0;
301}
302
303/* called periodically */
304int osysmon_ctrl_poll(struct value_node *parent)
305{
306 struct ctrl_client *cc;
307 llist_for_each_entry(cc, &g_oss->ctrl_clients, list)
308 ctrl_client_poll(cc, parent);
309 return 0;
310}