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