comparison src/ctrl_telnet.c @ 125:e413158cae13

Add ushare project files.
author naoyan@johnstown.minaminoshima.org
date Sun, 03 Oct 2010 11:35:19 +0900
parents
children
comparison
equal deleted inserted replaced
124:9c7bc6c0327e 125:e413158cae13
1 /* ctrltelnet.c - Telnet controler
2 * Copyright (C) 2005-2007 Sven Almgren <sven@tras.se>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #define STR(x) _STR(x)
21 #define _STR(x) __STR(x)
22 #define __STR(x) #x
23
24 #include "config.h"
25 #include "ctrl_telnet.h"
26 #include "minmax.h"
27 #include "trace.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/select.h> /* For select */
33 #include <sys/time.h>
34 #include <unistd.h> /* For pipe */
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <pthread.h>
39 #include <stdarg.h>
40
41 #if (defined(____DISABLE_MUTEX) || 0)
42 #define pthread_mutex_lock(x) printf(">>>> Locking " __FILE__ ":" STR(__LINE__) " \t" #x "\n");
43 #define pthread_mutex_unlock(x) printf("<<<< Unlocking " __FILE__ ":" STR(__LINE__) " \t" #x "\n");
44 #endif
45
46 /**
47 * @brief Structure holding data between the staring rutine and the thread
48 */
49 typedef struct telnet_thread_data_t
50 {
51 pthread_t thread;
52
53 /* Litening socket */
54 int listener;
55
56 /* Socket used to terminate loop:
57 0 is reading and 1 is sending, kill by sending to 1 */
58 int killer[2];
59
60 /* Our socket address */
61 struct sockaddr_in local_address;
62
63 /* Shared data buffer that can be used by others... */
64 char shared_buffer[CTRL_TELNET_SHARED_BUFFER_SIZE];
65
66 ctrl_telnet_client *clients;
67 } telnet_thread_data;
68
69 /**
70 * @brief Struct for registerd commands
71 */
72 typedef struct telnet_function_list_t
73 {
74 /* Function name, or keyword, if you like */
75 char *name;
76 char *description;
77 ctrl_telnet_command_ptr function;
78
79 struct telnet_function_list_t *next;
80 } telnet_function_list;
81
82 /* Static yes used to set socketoptions */
83 static int yes = 1;
84 static telnet_thread_data ttd;
85 static telnet_function_list* functions = NULL;
86 static pthread_mutex_t functions_lock = PTHREAD_MUTEX_INITIALIZER;
87 static pthread_mutex_t startstop_lock = PTHREAD_MUTEX_INITIALIZER;
88 static pthread_mutex_t shared_lock = PTHREAD_MUTEX_INITIALIZER;
89 static int started = 0;
90
91 /* Threadfunction, core in telnet controler */
92 /**
93 * @brief Thread function
94 *
95 * @param data Not used, leave as NULL
96 */
97 static void *ctrl_telnet_thread (void *data);
98
99 /**
100 * @brief Adds a new client to our list of new ones
101 *
102 * @param client to add
103 */
104 static void ctrl_telnet_client_add (ctrl_telnet_client *client);
105
106 /**
107 * @brief Removes "client" from our list of clients
108 */
109 static void ctrl_telnet_client_remove (ctrl_telnet_client *client);
110
111 /**
112 * @brief Updates an fd_set to contain the current set of clients
113 *
114 * @return max fd found in list
115 */
116 static int ctrl_telnet_fix_fdset (fd_set* readable);
117
118 static void ctrl_telnet_tokenize (char *raw, int *argc, char ***argv);
119
120 static int ctrl_telnet_client_recv (ctrl_telnet_client *client);
121 static int ctrl_telnet_client_execute (ctrl_telnet_client *client);
122 static int ctrl_telnet_client_execute_line (ctrl_telnet_client *client,
123 char *line);
124 static int ctrl_telnet_client_execute_line_safe (ctrl_telnet_client *client,
125 char *line);
126 static void ctrl_telnet_register_internals();
127
128 /**
129 * @brief Starts a Telnet bound control interface
130 *
131 * @return 0 on success, -1 on error
132 */
133 int
134 ctrl_telnet_start (int port)
135 {
136 /* Start by making us threadsafe... */
137 pthread_mutex_lock (&startstop_lock);
138
139 /* Create listener socket */
140 ttd.listener = socket (PF_INET, SOCK_STREAM, 0);
141 if (ttd.listener == -1)
142 {
143 perror ("socket");
144 pthread_mutex_unlock (&startstop_lock);
145 return -1;
146 }
147
148 /* Clears us from "address already in use" errors */
149 if (setsockopt (ttd.listener, SOL_SOCKET, SO_REUSEADDR,
150 &yes, sizeof (int)) == -1)
151 perror ("setsockopt");
152
153 ttd.local_address.sin_family = AF_INET;
154 ttd.local_address.sin_addr.s_addr = INADDR_ANY;
155 ttd.local_address.sin_port = htons (port);
156 memset (&ttd.local_address.sin_zero, '\0',
157 sizeof (ttd.local_address.sin_zero));
158
159 if (bind (ttd.listener, (struct sockaddr *) &ttd.local_address,
160 sizeof (ttd.local_address)) == -1)
161 {
162 perror ("bind");
163 pthread_mutex_unlock (&startstop_lock);
164 return -1;
165 }
166
167 if (listen (ttd.listener, CTRL_TELNET_BACKLOG) == -1)
168 {
169 perror ("listen");
170 pthread_mutex_unlock (&startstop_lock);
171 return -1;
172 }
173
174 print_log (ULOG_NORMAL, "Listening on telnet port %u\n", port);
175
176 /* Create killer pipes */
177 if (pipe (ttd.killer))
178 {
179 perror ("Failed to create killer pipe");
180 pthread_mutex_unlock (&startstop_lock);
181 return -1; /* FIXME. Kill all sockets... not critical,, but still */
182 }
183
184 if (pthread_create (&ttd.thread, NULL, ctrl_telnet_thread, NULL))
185 {
186 /* FIXME: Killall sockets... */
187 perror ("Failed to create thread");
188 pthread_mutex_unlock (&startstop_lock);
189 return -1;
190 }
191
192 started = 1;
193 ctrl_telnet_register_internals ();
194 pthread_mutex_unlock (&startstop_lock);
195
196 return 0;
197 }
198
199 /**
200 * @brief Stops all telnet bound control interfaces
201 */
202 void
203 ctrl_telnet_stop (void)
204 {
205 pthread_mutex_lock (&startstop_lock);
206
207 if (!started)
208 {
209 pthread_mutex_unlock (&startstop_lock);
210 return;
211 }
212
213 /* yes is int, which is bigger then char, so this should be safe */
214 write (ttd.killer[1], &yes, sizeof (char));
215
216 pthread_mutex_unlock (&startstop_lock);
217 pthread_join (ttd.thread, NULL);
218 }
219
220 /**
221 * @brief Telnet thread function
222 */
223 static void *
224 ctrl_telnet_thread (void *a __attribute__ ((unused)))
225 {
226 /* fd_set with readable clients */
227 fd_set fd_readable;
228
229 /* Pointer to a client object */
230 ctrl_telnet_client *client;
231
232 int fd_max;
233
234 while (1)
235 {
236 /* Get fds */
237 fd_max = ctrl_telnet_fix_fdset (&fd_readable);
238
239 if (select (fd_max + 1, &fd_readable, NULL, NULL, NULL) == -1)
240 {
241 perror ("select");
242 /* FIXME: Close sockets */
243 return NULL;
244 }
245
246 /* Check killer */
247 if (FD_ISSET (ttd.killer[0], &fd_readable))
248 {
249 /* FIXME: TODO: Shut down sockets... */
250
251 /* Close listener and killer */
252 close (ttd.listener);
253 close (ttd.killer[0]);
254 close (ttd.killer[1]);
255
256 /* Check which fds that had anyhting to say... */
257 client = ttd.clients;
258
259 /* Say goodby to clients */
260 while (client)
261 {
262 ctrl_telnet_client *current = client;
263 ctrl_telnet_client_send (current,
264 "\nServer is going down, Bye bye\n");
265 client = client->next;
266 ctrl_telnet_client_remove (current);
267 }
268
269 pthread_mutex_lock (&functions_lock);
270
271 while (functions)
272 {
273 telnet_function_list *head = functions;
274 functions = functions->next;
275
276 free (head->name);
277 if (head->description)
278 free (head->description);
279
280 free (head);
281 }
282
283 pthread_mutex_unlock (&functions_lock);
284
285 return NULL;
286 }
287
288 /* Check for new connection */
289 if (FD_ISSET (ttd.listener, &fd_readable))
290 {
291 socklen_t sl_addr;
292
293 /* Create client object */
294 client = malloc (sizeof (ctrl_telnet_client));
295
296 if (!client)
297 {
298 perror ("Failed to create new client");
299 return NULL;
300 }
301
302 memset (client, '\0', sizeof (ctrl_telnet_client));
303 sl_addr = sizeof (client->remote_address);
304
305 client->socket = accept (ttd.listener,
306 (struct sockaddr *) &client->remote_address,
307 &sl_addr);
308 if (client->socket == -1)
309 {
310 perror ("accept");
311 free (client);
312 }
313 else
314 {
315 ctrl_telnet_client_add (client);
316 ctrl_telnet_client_execute_line_safe (client, "banner");
317 ctrl_telnet_client_sendf (client, "For a list of registered commands type \"help\"\n");
318 ctrl_telnet_client_send (client, "\n> ");
319 }
320 }
321
322 /* Check which fds that had anyhting to say... */
323 client = ttd.clients;
324
325 /* Run through all clients and check if there's data avalible
326 with FD_ISSET(current->socket) */
327 while (client)
328 {
329 ctrl_telnet_client *current = client;
330 client = client->next;
331
332 if (FD_ISSET (current->socket, &fd_readable))
333 {
334 if (ctrl_telnet_client_recv (current) <= 0)
335 {
336 ctrl_telnet_client_remove (current);
337 continue;
338 }
339
340 if (current->ready)
341 {
342 ctrl_telnet_client_execute (current);
343
344 if (!current->exiting)
345 ctrl_telnet_client_send (current, "\n> ");
346 else
347 ctrl_telnet_client_remove (current);
348 }
349 }
350 }
351 }
352 }
353
354 /**
355 * @brief Adds a new client to our list of new ones
356 *
357 * @note This funtion is only called from a single thread,
358 * as such it won't need to be threadsafe
359 * @param client to add
360 */
361 static void
362 ctrl_telnet_client_add (ctrl_telnet_client *client)
363 {
364 client->next = ttd.clients;
365 ttd.clients = client;
366 }
367
368 /**
369 * @brief Removes "client" from our list of clients
370 *
371 * @note This funtion is only called from a single thread,
372 * as such it won't need to be threadsafe
373 * @param client to remove
374 */
375 static void
376 ctrl_telnet_client_remove (ctrl_telnet_client *client)
377 {
378 ctrl_telnet_client *tmp;
379
380 /* Start by dealing with our head */
381 if (client == ttd.clients)
382 ttd.clients = client->next;
383 else
384 {
385 for (tmp = ttd.clients; tmp->next; tmp = tmp->next)
386 {
387 if (tmp->next == client)
388 {
389 tmp->next = tmp->next->next;
390 break;
391 }
392 }
393 }
394
395 close (client->socket);
396
397 free (client);
398 }
399
400 /**
401 * @brief Clears readable fd_set and adds every client to it,
402 * returns max fd found
403 *
404 * @param readable fd_set to update
405 * @return Biggest fd
406 */
407 static int
408 ctrl_telnet_fix_fdset (fd_set *readable)
409 {
410 int maxfd;
411 ctrl_telnet_client *client;
412
413 maxfd = MAX (ttd.killer[0], ttd.listener);
414
415 FD_ZERO (readable);
416 FD_SET (ttd.listener, readable);
417 FD_SET (ttd.killer[0], readable);
418
419 client = ttd.clients;
420
421 while (client)
422 {
423 if (client->socket > maxfd)
424 maxfd = client->socket;
425
426 FD_SET (client->socket, readable);
427
428 client = client->next;
429 }
430
431 return maxfd;
432 }
433
434 static int
435 ctrl_telnet_client_recv (ctrl_telnet_client *client)
436 {
437 int i;
438 int nbytes;
439 int buffer_free = CTRL_CLIENT_RECV_BUFFER_SIZE - client->buffer_recv_current - 1;
440
441 nbytes = recv (client->socket,
442 client->buffer_recv + client->buffer_recv_current,
443 buffer_free, 0);
444 if (nbytes <= 0)
445 {
446 close (client->socket);
447 return nbytes;
448 }
449
450 client->buffer_recv_current += nbytes;
451 client->buffer_recv[client->buffer_recv_current] = '\0';
452
453 for (i = 0; i < client->buffer_recv_current; i++)
454 if (client->buffer_recv[i] == '\n')
455 client->ready = 1;
456
457 return nbytes;
458 }
459
460 int
461 ctrl_telnet_client_send (const ctrl_telnet_client *client, const char *string)
462 {
463 const char* cc = string;
464 int len = strlen (cc);
465 int sent = 0;
466 int senttotal = 0;
467
468 while ((cc - string) < len)
469 {
470 /* Use nonblocking just as a precation...
471 and a failed write won't _really_ kill us */
472 sent = send (client->socket, string, len - (cc - string), MSG_DONTWAIT);
473
474 /* This will mark the socket as dead... just to be safe..
475 and its only a telnet interface... reconnect and do it again */
476 if (sent == -1)
477 return -1;
478
479 senttotal += sent;
480 cc += sent;
481 }
482
483 return senttotal;
484 }
485
486 int
487 ctrl_telnet_client_sendf (const ctrl_telnet_client *client,
488 const char *format, ...)
489 {
490 int retval;
491 va_list ap;
492 int len;
493
494 pthread_mutex_lock (&shared_lock);
495
496 va_start (ap, format);
497 len = vsnprintf (ttd.shared_buffer,
498 CTRL_TELNET_SHARED_BUFFER_SIZE, format, ap);
499 va_end (ap);
500
501 /* Check if the message fitted inside the buffer, if not,
502 either exit or adjust len to be buffersize, I choose exit for now */
503 if (len >= CTRL_TELNET_SHARED_BUFFER_SIZE)
504 {
505 pthread_mutex_unlock (&shared_lock);
506 /* FIXME: Return error or send what we've got? */
507 return -1; /* Buffer was to small */
508 }
509
510 /* TODO: Might be good to have the option to specify str length so
511 send doesn't have to recompute it... */
512 retval = ctrl_telnet_client_send (client, ttd.shared_buffer);
513
514 pthread_mutex_unlock (&shared_lock);
515
516 return retval;
517 }
518
519 int
520 ctrl_telnet_client_sendsf (const ctrl_telnet_client *client,
521 char *buffer, int buffersize,
522 const char *format, ...)
523 {
524 va_list ap;
525 int len;
526
527 va_start (ap, format);
528 len = vsnprintf (buffer, buffersize, format, ap);
529 va_end (ap);
530
531 /* Check if the message fitted inside the buffer, if not,
532 either exit or adjust len to be buffersize, I choose exit for now */
533 if (len >= buffersize)
534 return -1; /* Buffer was to small */
535
536 /* TODO: Might be good to have the option to specify str length
537 so send doesn't have to recompute it... */
538 return ctrl_telnet_client_send (client, buffer);
539 }
540
541 /* FIXME: Ulgy non optimised version */
542 static int
543 ctrl_telnet_client_execute (ctrl_telnet_client *client)
544 {
545 int i = 0;
546
547 /* Check buffer for complete lines and execute them,,, */
548 for (i = 0; i < client->buffer_recv_current; i++)
549 {
550 if (client->buffer_recv[i] == '\n' || client->buffer_recv[i] == '\r')
551 {
552 /* Replace newline with null (or \r) */
553 client->buffer_recv[i] = '\0';
554
555 /* Send line to execution */
556 ctrl_telnet_client_execute_line_safe (client, client->buffer_recv);
557
558 /* Check if next is either newline or CR, strip that too, if needed */
559 if ((i + 1 < CTRL_CLIENT_RECV_BUFFER_SIZE) &&
560 (client->buffer_recv[i+1]=='\n' || client->buffer_recv[i+1]=='\r'))
561 client->buffer_recv[++i] = '\0';
562
563 /* Remove processed line */
564 memmove (client->buffer_recv, client->buffer_recv + i,
565 client->buffer_recv_current - 1);
566 client->buffer_recv_current -= (i + 1);
567 i = -1;
568 }
569 }
570
571 return 0; /* No syntax error checking yet */
572 }
573
574 static int
575 ctrl_telnet_client_execute_line_safe (ctrl_telnet_client *client, char *line)
576 {
577 int retval;
578
579 pthread_mutex_lock (&functions_lock);
580 retval = ctrl_telnet_client_execute_line (client, line);
581 pthread_mutex_unlock (&functions_lock);
582
583 return retval;
584 }
585
586 static int
587 ctrl_telnet_client_execute_line (ctrl_telnet_client *client, char *line)
588 {
589 int argc = 0;
590 char **argv = NULL;
591 telnet_function_list *node;
592 char *line2 = strdup (line); /* To make it safer */
593 ctrl_telnet_tokenize (line2, &argc, &argv);
594
595 node = functions;
596
597 if (*argv[0] == '\0')
598 {
599 free (argv);
600 free (line2);
601 return 0;
602 }
603
604 while (node)
605 {
606 if (!strcmp (node->name, argv[0]))
607 {
608 node->function (client, argc, argv);
609 break;
610 }
611
612 node = node->next;
613 }
614
615 if (!node)
616 ctrl_telnet_client_sendf (client, "%s: Command not found\n", argv[0]);
617
618 free (argv);
619 free (line2);
620
621 return strlen (line);
622 }
623
624 void
625 ctrl_telnet_register (const char *funcname,
626 ctrl_telnet_command_ptr funcptr,
627 const char *description)
628 {
629 telnet_function_list *function;
630
631 function = malloc (sizeof (telnet_function_list));
632 function->name = strdup (funcname); /* Mayby use strndup...? */
633 function->description = description ? strdup (description) : NULL;
634 function->function = funcptr;
635
636 pthread_mutex_lock (&functions_lock);
637 function->next = functions;
638 functions = function;
639 pthread_mutex_unlock (&functions_lock);
640 }
641
642 /* Warning: This WILL edit the input string... use strdup or something
643 if needed, also remember to free() argv as the first array is dynamic */
644 /* If *argv != NULL it'll first be free()ed... or realloc,
645 make sure to clear *argv to null on initialization */
646 static void
647 ctrl_telnet_tokenize (char *raw, int *argc, char ***argv)
648 {
649 int i;
650 int has_backslash = 0;
651 int has_quote = 0;
652 char *pc = raw;
653
654 if (!raw || !argc || !argv)
655 {
656 perror ("NULL in " __FILE__ " at line " STR (__LINE__));
657 return;
658 }
659
660 /* (1/3) First run is just to count our arguments... */
661 *argc = (raw[0] == '\0') ? 0 : 1;
662
663 pc = raw;
664 while (*pc)
665 {
666 switch (*pc)
667 {
668 case '\\':
669 if (!has_backslash)
670 has_backslash = 2; /* FULHACK */
671 break;
672 case ' ':
673 if (!has_backslash && !has_quote)
674 (*argc)++;
675 break;
676 case '"':
677 if (!has_backslash)
678 has_quote = !has_quote;
679
680 break;
681 }
682
683 /* When we get a BS we set it to two, this makes it one,
684 next run it will still be 1, then one after that is zero... FULHACK */
685 if (has_backslash)
686 has_backslash--;
687
688 pc++;
689 }
690
691 /* Create argv */
692 *argv = malloc (sizeof (char **) * ((*argc) + 1));
693
694 /* (2/3) Parse throu one more time, this time filling argv (Pass 2 / 3) */
695 i = 0;
696 pc = raw;
697 has_backslash = 0;
698 has_quote = 0;
699 (*argv)[0] = raw;
700
701 while (*pc)
702 {
703 switch (*pc)
704 {
705 case '\\':
706 if (!has_backslash)
707 has_backslash = 2; /* FULHACK */
708 break;
709 case ' ':
710 if (!has_backslash && !has_quote)
711 {
712 *pc = '\0';
713 (*argv)[++i] = pc+1;
714 pc++;
715 continue;
716 }
717 break;
718 case '"':
719 if (!has_backslash)
720 has_quote = !has_quote;
721 break;
722 }
723
724 /* When we get a BS we set it to two, this makes it one,
725 next run it will still be 1, then one after that is zero... FULHACK */
726 if (has_backslash)
727 has_backslash--;
728
729 pc++;
730 }
731
732 /* Make last element (argc) point to null... */
733 (*argv)[++i] = NULL;
734
735 /* (3/3) Parse arguments to remove escapings and such */
736 for (i = 0; (*argv)[i]; i++)
737 {
738 /* Set up environment */
739 pc = (*argv)[i];
740 has_backslash = 0;
741 has_quote = 0;
742
743 /* Remove leading and ending quotes, if existing */
744 if (*pc == '"')
745 {
746 int len = strlen (pc);
747
748 if (len > 0 && pc[len - 1] == '"')
749 pc[len - 1] = '\0';
750 memmove (pc, pc + 1, len);
751 }
752
753 /* Remove any special characters */
754 while (*pc)
755 {
756 switch (*pc)
757 {
758 case '\\':
759 if (!has_backslash)
760 {
761 has_backslash = 2; /* FULHACK */
762 break;
763 }
764 /* Else: fall through */
765 case ' ':
766 case '"':
767 if (has_backslash)
768 {
769 pc--;
770 memmove (pc, pc + 1, strlen (pc)); /* FIXME: Not cheap */
771 }
772 break;
773 }
774
775 /* When we get a BS we set it to two, this makes it one,
776 next run it will still be 1, then one after that is zero... */
777 if (has_backslash)
778 has_backslash--;
779
780 pc++;
781 }
782 }
783 }
784
785 static void
786 help (ctrl_telnet_client *client, int argc, char **argv)
787 {
788 int hidden = 0;
789 ctrl_telnet_client_execute_line (client, "banner");
790
791 if (argc < 2)
792 {
793 ctrl_telnet_client_send (client, "\n");
794 ctrl_telnet_client_send (client, "Usage: help TOPIC\n");
795 ctrl_telnet_client_send (client, "Valid topics are\n");
796 ctrl_telnet_client_send
797 (client, " commands - For a list of registed commands\n");
798 ctrl_telnet_client_send
799 (client, " syntax - For a description of the interface syntax\n");
800 return;
801 }
802 else
803 {
804 if (!strcmp ("commands", argv[1]))
805 {
806 telnet_function_list *node;
807
808 node = functions;
809 ctrl_telnet_client_send
810 (client, "Registered command (command - description)\n");
811 ctrl_telnet_client_send
812 (client, "=======================================================\n");
813
814 while (node)
815 {
816 /* Make functions without descriptions invisible */
817 if (node->description)
818 ctrl_telnet_client_sendf (client, " %s - %s\n",
819 node->name, node->description);
820 else
821 hidden++;
822
823 node = node->next;
824 }
825
826 if (hidden)
827 ctrl_telnet_client_sendf
828 (client, "There's also %i hidden functions\n", hidden);
829 } /* commands */
830 else if (!strcmp ("syntax", argv[1]))
831 {
832 ctrl_telnet_client_send
833 (client, "Syntax is easy: command parameters\n");
834 ctrl_telnet_client_send
835 (client, " Each new word is a new argument, unless the space is precided\n");
836 ctrl_telnet_client_send
837 (client, " a backslash (\\), or if a set of words are surrounded by quotes\n");
838 ctrl_telnet_client_send
839 (client, " (\"). To get a litteral quote you can escape it as \\\".\n");
840 ctrl_telnet_client_send (client, "\n");
841 ctrl_telnet_client_send (client, "STUB\n");
842 }
843 else
844 ctrl_telnet_client_send (client, "Unknown topic\n");
845 }
846 }
847
848 static void
849 banner (ctrl_telnet_client *client,
850 int argc __attribute__ ((unused)),
851 char **argv __attribute__ ((unused)))
852 {
853 ctrl_telnet_client_sendf (client, "%s (%s) (Built %s)\n",
854 PACKAGE_NAME, VERSION, __DATE__);
855 }
856
857 static void
858 echo (ctrl_telnet_client *client, int argc, char **argv)
859 {
860 int i;
861
862 for (i = 1; i < argc; i++)
863 ctrl_telnet_client_sendf (client, "%s%s", (i > 1 ? " " : ""), argv[i]);
864 ctrl_telnet_client_send (client, "\n");
865 }
866
867 static void
868 echod (ctrl_telnet_client *client, int argc, char **argv)
869 {
870 int i;
871
872 ctrl_telnet_client_sendf (client, "Argc: %i\n", argc);
873
874 for (i = 0; i < argc; i++)
875 ctrl_telnet_client_sendf (client, "%i: '%s'\n", i, argv[i]);
876 }
877
878 static void
879 ctrl_telnet_exit (ctrl_telnet_client *client,
880 int argc __attribute__ ((unused)),
881 char **argv __attribute__ ((unused)))
882 {
883 client->exiting = 1;
884 ctrl_telnet_client_send (client, "Bye bye\n");
885 }
886
887 static void
888 ctrl_telnet_register_internals (void)
889 {
890 ctrl_telnet_register ("echo", echo, "Echos all arguments");
891 ctrl_telnet_register ("echod", echod, "Echos all arguments but with each argument on a new line... DEBUG");
892 ctrl_telnet_register ("help", help, "Display help");
893 ctrl_telnet_register ("banner", banner, NULL);
894 ctrl_telnet_register ("exit", ctrl_telnet_exit, "Exits this interface (Or CTRL+D then Enter)");
895 /* CTRL+D... But it has to be fallowd by a new line */
896 ctrl_telnet_register ("\4", ctrl_telnet_exit, NULL);
897 }