73d25611827608a4ee346d3ecbc248223086f129
[oonf.git] / src-api / tools / olsr_telnet.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2013, 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
42 #include "common/common_types.h"
43 #include "common/avl.h"
44 #include "common/avl_comp.h"
45 #include "common/netaddr.h"
46 #include "common/netaddr_acl.h"
47
48 #include "config/cfg_schema.h"
49
50 #include "core/olsr_logging.h"
51 #include "core/olsr_class.h"
52 #include "core/olsr_plugins.h"
53 #include "core/olsr_stream_socket.h"
54 #include "core/olsr_timer.h"
55 #include "core/olsr_subsystem.h"
56
57 #include "tools/olsr_cfg.h"
58 #include "tools/olsr_telnet.h"
59
60 /* static function prototypes */
61 static int _init(void);
62 static void _cleanup(void);
63
64 static void _call_stop_handler(struct olsr_telnet_data *data);
65 static void _cb_config_changed(void);
66 static int _cb_telnet_init(struct olsr_stream_session *);
67 static void _cb_telnet_cleanup(struct olsr_stream_session *);
68 static void _cb_telnet_create_error(struct olsr_stream_session *,
69     enum olsr_stream_errors);
70 static enum olsr_stream_session_state _cb_telnet_receive_data(
71     struct olsr_stream_session *);
72 static enum olsr_telnet_result _telnet_handle_command(
73     struct olsr_telnet_data *);
74 static struct olsr_telnet_command *_check_telnet_command(
75     struct olsr_telnet_data *data, const char *name,
76     struct olsr_telnet_command *cmd);
77
78 static void _cb_telnet_repeat_timer(void *data);
79 static enum olsr_telnet_result _cb_telnet_quit(struct olsr_telnet_data *data);
80 static enum olsr_telnet_result _cb_telnet_help(struct olsr_telnet_data *data);
81 static enum olsr_telnet_result _cb_telnet_echo(struct olsr_telnet_data *data);
82 static enum olsr_telnet_result _cb_telnet_repeat(struct olsr_telnet_data *data);
83 static enum olsr_telnet_result _cb_telnet_timeout(struct olsr_telnet_data *data);
84 static enum olsr_telnet_result _cb_telnet_version(struct olsr_telnet_data *data);
85 static enum olsr_telnet_result _cb_telnet_plugin(struct olsr_telnet_data *data);
86
87 /* configuration of telnet server */
88 static struct cfg_schema_entry telnet_entries[] = {
89   CFG_MAP_ACL_V46(olsr_stream_managed_config,
90       acl, "acl", "127.0.0.1", "Access control list for telnet interface"),
91   CFG_MAP_NETADDR_V4(olsr_stream_managed_config,
92       bindto_v4, "bindto_v4", "127.0.0.1", "Bind telnet ipv4 socket to this address", false, true),
93   CFG_MAP_NETADDR_V6(olsr_stream_managed_config,
94       bindto_v6, "bindto_v6", "::1", "Bind telnet ipv6 socket to this address", false, true),
95   CFG_MAP_INT_MINMAX(olsr_stream_managed_config,
96       port, "port", "2006", "Network port for telnet interface", 1, 65535),
97 };
98
99 static struct cfg_schema_section telnet_section = {
100   .type = "telnet",
101   .mode = CFG_SSMODE_UNNAMED_OPTIONAL_STARTUP_TRIGGER,
102   .help = "Settings for the telnet interface",
103   .cb_delta_handler = _cb_config_changed,
104   .entries = telnet_entries,
105   .entry_count = ARRAYSIZE(telnet_entries),
106 };
107
108 /* built-in telnet commands */
109 static struct olsr_telnet_command _builtin[] = {
110   TELNET_CMD("quit", _cb_telnet_quit, "Ends telnet session"),
111   TELNET_CMD("exit", _cb_telnet_quit, "Ends telnet session"),
112   TELNET_CMD("help", _cb_telnet_help,
113       "help: Display the online help text and a list of commands"),
114   TELNET_CMD("echo", _cb_telnet_echo,"echo <string>: Prints a string"),
115   TELNET_CMD("repeat", _cb_telnet_repeat,
116       "repeat <seconds> <command>: Repeats a telnet command every X seconds"),
117   TELNET_CMD("timeout", _cb_telnet_timeout,
118       "timeout <seconds> :Sets telnet session timeout"),
119   TELNET_CMD("version", _cb_telnet_version, "Displays version of the program"),
120   TELNET_CMD("plugin", _cb_telnet_plugin,
121         "control plugins dynamically, parameters are 'list',"
122         "'load <plugin>' and 'unload <plugin>'"),
123 };
124
125 /* subsystem definition */
126 struct oonf_subsystem oonf_telnet_subsystem = {
127   .init = _init,
128   .cleanup = _cleanup,
129   .cfg_section = &telnet_section,
130 };
131
132 /* telnet session handling */
133 static struct olsr_class _telnet_memcookie = {
134   .name = "telnet session",
135   .size = sizeof(struct olsr_telnet_session),
136 };
137 static struct olsr_timer_info _telnet_repeat_timerinfo = {
138   .name = "txt repeat timer",
139   .callback = _cb_telnet_repeat_timer,
140   .periodic = true,
141 };
142
143 static struct olsr_stream_managed _telnet_managed = {
144   .config = {
145     .session_timeout = 120000, /* 120 seconds */
146     .maximum_input_buffer = 4096,
147     .allowed_sessions = 3,
148     .memcookie = &_telnet_memcookie,
149     .init = _cb_telnet_init,
150     .cleanup = _cb_telnet_cleanup,
151     .receive_data = _cb_telnet_receive_data,
152     .create_error = _cb_telnet_create_error,
153   },
154 };
155
156 struct avl_tree telnet_cmd_tree;
157
158 /**
159  * Initialize telnet subsystem
160  * @return always returns 0
161  */
162 static int
163 _init(void) {
164   size_t i;
165
166   olsr_class_add(&_telnet_memcookie);
167   olsr_timer_add(&_telnet_repeat_timerinfo );
168
169   olsr_stream_add_managed(&_telnet_managed);
170
171   /* initialize telnet commands */
172   avl_init(&telnet_cmd_tree, avl_comp_strcasecmp, false);
173   for (i=0; i<ARRAYSIZE(_builtin); i++) {
174     olsr_telnet_add(&_builtin[i]);
175   }
176   return 0;
177 }
178
179 /**
180  * Cleanup all allocated data of telnet subsystem
181  */
182 static void
183 _cleanup(void) {
184   olsr_stream_remove_managed(&_telnet_managed, true);
185   olsr_class_remove(&_telnet_memcookie);
186 }
187
188 /**
189  * Add a new telnet command to telnet subsystem
190  * @param command pointer to initialized telnet command object
191  * @return -1 if an error happened, 0 otherwise
192  */
193 int
194 olsr_telnet_add(struct olsr_telnet_command *command) {
195   command->_node.key = command->command;
196   if (avl_insert(&telnet_cmd_tree, &command->_node)) {
197     return -1;
198   }
199   return 0;
200 }
201
202 /**
203  * Remove a telnet command from telnet subsystem
204  * @param command pointer to telnet command object
205  */
206 void
207 olsr_telnet_remove(struct olsr_telnet_command *command) {
208   avl_remove(&telnet_cmd_tree, &command->_node);
209 }
210
211 /**
212  * Stop a currently running continuous telnet command
213  * @param data pointer to telnet data
214  */
215 void
216 olsr_telnet_stop(struct olsr_telnet_data *data) {
217   _call_stop_handler(data);
218   data->show_echo = true;
219   abuf_puts(data->out, "> ");
220   olsr_telnet_flush_session(data);
221 }
222
223 /**
224  * Execute a telnet command.
225  * @param cmd pointer to name of command
226  * @param para pointer to parameter string
227  * @param out buffer for output of command
228  * @param remote pointer to address which triggers the execution
229  * @return result of telnet command
230  */
231 enum olsr_telnet_result
232 olsr_telnet_execute(const char *cmd, const char *para,
233     struct autobuf *out, struct netaddr *remote) {
234   struct olsr_telnet_data data;
235   enum olsr_telnet_result result;
236
237   memset(&data, 0, sizeof(data));
238   data.command = cmd;
239   data.parameter = para;
240   data.out = out;
241   data.remote = remote;
242
243   result = _telnet_handle_command(&data);
244   olsr_telnet_stop(&data);
245   return result;
246 }
247
248 /**
249  * Handler for configuration changes
250  */
251 static void
252 _cb_config_changed(void) {
253   struct olsr_stream_managed_config config;
254
255   /* generate binary config */
256   memset(&config, 0, sizeof(config));
257   if (cfg_schema_tobin(&config, telnet_section.post,
258       telnet_entries, ARRAYSIZE(telnet_entries))) {
259     /* error in conversion */
260     OLSR_WARN(LOG_TELNET, "Cannot map telnet config to binary data");
261     goto apply_config_failed;
262   }
263
264   if (olsr_stream_apply_managed(&_telnet_managed, &config)) {
265     /* error while updating sockets */
266     goto apply_config_failed;
267   }
268
269   /* fall through */
270 apply_config_failed:
271   netaddr_acl_remove(&config.acl);
272 }
273
274 /**
275  * Initialization of incoming telnet session
276  * @param session pointer to TCP session
277  * @return 0
278  */
279 static int
280 _cb_telnet_init(struct olsr_stream_session *session) {
281   struct olsr_telnet_session *telnet_session;
282
283   /* get telnet session pointer */
284   telnet_session = (struct olsr_telnet_session *)session;
285
286   telnet_session->data.show_echo = true;
287   telnet_session->data.stop_handler = NULL;
288   telnet_session->data.timeout_value = 120000;
289   telnet_session->data.out = &telnet_session->session.out;
290   telnet_session->data.remote = &telnet_session->session.remote_address;
291
292   list_init_head(&telnet_session->data.cleanup_list);
293
294   return 0;
295 }
296
297 /**
298  * Cleanup of telnet session
299  * @param session pointer to TCP session
300  */
301 static void
302 _cb_telnet_cleanup(struct olsr_stream_session *session) {
303   struct olsr_telnet_session *telnet_session;
304   struct olsr_telnet_cleanup *handler, *it;
305
306   /* get telnet session pointer */
307   telnet_session = (struct olsr_telnet_session *)session;
308
309   /* stop continuous commands */
310   olsr_telnet_stop(&telnet_session->data);
311
312   /* call all cleanup handlers */
313   list_for_each_element_safe(&telnet_session->data.cleanup_list, handler, node, it) {
314     /* remove from list first */
315     olsr_telnet_remove_cleanup(handler);
316
317     /* after this command the handler pointer might not be valid anymore */
318     handler->cleanup_handler(handler);
319   }
320 }
321
322 /**
323  * Create error string for telnet session
324  * @param session pointer to TCP stream
325  * @param error TCP error code to generate
326  */
327 static void
328 _cb_telnet_create_error(struct olsr_stream_session *session,
329     enum olsr_stream_errors error) {
330   switch(error) {
331     case STREAM_REQUEST_TOO_LARGE:
332       abuf_puts(&session->out, "Input buffer overflow, ending connection\n");
333       break;
334     case STREAM_SERVICE_UNAVAILABLE:
335       abuf_puts(&session->out, "Telnet service unavailable, too many sessions\n");
336       break;
337     case STREAM_REQUEST_FORBIDDEN:
338     default:
339       /* no message */
340       break;
341   }
342 }
343
344 /**
345  * Stop a continuous telnet command
346  * @param data pointer to telnet data
347  */
348 static void
349 _call_stop_handler(struct olsr_telnet_data *data) {
350   void (*stop_handler)(struct olsr_telnet_data *);
351
352   if (data->stop_handler) {
353     /*
354      * make sure that stop_handler is not set anymore when
355      * it is called.
356      */
357     stop_handler = data->stop_handler;
358     data->stop_handler = NULL;
359
360     /* call stop handler */
361     stop_handler(data);
362   }
363 }
364
365 /**
366  * Handler for receiving data from telnet session
367  * @param session pointer to TCP session
368  * @return TCP session state
369  */
370 static enum olsr_stream_session_state
371 _cb_telnet_receive_data(struct olsr_stream_session *session) {
372   static const char defaultCommand[] = "/link/neigh/topology/hna/mid/routes";
373   static char tmpbuf[128];
374
375   struct olsr_telnet_session *telnet_session;
376   enum olsr_telnet_result cmd_result;
377   char *eol;
378   int len;
379   bool processedCommand = false, chainCommands = false;
380
381   /* get telnet session pointer */
382   telnet_session = (struct olsr_telnet_session *)session;
383
384   /* loop over input */
385   while (abuf_getlen(&session->in) > 0) {
386     char *para = NULL, *cmd = NULL, *next = NULL;
387
388     /* search for end of line */
389     eol = memchr(abuf_getptr(&session->in), '\n', abuf_getlen(&session->in));
390
391     if (eol == NULL) {
392       break;
393     }
394
395     /* terminate line with a 0 */
396     if (eol != abuf_getptr(&session->in) && eol[-1] == '\r') {
397       eol[-1] = 0;
398     }
399     *eol++ = 0;
400
401     /* handle line */
402     OLSR_DEBUG(LOG_TELNET, "Interactive console: %s\n", abuf_getptr(&session->in));
403     cmd = abuf_getptr(&session->in);
404     processedCommand = true;
405
406     /* apply default command */
407     if (strcmp(cmd, "/") == 0) {
408       strcpy(tmpbuf, defaultCommand);
409       cmd = tmpbuf;
410     }
411
412     if (cmd[0] == '/') {
413       cmd++;
414       chainCommands = true;
415     }
416     while (cmd) {
417       len = abuf_getlen(&session->out);
418
419       /* handle difference between multicommand and singlecommand mode */
420       if (chainCommands) {
421         next = strchr(cmd, '/');
422         if (next) {
423           *next++ = 0;
424         }
425       }
426       para = strchr(cmd, ' ');
427       if (para != NULL) {
428         *para++ = 0;
429       }
430
431       /* if we are doing continous output, stop it ! */
432       _call_stop_handler(&telnet_session->data);
433
434       if (strlen(cmd) != 0) {
435         OLSR_DEBUG(LOG_TELNET, "Processing telnet command: '%s' '%s'",
436             cmd, para);
437
438         telnet_session->data.command = cmd;
439         telnet_session->data.parameter = para;
440
441         cmd_result = _telnet_handle_command(&telnet_session->data);
442         if (abuf_has_failed(telnet_session->data.out)) {
443           cmd_result = TELNET_RESULT_INTERNAL_ERROR;
444         }
445
446         switch (cmd_result) {
447           case TELNET_RESULT_ACTIVE:
448             break;
449           case TELNET_RESULT_CONTINOUS:
450             telnet_session->data.show_echo = false;
451             break;
452           case _TELNET_RESULT_UNKNOWN_COMMAND:
453             abuf_setlen(&session->out, len);
454             abuf_appendf(&session->out, "Error, unknown command '%s'\n", cmd);
455             break;
456           case TELNET_RESULT_QUIT:
457             return STREAM_SESSION_SEND_AND_QUIT;
458           case TELNET_RESULT_INTERNAL_ERROR:
459           default:
460             /* reset stream */
461             abuf_setlen(&session->out, len);
462             abuf_appendf(&session->out,
463                 "Error in autobuffer during command '%s'.\n", cmd);
464             break;
465         }
466         /* put an empty line behind each command */
467         if (telnet_session->data.show_echo) {
468           abuf_puts(&session->out, "\n");
469         }
470       }
471       cmd = next;
472     }
473
474     /* remove line from input buffer */
475     abuf_pull(&session->in, eol - abuf_getptr(&session->in));
476
477     if (abuf_getptr(&session->in)[0] == '/') {
478       /* end of multiple command line */
479       return STREAM_SESSION_SEND_AND_QUIT;
480     }
481   }
482
483   /* reset timeout */
484   olsr_stream_set_timeout(session, telnet_session->data.timeout_value);
485
486   /* print prompt */
487   if (processedCommand && session->state == STREAM_SESSION_ACTIVE
488       && telnet_session->data.show_echo) {
489     abuf_puts(&session->out, "> ");
490   }
491
492   return STREAM_SESSION_ACTIVE;
493 }
494
495 /**
496  * Helper function to call telnet command handler
497  * @param data pointer to telnet data
498  * @return telnet command result
499  */
500 static enum olsr_telnet_result
501 _telnet_handle_command(struct olsr_telnet_data *data) {
502   struct olsr_telnet_command *cmd;
503 #if OONF_LOGGING_LEVEL >= OONF_LOGGING_LEVEL_INFO
504   struct netaddr_str buf;
505 #endif
506   cmd = _check_telnet_command(data, data->command, NULL);
507   if (cmd == NULL) {
508     return _TELNET_RESULT_UNKNOWN_COMMAND;
509   }
510
511   OLSR_INFO(LOG_TELNET, "Executing command from %s: %s %s",
512       netaddr_to_string(&buf, data->remote), data->command,
513       data->parameter == NULL ? "" : data->parameter);
514   return cmd->handler(data);
515 }
516
517 /**
518  * Checks for existing (and allowed) telnet command.
519  * Either name or cmd should be NULL, but not both.
520  * @param data pointer to telnet data
521  * @param name pointer to command name (might be NULL)
522  * @param cmd pointer to telnet command object (might be NULL)
523  * @return telnet command object or NULL if not found or forbidden
524  */
525 static struct olsr_telnet_command *
526 _check_telnet_command(struct olsr_telnet_data *data,
527     const char *name, struct olsr_telnet_command *cmd) {
528 #if OONF_LOGGING_LEVEL >= OONF_LOGGING_LEVEL_DEBUG
529   struct netaddr_str buf;
530 #endif
531
532   // TODO: split into two functions
533   if (cmd == NULL) {
534     cmd = avl_find_element(&telnet_cmd_tree, name, cmd, _node);
535     if (cmd == NULL) {
536       return cmd;
537     }
538   }
539   if (cmd->acl == NULL) {
540     return cmd;
541   }
542
543   if (!netaddr_acl_check_accept(cmd->acl, data->remote)) {
544     OLSR_DEBUG(LOG_TELNET, "Blocked telnet command '%s' to '%s' because of acl",
545         cmd->command, netaddr_to_string(&buf, data->remote));
546     return NULL;
547   }
548   return cmd;
549 }
550
551 /**
552  * Telnet command 'quit'
553  * @param data pointer to telnet data
554  * @return telnet command result
555  */
556 static enum olsr_telnet_result
557 _cb_telnet_quit(struct olsr_telnet_data *data __attribute__((unused))) {
558   return TELNET_RESULT_QUIT;
559 }
560
561 /**
562  * Telnet command 'help'
563  * @param data pointer to telnet data
564  * @return telnet command result
565  */
566 static enum olsr_telnet_result
567 _cb_telnet_help(struct olsr_telnet_data *data) {
568   struct olsr_telnet_command *cmd;
569
570   if (data->parameter != NULL && data->parameter[0] != 0) {
571     cmd = _check_telnet_command(data, data->parameter, NULL);
572     if (cmd == NULL) {
573       abuf_appendf(data->out, "No help text found for command: %s\n", data->parameter);
574       return TELNET_RESULT_ACTIVE;
575     }
576
577     if (cmd->help_handler) {
578       cmd->help_handler(data);
579     }
580     else {
581       if (abuf_appendf(data->out, "%s", cmd->help) < 0) {
582         return TELNET_RESULT_INTERNAL_ERROR;
583       }
584     }
585     return TELNET_RESULT_ACTIVE;
586   }
587
588   if (abuf_puts(data->out, "Known commands:\n") < 0) {
589     return TELNET_RESULT_INTERNAL_ERROR;
590   }
591
592   avl_for_each_element(&telnet_cmd_tree, cmd, _node) {
593     if (_check_telnet_command(data, NULL, cmd)) {
594       if (abuf_appendf(data->out, "  %s\n", cmd->command) < 0) {
595         return TELNET_RESULT_INTERNAL_ERROR;
596       }
597     }
598   }
599
600   if (abuf_puts(data->out, "Use 'help <command> to see a help text for one command\n") < 0) {
601     return TELNET_RESULT_INTERNAL_ERROR;
602   }
603   return TELNET_RESULT_ACTIVE;
604 }
605
606 /**
607  * Telnet command 'echo'
608  * @param data pointer to telnet data
609  * @return telnet command result
610  */
611 static enum olsr_telnet_result
612 _cb_telnet_echo(struct olsr_telnet_data *data) {
613
614   if (abuf_appendf(data->out, "%s\n",
615       data->parameter == NULL ? "" : data->parameter) < 0) {
616     return TELNET_RESULT_INTERNAL_ERROR;
617   }
618   return TELNET_RESULT_ACTIVE;
619 }
620
621 /**
622  * Telnet command 'timeout'
623  * @param data pointer to telnet data
624  * @return telnet command result
625  */
626 static enum olsr_telnet_result
627 _cb_telnet_timeout(struct olsr_telnet_data *data) {
628   if (data->parameter == NULL) {
629     data->timeout_value = 0;
630   }
631   else {
632     data->timeout_value = (uint32_t)strtoul(data->parameter, NULL, 10) * 1000;
633   }
634   return TELNET_RESULT_ACTIVE;
635 }
636
637 /**
638  * Stop handler for repeating telnet commands
639  * @param data pointer to telnet data
640  */
641 static void
642 _cb_telnet_repeat_stophandler(struct olsr_telnet_data *data) {
643   olsr_timer_stop((struct olsr_timer_entry *)data->stop_data[0]);
644   free(data->stop_data[0]);
645   free(data->stop_data[1]);
646
647   data->stop_handler = NULL;
648   data->stop_data[0] = NULL;
649   data->stop_data[1] = NULL;
650   data->stop_data[2] = NULL;
651 }
652
653 /**
654  * Timer event handler for repeating telnet commands
655  * @param ptr pointer to custom data
656  */
657 static void
658 _cb_telnet_repeat_timer(void *ptr) {
659   struct olsr_telnet_data *telnet_data = ptr;
660   struct olsr_telnet_session *session;
661
662   /* set command/parameter with repeat settings */
663   telnet_data->command = telnet_data->stop_data[1];
664   telnet_data->parameter = telnet_data->stop_data[2];
665
666   if (_telnet_handle_command(telnet_data) != TELNET_RESULT_ACTIVE) {
667     _call_stop_handler(telnet_data);
668   }
669
670   /* reconstruct original session pointer */
671   session = container_of(telnet_data, struct olsr_telnet_session, data);
672   olsr_stream_flush(&session->session);
673 }
674
675 /**
676  * Telnet command 'repeat'
677  * @param data pointer to telnet data
678  * @return telnet command result
679  */
680 static enum olsr_telnet_result
681 _cb_telnet_repeat(struct olsr_telnet_data *data) {
682   struct olsr_timer_entry *timer;
683   int interval = 0;
684   char *ptr = NULL;
685
686   if (data->stop_handler) {
687     abuf_puts(data->out, "Error, you cannot stack continous output commands\n");
688     return TELNET_RESULT_ACTIVE;
689   }
690
691   if (data->parameter == NULL || (ptr = strchr(data->parameter, ' ')) == NULL) {
692     abuf_puts(data->out, "Missing parameters for repeat\n");
693     return TELNET_RESULT_ACTIVE;
694   }
695
696   ptr++;
697
698   interval = atoi(data->parameter);
699   if (interval < 1) {
700     abuf_puts(data->out, "Please specify an interval >= 1\n");
701     return TELNET_RESULT_ACTIVE;
702   }
703
704   timer = calloc(1, sizeof(*timer));
705   if (timer == NULL) {
706     return TELNET_RESULT_INTERNAL_ERROR;
707   }
708
709   timer->cb_context = data;
710   timer->info = &_telnet_repeat_timerinfo;
711   olsr_timer_start(timer, interval * 1000);
712
713   data->stop_handler = _cb_telnet_repeat_stophandler;
714   data->stop_data[0] = timer;
715   data->stop_data[1] = strdup(ptr);
716   data->stop_data[2] = NULL;
717
718   /* split command/parameter and remember it */
719   ptr = strchr(data->stop_data[1], ' ');
720   if (ptr != NULL) {
721     /* found a parameter */
722     *ptr++ = 0;
723     data->stop_data[2] = ptr;
724   }
725
726   /* start command the first time */
727   data->command = data->stop_data[1];
728   data->parameter = data->stop_data[2];
729
730   if (_telnet_handle_command(data) != TELNET_RESULT_ACTIVE) {
731     _call_stop_handler(data);
732   }
733
734   return TELNET_RESULT_CONTINOUS;
735 }
736
737 /**
738  * Telnet command 'version'
739  * @param data pointer to telnet data
740  * @return telnet command result
741  */
742 static enum olsr_telnet_result
743 _cb_telnet_version(struct olsr_telnet_data *data) {
744   olsr_log_printversion(data->out);
745   return TELNET_RESULT_ACTIVE;
746 }
747
748 /**
749  * Telnet command 'plugin'
750  * @param data pointer to telnet data
751  * @return telnet command result
752  */
753 static enum olsr_telnet_result
754 _cb_telnet_plugin(struct olsr_telnet_data *data) {
755   struct oonf_subsystem *plugin;
756   const char *plugin_name = NULL;
757
758   if (data->parameter == NULL || strcasecmp(data->parameter, "list") == 0) {
759     abuf_puts(data->out, "Plugins:\n");
760
761     avl_for_each_element(&olsr_plugin_tree, plugin, _node) {
762       abuf_appendf(data->out, "\t%s\n", plugin->name);
763     }
764     return TELNET_RESULT_ACTIVE;
765   }
766
767   plugin_name = strchr(data->parameter, ' ');
768   if (plugin_name == NULL) {
769     abuf_appendf(data->out, "Error, missing or unknown parameter\n");
770   }
771
772   /* skip whitespaces */
773   while (isspace(*plugin_name)) {
774     plugin_name++;
775   }
776
777   plugin = olsr_plugins_get(plugin_name);
778   if (str_hasnextword(data->parameter, "load") == NULL) {
779     if (plugin != NULL) {
780       abuf_appendf(data->out, "Plugin %s already loaded\n", plugin_name);
781       return TELNET_RESULT_ACTIVE;
782     }
783     plugin = olsr_plugins_load(plugin_name);
784     if (plugin != NULL) {
785       abuf_appendf(data->out, "Plugin %s successfully loaded\n", plugin_name);
786     }
787     else {
788       abuf_appendf(data->out, "Could not load plugin %s\n", plugin_name);
789     }
790     return TELNET_RESULT_ACTIVE;
791   }
792
793   if (plugin == NULL) {
794     abuf_appendf(data->out, "Error, could not find plugin '%s'.\n", plugin_name);
795     return TELNET_RESULT_ACTIVE;
796   }
797
798   if (str_hasnextword(data->parameter, "unload") == NULL) {
799     if (olsr_plugins_unload(plugin)) {
800       abuf_appendf(data->out, "Could not unload plugin %s\n", plugin_name);
801     }
802     else {
803       abuf_appendf(data->out, "Plugin %s successfully unloaded\n", plugin_name);
804     }
805     return TELNET_RESULT_ACTIVE;
806   }
807
808   abuf_appendf(data->out, "Unknown command '%s %s %s'.\n",
809       data->command, data->parameter, plugin_name);
810   return TELNET_RESULT_ACTIVE;
811 }