blob: 24b388b307ffc94fbb66af6e1e8908be0052ddcf [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file control_cmd.c
2 * SNMP-like status interface. */
3/*
Daniel Willmann1264cb42010-10-21 15:00:36 +02004 * (C) 2010-2011 by Daniel Willmann <daniel@totalueberwachung.de>
5 * (C) 2010-2011 by On-Waves
6 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
25#include <ctype.h>
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <time.h>
31#include <unistd.h>
32
Harald Welte1238cc62014-08-20 19:50:04 +020033#include <osmocom/ctrl/control_cmd.h>
Daniel Willmann1264cb42010-10-21 15:00:36 +020034
35#include <osmocom/core/msgb.h>
36#include <osmocom/core/talloc.h>
Max70c7d412017-02-24 13:59:14 +010037#include <osmocom/core/utils.h>
Daniel Willmann1264cb42010-10-21 15:00:36 +020038#include <osmocom/vty/command.h>
39#include <osmocom/vty/vector.h>
40
41extern vector ctrl_node_vec;
42
Max70c7d412017-02-24 13:59:14 +010043const struct value_string ctrl_type_vals[] = {
Neels Hofmeyr7c1ec8c2017-03-02 14:32:52 +010044 { CTRL_TYPE_UNKNOWN, "(unknown)" },
Max70c7d412017-02-24 13:59:14 +010045 { CTRL_TYPE_GET, "GET" },
46 { CTRL_TYPE_SET, "SET" },
47 { CTRL_TYPE_GET_REPLY, "GET_REPLY" },
48 { CTRL_TYPE_SET_REPLY, "SET_REPLY" },
49 { CTRL_TYPE_TRAP, "TRAP" },
50 { CTRL_TYPE_ERROR, "ERROR" },
Neels Hofmeyr7c1ec8c2017-03-02 14:32:52 +010051 { 0, NULL }
Daniel Willmann1264cb42010-10-21 15:00:36 +020052};
53
Daniel Willmann1264cb42010-10-21 15:00:36 +020054/* Functions from libosmocom */
55extern vector cmd_make_descvec(const char *string, const char *descstr);
56
57/* Get the ctrl_cmd_element that matches this command */
58static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node)
59{
60 int index, j;
61 const char *desc;
62 struct ctrl_cmd_element *cmd_el;
63 struct ctrl_cmd_struct *cmd_desc;
64 char *str;
65
66 for (index = 0; index < vector_active(node); index++) {
67 if ((cmd_el = vector_slot(node, index))) {
68 cmd_desc = &cmd_el->strcmd;
69 if (cmd_desc->nr_commands > vector_active(vline))
70 continue;
Holger Hans Peter Freyther5fb265e2015-04-05 14:36:31 +020071 for (j =0; j < vector_active(vline) && j < cmd_desc->nr_commands; j++) {
Daniel Willmann1264cb42010-10-21 15:00:36 +020072 str = vector_slot(vline, j);
73 desc = cmd_desc->command[j];
74 if (desc[0] == '*')
75 return cmd_el; /* Partial match */
76 if (strcmp(desc, str) != 0)
77 break;
78 }
79 /* We went through all the elements and all matched */
80 if (j == cmd_desc->nr_commands)
81 return cmd_el;
82 }
83 }
84
85 return NULL;
86}
87
88int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data)
89{
90 int ret = CTRL_CMD_ERROR;
91 struct ctrl_cmd_element *cmd_el;
92
93 if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) {
94 command->reply = "Trying to execute something not GET or SET";
95 goto out;
96 }
97 if ((command->type == CTRL_TYPE_SET) && (!command->value)) {
98 command->reply = "SET without a value";
99 goto out;
100 }
101
102 if (!vline)
103 goto out;
104
105 cmd_el = ctrl_cmd_get_element_match(vline, node);
106
107 if (!cmd_el) {
108 command->reply = "Command not found";
109 goto out;
110 }
111
112 if (command->type == CTRL_TYPE_SET) {
113 if (!cmd_el->set) {
114 command->reply = "SET not implemented";
115 goto out;
116 }
117 if (cmd_el->verify) {
118 if ((ret = cmd_el->verify(command, command->value, data))) {
119 ret = CTRL_CMD_ERROR;
Daniel Willmann9225ab12011-07-28 21:38:51 +0200120 /* If verify() set an appropriate error message, don't change it. */
121 if (!command->reply)
122 command->reply = "Value failed verification.";
Daniel Willmann1264cb42010-10-21 15:00:36 +0200123 goto out;
124 }
Daniel Willmann1264cb42010-10-21 15:00:36 +0200125 }
126 ret = cmd_el->set(command, data);
127 goto out;
128 } else if (command->type == CTRL_TYPE_GET) {
129 if (!cmd_el->get) {
130 command->reply = "GET not implemented";
131 goto out;
132 }
133 ret = cmd_el->get(command, data);
134 goto out;
135 }
136out:
137 if (ret == CTRL_CMD_REPLY) {
138 if (command->type == CTRL_TYPE_SET) {
139 command->type = CTRL_TYPE_SET_REPLY;
140 } else if (command->type == CTRL_TYPE_GET) {
141 command->type = CTRL_TYPE_GET_REPLY;
142 }
143 } else if (ret == CTRL_CMD_ERROR) {
144 command->type = CTRL_TYPE_ERROR;
145 }
146 return ret;
147}
148
149static void add_word(struct ctrl_cmd_struct *cmd,
150 const char *start, const char *end)
151{
152 if (!cmd->command) {
153 cmd->command = talloc_zero_array(tall_vty_vec_ctx,
154 char*, 1);
155 cmd->nr_commands = 0;
156 } else {
157 cmd->command = talloc_realloc(tall_vty_vec_ctx,
158 cmd->command, char*,
159 cmd->nr_commands + 1);
160 }
161
162 cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command,
163 start, end - start);
164}
165
166static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name)
167{
168 const char *cur, *word;
169
170 for (cur = name, word = NULL; cur[0] != '\0'; ++cur) {
171 /* warn about optionals */
172 if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') {
Harald Welte7fd0c832014-08-20 19:58:13 +0200173 LOGP(DLCTRL, LOGL_ERROR,
Daniel Willmann1264cb42010-10-21 15:00:36 +0200174 "Optionals are not supported in '%s'\n", name);
175 goto failure;
176 }
177
178 if (isspace(cur[0])) {
179 if (word) {
180 add_word(cmd, word, cur);
181 word = NULL;
182 }
183 continue;
184 }
185
186 if (!word)
187 word = cur;
188 }
189
190 if (word)
191 add_word(cmd, word, cur);
192
193 return;
194failure:
195 cmd->nr_commands = 0;
196 talloc_free(cmd->command);
197}
198
199int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd)
200{
201 vector cmds_vec;
202
203 cmds_vec = vector_lookup_ensure(ctrl_node_vec, node);
204
205 if (!cmds_vec) {
206 cmds_vec = vector_init(5);
207 if (!cmds_vec) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200208 LOGP(DLCTRL, LOGL_ERROR, "vector_init failed.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200209 return -ENOMEM;
210 }
211 vector_set_index(ctrl_node_vec, node, cmds_vec);
212 }
213
214 vector_set(cmds_vec, cmd);
215
216 create_cmd_struct(&cmd->strcmd, cmd->name);
217 return 0;
218}
219
Holger Hans Peter Freythera6b34012011-08-22 23:44:32 +0200220struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type)
221{
222 struct ctrl_cmd *cmd;
223
224 cmd = talloc_zero(ctx, struct ctrl_cmd);
225 if (!cmd)
226 return NULL;
227
228 cmd->type = type;
229 return cmd;
230}
231
Daniel Willmann8b7a9622011-03-17 15:37:54 +0100232struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd)
233{
234 struct ctrl_cmd *cmd2;
235
236 cmd2 = talloc_zero(ctx, struct ctrl_cmd);
237 if (!cmd2)
238 return NULL;
239
240 cmd2->type = cmd->type;
241 if (cmd->id) {
242 cmd2->id = talloc_strdup(cmd2, cmd->id);
243 if (!cmd2->id)
244 goto err;
245 }
246 if (cmd->variable) {
247 cmd2->variable = talloc_strdup(cmd2, cmd->variable);
248 if (!cmd2->variable)
249 goto err;
250 }
251 if (cmd->value) {
252 cmd2->value = talloc_strdup(cmd2, cmd->value);
253 if (!cmd2->value)
254 goto err;
255 }
256 if (cmd->reply) {
257 cmd2->reply = talloc_strdup(cmd2, cmd->reply);
258 if (!cmd2->reply)
259 goto err;
260 }
261
262 return cmd2;
263err:
264 talloc_free(cmd2);
265 return NULL;
266}
267
Daniel Willmann1264cb42010-10-21 15:00:36 +0200268struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
269{
270 char *str, *tmp, *saveptr = NULL;
271 char *var, *val;
272 struct ctrl_cmd *cmd;
273
274 cmd = talloc_zero(ctx, struct ctrl_cmd);
275 if (!cmd) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200276 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200277 return NULL;
278 }
279
280 /* Make sure input is NULL terminated */
281 msgb_put_u8(msg, 0);
282 str = (char *) msg->l2h;
283
Harald Welteedf6fe72016-11-26 10:06:07 +0100284 OSMO_ASSERT(str);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200285 tmp = strtok_r(str, " ", &saveptr);
286 if (!tmp) {
287 cmd->type = CTRL_TYPE_ERROR;
288 cmd->id = "err";
289 cmd->reply = "Request malformed";
290 goto err;
291 }
292
Max70c7d412017-02-24 13:59:14 +0100293 cmd->type = get_string_value(ctrl_type_vals, tmp);
Pau Espin Pedrol8aa85bd2017-06-23 12:43:31 +0200294 if ((int)cmd->type < 0 || cmd->type == CTRL_TYPE_UNKNOWN) {
Daniel Willmann1264cb42010-10-21 15:00:36 +0200295 cmd->type = CTRL_TYPE_ERROR;
296 cmd->id = "err";
297 cmd->reply = "Request type unknown";
298 goto err;
299 }
300
301 tmp = strtok_r(NULL, " ", &saveptr);
302
303 if (!tmp) {
304 cmd->type = CTRL_TYPE_ERROR;
305 cmd->id = "err";
306 cmd->reply = "Missing ID";
307 goto err;
308 }
309 cmd->id = talloc_strdup(cmd, tmp);
310 if (!cmd->id)
311 goto oom;
312
313 switch (cmd->type) {
314 case CTRL_TYPE_GET:
315 var = strtok_r(NULL, " ", &saveptr);
316 if (!var) {
317 cmd->type = CTRL_TYPE_ERROR;
318 cmd->reply = "GET incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200319 LOGP(DLCTRL, LOGL_NOTICE, "GET Command incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200320 goto err;
321 }
322 cmd->variable = talloc_strdup(cmd, var);
Harald Welte7fd0c832014-08-20 19:58:13 +0200323 LOGP(DLCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200324 break;
325 case CTRL_TYPE_SET:
326 var = strtok_r(NULL, " ", &saveptr);
Holger Hans Peter Freyther5ad742d2014-08-08 19:41:28 +0200327 val = strtok_r(NULL, "\n", &saveptr);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200328 if (!var || !val) {
329 cmd->type = CTRL_TYPE_ERROR;
330 cmd->reply = "SET incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200331 LOGP(DLCTRL, LOGL_NOTICE, "SET Command incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200332 goto err;
333 }
334 cmd->variable = talloc_strdup(cmd, var);
335 cmd->value = talloc_strdup(cmd, val);
336 if (!cmd->variable || !cmd->value)
337 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200338 LOGP(DLCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200339 break;
340 case CTRL_TYPE_GET_REPLY:
341 case CTRL_TYPE_SET_REPLY:
342 case CTRL_TYPE_TRAP:
343 var = strtok_r(NULL, " ", &saveptr);
344 val = strtok_r(NULL, " ", &saveptr);
345 if (!var || !val) {
346 cmd->type = CTRL_TYPE_ERROR;
347 cmd->reply = "Trap/Reply incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200348 LOGP(DLCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200349 goto err;
350 }
351 cmd->variable = talloc_strdup(cmd, var);
352 cmd->reply = talloc_strdup(cmd, val);
353 if (!cmd->variable || !cmd->reply)
354 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200355 LOGP(DLCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200356 break;
357 case CTRL_TYPE_ERROR:
358 var = strtok_r(NULL, "\0", &saveptr);
359 if (!var) {
360 cmd->reply = "";
361 goto err;
362 }
363 cmd->reply = talloc_strdup(cmd, var);
364 if (!cmd->reply)
365 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200366 LOGP(DLCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200367 break;
368 case CTRL_TYPE_UNKNOWN:
369 default:
370 cmd->type = CTRL_TYPE_ERROR;
371 cmd->reply = "Unknown type";
372 goto err;
373 }
374
375 return cmd;
376oom:
377 cmd->type = CTRL_TYPE_ERROR;
378 cmd->id = "err";
379 cmd->reply = "OOM";
380err:
381 talloc_free(cmd);
382 return NULL;
383}
384
385struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
386{
387 struct msgb *msg;
Max70c7d412017-02-24 13:59:14 +0100388 const char *type;
389 char *tmp;
Daniel Willmann1264cb42010-10-21 15:00:36 +0200390
391 if (!cmd->id)
392 return NULL;
393
394 msg = msgb_alloc_headroom(4096, 128, "ctrl command make");
395 if (!msg)
396 return NULL;
397
Max70c7d412017-02-24 13:59:14 +0100398 type = get_value_string(ctrl_type_vals, cmd->type);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200399
400 switch (cmd->type) {
401 case CTRL_TYPE_GET:
402 if (!cmd->variable)
403 goto err;
404
405 tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
406 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200407 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200408 goto err;
409 }
410
411 msg->l2h = msgb_put(msg, strlen(tmp));
412 memcpy(msg->l2h, tmp, strlen(tmp));
413 talloc_free(tmp);
414 break;
415 case CTRL_TYPE_SET:
416 if (!cmd->variable || !cmd->value)
417 goto err;
418
419 tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
420 cmd->value);
421 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200422 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200423 goto err;
424 }
425
426 msg->l2h = msgb_put(msg, strlen(tmp));
427 memcpy(msg->l2h, tmp, strlen(tmp));
428 talloc_free(tmp);
429 break;
430 case CTRL_TYPE_GET_REPLY:
431 case CTRL_TYPE_SET_REPLY:
432 case CTRL_TYPE_TRAP:
433 if (!cmd->variable || !cmd->reply)
434 goto err;
435
436 tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
437 cmd->reply);
438 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200439 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200440 goto err;
441 }
442
443 msg->l2h = msgb_put(msg, strlen(tmp));
444 memcpy(msg->l2h, tmp, strlen(tmp));
445 talloc_free(tmp);
446 break;
447 case CTRL_TYPE_ERROR:
448 if (!cmd->reply)
449 goto err;
450
451 tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id,
452 cmd->reply);
453 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200454 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200455 goto err;
456 }
457
458 msg->l2h = msgb_put(msg, strlen(tmp));
459 memcpy(msg->l2h, tmp, strlen(tmp));
460 talloc_free(tmp);
461 break;
462 default:
Harald Welte7fd0c832014-08-20 19:58:13 +0200463 LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200464 goto err;
465 break;
466 }
467
468 return msg;
469
470err:
471 msgb_free(msg);
472 return NULL;
473}
Harald Welte39c9e7b2014-08-22 00:28:51 +0200474
475struct ctrl_cmd_def *
476ctrl_cmd_def_make(const void *ctx, struct ctrl_cmd *cmd, void *data, unsigned int secs)
477{
478 struct ctrl_cmd_def *cd;
479
480 if (!cmd->ccon)
481 return NULL;
482
483 cd = talloc_zero(ctx, struct ctrl_cmd_def);
484
485 cd->cmd = cmd;
486 cd->data = data;
487
488 /* add to per-connection list of deferred commands */
489 llist_add(&cd->list, &cmd->ccon->def_cmds);
490
491 return cd;
492}
493
494int ctrl_cmd_def_is_zombie(struct ctrl_cmd_def *cd)
495{
496 /* luckily we're still alive */
497 if (cd->cmd)
498 return 0;
499
500 /* if we are a zombie, make sure we really die */
501 llist_del(&cd->list);
502 talloc_free(cd);
503
504 return 1;
505}
506
507int ctrl_cmd_def_send(struct ctrl_cmd_def *cd)
508{
509 struct ctrl_cmd *cmd = cd->cmd;
510
511 int rc;
512
513 /* Deferred commands can only be responses to GET/SET or ERROR, but
514 * never TRAP or anything else */
515 switch (cmd->type) {
516 case CTRL_TYPE_GET:
517 cmd->type = CTRL_TYPE_GET_REPLY;
518 break;
519 case CTRL_TYPE_SET:
520 cmd->type = CTRL_TYPE_SET_REPLY;
521 break;
522 default:
523 cmd->type = CTRL_TYPE_ERROR;
524 }
525
526 rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd);
527
528 talloc_free(cmd);
529 llist_del(&cd->list);
530 talloc_free(cd);
531
532 return rc;
533}