blob: 4e156b819c4eb8bd32d930430e353a92ee6a9fa8 [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;
89 for (j =0; j < vector_active(vline); j++) {
90 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
302 tmp = strtok_r(str, " ", &saveptr);
303 if (!tmp) {
304 cmd->type = CTRL_TYPE_ERROR;
305 cmd->id = "err";
306 cmd->reply = "Request malformed";
307 goto err;
308 }
309
310 cmd->type = ctrl_cmd_str2type(tmp);
311 if (cmd->type == CTRL_TYPE_UNKNOWN) {
312 cmd->type = CTRL_TYPE_ERROR;
313 cmd->id = "err";
314 cmd->reply = "Request type unknown";
315 goto err;
316 }
317
318 tmp = strtok_r(NULL, " ", &saveptr);
319
320 if (!tmp) {
321 cmd->type = CTRL_TYPE_ERROR;
322 cmd->id = "err";
323 cmd->reply = "Missing ID";
324 goto err;
325 }
326 cmd->id = talloc_strdup(cmd, tmp);
327 if (!cmd->id)
328 goto oom;
329
330 switch (cmd->type) {
331 case CTRL_TYPE_GET:
332 var = strtok_r(NULL, " ", &saveptr);
333 if (!var) {
334 cmd->type = CTRL_TYPE_ERROR;
335 cmd->reply = "GET incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200336 LOGP(DLCTRL, LOGL_NOTICE, "GET Command incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200337 goto err;
338 }
339 cmd->variable = talloc_strdup(cmd, var);
Harald Welte7fd0c832014-08-20 19:58:13 +0200340 LOGP(DLCTRL, LOGL_DEBUG, "Command: GET %s\n", cmd->variable);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200341 break;
342 case CTRL_TYPE_SET:
343 var = strtok_r(NULL, " ", &saveptr);
Holger Hans Peter Freyther5ad742d2014-08-08 19:41:28 +0200344 val = strtok_r(NULL, "\n", &saveptr);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200345 if (!var || !val) {
346 cmd->type = CTRL_TYPE_ERROR;
347 cmd->reply = "SET incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200348 LOGP(DLCTRL, LOGL_NOTICE, "SET Command incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200349 goto err;
350 }
351 cmd->variable = talloc_strdup(cmd, var);
352 cmd->value = talloc_strdup(cmd, val);
353 if (!cmd->variable || !cmd->value)
354 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200355 LOGP(DLCTRL, LOGL_DEBUG, "Command: SET %s = %s\n", cmd->variable, cmd->value);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200356 break;
357 case CTRL_TYPE_GET_REPLY:
358 case CTRL_TYPE_SET_REPLY:
359 case CTRL_TYPE_TRAP:
360 var = strtok_r(NULL, " ", &saveptr);
361 val = strtok_r(NULL, " ", &saveptr);
362 if (!var || !val) {
363 cmd->type = CTRL_TYPE_ERROR;
364 cmd->reply = "Trap/Reply incomplete";
Harald Welte7fd0c832014-08-20 19:58:13 +0200365 LOGP(DLCTRL, LOGL_NOTICE, "Trap/Reply incomplete\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200366 goto err;
367 }
368 cmd->variable = talloc_strdup(cmd, var);
369 cmd->reply = talloc_strdup(cmd, val);
370 if (!cmd->variable || !cmd->reply)
371 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200372 LOGP(DLCTRL, LOGL_DEBUG, "Command: TRAP/REPLY %s: %s\n", cmd->variable, cmd->reply);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200373 break;
374 case CTRL_TYPE_ERROR:
375 var = strtok_r(NULL, "\0", &saveptr);
376 if (!var) {
377 cmd->reply = "";
378 goto err;
379 }
380 cmd->reply = talloc_strdup(cmd, var);
381 if (!cmd->reply)
382 goto oom;
Harald Welte7fd0c832014-08-20 19:58:13 +0200383 LOGP(DLCTRL, LOGL_DEBUG, "Command: ERROR %s\n", cmd->reply);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200384 break;
385 case CTRL_TYPE_UNKNOWN:
386 default:
387 cmd->type = CTRL_TYPE_ERROR;
388 cmd->reply = "Unknown type";
389 goto err;
390 }
391
392 return cmd;
393oom:
394 cmd->type = CTRL_TYPE_ERROR;
395 cmd->id = "err";
396 cmd->reply = "OOM";
397err:
398 talloc_free(cmd);
399 return NULL;
400}
401
402struct msgb *ctrl_cmd_make(struct ctrl_cmd *cmd)
403{
404 struct msgb *msg;
405 char *type, *tmp;
406
407 if (!cmd->id)
408 return NULL;
409
410 msg = msgb_alloc_headroom(4096, 128, "ctrl command make");
411 if (!msg)
412 return NULL;
413
414 type = ctrl_cmd_type2str(cmd->type);
415
416 switch (cmd->type) {
417 case CTRL_TYPE_GET:
418 if (!cmd->variable)
419 goto err;
420
421 tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id, cmd->variable);
422 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200423 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200424 goto err;
425 }
426
427 msg->l2h = msgb_put(msg, strlen(tmp));
428 memcpy(msg->l2h, tmp, strlen(tmp));
429 talloc_free(tmp);
430 break;
431 case CTRL_TYPE_SET:
432 if (!cmd->variable || !cmd->value)
433 goto err;
434
435 tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
436 cmd->value);
437 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200438 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200439 goto err;
440 }
441
442 msg->l2h = msgb_put(msg, strlen(tmp));
443 memcpy(msg->l2h, tmp, strlen(tmp));
444 talloc_free(tmp);
445 break;
446 case CTRL_TYPE_GET_REPLY:
447 case CTRL_TYPE_SET_REPLY:
448 case CTRL_TYPE_TRAP:
449 if (!cmd->variable || !cmd->reply)
450 goto err;
451
452 tmp = talloc_asprintf(cmd, "%s %s %s %s", type, cmd->id, cmd->variable,
453 cmd->reply);
454 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200455 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200456 goto err;
457 }
458
459 msg->l2h = msgb_put(msg, strlen(tmp));
460 memcpy(msg->l2h, tmp, strlen(tmp));
461 talloc_free(tmp);
462 break;
463 case CTRL_TYPE_ERROR:
464 if (!cmd->reply)
465 goto err;
466
467 tmp = talloc_asprintf(cmd, "%s %s %s", type, cmd->id,
468 cmd->reply);
469 if (!tmp) {
Harald Welte7fd0c832014-08-20 19:58:13 +0200470 LOGP(DLCTRL, LOGL_ERROR, "Failed to allocate cmd.\n");
Daniel Willmann1264cb42010-10-21 15:00:36 +0200471 goto err;
472 }
473
474 msg->l2h = msgb_put(msg, strlen(tmp));
475 memcpy(msg->l2h, tmp, strlen(tmp));
476 talloc_free(tmp);
477 break;
478 default:
Harald Welte7fd0c832014-08-20 19:58:13 +0200479 LOGP(DLCTRL, LOGL_NOTICE, "Unknown command type %i\n", cmd->type);
Daniel Willmann1264cb42010-10-21 15:00:36 +0200480 goto err;
481 break;
482 }
483
484 return msg;
485
486err:
487 msgb_free(msg);
488 return NULL;
489}
Harald Welte39c9e7b2014-08-22 00:28:51 +0200490
491struct ctrl_cmd_def *
492ctrl_cmd_def_make(const void *ctx, struct ctrl_cmd *cmd, void *data, unsigned int secs)
493{
494 struct ctrl_cmd_def *cd;
495
496 if (!cmd->ccon)
497 return NULL;
498
499 cd = talloc_zero(ctx, struct ctrl_cmd_def);
500
501 cd->cmd = cmd;
502 cd->data = data;
503
504 /* add to per-connection list of deferred commands */
505 llist_add(&cd->list, &cmd->ccon->def_cmds);
506
507 return cd;
508}
509
510int ctrl_cmd_def_is_zombie(struct ctrl_cmd_def *cd)
511{
512 /* luckily we're still alive */
513 if (cd->cmd)
514 return 0;
515
516 /* if we are a zombie, make sure we really die */
517 llist_del(&cd->list);
518 talloc_free(cd);
519
520 return 1;
521}
522
523int ctrl_cmd_def_send(struct ctrl_cmd_def *cd)
524{
525 struct ctrl_cmd *cmd = cd->cmd;
526
527 int rc;
528
529 /* Deferred commands can only be responses to GET/SET or ERROR, but
530 * never TRAP or anything else */
531 switch (cmd->type) {
532 case CTRL_TYPE_GET:
533 cmd->type = CTRL_TYPE_GET_REPLY;
534 break;
535 case CTRL_TYPE_SET:
536 cmd->type = CTRL_TYPE_SET_REPLY;
537 break;
538 default:
539 cmd->type = CTRL_TYPE_ERROR;
540 }
541
542 rc = ctrl_cmd_send(&cmd->ccon->write_queue, cmd);
543
544 talloc_free(cmd);
545 llist_del(&cd->list);
546 talloc_free(cd);
547
548 return rc;
549}