blob: a4e2444213677eca306401a4b03034164c1039ee [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.
Harald Welte1a36f332018-06-04 17:36:33 +020017 */
18
19#include <string.h>
20
21#include <osmocom/vty/vty.h>
22#include <osmocom/vty/command.h>
23
Max5d42b8e2019-02-07 17:28:01 +010024#include "client.h"
Harald Welte1a36f332018-06-04 17:36:33 +020025#include "osysmon.h"
26#include "simple_ctrl.h"
27#include "value_node.h"
28
29/***********************************************************************
30 * Data Model
31 ***********************************************************************/
32
33/* a single CTRL client */
34struct ctrl_client {
35 /* links to osysmon.ctrl_clients */
36 struct llist_head list;
Max5d42b8e2019-02-07 17:28:01 +010037 struct host_cfg *cfg;
Harald Welte1a36f332018-06-04 17:36:33 +020038 struct simple_ctrl_handle *sch;
39 /* list of ctrl_client_get_var objects */
40 struct llist_head get_vars;
41};
42
43/* a variable we are GETing via a ctrl_client */
44struct ctrl_client_get_var {
45 /* links to ctrl_client.get_vars */
46 struct llist_head list;
47 /* back-link to ctrl_client */
48 struct ctrl_client *cc;
49 struct {
50 /* CTRL variable name */
51 const char *name;
52 /* display name, if any */
53 const char *display_name;
54 } cfg;
Harald Welte1a36f332018-06-04 17:36:33 +020055};
56
57static struct ctrl_client *ctrl_client_find(struct osysmon_state *os, const char *name)
58{
59 struct ctrl_client *cc;
60 llist_for_each_entry(cc, &os->ctrl_clients, list) {
Max5d42b8e2019-02-07 17:28:01 +010061 if (match_config(cc->cfg, name, MATCH_NAME))
Harald Welte1a36f332018-06-04 17:36:33 +020062 return cc;
63 }
64 return NULL;
65}
66
67static struct ctrl_client *ctrl_client_create(struct osysmon_state *os, const char *name,
68 const char *host, uint16_t port)
69{
70 struct ctrl_client *cc;
71
72 if (ctrl_client_find(os, name))
73 return NULL;
74
75 cc = talloc_zero(os, struct ctrl_client);
76 if (!cc)
77 return NULL;
Max5d42b8e2019-02-07 17:28:01 +010078
79 cc->cfg = host_cfg_alloc(cc, name, host, port);
80 if (!cc->cfg) {
81 talloc_free(cc);
82 return NULL;
83 }
84
Harald Welte1a36f332018-06-04 17:36:33 +020085 INIT_LLIST_HEAD(&cc->get_vars);
86 llist_add_tail(&cc->list, &os->ctrl_clients);
87 /* FIXME */
88 return cc;
89}
90
91static void ctrl_client_destroy(struct ctrl_client *cc)
92{
93 /* FIXME */
94 llist_del(&cc->list);
95 talloc_free(cc);
96}
97
98static struct ctrl_client_get_var *
99ctrl_client_get_var_find_or_create(struct ctrl_client *cc, const char *name)
100{
101 struct ctrl_client_get_var *gv;
102 llist_for_each_entry(gv, &cc->get_vars, list) {
103 if (!strcmp(name, gv->cfg.name))
104 return gv;
105 }
106 gv = talloc_zero(cc, struct ctrl_client_get_var);
107 if (!gv)
108 return NULL;
109 gv->cc = cc;
110 gv->cfg.name = talloc_strdup(gv, name);
111 llist_add_tail(&gv->list, &cc->get_vars);
112 return gv;
113}
114
115/***********************************************************************
116 * VTY
117 ***********************************************************************/
118
119static struct cmd_node ctrl_client_node = {
120 CTRL_CLIENT_NODE,
121 "%s(config-ctrlclient)# ",
122 1,
123};
124
125static struct cmd_node ctrl_client_getvar_node = {
126 CTRL_CLIENT_GETVAR_NODE,
127 "%s(config-ctrlclient-getvar)# ",
128 1,
129};
130
131int osysmon_ctrl_go_parent(struct vty *vty)
132{
133 switch (vty->node) {
134 case CTRL_CLIENT_NODE:
135 vty->node = CONFIG_NODE;
136 vty->index = NULL;
137 break;
138 case CTRL_CLIENT_GETVAR_NODE:
139 vty->node = CTRL_CLIENT_NODE;
140 {
141 struct ctrl_client_get_var *gv = vty->index;
142 vty->index = gv->cc;
143 }
144 break;
145 default:
146 break;
147 }
148 return vty->node;
149}
150
151
152DEFUN(cfg_ctrl_client, cfg_ctrl_client_cmd,
153 "ctrl-client NAME A.B.C.D <1-65535>",
154 "")
155{
156 struct ctrl_client *cc;
157 cc = ctrl_client_find(g_oss, argv[0]);
158 if (cc) {
Max5d42b8e2019-02-07 17:28:01 +0100159 if ((strcmp(cc->cfg->remote_host, argv[1])) ||
160 (cc->cfg->remote_port != atoi(argv[2]))) {
Harald Welte1a36f332018-06-04 17:36:33 +0200161 vty_out(vty, "Client %s has different IP/port, please remove it first%s",
Max5d42b8e2019-02-07 17:28:01 +0100162 cc->cfg->name, VTY_NEWLINE);
Harald Welte1a36f332018-06-04 17:36:33 +0200163 return CMD_WARNING;
164 }
165 } else
166 cc = ctrl_client_create(g_oss, argv[0], argv[1], atoi(argv[2]));
167 OSMO_ASSERT(cc);
168
169 vty->node = CTRL_CLIENT_NODE;
170 vty->index = cc;
171 return CMD_SUCCESS;
172}
173
174DEFUN(cfg_no_ctrl_client, cfg_no_ctrl_client_cmd,
175 "no ctrl-client NAME",
176 NO_STR "")
177{
178 struct ctrl_client *cc;
179 cc = ctrl_client_find(g_oss, argv[0]);
180 if (!cc) {
181 vty_out(vty, "Client %s doesn't exist%s", argv[0], VTY_NEWLINE);
182 return CMD_WARNING;
183 }
184 ctrl_client_destroy(cc);
185 return CMD_SUCCESS;
186}
187
188DEFUN(cfg_ctrlc_get_var, cfg_ctrlc_get_var_cmd,
189 "get-variable NAME",
190 "")
191{
192 struct ctrl_client *cc = vty->index;
193 struct ctrl_client_get_var *ccgv;
194
195 ccgv = ctrl_client_get_var_find_or_create(cc, argv[0]);
196 OSMO_ASSERT(ccgv);
197
198 vty->node = CTRL_CLIENT_GETVAR_NODE;
199 vty->index = ccgv;
200 return CMD_SUCCESS;
201}
202
203DEFUN(cfg_ctrlc_no_get_var, cfg_ctrlc_no_get_var_cmd,
204 "no get-variable NAME",
205 NO_STR "")
206{
207 struct ctrl_client *cc = vty->index;
208 struct ctrl_client_get_var *ccgv;
209
210 ccgv = ctrl_client_get_var_find_or_create(cc, argv[0]);
211 talloc_free(ccgv);
212 return CMD_SUCCESS;
213}
214
215static void write_one_ctrl_client(struct vty *vty, struct ctrl_client *cc)
216{
217 struct ctrl_client_get_var *ccgv;
Max5d42b8e2019-02-07 17:28:01 +0100218 vty_out(vty, "ctrl-client %s %s %u%s", cc->cfg->name,
219 cc->cfg->remote_host, cc->cfg->remote_port, VTY_NEWLINE);
Harald Welte1a36f332018-06-04 17:36:33 +0200220 llist_for_each_entry(ccgv, &cc->get_vars, list) {
221 vty_out(vty, " get-variable %s%s", ccgv->cfg.name, VTY_NEWLINE);
222 if (ccgv->cfg.display_name)
223 vty_out(vty, " display-name %s%s", ccgv->cfg.display_name, VTY_NEWLINE);
224 }
225}
226
227static int config_write_ctrl_client(struct vty *vty)
228{
229 struct ctrl_client *cc;
230
231 llist_for_each_entry(cc, &g_oss->ctrl_clients, list)
232 write_one_ctrl_client(vty, cc);
233 return CMD_SUCCESS;
234}
235
236static void osysmon_ctrl_vty_init(void)
237{
238 install_element(CONFIG_NODE, &cfg_ctrl_client_cmd);
239 install_element(CONFIG_NODE, &cfg_no_ctrl_client_cmd);
240 install_node(&ctrl_client_node, config_write_ctrl_client);
241 install_element(CTRL_CLIENT_NODE, &cfg_ctrlc_get_var_cmd);
242 install_element(CTRL_CLIENT_NODE, &cfg_ctrlc_no_get_var_cmd);
243 install_node(&ctrl_client_getvar_node, NULL);
244 //install_element(CTRL_CLIENT_GETVAR_NODE, &cfg_getvar_disp_name_cmd);
245}
246
247
248/***********************************************************************
249 * Runtime Code
250 ***********************************************************************/
251
252/* called once on startup before config file parsing */
253int osysmon_ctrl_init()
254{
255 osysmon_ctrl_vty_init();
256 return 0;
257}
258
259static int ctrl_client_poll(struct ctrl_client *cc, struct value_node *parent)
260{
261 struct ctrl_client_get_var *ccgv;
Max5d42b8e2019-02-07 17:28:01 +0100262 struct value_node *vn_clnt = value_node_add(parent, cc->cfg->name, NULL);
Harald Welte1a36f332018-06-04 17:36:33 +0200263
264 /* attempt to re-connect */
265 if (!cc->sch)
Max5d42b8e2019-02-07 17:28:01 +0100266 cc->sch = simple_ctrl_open(cc, cc->cfg->remote_host, cc->cfg->remote_port, 1000);
Harald Welte1a36f332018-06-04 17:36:33 +0200267 /* abort, if that failed */
268 if (!cc->sch) {
Harald Welte1a36f332018-06-04 17:36:33 +0200269 return -1;
270 }
271
272 llist_for_each_entry(ccgv, &cc->get_vars, list) {
273 char *value = simple_ctrl_get(cc->sch, ccgv->cfg.name);
Daniel Willmann046ccf42018-06-05 17:15:42 +0200274
Daniel Willmann1c2ef1a2018-06-05 17:27:17 +0200275 /* FIXME: Distinguish between ERROR reply and
276 * connection issues */
277 /* Close connection on error */
278 if (!value) {
279 simple_ctrl_close(cc->sch);
280 cc->sch = NULL;
281 return 0;
282 }
283
Maxf41973e2019-01-25 18:40:11 +0100284 value_node_add(vn_clnt, ccgv->cfg.name, value);
Harald Welte1a36f332018-06-04 17:36:33 +0200285 free(value); /* no talloc, this is from sscanf() */
Harald Welte1a36f332018-06-04 17:36:33 +0200286 }
287 return 0;
288}
289
290/* called periodically */
291int osysmon_ctrl_poll(struct value_node *parent)
292{
293 struct ctrl_client *cc;
294 llist_for_each_entry(cc, &g_oss->ctrl_clients, list)
295 ctrl_client_poll(cc, parent);
296 return 0;
297}