blob: 2cf66cf3e6ec8a8d01596b12461210dfc9dcfc94 [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>
36
37#include <osmocom/vty/command.h>
38#include <osmocom/vty/vector.h>
39
40extern vector ctrl_node_vec;
41
42static struct ctrl_cmd_map ccm[] = {
43 {"GET", CTRL_TYPE_GET},
44 {"SET", CTRL_TYPE_SET},
45 {"GET_REPLY", CTRL_TYPE_GET_REPLY},
46 {"SET_REPLY", CTRL_TYPE_SET_REPLY},
47 {"TRAP", CTRL_TYPE_TRAP},
48 {"ERROR", CTRL_TYPE_ERROR},
49 {NULL}
50};
51
Harald Weltebfdc2592014-08-21 15:18:04 +020052static int ctrl_cmd_str2type(char *s)
Daniel Willmann1264cb42010-10-21 15:00:36 +020053{
54 int i;
55 for (i=0; ccm[i].cmd != NULL; i++) {
56 if (strcasecmp(s, ccm[i].cmd) == 0)
57 return ccm[i].type;
58 }
59 return CTRL_TYPE_UNKNOWN;
60}
61
Harald Weltebfdc2592014-08-21 15:18:04 +020062static char *ctrl_cmd_type2str(int type)
Daniel Willmann1264cb42010-10-21 15:00:36 +020063{
64 int i;
65 for (i=0; ccm[i].cmd != NULL; i++) {
66 if (ccm[i].type == type)
67 return ccm[i].cmd;
68 }
69 return NULL;
70}
71
72/* Functions from libosmocom */
73extern vector cmd_make_descvec(const char *string, const char *descstr);
74
75/* Get the ctrl_cmd_element that matches this command */
76static struct ctrl_cmd_element *ctrl_cmd_get_element_match(vector vline, vector node)
77{
78 int index, j;
79 const char *desc;
80 struct ctrl_cmd_element *cmd_el;
81 struct ctrl_cmd_struct *cmd_desc;
82 char *str;
83
84 for (index = 0; index < vector_active(node); index++) {
85 if ((cmd_el = vector_slot(node, index))) {
86 cmd_desc = &cmd_el->strcmd;
87 if (cmd_desc->nr_commands > vector_active(vline))
88 continue;
Holger Hans Peter Freyther5fb265e2015-04-05 14:36:31 +020089 for (j =0; j < vector_active(vline) && j < cmd_desc->nr_commands; j++) {
Daniel Willmann1264cb42010-10-21 15:00:36 +020090 str = vector_slot(vline, j);
91 desc = cmd_desc->command[j];
92 if (desc[0] == '*')
93 return cmd_el; /* Partial match */
94 if (strcmp(desc, str) != 0)
95 break;
96 }
97 /* We went through all the elements and all matched */
98 if (j == cmd_desc->nr_commands)
99 return cmd_el;
100 }
101 }
102
103 return NULL;
104}
105
106int ctrl_cmd_exec(vector vline, struct ctrl_cmd *command, vector node, void *data)
107{
108 int ret = CTRL_CMD_ERROR;
109 struct ctrl_cmd_element *cmd_el;
110
111 if ((command->type != CTRL_TYPE_GET) && (command->type != CTRL_TYPE_SET)) {
112 command->reply = "Trying to execute something not GET or SET";
113 goto out;
114 }
115 if ((command->type == CTRL_TYPE_SET) && (!command->value)) {
116 command->reply = "SET without a value";
117 goto out;
118 }
119
120 if (!vline)
121 goto out;
122
123 cmd_el = ctrl_cmd_get_element_match(vline, node);
124
125 if (!cmd_el) {
126 command->reply = "Command not found";
127 goto out;
128 }
129
130 if (command->type == CTRL_TYPE_SET) {
131 if (!cmd_el->set) {
132 command->reply = "SET not implemented";
133 goto out;
134 }
135 if (cmd_el->verify) {
136 if ((ret = cmd_el->verify(command, command->value, data))) {
137 ret = CTRL_CMD_ERROR;
Daniel Willmann9225ab12011-07-28 21:38:51 +0200138 /* If verify() set an appropriate error message, don't change it. */
139 if (!command->reply)
140 command->reply = "Value failed verification.";
Daniel Willmann1264cb42010-10-21 15:00:36 +0200141 goto out;
142 }
Daniel Willmann1264cb42010-10-21 15:00:36 +0200143 }
144 ret = cmd_el->set(command, data);
145 goto out;
146 } else if (command->type == CTRL_TYPE_GET) {
147 if (!cmd_el->get) {
148 command->reply = "GET not implemented";
149 goto out;
150 }
151 ret = cmd_el->get(command, data);
152 goto out;
153 }
154out:
155 if (ret == CTRL_CMD_REPLY) {
156 if (command->type == CTRL_TYPE_SET) {
157 command->type = CTRL_TYPE_SET_REPLY;
158 } else if (command->type == CTRL_TYPE_GET) {
159 command->type = CTRL_TYPE_GET_REPLY;
160 }
161 } else if (ret == CTRL_CMD_ERROR) {
162 command->type = CTRL_TYPE_ERROR;
163 }
164 return ret;
165}
166
167static void add_word(struct ctrl_cmd_struct *cmd,
168 const char *start, const char *end)
169{
170 if (!cmd->command) {
171 cmd->command = talloc_zero_array(tall_vty_vec_ctx,
172 char*, 1);
173 cmd->nr_commands = 0;
174 } else {
175 cmd->command = talloc_realloc(tall_vty_vec_ctx,
176 cmd->command, char*,
177 cmd->nr_commands + 1);
178 }
179
180 cmd->command[cmd->nr_commands++] = talloc_strndup(cmd->command,
181 start, end - start);
182}
183
184static void create_cmd_struct(struct ctrl_cmd_struct *cmd, const char *name)
185{
186 const char *cur, *word;
187
188 for (cur = name, word = NULL; cur[0] != '\0'; ++cur) {
189 /* warn about optionals */
190 if (cur[0] == '(' || cur[0] == ')' || cur[0] == '|') {
Harald Welte7fd0c832014-08-20 19:58:13 +0200191 LOGP(DLCTRL, LOGL_ERROR,
Daniel Willmann1264cb42010-10-21 15:00:36 +0200192 "Optionals are not supported in '%s'\n", name);
193 goto failure;
194 }
195
196 if (isspace(cur[0])) {
197 if (word) {
198 add_word(cmd, word, cur);
199 word = NULL;
200 }
201 continue;
202 }
203
204 if (!word)
205 word = cur;
206 }
207
208 if (word)
209 add_word(cmd, word, cur);
210
211 return;
212failure:
213 cmd->nr_commands = 0;
214 talloc_free(cmd->command);
215}
216
217int ctrl_cmd_install(enum ctrl_node_type node, struct ctrl_cmd_element *cmd)
218{
219 vector cmds_vec;
220
221 cmds_vec = vector_lookup_ensure(ctrl_node_vec, node);
222
223 if (!cmds_vec) {
224 cmds_vec = vector_init(5);
225 if (!cmds_vec) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200226 LOGP(DLCTRL, LOGL_ERROR, "vector_init failed.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200227 return -ENOMEM;
228 }
229 vector_set_index(ctrl_node_vec, node, cmds_vec);
230 }
231
232 vector_set(cmds_vec, cmd);
233
234 create_cmd_struct(&cmd->strcmd, cmd->name);
235 return 0;
236}
237
Holger Hans Peter Freythera6b34012011-08-22 23:44:32 +0200238struct ctrl_cmd *ctrl_cmd_create(void *ctx, enum ctrl_type type)
239{
240 struct ctrl_cmd *cmd;
241
242 cmd = talloc_zero(ctx, struct ctrl_cmd);
243 if (!cmd)
244 return NULL;
245
246 cmd->type = type;
247 return cmd;
248}
249
Daniel Willmann8b7a9622011-03-17 15:37:54 +0100250struct ctrl_cmd *ctrl_cmd_cpy(void *ctx, struct ctrl_cmd *cmd)
251{
252 struct ctrl_cmd *cmd2;
253
254 cmd2 = talloc_zero(ctx, struct ctrl_cmd);
255 if (!cmd2)
256 return NULL;
257
258 cmd2->type = cmd->type;
259 if (cmd->id) {
260 cmd2->id = talloc_strdup(cmd2, cmd->id);
261 if (!cmd2->id)
262 goto err;
263 }
264 if (cmd->variable) {
265 cmd2->variable = talloc_strdup(cmd2, cmd->variable);
266 if (!cmd2->variable)
267 goto err;
268 }
269 if (cmd->value) {
270 cmd2->value = talloc_strdup(cmd2, cmd->value);
271 if (!cmd2->value)
272 goto err;
273 }
274 if (cmd->reply) {
275 cmd2->reply = talloc_strdup(cmd2, cmd->reply);
276 if (!cmd2->reply)
277 goto err;
278 }
279
280 return cmd2;
281err:
282 talloc_free(cmd2);
283 return NULL;
284}
285
Daniel Willmann1264cb42010-10-21 15:00:36 +0200286struct ctrl_cmd *ctrl_cmd_parse(void *ctx, struct msgb *msg)
287{
288 char *str, *tmp, *saveptr = NULL;
289 char *var, *val;
290 struct ctrl_cmd *cmd;
291
292 cmd = talloc_zero(ctx, struct ctrl_cmd);
293 if (!cmd) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200294 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200295 return NULL;
296 }
297
298 /* Make sure input is NULL terminated */
299 msgb_put_u8(msg, 0);
300 str = (char *) msg->l2h;
301
Harald Welteedf6fe72016-11-26 10:06:07 +0100302 OSMO_ASSERT(str);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200303 tmp = strtok_r(str, " ", &saveptr);
304 if (!tmp) {
305 cmd->type = CTRL_TYPE_ERROR;
306 cmd->id = "err";
307 cmd->reply = "Request malformed";
308 goto err;
309 }
310
311 cmd->type = ctrl_cmd_str2type(tmp);
312 if (cmd->type == CTRL_TYPE_UNKNOWN) {
313 cmd->type = CTRL_TYPE_ERROR;
314 cmd->id = "err";
315 cmd->reply = "Request type unknown";
316 goto err;
317 }
318
319 tmp = strtok_r(NULL, " ", &saveptr);
320
321 if (!tmp) {
322 cmd->type = CTRL_TYPE_ERROR;
323 cmd->id = "err";
324 cmd->reply = "Missing ID";
325 goto err;
326 }
327 cmd->id = talloc_strdup(cmd, tmp);
328 if (!cmd->id)
329 goto oom;
330
331 switch (cmd->type) {
332 case CTRL_TYPE_GET:
333 var = strtok_r(NULL, " ", &saveptr);
334 if (!var) {
335 cmd->type = CTRL_TYPE_ERROR;
336 cmd->reply = "GET incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200337 LOGP(DLCTRL, LOGL_NOTICE, "GET Command incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200338 goto err;
339 }
340 cmd->variable = talloc_strdup(cmd, var);
Harald Welte7fd0c832014-08-20 19:58:13 +0200341 LOGP(DLCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200342 break;
343 case CTRL_TYPE_SET:
344 var = strtok_r(NULL, " ", &saveptr);
Holger Hans Peter Freyther5ad742d2014-08-08 19:41:28 +0200345 val = strtok_r(NULL, "\n", &saveptr);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200346 if (!var || !val) {
347 cmd->type = CTRL_TYPE_ERROR;
348 cmd->reply = "SET incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200349 LOGP(DLCTRL, LOGL_NOTICE, "SET Command incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200350 goto err;
351 }
352 cmd->variable = talloc_strdup(cmd, var);
353 cmd->value = talloc_strdup(cmd, val);
354 if (!cmd->variable || !cmd->value)
355 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200356 LOGP(DLCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200357 break;
358 case CTRL_TYPE_GET_REPLY:
359 case CTRL_TYPE_SET_REPLY:
360 case CTRL_TYPE_TRAP:
361 var = strtok_r(NULL, " ", &saveptr);
362 val = strtok_r(NULL, " ", &saveptr);
363 if (!var || !val) {
364 cmd->type = CTRL_TYPE_ERROR;
365 cmd->reply = "Trap/Reply incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200366 LOGP(DLCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200367 goto err;
368 }
369 cmd->variable = talloc_strdup(cmd, var);
370 cmd->reply = talloc_strdup(cmd, val);
371 if (!cmd->variable || !cmd->reply)
372 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200373 LOGP(DLCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200374 break;
375 case CTRL_TYPE_ERROR:
376 var = strtok_r(NULL, "\0", &saveptr);
377 if (!var) {
378 cmd->reply = "";
379 goto err;
380 }
381 cmd->reply = talloc_strdup(cmd, var);
382 if (!cmd->reply)
383 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200384 LOGP(DLCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200385 break;
386 case CTRL_TYPE_UNKNOWN:
387 default:
388 cmd->type = CTRL_TYPE_ERROR;
389 cmd->reply = "Unknown type";
390 goto err;
391 }
392
393 return cmd;
394oom:
395 cmd->type = CTRL_TYPE_ERROR;
396 cmd->id = "err";
397 cmd->reply = "OOM";
398err:
399 talloc_free(cmd);
400 return NULL;
401}
402
403struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
404{
405 struct msgb *msg;
406 char *type, *tmp;
407
408 if (!cmd->id)
409 return NULL;
410
411 msg = msgb_alloc_headroom(4096, 128, "ctrl command make");
412 if (!msg)
413 return NULL;
414
415 type = ctrl_cmd_type2str(cmd->type);
416
417 switch (cmd->type) {
418 case CTRL_TYPE_GET:
419 if (!cmd->variable)
420 goto err;
421
422 tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
423 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200424 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200425 goto err;
426 }
427
428 msg->l2h = msgb_put(msg, strlen(tmp));
429 memcpy(msg->l2h, tmp, strlen(tmp));
430 talloc_free(tmp);
431 break;
432 case CTRL_TYPE_SET:
433 if (!cmd->variable || !cmd->value)
434 goto err;
435
436 tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
437 cmd->value);
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_GET_REPLY:
448 case CTRL_TYPE_SET_REPLY:
449 case CTRL_TYPE_TRAP:
450 if (!cmd->variable || !cmd->reply)
451 goto err;
452
453 tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
454 cmd->reply);
455 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200456 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200457 goto err;
458 }
459
460 msg->l2h = msgb_put(msg, strlen(tmp));
461 memcpy(msg->l2h, tmp, strlen(tmp));
462 talloc_free(tmp);
463 break;
464 case CTRL_TYPE_ERROR:
465 if (!cmd->reply)
466 goto err;
467
468 tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id,
469 cmd->reply);
470 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200471 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200472 goto err;
473 }
474
475 msg->l2h = msgb_put(msg, strlen(tmp));
476 memcpy(msg->l2h, tmp, strlen(tmp));
477 talloc_free(tmp);
478 break;
479 default:
Harald Welte7fd0c832014-08-20 19:58:13 +0200480 LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200481 goto err;
482 break;
483 }
484
485 return msg;
486
487err:
488 msgb_free(msg);
489 return NULL;
490}
Harald Welte39c9e7b2014-08-22 00:28:51 +0200491
492struct ctrl_cmd_def *
493ctrl_cmd_def_make(const void *ctx, struct ctrl_cmd *cmd, void *data, unsigned int secs)
494{
495 struct ctrl_cmd_def *cd;
496
497 if (!cmd->ccon)
498 return NULL;
499
500 cd = talloc_zero(ctx, struct ctrl_cmd_def);
501
502 cd->cmd = cmd;
503 cd->data = data;
504
505 /* add to per-connection list of deferred commands */
506 llist_add(&cd->list, &cmd->ccon->def_cmds);
507
508 return cd;
509}
510
511int ctrl_cmd_def_is_zombie(struct ctrl_cmd_def *cd)
512{
513 /* luckily we're still alive */
514 if (cd->cmd)
515 return 0;
516
517 /* if we are a zombie, make sure we really die */
518 llist_del(&cd->list);
519 talloc_free(cd);
520
521 return 1;
522}
523
524int ctrl_cmd_def_send(struct ctrl_cmd_def *cd)
525{
526 struct ctrl_cmd *cmd = cd->cmd;
527
528 int rc;
529
530 /* Deferred commands can only be responses to GET/SET or ERROR, but
531 * never TRAP or anything else */
532 switch (cmd->type) {
533 case CTRL_TYPE_GET:
534 cmd->type = CTRL_TYPE_GET_REPLY;
535 break;
536 case CTRL_TYPE_SET:
537 cmd->type = CTRL_TYPE_SET_REPLY;
538 break;
539 default:
540 cmd->type = CTRL_TYPE_ERROR;
541 }
542
543 rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd);
544
545 talloc_free(cmd);
546 llist_del(&cd->list);
547 talloc_free(cd);
548
549 return rc;
550}