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