blob: 25c2b4c88e1fb9cf2f034283896703002c0e73fe [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;
Harald Welte1a36f332018-06-04 17:36:33 +020066};
67
68static struct ctrl_client *ctrl_client_find(struct osysmon_state *os, const char *name)
69{
70 struct ctrl_client *cc;
71 llist_for_each_entry(cc, &os->ctrl_clients, list) {
72 if (!strcmp(name, cc->cfg.name))
73 return cc;
74 }
75 return NULL;
76}
77
78static struct ctrl_client *ctrl_client_create(struct osysmon_state *os, const char *name,
79 const char *host, uint16_t port)
80{
81 struct ctrl_client *cc;
82
83 if (ctrl_client_find(os, name))
84 return NULL;
85
86 cc = talloc_zero(os, struct ctrl_client);
87 if (!cc)
88 return NULL;
89 cc->cfg.name = talloc_strdup(cc, name);
90 cc->cfg.remote_host = talloc_strdup(cc, host);
91 cc->cfg.remote_port = port;
92 INIT_LLIST_HEAD(&cc->get_vars);
93 llist_add_tail(&cc->list, &os->ctrl_clients);
94 /* FIXME */
95 return cc;
96}
97
98static void ctrl_client_destroy(struct ctrl_client *cc)
99{
100 /* FIXME */
101 llist_del(&cc->list);
102 talloc_free(cc);
103}
104
105static struct ctrl_client_get_var *
106ctrl_client_get_var_find_or_create(struct ctrl_client *cc, const char *name)
107{
108 struct ctrl_client_get_var *gv;
109 llist_for_each_entry(gv, &cc->get_vars, list) {
110 if (!strcmp(name, gv->cfg.name))
111 return gv;
112 }
113 gv = talloc_zero(cc, struct ctrl_client_get_var);
114 if (!gv)
115 return NULL;
116 gv->cc = cc;
117 gv->cfg.name = talloc_strdup(gv, name);
118 llist_add_tail(&gv->list, &cc->get_vars);
119 return gv;
120}
121
122/***********************************************************************
123 * VTY
124 ***********************************************************************/
125
126static struct cmd_node ctrl_client_node = {
127 CTRL_CLIENT_NODE,
128 "%s(config-ctrlclient)# ",
129 1,
130};
131
132static struct cmd_node ctrl_client_getvar_node = {
133 CTRL_CLIENT_GETVAR_NODE,
134 "%s(config-ctrlclient-getvar)# ",
135 1,
136};
137
138int osysmon_ctrl_go_parent(struct vty *vty)
139{
140 switch (vty->node) {
141 case CTRL_CLIENT_NODE:
142 vty->node = CONFIG_NODE;
143 vty->index = NULL;
144 break;
145 case CTRL_CLIENT_GETVAR_NODE:
146 vty->node = CTRL_CLIENT_NODE;
147 {
148 struct ctrl_client_get_var *gv = vty->index;
149 vty->index = gv->cc;
150 }
151 break;
152 default:
153 break;
154 }
155 return vty->node;
156}
157
158
159DEFUN(cfg_ctrl_client, cfg_ctrl_client_cmd,
160 "ctrl-client NAME A.B.C.D <1-65535>",
161 "")
162{
163 struct ctrl_client *cc;
164 cc = ctrl_client_find(g_oss, argv[0]);
165 if (cc) {
166 if ((strcmp(cc->cfg.remote_host, argv[1])) ||
167 (cc->cfg.remote_port != atoi(argv[2]))) {
168 vty_out(vty, "Client %s has different IP/port, please remove it first%s",
169 cc->cfg.name, VTY_NEWLINE);
170 return CMD_WARNING;
171 }
172 } else
173 cc = ctrl_client_create(g_oss, argv[0], argv[1], atoi(argv[2]));
174 OSMO_ASSERT(cc);
175
176 vty->node = CTRL_CLIENT_NODE;
177 vty->index = cc;
178 return CMD_SUCCESS;
179}
180
181DEFUN(cfg_no_ctrl_client, cfg_no_ctrl_client_cmd,
182 "no ctrl-client NAME",
183 NO_STR "")
184{
185 struct ctrl_client *cc;
186 cc = ctrl_client_find(g_oss, argv[0]);
187 if (!cc) {
188 vty_out(vty, "Client %s doesn't exist%s", argv[0], VTY_NEWLINE);
189 return CMD_WARNING;
190 }
191 ctrl_client_destroy(cc);
192 return CMD_SUCCESS;
193}
194
195DEFUN(cfg_ctrlc_get_var, cfg_ctrlc_get_var_cmd,
196 "get-variable NAME",
197 "")
198{
199 struct ctrl_client *cc = vty->index;
200 struct ctrl_client_get_var *ccgv;
201
202 ccgv = ctrl_client_get_var_find_or_create(cc, argv[0]);
203 OSMO_ASSERT(ccgv);
204
205 vty->node = CTRL_CLIENT_GETVAR_NODE;
206 vty->index = ccgv;
207 return CMD_SUCCESS;
208}
209
210DEFUN(cfg_ctrlc_no_get_var, cfg_ctrlc_no_get_var_cmd,
211 "no get-variable NAME",
212 NO_STR "")
213{
214 struct ctrl_client *cc = vty->index;
215 struct ctrl_client_get_var *ccgv;
216
217 ccgv = ctrl_client_get_var_find_or_create(cc, argv[0]);
218 talloc_free(ccgv);
219 return CMD_SUCCESS;
220}
221
222static void write_one_ctrl_client(struct vty *vty, struct ctrl_client *cc)
223{
224 struct ctrl_client_get_var *ccgv;
225 vty_out(vty, "ctrl-client %s %s %u%s", cc->cfg.name,
226 cc->cfg.remote_host, cc->cfg.remote_port, VTY_NEWLINE);
227 llist_for_each_entry(ccgv, &cc->get_vars, list) {
228 vty_out(vty, " get-variable %s%s", ccgv->cfg.name, VTY_NEWLINE);
229 if (ccgv->cfg.display_name)
230 vty_out(vty, " display-name %s%s", ccgv->cfg.display_name, VTY_NEWLINE);
231 }
232}
233
234static int config_write_ctrl_client(struct vty *vty)
235{
236 struct ctrl_client *cc;
237
238 llist_for_each_entry(cc, &g_oss->ctrl_clients, list)
239 write_one_ctrl_client(vty, cc);
240 return CMD_SUCCESS;
241}
242
243static void osysmon_ctrl_vty_init(void)
244{
245 install_element(CONFIG_NODE, &cfg_ctrl_client_cmd);
246 install_element(CONFIG_NODE, &cfg_no_ctrl_client_cmd);
247 install_node(&ctrl_client_node, config_write_ctrl_client);
248 install_element(CTRL_CLIENT_NODE, &cfg_ctrlc_get_var_cmd);
249 install_element(CTRL_CLIENT_NODE, &cfg_ctrlc_no_get_var_cmd);
250 install_node(&ctrl_client_getvar_node, NULL);
251 //install_element(CTRL_CLIENT_GETVAR_NODE, &cfg_getvar_disp_name_cmd);
252}
253
254
255/***********************************************************************
256 * Runtime Code
257 ***********************************************************************/
258
259/* called once on startup before config file parsing */
260int osysmon_ctrl_init()
261{
262 osysmon_ctrl_vty_init();
263 return 0;
264}
265
266static int ctrl_client_poll(struct ctrl_client *cc, struct value_node *parent)
267{
268 struct ctrl_client_get_var *ccgv;
269 struct value_node *vn_clnt = value_node_add(parent, parent, cc->cfg.name, NULL);
270
271 /* attempt to re-connect */
272 if (!cc->sch)
273 cc->sch = simple_ctrl_open(cc, cc->cfg.remote_host, cc->cfg.remote_port, 1000);
274 /* abort, if that failed */
275 if (!cc->sch) {
Harald Welte1a36f332018-06-04 17:36:33 +0200276 return -1;
277 }
278
279 llist_for_each_entry(ccgv, &cc->get_vars, list) {
280 char *value = simple_ctrl_get(cc->sch, ccgv->cfg.name);
Daniel Willmann046ccf42018-06-05 17:15:42 +0200281
Daniel Willmann1c2ef1a2018-06-05 17:27:17 +0200282 /* FIXME: Distinguish between ERROR reply and
283 * connection issues */
284 /* Close connection on error */
285 if (!value) {
286 simple_ctrl_close(cc->sch);
287 cc->sch = NULL;
288 return 0;
289 }
290
Harald Welteb3ff6412018-06-04 18:21:46 +0200291 value_node_add(vn_clnt, vn_clnt, ccgv->cfg.name, value);
Harald Welte1a36f332018-06-04 17:36:33 +0200292 free(value); /* no talloc, this is from sscanf() */
Harald Welte1a36f332018-06-04 17:36:33 +0200293 }
294 return 0;
295}
296
297/* called periodically */
298int osysmon_ctrl_poll(struct value_node *parent)
299{
300 struct ctrl_client *cc;
301 llist_for_each_entry(cc, &g_oss->ctrl_clients, list)
302 ctrl_client_poll(cc, parent);
303 return 0;
304}