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