Squashed commit of the following:
[olsrd.git] / src / olsr_comport_txt.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41 #include <string.h>
42 #include <stdlib.h>
43
44 #include "olsr_cfg.h"
45 #include "olsr_logging.h"
46 #include "olsr_cookie.h"
47 #include "olsr_ip_acl.h"
48 #include "olsr.h"
49 #include "ipcalc.h"
50 #include "scheduler.h"
51 #include "olsr_comport.h"
52 #include "olsr_comport_txt.h"
53 #include "plugin_loader.h"
54
55 #define OLSR_FOR_EACH_TXTCMD_ENTRY(cmd, iterator) avl_for_each_element_safe(&txt_normal_tree, cmd, node, iterator.loop, iterator.safe)
56
57 struct txt_repeat_data {
58   struct timer_entry *timer;
59   struct autobuf *buf;
60   char *cmd;
61   char *param;
62   bool csv;
63 };
64
65 static struct avl_tree txt_normal_tree, txt_help_tree;
66 static struct olsr_cookie_info *txtcommand_cookie, *txt_repeattimer_cookie;
67
68 static enum olsr_txtcommand_result olsr_txtcmd_quit(
69     struct comport_connection *con, const char *cmd, const char *param);
70 static enum olsr_txtcommand_result olsr_txtcmd_help(
71     struct comport_connection *con, const char *cmd, const char *param);
72 static enum olsr_txtcommand_result olsr_txtcmd_echo(
73     struct comport_connection *con, const char *cmd, const char *param);
74 static enum olsr_txtcommand_result olsr_txtcmd_repeat(
75     struct comport_connection *con, const char *cmd, const char *param);
76 static enum olsr_txtcommand_result olsr_txtcmd_timeout(
77     struct comport_connection *con, const char *cmd, const char *param);
78 static enum olsr_txtcommand_result olsr_txtcmd_version(
79     struct comport_connection *con, const char *cmd, const char *param);
80 static enum olsr_txtcommand_result olsr_txtcmd_plugin(
81     struct comport_connection *con, const char *cmd, const char *param);
82 static enum olsr_txtcommand_result olsr_txtcmd_displayhelp(
83     struct comport_connection *con, const char *cmd, const char *param);
84
85
86 static const char *txt_internal_names[] = {
87   "quit",
88   "exit",
89   "help",
90   "echo",
91   "repeat",
92   "timeout",
93   "version",
94   "plugin"
95 };
96
97 static const char *txt_internal_help[] = {
98   "shuts down the terminal connection\n",
99   "shuts down the terminal connection\n",
100   "display the online help text\n",
101   "switchs the prompt on/off\n",
102   "repeat <interval> <command>: repeats a command every <interval> seconds\n",
103   "timeout <interval>: set the timeout interval to <interval> seconds, 0 means no timeout\n"
104   "displays the version of the olsrd\n"
105   "control olsr plugins dynamically, parameters are 'list', 'activate <plugin>', 'deactivate <plugin>', "
106     "'load <plugin>' and 'unload <plugin>'"
107 };
108
109 static olsr_txthandler txt_internal_handlers[] = {
110   olsr_txtcmd_quit,
111   olsr_txtcmd_quit,
112   olsr_txtcmd_help,
113   olsr_txtcmd_echo,
114   olsr_txtcmd_repeat,
115   olsr_txtcmd_timeout,
116   olsr_txtcmd_version,
117   olsr_txtcmd_plugin
118 };
119
120 static struct olsr_txtcommand *txt_internal_normalcmd[ARRAYSIZE(txt_internal_names)];
121 static struct olsr_txtcommand *txt_internal_helpcmd[ARRAYSIZE(txt_internal_names)];
122
123 void
124 olsr_com_init_txt(void) {
125   size_t i;
126
127   avl_init(&txt_normal_tree, &avl_comp_strcasecmp, false, NULL);
128   avl_init(&txt_help_tree, &avl_comp_strcasecmp, false, NULL);
129
130   txtcommand_cookie = olsr_alloc_cookie("comport txt commands", OLSR_COOKIE_TYPE_MEMORY);
131   olsr_cookie_set_memory_size(txtcommand_cookie, sizeof(struct olsr_txtcommand));
132
133   txt_repeattimer_cookie = olsr_alloc_cookie("txt repeat timer", OLSR_COOKIE_TYPE_TIMER);
134
135   for (i=0; i < ARRAYSIZE(txt_internal_names); i++) {
136     txt_internal_normalcmd[i] = olsr_com_add_normal_txtcommand(txt_internal_names[i], txt_internal_handlers[i]);
137     txt_internal_helpcmd[i] = olsr_com_add_help_txtcommand(txt_internal_names[i], olsr_txtcmd_displayhelp);
138   }
139 }
140
141 void
142 olsr_com_destroy_txt(void) {
143   size_t i;
144   for (i=0; i < ARRAYSIZE(txt_internal_names); i++) {
145     olsr_com_remove_normal_txtcommand(txt_internal_normalcmd[i]);
146     olsr_com_remove_help_txtcommand(txt_internal_helpcmd[i]);
147   }
148 }
149 struct olsr_txtcommand *
150 olsr_com_add_normal_txtcommand (const char *command, olsr_txthandler handler) {
151   struct olsr_txtcommand *txt;
152
153   txt = olsr_cookie_malloc(txtcommand_cookie);
154   txt->node.key = strdup(command);
155   txt->handler = handler;
156
157   avl_insert(&txt_normal_tree, &txt->node);
158   return txt;
159 }
160
161 struct olsr_txtcommand *
162 olsr_com_add_help_txtcommand (const char *command, olsr_txthandler handler) {
163   struct olsr_txtcommand *txt;
164
165   txt = olsr_cookie_malloc(txtcommand_cookie);
166   txt->node.key = strdup(command);
167   txt->handler = handler;
168
169   avl_insert(&txt_help_tree, &txt->node);
170   return txt;
171 }
172
173 void olsr_com_remove_normal_txtcommand (struct olsr_txtcommand *cmd) {
174   avl_delete(&txt_normal_tree, &cmd->node);
175   free(cmd->node.key);
176   olsr_cookie_free(txtcommand_cookie, cmd);
177 }
178
179 void olsr_com_remove_help_txtcommand (struct olsr_txtcommand *cmd) {
180   avl_delete(&txt_help_tree, &cmd->node);
181   free(cmd->node.key);
182   olsr_cookie_free(txtcommand_cookie, cmd);
183 }
184
185 enum olsr_txtcommand_result
186 olsr_com_handle_txtcommand(struct comport_connection *con, char *cmd, char *param) {
187   struct olsr_txtcommand *ptr;
188
189   ptr = (struct olsr_txtcommand *) avl_find(&txt_normal_tree, cmd);
190
191   OLSR_DEBUG(LOG_COMPORT, "Looking for command '%s': %s\n",
192     cmd, ptr == NULL ? "unknown" : "available");
193   if (ptr == NULL) {
194     return UNKNOWN;
195   }
196
197   if (ptr->acl) {
198     if (!ip_acl_acceptable(ptr->acl, &con->addr, olsr_cnf->ip_version)) {
199 #if !defined REMOVE_LOG_DEBUG
200       struct ipaddr_str buf;
201 #endif
202       OLSR_DEBUG(LOG_COMPORT, "ACL for command '%s' does not allow access from %s\n",
203         cmd, olsr_ip_to_string(&buf, &con->addr));
204       return UNKNOWN;
205     }
206   }
207
208   return ptr->handler(con, cmd, param);
209 }
210
211 void olsr_com_parse_txt(struct comport_connection *con,
212     unsigned int flags  __attribute__ ((unused))) {
213   static char defaultCommand[] = "/link/neigh/topology/hna/mid/routes";
214   static char tmpbuf[128];
215
216   enum olsr_txtcommand_result res;
217   char *eol;
218   int len;
219   bool processedCommand = false, chainCommands = false;
220   uint32_t old_timeout;
221
222   old_timeout = con->timeout_value;
223
224   /* loop over input */
225   while (con->in.len > 0 && con->state == INTERACTIVE) {
226     char *para = NULL, *cmd = NULL, *next = NULL;
227
228     /* search for end of line */
229     eol = memchr(con->in.buf, '\n', con->in.len);
230
231     if (eol == NULL) {
232       break;
233     }
234
235     /* terminate line with a 0 */
236     if (eol != con->in.buf && eol[-1] == '\r') {
237       eol[-1] = 0;
238     }
239     *eol++ = 0;
240
241     /* handle line */
242     OLSR_DEBUG(LOG_COMPORT, "Interactive console: %s\n", con->in.buf);
243     cmd = &con->in.buf[0];
244     processedCommand = true;
245
246     /* apply default command */
247     if (strcmp(cmd, "/") == 0) {
248       strcpy(tmpbuf, defaultCommand);
249       cmd = tmpbuf;
250     }
251
252     if (cmd[0] == '/') {
253       cmd++;
254       chainCommands = true;
255     }
256     while (cmd) {
257       len = con->out.len;
258
259       /* handle difference between multicommand and singlecommand mode */
260       if (chainCommands) {
261         next = strchr(cmd, '/');
262         if (next) {
263           *next++ = 0;
264         }
265       }
266       para = strchr(cmd, ' ');
267       if (para != NULL) {
268         *para++ = 0;
269       }
270
271       /* if we are doing continous output, stop it ! */
272       if (con->stop_handler) {
273         con->stop_handler(con);
274         con->stop_handler = NULL;
275       }
276
277       if (strlen(cmd) != 0) {
278         res = olsr_com_handle_txtcommand(con, cmd, para);
279         switch (res) {
280           case CONTINUE:
281             break;
282           case CONTINOUS:
283             break;
284           case ABUF_ERROR:
285             con->out.len = len;
286             abuf_appendf(&con->out,
287                 "Error in autobuffer during command '%s'.\n", cmd);
288             break;
289           case UNKNOWN:
290             con->out.len = len;
291             abuf_appendf(&con->out, "Error, unknown command '%s'\n", cmd);
292             break;
293           case QUIT:
294             con->state = SEND_AND_QUIT;
295             break;
296         }
297         /* put an empty line behind each command */
298         if (con->show_echo) {
299           abuf_puts(&con->out, "\n");
300         }
301       }
302       cmd = next;
303     }
304
305     /* remove line from input buffer */
306     abuf_pull(&con->in, eol - con->in.buf);
307
308     if (con->in.buf[0] == '/') {
309       /* end of multiple command line */
310       con->state = SEND_AND_QUIT;
311     }
312   }
313
314   /* reset timeout */
315   olsr_change_timer(con->timeout, con->timeout_value, 0, false);
316
317   /* print prompt */
318   if (processedCommand && con->state == INTERACTIVE && con->show_echo) {
319     abuf_puts(&con->out, "> ");
320   }
321 }
322
323 static enum olsr_txtcommand_result
324 olsr_txtcmd_quit(struct comport_connection *con __attribute__ ((unused)),
325     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused))) {
326   return QUIT;
327 }
328
329 static enum olsr_txtcommand_result
330 olsr_txtcmd_displayhelp(struct comport_connection *con,
331     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused))) {
332   size_t i;
333
334   for (i=0; i<ARRAYSIZE(txt_internal_names); i++) {
335     if (strcasecmp(txt_internal_names[i], cmd) == 0) {
336       abuf_puts(&con->out, txt_internal_help[i]);
337       return CONTINUE;
338     }
339   }
340   return UNKNOWN;
341 }
342
343 static enum olsr_txtcommand_result
344 olsr_txtcmd_help(struct comport_connection *con,
345     const char *cmd __attribute__ ((unused)), const char *param) {
346   struct olsr_txtcommand *ptr;
347   struct list_iterator iterator;
348
349   if (param != NULL) {
350     ptr = avl_find_element(&txt_help_tree, cmd, ptr, node);
351     if (ptr != NULL) {
352       return ptr->handler(con, param, NULL);
353     }
354     return UNKNOWN;
355   }
356
357   if (abuf_puts(&con->out, "Known commands:\n") < 0) {
358     return ABUF_ERROR;
359   }
360
361   OLSR_FOR_EACH_TXTCMD_ENTRY(ptr, iterator) {
362     if (abuf_appendf(&con->out, "  %s\n", (char *)ptr->node.key) < 0) {
363       return ABUF_ERROR;
364     }
365   }
366
367   if (abuf_puts(&con->out, "Use 'help <command> to see a help text for a certain command\n") < 0) {
368     return ABUF_ERROR;
369   }
370   return CONTINUE;
371 }
372
373 static enum olsr_txtcommand_result
374 olsr_txtcmd_echo(struct comport_connection *con,
375     const char *cmd __attribute__ ((unused)), const char *param) {
376   if(param == NULL) {
377         if (abuf_appendf(&con->out, "Error, echo needs an argument. Try 'on' or 'off'\n") < 0) {
378       return ABUF_ERROR;
379     }
380         return CONTINUE;
381   }
382   
383   if (strcasecmp(param, "on") == 0) {
384     con->show_echo = true;
385   }
386   else if (strcasecmp(param, "off") == 0) {
387     con->show_echo = false;
388   }
389   else {
390     if (abuf_appendf(&con->out, "Error, unknown argument '%s' for echo. Try 'on' or 'off'\n", param) < 0) {
391       return ABUF_ERROR;
392     }
393   }
394   return CONTINUE;
395 }
396
397 static enum olsr_txtcommand_result
398 olsr_txtcmd_timeout(struct comport_connection *con,
399     const char *cmd __attribute__ ((unused)), const char *param) {
400   con->timeout_value = (uint32_t)strtoul(param, NULL, 10) * 1000;
401   return CONTINUE;
402 }
403
404 static void olsr_txt_repeat_stophandler(struct comport_connection *con) {
405   olsr_stop_timer((struct timer_entry *)con->stop_data[0]);
406   free(con->stop_data[1]);
407
408   con->stop_handler = NULL;
409   con->stop_data[0] = NULL;
410   con->stop_data[1] = NULL;
411   con->stop_data[2] = NULL;
412 }
413
414 static void olsr_txt_repeat_timer(void *data) {
415   struct comport_connection *con = data;
416
417   if (olsr_com_handle_txtcommand(con, con->stop_data[1], con->stop_data[2]) != CONTINUE) {
418     con->stop_handler(con);
419   }
420   olsr_com_activate_output(con);
421 }
422
423 static enum olsr_txtcommand_result
424 olsr_txtcmd_repeat(struct comport_connection *con,
425     const char *cmd __attribute__ ((unused)), const char *param) {
426   int interval = 0;
427   char *ptr;
428   struct timer_entry *timer;
429
430   if (con->stop_handler) {
431     abuf_puts(&con->out, "Error, you cannot stack continous output commands\n");
432     return CONTINUE;
433   }
434
435   if (param == NULL || (ptr = strchr(param, ' ')) == NULL) {
436     abuf_puts(&con->out, "Missing parameters for repeat\n");
437     return CONTINUE;
438   }
439
440   ptr++;
441
442   interval = atoi(param);
443
444   timer = olsr_start_timer(interval * 1000, 0, true, &olsr_txt_repeat_timer, con, txt_repeattimer_cookie);
445   con->stop_handler = olsr_txt_repeat_stophandler;
446   con->stop_data[0] = timer;
447   con->stop_data[1] = strdup(ptr);
448   con->stop_data[2] = NULL;
449
450   /* split command/parameter and remember it */
451   ptr = strchr(con->stop_data[1], ' ');
452   if (ptr != NULL) {
453     /* found a parameter */
454     *ptr++ = 0;
455     con->stop_data[2] = ptr;
456   }
457
458   /* start command the first time */
459   if (olsr_com_handle_txtcommand(con, con->stop_data[1], con->stop_data[2]) != CONTINUE) {
460     con->stop_handler(con);
461   }
462   return CONTINOUS;
463 }
464
465 static enum olsr_txtcommand_result
466 olsr_txtcmd_version(struct comport_connection *con,
467     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused))) {
468   abuf_appendf(&con->out, " *** %s ***\n Build date: %s on %s\n http://www.olsr.org\n\n",
469       olsrd_version, build_date, build_host);
470   return CONTINUE;
471 }
472
473 static enum olsr_txtcommand_result
474 olsr_txtcmd_plugin(struct comport_connection *con, const char *cmd, const char *param) {
475   struct olsr_plugin *plugin;
476   struct list_iterator iterator;
477   char *para2 = NULL;
478   if (param == NULL || strcasecmp(param, "list") == 0) {
479     if (abuf_puts(&con->out, "Table:\n") < 0) {
480       return ABUF_ERROR;
481     }
482     OLSR_FOR_ALL_PLUGIN_ENTRIES(plugin, iterator) {
483       if (abuf_appendf(&con->out, " %-30s\t%s\t%s\n",
484           plugin->name, plugin->internal_active ? "active" : "", plugin->internal_dlhandle == NULL ? "static" : "") < 0) {
485         return ABUF_ERROR;
486       }
487     }
488     return CONTINUE;
489   }
490
491   para2 = strchr(param, ' ');
492   if (para2 == NULL) {
493     if (abuf_appendf(&con->out, "Error, missing or unknown parameter\n") < 0) {
494       return ABUF_ERROR;
495     }
496     return CONTINUE;
497   }
498   *para2++ = 0;
499
500   plugin = olsr_get_plugin(para2);
501   if (strcasecmp(param, "load") == 0) {
502     if (plugin != NULL) {
503       abuf_appendf(&con->out, "Plugin %s already loaded\n", para2);
504       return CONTINUE;
505     }
506     plugin = olsr_init_plugin(para2);
507     if (plugin != NULL) {
508       abuf_appendf(&con->out, "Plugin %s successfully loaded\n", para2);
509     }
510     else {
511       abuf_appendf(&con->out, "Could not load plugin %s\n", para2);
512     }
513     return CONTINUE;
514   }
515
516   if (plugin == NULL) {
517     if (abuf_appendf(&con->out, "Error, could not find plugin '%s'.\n", para2) < 0) {
518       return ABUF_ERROR;
519     }
520     return CONTINUE;
521   }
522   if (strcasecmp(param, "activate") == 0) {
523     if (plugin->internal_active) {
524       abuf_appendf(&con->out, "Plugin %s already active\n", para2);
525     }
526     else if (olsr_enable_plugin(plugin)) {
527       abuf_appendf(&con->out, "Could not activate plugin %s\n", para2);
528     }
529     else {
530       abuf_appendf(&con->out, "Plugin %s successfully activated\n", para2);
531     }
532   }
533   else if (strcasecmp(param, "deactivate") == 0) {
534     if (!plugin->internal_active) {
535       abuf_appendf(&con->out, "Plugin %s is not active\n", para2);
536     }
537     else if (olsr_disable_plugin(plugin)) {
538       abuf_appendf(&con->out, "Could not deactivate plugin %s\n", para2);
539     }
540     else {
541       abuf_appendf(&con->out, "Plugin %s successfully deactivated\n", para2);
542     }
543   }
544   else if (strcasecmp(param, "unload") == 0) {
545     if (plugin->internal_dlhandle == NULL) {
546       abuf_appendf(&con->out, "Plugin %s is static and cannot be unloaded\n", para2);
547     }
548     else if (olsr_exit_plugin(plugin)) {
549       abuf_appendf(&con->out, "Could not unload plugin %s\n", para2);
550     }
551     else {
552       abuf_appendf(&con->out, "Plugin %s successfully unloaded\n", para2);
553     }
554   }
555   else {
556     abuf_appendf(&con->out, "Unknown command '%s %s %s'.\n", cmd, param, para2);
557   }
558   return CONTINUE;
559 }