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