blob: cdec8efbc67c891200f2477996f688b658709c50 [file] [log] [blame]
Daniel Willmann1264cb42010-10-21 15:00:36 +02001/* SNMP-like status interface
2 *
3 * (C) 2010-2011 by Daniel Willmann <daniel@totalueberwachung.de>
4 * (C) 2010-2011 by On-Waves
5 *
6 * All Rights Reserved
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 along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24#include <ctype.h>
25#include <errno.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <time.h>
30#include <unistd.h>
31
Harald Welte1238cc62014-08-20 19:50:04 +020032#include <osmocom/ctrl/control_cmd.h>
Daniel Willmann1264cb42010-10-21 15:00:36 +020033
34#include <osmocom/core/msgb.h>
35#include <osmocom/core/talloc.h>
Max70c7d412017-02-24 13:59:14 +010036#include <osmocom/core/utils.h>
Daniel Willmann1264cb42010-10-21 15:00:36 +020037#include <osmocom/vty/command.h>
38#include <osmocom/vty/vector.h>
39
40extern vector ctrl_node_vec;
41
Max70c7d412017-02-24 13:59:14 +010042const struct value_string ctrl_type_vals[] = {
43 { CTRL_TYPE_GET, "GET" },
44 { CTRL_TYPE_SET, "SET" },
45 { CTRL_TYPE_GET_REPLY, "GET_REPLY" },
46 { CTRL_TYPE_SET_REPLY, "SET_REPLY" },
47 { CTRL_TYPE_TRAP, "TRAP" },
48 { CTRL_TYPE_ERROR, "ERROR" },
49 { CTRL_TYPE_UNKNOWN, NULL }
Daniel Willmann1264cb42010-10-21 15:00:36 +020050};
51
Daniel Willmann1264cb42010-10-21 15:00:36 +020052/* Functions from libosmocom */
53extern vector cmd_make_descvec(const char *string, const char *descstr);
54
55/* Get the ctrl_cmd_element that matches this command */
56static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node)
57{
58 int index, j;
59 const char *desc;
60 struct ctrl_cmd_element *cmd_el;
61 struct ctrl_cmd_struct *cmd_desc;
62 char *str;
63
64 for (index = 0; index < vector_active(node); index++) {
65 if ((cmd_el = vector_slot(node, index))) {
66 cmd_desc = &cmd_el->strcmd;
67 if (cmd_desc->nr_commands > vector_active(vline))
68 continue;
Holger Hans Peter Freyther5fb265e2015-04-05 14:36:31 +020069 for (j =0; j < vector_active(vline) && j < cmd_desc->nr_commands; j++) {
Daniel Willmann1264cb42010-10-21 15:00:36 +020070 str = vector_slot(vline, j);
71 desc = cmd_desc->command[j];
72 if (desc[0] == '*')
73 return cmd_el; /* Partial match */
74 if (strcmp(desc, str) != 0)
75 break;
76 }
77 /* We went through all the elements and all matched */
78 if (j == cmd_desc->nr_commands)
79 return cmd_el;
80 }
81 }
82
83 return NULL;
84}
85
86int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data)
87{
88 int ret = CTRL_CMD_ERROR;
89 struct ctrl_cmd_element *cmd_el;
90
91 if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) {
92 command->reply = "Trying to execute something not GET or SET";
93 goto out;
94 }
95 if ((command->type == CTRL_TYPE_SET) && (!command->value)) {
96 command->reply = "SET without a value";
97 goto out;
98 }
99
100 if (!vline)
101 goto out;
102
103 cmd_el = ctrl_cmd_get_element_match(vline, node);
104
105 if (!cmd_el) {
106 command->reply = "Command not found";
107 goto out;
108 }
109
110 if (command->type == CTRL_TYPE_SET) {
111 if (!cmd_el->set) {
112 command->reply = "SET not implemented";
113 goto out;
114 }
115 if (cmd_el->verify) {
116 if ((ret = cmd_el->verify(command, command->value, data))) {
117 ret = CTRL_CMD_ERROR;
Daniel Willmann9225ab12011-07-28 21:38:51 +0200118 /* If verify() set an appropriate error message, don't change it. */
119 if (!command->reply)
120 command->reply = "Value failed verification.";
Daniel Willmann1264cb42010-10-21 15:00:36 +0200121 goto out;
122 }
Daniel Willmann1264cb42010-10-21 15:00:36 +0200123 }
124 ret = cmd_el->set(command, data);
125 goto out;
126 } else if (command->type == CTRL_TYPE_GET) {
127 if (!cmd_el->get) {
128 command->reply = "GET not implemented";
129 goto out;
130 }
131 ret = cmd_el->get(command, data);
132 goto out;
133 }
134out:
135 if (ret == CTRL_CMD_REPLY) {
136 if (command->type == CTRL_TYPE_SET) {
137 command->type = CTRL_TYPE_SET_REPLY;
138 } else if (command->type == CTRL_TYPE_GET) {
139 command->type = CTRL_TYPE_GET_REPLY;
140 }
141 } else if (ret == CTRL_CMD_ERROR) {
142 command->type = CTRL_TYPE_ERROR;
143 }
144 return ret;
145}
146
147static void add_word(struct ctrl_cmd_struct *cmd,
148 const char *start, const char *end)
149{
150 if (!cmd->command) {
151 cmd->command = talloc_zero_array(tall_vty_vec_ctx,
152 char*, 1);
153 cmd->nr_commands = 0;
154 } else {
155 cmd->command = talloc_realloc(tall_vty_vec_ctx,
156 cmd->command, char*,
157 cmd->nr_commands + 1);
158 }
159
160 cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command,
161 start, end - start);
162}
163
164static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name)
165{
166 const char *cur, *word;
167
168 for (cur = name, word = NULL; cur[0] != '\0'; ++cur) {
169 /* warn about optionals */
170 if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') {
Harald Welte7fd0c832014-08-20 19:58:13 +0200171 LOGP(DLCTRL, LOGL_ERROR,
Daniel Willmann1264cb42010-10-21 15:00:36 +0200172 "Optionals are not supported in '%s'\n", name);
173 goto failure;
174 }
175
176 if (isspace(cur[0])) {
177 if (word) {
178 add_word(cmd, word, cur);
179 word = NULL;
180 }
181 continue;
182 }
183
184 if (!word)
185 word = cur;
186 }
187
188 if (word)
189 add_word(cmd, word, cur);
190
191 return;
192failure:
193 cmd->nr_commands = 0;
194 talloc_free(cmd->command);
195}
196
197int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd)
198{
199 vector cmds_vec;
200
201 cmds_vec = vector_lookup_ensure(ctrl_node_vec, node);
202
203 if (!cmds_vec) {
204 cmds_vec = vector_init(5);
205 if (!cmds_vec) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200206 LOGP(DLCTRL, LOGL_ERROR, "vector_init failed.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200207 return -ENOMEM;
208 }
209 vector_set_index(ctrl_node_vec, node, cmds_vec);
210 }
211
212 vector_set(cmds_vec, cmd);
213
214 create_cmd_struct(&cmd->strcmd, cmd->name);
215 return 0;
216}
217
Holger Hans Peter Freythera6b34012011-08-22 23:44:32 +0200218struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type)
219{
220 struct ctrl_cmd *cmd;
221
222 cmd = talloc_zero(ctx, struct ctrl_cmd);
223 if (!cmd)
224 return NULL;
225
226 cmd->type = type;
227 return cmd;
228}
229
Daniel Willmann8b7a9622011-03-17 15:37:54 +0100230struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd)
231{
232 struct ctrl_cmd *cmd2;
233
234 cmd2 = talloc_zero(ctx, struct ctrl_cmd);
235 if (!cmd2)
236 return NULL;
237
238 cmd2->type = cmd->type;
239 if (cmd->id) {
240 cmd2->id = talloc_strdup(cmd2, cmd->id);
241 if (!cmd2->id)
242 goto err;
243 }
244 if (cmd->variable) {
245 cmd2->variable = talloc_strdup(cmd2, cmd->variable);
246 if (!cmd2->variable)
247 goto err;
248 }
249 if (cmd->value) {
250 cmd2->value = talloc_strdup(cmd2, cmd->value);
251 if (!cmd2->value)
252 goto err;
253 }
254 if (cmd->reply) {
255 cmd2->reply = talloc_strdup(cmd2, cmd->reply);
256 if (!cmd2->reply)
257 goto err;
258 }
259
260 return cmd2;
261err:
262 talloc_free(cmd2);
263 return NULL;
264}
265
Daniel Willmann1264cb42010-10-21 15:00:36 +0200266struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
267{
268 char *str, *tmp, *saveptr = NULL;
269 char *var, *val;
270 struct ctrl_cmd *cmd;
271
272 cmd = talloc_zero(ctx, struct ctrl_cmd);
273 if (!cmd) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200274 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200275 return NULL;
276 }
277
278 /* Make sure input is NULL terminated */
279 msgb_put_u8(msg, 0);
280 str = (char *) msg->l2h;
281
Harald Welteedf6fe72016-11-26 10:06:07 +0100282 OSMO_ASSERT(str);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200283 tmp = strtok_r(str, " ", &saveptr);
284 if (!tmp) {
285 cmd->type = CTRL_TYPE_ERROR;
286 cmd->id = "err";
287 cmd->reply = "Request malformed";
288 goto err;
289 }
290
Max70c7d412017-02-24 13:59:14 +0100291 cmd->type = get_string_value(ctrl_type_vals, tmp);
Neels Hofmeyrcb5c4ed2017-03-02 14:32:52 +0100292 if (cmd->type < 0 || cmd->type == CTRL_TYPE_UNKNOWN) {
Daniel Willmann1264cb42010-10-21 15:00:36 +0200293 cmd->type = CTRL_TYPE_ERROR;
294 cmd->id = "err";
295 cmd->reply = "Request type unknown";
296 goto err;
297 }
298
299 tmp = strtok_r(NULL, " ", &saveptr);
300
301 if (!tmp) {
302 cmd->type = CTRL_TYPE_ERROR;
303 cmd->id = "err";
304 cmd->reply = "Missing ID";
305 goto err;
306 }
307 cmd->id = talloc_strdup(cmd, tmp);
308 if (!cmd->id)
309 goto oom;
310
311 switch (cmd->type) {
312 case CTRL_TYPE_GET:
313 var = strtok_r(NULL, " ", &saveptr);
314 if (!var) {
315 cmd->type = CTRL_TYPE_ERROR;
316 cmd->reply = "GET incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200317 LOGP(DLCTRL, LOGL_NOTICE, "GET Command incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200318 goto err;
319 }
320 cmd->variable = talloc_strdup(cmd, var);
Harald Welte7fd0c832014-08-20 19:58:13 +0200321 LOGP(DLCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200322 break;
323 case CTRL_TYPE_SET:
324 var = strtok_r(NULL, " ", &saveptr);
Holger Hans Peter Freyther5ad742d2014-08-08 19:41:28 +0200325 val = strtok_r(NULL, "\n", &saveptr);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200326 if (!var || !val) {
327 cmd->type = CTRL_TYPE_ERROR;
328 cmd->reply = "SET incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200329 LOGP(DLCTRL, LOGL_NOTICE, "SET Command incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200330 goto err;
331 }
332 cmd->variable = talloc_strdup(cmd, var);
333 cmd->value = talloc_strdup(cmd, val);
334 if (!cmd->variable || !cmd->value)
335 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200336 LOGP(DLCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200337 break;
338 case CTRL_TYPE_GET_REPLY:
339 case CTRL_TYPE_SET_REPLY:
340 case CTRL_TYPE_TRAP:
341 var = strtok_r(NULL, " ", &saveptr);
342 val = strtok_r(NULL, " ", &saveptr);
343 if (!var || !val) {
344 cmd->type = CTRL_TYPE_ERROR;
345 cmd->reply = "Trap/Reply incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200346 LOGP(DLCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200347 goto err;
348 }
349 cmd->variable = talloc_strdup(cmd, var);
350 cmd->reply = talloc_strdup(cmd, val);
351 if (!cmd->variable || !cmd->reply)
352 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200353 LOGP(DLCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200354 break;
355 case CTRL_TYPE_ERROR:
356 var = strtok_r(NULL, "\0", &saveptr);
357 if (!var) {
358 cmd->reply = "";
359 goto err;
360 }
361 cmd->reply = talloc_strdup(cmd, var);
362 if (!cmd->reply)
363 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200364 LOGP(DLCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200365 break;
366 case CTRL_TYPE_UNKNOWN:
367 default:
368 cmd->type = CTRL_TYPE_ERROR;
369 cmd->reply = "Unknown type";
370 goto err;
371 }
372
373 return cmd;
374oom:
375 cmd->type = CTRL_TYPE_ERROR;
376 cmd->id = "err";
377 cmd->reply = "OOM";
378err:
379 talloc_free(cmd);
380 return NULL;
381}
382
383struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
384{
385 struct msgb *msg;
Max70c7d412017-02-24 13:59:14 +0100386 const char *type;
387 char *tmp;
Daniel Willmann1264cb42010-10-21 15:00:36 +0200388
389 if (!cmd->id)
390 return NULL;
391
392 msg = msgb_alloc_headroom(4096, 128, "ctrl command make");
393 if (!msg)
394 return NULL;
395
Max70c7d412017-02-24 13:59:14 +0100396 type = get_value_string(ctrl_type_vals, cmd->type);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200397
398 switch (cmd->type) {
399 case CTRL_TYPE_GET:
400 if (!cmd->variable)
401 goto err;
402
403 tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
404 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200405 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200406 goto err;
407 }
408
409 msg->l2h = msgb_put(msg, strlen(tmp));
410 memcpy(msg->l2h, tmp, strlen(tmp));
411 talloc_free(tmp);
412 break;
413 case CTRL_TYPE_SET:
414 if (!cmd->variable || !cmd->value)
415 goto err;
416
417 tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
418 cmd->value);
419 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200420 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200421 goto err;
422 }
423
424 msg->l2h = msgb_put(msg, strlen(tmp));
425 memcpy(msg->l2h, tmp, strlen(tmp));
426 talloc_free(tmp);
427 break;
428 case CTRL_TYPE_GET_REPLY:
429 case CTRL_TYPE_SET_REPLY:
430 case CTRL_TYPE_TRAP:
431 if (!cmd->variable || !cmd->reply)
432 goto err;
433
434 tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
435 cmd->reply);
436 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200437 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200438 goto err;
439 }
440
441 msg->l2h = msgb_put(msg, strlen(tmp));
442 memcpy(msg->l2h, tmp, strlen(tmp));
443 talloc_free(tmp);
444 break;
445 case CTRL_TYPE_ERROR:
446 if (!cmd->reply)
447 goto err;
448
449 tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id,
450 cmd->reply);
451 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200452 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200453 goto err;
454 }
455
456 msg->l2h = msgb_put(msg, strlen(tmp));
457 memcpy(msg->l2h, tmp, strlen(tmp));
458 talloc_free(tmp);
459 break;
460 default:
Harald Welte7fd0c832014-08-20 19:58:13 +0200461 LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200462 goto err;
463 break;
464 }
465
466 return msg;
467
468err:
469 msgb_free(msg);
470 return NULL;
471}
Harald Welte39c9e7b2014-08-22 00:28:51 +0200472
473struct ctrl_cmd_def *
474ctrl_cmd_def_make(const void *ctx, struct ctrl_cmd *cmd, void *data, unsigned int secs)
475{
476 struct ctrl_cmd_def *cd;
477
478 if (!cmd->ccon)
479 return NULL;
480
481 cd = talloc_zero(ctx, struct ctrl_cmd_def);
482
483 cd->cmd = cmd;
484 cd->data = data;
485
486 /* add to per-connection list of deferred commands */
487 llist_add(&cd->list, &cmd->ccon->def_cmds);
488
489 return cd;
490}
491
492int ctrl_cmd_def_is_zombie(struct ctrl_cmd_def *cd)
493{
494 /* luckily we're still alive */
495 if (cd->cmd)
496 return 0;
497
498 /* if we are a zombie, make sure we really die */
499 llist_del(&cd->list);
500 talloc_free(cd);
501
502 return 1;
503}
504
505int ctrl_cmd_def_send(struct ctrl_cmd_def *cd)
506{
507 struct ctrl_cmd *cmd = cd->cmd;
508
509 int rc;
510
511 /* Deferred commands can only be responses to GET/SET or ERROR, but
512 * never TRAP or anything else */
513 switch (cmd->type) {
514 case CTRL_TYPE_GET:
515 cmd->type = CTRL_TYPE_GET_REPLY;
516 break;
517 case CTRL_TYPE_SET:
518 cmd->type = CTRL_TYPE_SET_REPLY;
519 break;
520 default:
521 cmd->type = CTRL_TYPE_ERROR;
522 }
523
524 rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd);
525
526 talloc_free(cmd);
527 llist_del(&cd->list);
528 talloc_free(cd);
529
530 return rc;
531}