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