Merge branch 'master' into http-server
[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
43 #include "olsr_cfg.h"
44 #include "olsr_logging.h"
45 #include "olsr_cookie.h"
46 #include "olsr_ip_acl.h"
47 #include "olsr.h"
48 #include "scheduler.h"
49 #include "olsr_comport.h"
50 #include "olsr_comport_txt.h"
51 #include "plugin_loader.h"
52
53 struct txt_repeat_data {
54   struct timer_entry *timer;
55   struct autobuf *buf;
56   char *cmd;
57   char *param;
58   bool csv;
59 };
60
61 static struct avl_tree txt_normal_tree, txt_help_tree;
62 static struct olsr_cookie_info *txtcommand_cookie, *txt_repeattimer_cookie;
63
64 static enum olsr_txtcommand_result olsr_txtcmd_quit(
65     struct comport_connection *con, const char *cmd, const char *param);
66 static enum olsr_txtcommand_result olsr_txtcmd_help(
67     struct comport_connection *con, const char *cmd, const char *param);
68 static enum olsr_txtcommand_result olsr_txtcmd_echo(
69     struct comport_connection *con, const char *cmd, const char *param);
70 static enum olsr_txtcommand_result olsr_txtcmd_repeat(
71     struct comport_connection *con, const char *cmd, const char *param);
72 static enum olsr_txtcommand_result olsr_txtcmd_timeout(
73     struct comport_connection *con, const char *cmd, const char *param);
74 static enum olsr_txtcommand_result olsr_txtcmd_version(
75     struct comport_connection *con, const char *cmd, const char *param);
76 static enum olsr_txtcommand_result olsr_txtcmd_plugin(
77     struct comport_connection *con, const char *cmd, const char *param);
78 static enum olsr_txtcommand_result olsr_txtcmd_displayhelp(
79     struct comport_connection *con, const char *cmd, const char *param);
80
81
82 static const char *txt_internal_names[] = {
83   "quit",
84   "exit",
85   "help",
86   "echo",
87   "repeat",
88   "timeout",
89   "version",
90   "plugin"
91 };
92
93 static const char *txt_internal_help[] = {
94   "shuts down the terminal connection\n",
95   "shuts down the terminal connection\n",
96   "display the online help text\n",
97   "switchs the prompt on/off\n",
98   "repeat <interval> <command>: repeats a command every <interval> seconds\n",
99   "timeout <interval>: set the timeout interval to <interval> seconds, 0 means no timeout\n"
100   "displays the version of the olsrd\n"
101   "control olsr plugins dynamically, parameters are 'list', 'activate <plugin>', 'deactivate <plugin>', "
102     "'load <plugin>' and 'unload <plugin>'"
103 };
104
105 static olsr_txthandler txt_internal_handlers[] = {
106   olsr_txtcmd_quit,
107   olsr_txtcmd_quit,
108   olsr_txtcmd_help,
109   olsr_txtcmd_echo,
110   olsr_txtcmd_repeat,
111   olsr_txtcmd_timeout,
112   olsr_txtcmd_version,
113   olsr_txtcmd_plugin
114 };
115
116 static struct olsr_txtcommand *txt_internal_normalcmd[ARRAYSIZE(txt_internal_names)];
117 static struct olsr_txtcommand *txt_internal_helpcmd[ARRAYSIZE(txt_internal_names)];
118
119 void
120 olsr_com_init_txt(void) {
121   size_t i;
122
123   avl_init(&txt_normal_tree, &avl_comp_strcasecmp);
124   avl_init(&txt_help_tree, &avl_comp_strcasecmp);
125
126   txtcommand_cookie = olsr_alloc_cookie("comport txt commands", OLSR_COOKIE_TYPE_MEMORY);
127   olsr_cookie_set_memory_size(txtcommand_cookie, sizeof(struct olsr_txtcommand));
128
129   txt_repeattimer_cookie = olsr_alloc_cookie("txt repeat timer", OLSR_COOKIE_TYPE_TIMER);
130
131   for (i=0; i < ARRAYSIZE(txt_internal_names); i++) {
132     txt_internal_normalcmd[i] = olsr_com_add_normal_txtcommand(txt_internal_names[i], txt_internal_handlers[i]);
133     txt_internal_helpcmd[i] = olsr_com_add_help_txtcommand(txt_internal_names[i], olsr_txtcmd_displayhelp);
134   }
135 }
136
137 void
138 olsr_com_destroy_txt(void) {
139   size_t i;
140   for (i=0; i < ARRAYSIZE(txt_internal_names); i++) {
141     olsr_com_remove_normal_txtcommand(txt_internal_normalcmd[i]);
142     olsr_com_remove_help_txtcommand(txt_internal_helpcmd[i]);
143   }
144 }
145 struct olsr_txtcommand *
146 olsr_com_add_normal_txtcommand (const char *command, olsr_txthandler handler) {
147   struct olsr_txtcommand *txt;
148
149   txt = olsr_cookie_malloc(txtcommand_cookie);
150   txt->node.key = strdup(command);
151   txt->handler = handler;
152
153   avl_insert(&txt_normal_tree, &txt->node, false);
154   return txt;
155 }
156
157 struct olsr_txtcommand *
158 olsr_com_add_help_txtcommand (const char *command, olsr_txthandler handler) {
159   struct olsr_txtcommand *txt;
160
161   txt = olsr_cookie_malloc(txtcommand_cookie);
162   txt->node.key = strdup(command);
163   txt->handler = handler;
164
165   avl_insert(&txt_help_tree, &txt->node, false);
166   return txt;
167 }
168
169 void olsr_com_remove_normal_txtcommand (struct olsr_txtcommand *cmd) {
170   avl_delete(&txt_normal_tree, &cmd->node);
171   free(cmd->node.key);
172   olsr_cookie_free(txtcommand_cookie, cmd);
173 }
174
175 void olsr_com_remove_help_txtcommand (struct olsr_txtcommand *cmd) {
176   avl_delete(&txt_help_tree, &cmd->node);
177   free(cmd->node.key);
178   olsr_cookie_free(txtcommand_cookie, cmd);
179 }
180
181 enum olsr_txtcommand_result
182 olsr_com_handle_txtcommand(struct comport_connection *con, char *cmd, char *param) {
183   struct olsr_txtcommand *ptr;
184
185   ptr = (struct olsr_txtcommand *) avl_find(&txt_normal_tree, cmd);
186
187   OLSR_DEBUG(LOG_COMPORT, "Looking for command '%s': %s\n",
188     cmd, ptr ? "unknown" : "available");
189   if (ptr == NULL) {
190     return UNKNOWN;
191   }
192
193   if (ptr->acl) {
194     if (!ip_acl_acceptable(ptr->acl, &con->addr, olsr_cnf->ip_version)) {
195       return UNKNOWN;
196     }
197   }
198
199   return ptr->handler(con, cmd, param);
200 }
201
202 void olsr_com_parse_txt(struct comport_connection *con,
203     unsigned int flags  __attribute__ ((unused))) {
204   static char defaultCommand[] = "/link/neigh/topology/hna/mid/routes";
205   static char tmpbuf[128];
206
207   enum olsr_txtcommand_result res;
208   char *eol;
209   int len;
210   bool processedCommand = false, chainCommands = false;
211   uint32_t old_timeout;
212
213   old_timeout = con->timeout_value;
214
215   /* loop over input */
216   while (con->in.len > 0 && con->state == INTERACTIVE) {
217     char *para = NULL, *cmd = NULL, *next = NULL;
218
219     /* search for end of line */
220     eol = memchr(con->in.buf, '\n', con->in.len);
221
222     if (eol == NULL) {
223       break;
224     }
225
226     /* terminate line with a 0 */
227     if (eol != con->in.buf && eol[-1] == '\r') {
228       eol[-1] = 0;
229     }
230     *eol++ = 0;
231
232     /* handle line */
233     OLSR_DEBUG(LOG_COMPORT, "Interactive console: %s\n", con->in.buf);
234     cmd = &con->in.buf[0];
235     processedCommand = true;
236
237     /* apply default command */
238     if (strcmp(cmd, "/") == 0) {
239       strcpy(tmpbuf, defaultCommand);
240       cmd = tmpbuf;
241     }
242
243     if (cmd[0] == '/') {
244       cmd++;
245       chainCommands = true;
246     }
247     while (cmd) {
248       len = con->out.len;
249
250       /* handle difference between multicommand and singlecommand mode */
251       if (chainCommands) {
252         next = strchr(cmd, '/');
253         if (next) {
254           *next++ = 0;
255         }
256       }
257       para = strchr(cmd, ' ');
258       if (para != NULL) {
259         *para++ = 0;
260       }
261
262       /* if we are doing continous output, stop it ! */
263       if (con->stop_handler) {
264         con->stop_handler(con);
265         con->stop_handler = NULL;
266       }
267
268       if (strlen(cmd) != 0) {
269         res = olsr_com_handle_txtcommand(con, cmd, para);
270         switch (res) {
271           case CONTINUE:
272             break;
273           case CONTINOUS:
274             break;
275           case ABUF_ERROR:
276             con->out.len = len;
277             abuf_appendf(&con->out,
278                 "Error in autobuffer during command '%s'.\n", cmd);
279             break;
280           case UNKNOWN:
281             con->out.len = len;
282             abuf_appendf(&con->out, "Error, unknown command '%s'\n", cmd);
283             break;
284           case QUIT:
285             con->state = SEND_AND_QUIT;
286             break;
287         }
288         /* put an empty line behind each command */
289         if (con->show_echo) {
290           abuf_puts(&con->out, "\n");
291         }
292       }
293       cmd = next;
294     }
295
296     /* remove line from input buffer */
297     abuf_pull(&con->in, eol - con->in.buf);
298
299     if (con->in.buf[0] == '/') {
300       /* end of multiple command line */
301       con->state = SEND_AND_QUIT;
302     }
303   }
304
305   /* reset timeout */
306   olsr_change_timer(con->timeout, con->timeout_value, 0, false);
307
308   /* print prompt */
309   if (processedCommand && con->state == INTERACTIVE && con->show_echo) {
310     abuf_puts(&con->out, "> ");
311   }
312 }
313
314 static enum olsr_txtcommand_result
315 olsr_txtcmd_quit(struct comport_connection *con __attribute__ ((unused)),
316     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused))) {
317   return QUIT;
318 }
319
320 static enum olsr_txtcommand_result
321 olsr_txtcmd_displayhelp(struct comport_connection *con,
322     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused))) {
323   size_t i;
324
325   for (i=0; i<ARRAYSIZE(txt_internal_names); i++) {
326     if (strcasecmp(txt_internal_names[i], cmd) == 0) {
327       abuf_puts(&con->out, txt_internal_help[i]);
328       return CONTINUE;
329     }
330   }
331   return UNKNOWN;
332 }
333
334 static enum olsr_txtcommand_result
335 olsr_txtcmd_help(struct comport_connection *con,
336     const char *cmd __attribute__ ((unused)), const char *param) {
337   struct olsr_txtcommand *ptr;
338
339   if (param != NULL) {
340     ptr = (struct olsr_txtcommand *)avl_find(&txt_help_tree, cmd);
341     if (ptr != NULL) {
342       return ptr->handler(con, param, NULL);
343     }
344     return UNKNOWN;
345   }
346
347   if (abuf_puts(&con->out, "Known commands:\n") < 0) {
348     return ABUF_ERROR;
349   }
350
351   ptr = (struct olsr_txtcommand *)avl_walk_first(&txt_normal_tree);
352   while (ptr) {
353     if (abuf_appendf(&con->out, "  %s\n", (char *)ptr->node.key) < 0) {
354       return ABUF_ERROR;
355     }
356     ptr = (struct olsr_txtcommand *)avl_walk_next(&ptr->node);
357   }
358
359   if (abuf_puts(&con->out, "Use 'help <command> to see a help text for a certain command\n") < 0) {
360     return ABUF_ERROR;
361   }
362   return CONTINUE;
363 }
364
365 static enum olsr_txtcommand_result
366 olsr_txtcmd_echo(struct comport_connection *con,
367     const char *cmd __attribute__ ((unused)), const char *param) {
368   if(param == NULL) {
369         if (abuf_appendf(&con->out, "Error, echo needs an argument. Try 'on' or 'off'\n") < 0) {
370       return ABUF_ERROR;
371     }
372         return CONTINUE;
373   }
374   
375   if (strcasecmp(param, "on") == 0) {
376     con->show_echo = true;
377   }
378   else if (strcasecmp(param, "off") == 0) {
379     con->show_echo = false;
380   }
381   else {
382     if (abuf_appendf(&con->out, "Error, unknown argument '%s' for echo. Try 'on' or 'off'\n", param) < 0) {
383       return ABUF_ERROR;
384     }
385   }
386   return CONTINUE;
387 }
388
389 static enum olsr_txtcommand_result
390 olsr_txtcmd_timeout(struct comport_connection *con,
391     const char *cmd __attribute__ ((unused)), const char *param) {
392   con->timeout_value = (uint32_t)strtoul(param, NULL, 10) * 1000;
393   return CONTINUE;
394 }
395
396 static void olsr_txt_repeat_stophandler(struct comport_connection *con) {
397   olsr_stop_timer((struct timer_entry *)con->stop_data[0]);
398   free(con->stop_data[1]);
399
400   con->stop_handler = NULL;
401   con->stop_data[0] = NULL;
402   con->stop_data[1] = NULL;
403   con->stop_data[2] = NULL;
404 }
405
406 static void olsr_txt_repeat_timer(void *data) {
407   struct comport_connection *con = data;
408
409   if (olsr_com_handle_txtcommand(con, con->stop_data[1], con->stop_data[2]) != CONTINUE) {
410     con->stop_handler(con);
411   }
412   olsr_com_activate_output(con);
413 }
414
415 static enum olsr_txtcommand_result
416 olsr_txtcmd_repeat(struct comport_connection *con,
417     const char *cmd __attribute__ ((unused)), const char *param) {
418   int interval = 0;
419   char *ptr;
420   struct timer_entry *timer;
421
422   if (con->stop_handler) {
423     abuf_puts(&con->out, "Error, you cannot stack continous output commands\n");
424     return CONTINUE;
425   }
426
427   if (param == NULL || (ptr = strchr(param, ' ')) == NULL) {
428     abuf_puts(&con->out, "Missing parameters for repeat\n");
429     return CONTINUE;
430   }
431
432   ptr++;
433
434   interval = atoi(param);
435
436   timer = olsr_start_timer(interval * 1000, 0, true, &olsr_txt_repeat_timer, con, txt_repeattimer_cookie);
437   con->stop_handler = olsr_txt_repeat_stophandler;
438   con->stop_data[0] = timer;
439   con->stop_data[1] = strdup(ptr);
440   con->stop_data[2] = NULL;
441
442   /* split command/parameter and remember it */
443   ptr = strchr(con->stop_data[1], ' ');
444   if (ptr != NULL) {
445     /* found a parameter */
446     *ptr++ = 0;
447     con->stop_data[2] = ptr;
448   }
449
450   /* start command the first time */
451   if (olsr_com_handle_txtcommand(con, con->stop_data[1], con->stop_data[2]) != CONTINUE) {
452     con->stop_handler(con);
453   }
454   return CONTINOUS;
455 }
456
457 static enum olsr_txtcommand_result
458 olsr_txtcmd_version(struct comport_connection *con,
459     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused))) {
460   abuf_appendf(&con->out, " *** %s ***\n Build date: %s on %s\n http://www.olsr.org\n\n",
461       olsrd_version, build_date, build_host);
462   return CONTINUE;
463 }
464
465 static enum olsr_txtcommand_result
466 olsr_txtcmd_plugin(struct comport_connection *con, const char *cmd, const char *param) {
467   struct olsr_plugin *plugin;
468   char *para2 = NULL;
469   if (param == NULL || strcasecmp(param, "list") == 0) {
470     if (abuf_puts(&con->out, "Table:\n") < 0) {
471       return ABUF_ERROR;
472     }
473     OLSR_FOR_ALL_PLUGIN_ENTRIES(plugin) {
474       if (abuf_appendf(&con->out, " %-30s\t%s\t%s\n",
475           plugin->name, plugin->internal_active ? "active" : "", plugin->internal_dlhandle == NULL ? "static" : "") < 0) {
476         return ABUF_ERROR;
477       }
478     } OLSR_FOR_ALL_PLUGIN_ENTRIES_END()
479     return CONTINUE;
480   }
481
482   para2 = strchr(param, ' ');
483   if (para2 == NULL) {
484     if (abuf_appendf(&con->out, "Error, missing or unknown parameter\n") < 0) {
485       return ABUF_ERROR;
486     }
487     return CONTINUE;
488   }
489   *para2++ = 0;
490
491   plugin = olsr_get_plugin(para2);
492   if (strcasecmp(param, "load") == 0) {
493     if (plugin != NULL) {
494       abuf_appendf(&con->out, "Plugin %s already loaded\n", para2);
495       return CONTINUE;
496     }
497     plugin = olsr_load_plugin(para2);
498     if (plugin != NULL) {
499       abuf_appendf(&con->out, "Plugin %s successfully loaded\n", para2);
500     }
501     else {
502       abuf_appendf(&con->out, "Could not load plugin %s\n", para2);
503     }
504     return CONTINUE;
505   }
506
507   if (plugin == NULL) {
508     if (abuf_appendf(&con->out, "Error, could not find plugin '%s'.\n", para2) < 0) {
509       return ABUF_ERROR;
510     }
511     return CONTINUE;
512   }
513   if (strcasecmp(param, "activate") == 0) {
514     if (plugin->internal_active) {
515       abuf_appendf(&con->out, "Plugin %s already active\n", para2);
516     }
517     else if (olsr_activate_plugin(plugin)) {
518       abuf_appendf(&con->out, "Could not activate plugin %s\n", para2);
519     }
520     else {
521       abuf_appendf(&con->out, "Plugin %s successfully activated\n", para2);
522     }
523   }
524   else if (strcasecmp(param, "deactivate") == 0) {
525     if (!plugin->internal_active) {
526       abuf_appendf(&con->out, "Plugin %s is not active\n", para2);
527     }
528     else if (olsr_deactivate_plugin(plugin)) {
529       abuf_appendf(&con->out, "Could not deactivate plugin %s\n", para2);
530     }
531     else {
532       abuf_appendf(&con->out, "Plugin %s successfully deactivated\n", para2);
533     }
534   }
535   else if (strcasecmp(param, "unload") == 0) {
536     if (plugin->internal_dlhandle == NULL) {
537       abuf_appendf(&con->out, "Plugin %s is static and cannot be unloaded\n", para2);
538     }
539     else if (olsr_unload_plugin(plugin)) {
540       abuf_appendf(&con->out, "Could not unload plugin %s\n", para2);
541     }
542     else {
543       abuf_appendf(&con->out, "Plugin %s successfully unloaded\n", para2);
544     }
545   }
546   else {
547     abuf_appendf(&con->out, "Unknown command '%s %s %s'.\n", cmd, param, para2);
548   }
549   return CONTINUE;
550 }