Mercurial > pt1.oyama
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 } |