c6be4fefed87e00c0ae7a1ac3986b993e21d8210
[asterisk/asterisk.git] / cli.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Standard Command Line Interface
5  * 
6  * Copyright (C) 1999-2004, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/options.h>
18 #include <asterisk/cli.h>
19 #include <asterisk/module.h>
20 #include <asterisk/channel.h>
21 #include <asterisk/channel_pvt.h>
22 #include <asterisk/manager.h>
23 #include <asterisk/utils.h>
24 #include <asterisk/lock.h>
25 #include <sys/signal.h>
26 #include <stdio.h>
27 #include <signal.h>
28 #include <string.h>
29 /* For rl_filename_completion */
30 #include "editline/readline/readline.h"
31 /* For module directory */
32 #include "asterisk.h"
33 #include "build.h"
34 #include "astconf.h"
35
36 #define VERSION_INFO "Asterisk " ASTERISK_VERSION " built by " BUILD_USER "@" BUILD_HOSTNAME \
37         " on a " BUILD_MACHINE " running " BUILD_OS
38         
39 void ast_cli(int fd, char *fmt, ...)
40 {
41         char *stuff;
42         int res = 0;
43
44         va_list ap;
45         va_start(ap, fmt);
46         res = vasprintf(&stuff, fmt, ap);
47         va_end(ap);
48         if (res == -1) {
49                 ast_log(LOG_ERROR, "Out of memory\n");
50         } else {
51                 ast_carefulwrite(fd, stuff, strlen(stuff), 100);
52                 free(stuff);
53         }
54 }
55
56 AST_MUTEX_DEFINE_STATIC(clilock);
57
58 struct ast_cli_entry *helpers = NULL;
59
60 static char load_help[] = 
61 "Usage: load <module name>\n"
62 "       Loads the specified module into Asterisk.\n";
63
64 static char unload_help[] = 
65 "Usage: unload [-f|-h] <module name>\n"
66 "       Unloads the specified module from Asterisk.  The -f\n"
67 "       option causes the module to be unloaded even if it is\n"
68 "       in use (may cause a crash) and the -h module causes the\n"
69 "       module to be unloaded even if the module says it cannot, \n"
70 "       which almost always will cause a crash.\n";
71
72 static char help_help[] =
73 "Usage: help [topic]\n"
74 "       When called with a topic as an argument, displays usage\n"
75 "       information on the given command.  If called without a\n"
76 "       topic, it provides a list of commands.\n";
77
78 static char chanlist_help[] = 
79 "Usage: show channels [concise]\n"
80 "       Lists currently defined channels and some information about\n"
81 "       them.  If 'concise' is specified, format is abridged and in\n"
82 "       a more easily machine parsable format\n";
83
84 static char reload_help[] = 
85 "Usage: reload [module ...]\n"
86 "       Reloads configuration files for all listed modules which support\n"
87 "       reloading, or for all supported modules if none are listed.\n";
88
89 static char set_verbose_help[] = 
90 "Usage: set verbose <level>\n"
91 "       Sets level of verbose messages to be displayed.  0 means\n"
92 "       no messages should be displayed. Equivalent to -v[v[v...]]\n"
93 "       on startup\n";
94
95 static char set_debug_help[] = 
96 "Usage: set debug <level>\n"
97 "       Sets level of core debug messages to be displayed.  0 means\n"
98 "       no messages should be displayed. Equivalent to -d[d[d...]]\n"
99 "       on startup.\n";
100
101 static char softhangup_help[] =
102 "Usage: soft hangup <channel>\n"
103 "       Request that a channel be hung up.  The hangup takes effect\n"
104 "       the next time the driver reads or writes from the channel\n";
105
106 static int handle_load(int fd, int argc, char *argv[])
107 {
108         if (argc != 2)
109                 return RESULT_SHOWUSAGE;
110         if (ast_load_resource(argv[1])) {
111                 ast_cli(fd, "Unable to load module %s\n", argv[1]);
112                 return RESULT_FAILURE;
113         }
114         return RESULT_SUCCESS;
115 }
116
117 static int handle_reload(int fd, int argc, char *argv[])
118 {
119         int x;
120         int res;
121         if (argc < 1)
122                 return RESULT_SHOWUSAGE;
123         if (argc > 1) { 
124                 for (x=1;x<argc;x++) {
125                         res = ast_module_reload(argv[x]);
126                         switch(res) {
127                         case 0:
128                                 ast_cli(fd, "No such module '%s'\n", argv[x]);
129                                 break;
130                         case 1:
131                                 ast_cli(fd, "Module '%s' does not support reload\n", argv[x]);
132                                 break;
133                         }
134                 }
135         } else
136                 ast_module_reload(NULL);
137         return RESULT_SUCCESS;
138 }
139
140 static int handle_set_verbose(int fd, int argc, char *argv[])
141 {
142         int val = 0;
143         int oldval = 0;
144         /* Has a hidden 'at least' argument */
145         if ((argc != 3) && (argc != 4))
146                 return RESULT_SHOWUSAGE;
147         if ((argc == 4) && strcasecmp(argv[2], "atleast"))
148                 return RESULT_SHOWUSAGE;
149         oldval = option_verbose;
150         if (argc == 3)
151                 option_verbose = atoi(argv[2]);
152         else {
153                 val = atoi(argv[3]);
154                 if (val > option_verbose)
155                         option_verbose = val;
156         }
157         if (oldval != option_verbose && option_verbose > 0)
158                 ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
159         else if (oldval > 0 && option_verbose > 0)
160                 ast_cli(fd, "Verbosity is atleast %d\n", option_verbose);
161         else if (oldval > 0 && option_verbose == 0)
162                 ast_cli(fd, "Verbosity is now OFF\n");
163         return RESULT_SUCCESS;
164 }
165
166 static int handle_set_debug(int fd, int argc, char *argv[])
167 {
168         int val = 0;
169         int oldval = 0;
170         /* Has a hidden 'at least' argument */
171         if ((argc != 3) && (argc != 4))
172                 return RESULT_SHOWUSAGE;
173         if ((argc == 4) && strcasecmp(argv[2], "atleast"))
174                 return RESULT_SHOWUSAGE;
175         oldval = option_debug;
176         if (argc == 3)
177                 option_debug = atoi(argv[2]);
178         else {
179                 val = atoi(argv[3]);
180                 if (val > option_debug)
181                         option_debug = val;
182         }
183         if (oldval != option_debug && option_debug > 0)
184                 ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug);
185         else if (oldval > 0 && option_debug > 0)
186                 ast_cli(fd, "Core debug is atleast %d\n", option_debug);
187         else if (oldval > 0 && option_debug == 0)
188                 ast_cli(fd, "Core debug is now OFF\n");
189         return RESULT_SUCCESS;
190 }
191
192 static int handle_unload(int fd, int argc, char *argv[])
193 {
194         int x;
195         int force=AST_FORCE_SOFT;
196         if (argc < 2)
197                 return RESULT_SHOWUSAGE;
198         for (x=1;x<argc;x++) {
199                 if (argv[x][0] == '-') {
200                         switch(argv[x][1]) {
201                         case 'f':
202                                 force = AST_FORCE_FIRM;
203                                 break;
204                         case 'h':
205                                 force = AST_FORCE_HARD;
206                                 break;
207                         default:
208                                 return RESULT_SHOWUSAGE;
209                         }
210                 } else if (x !=  argc - 1) 
211                         return RESULT_SHOWUSAGE;
212                 else if (ast_unload_resource(argv[x], force)) {
213                         ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
214                         return RESULT_FAILURE;
215                 }
216         }
217         return RESULT_SUCCESS;
218 }
219
220 #define MODLIST_FORMAT  "%-25s %-40.40s %-10d\n"
221 #define MODLIST_FORMAT2 "%-25s %-40.40s %-10s\n"
222
223 AST_MUTEX_DEFINE_STATIC(climodentrylock);
224 static int climodentryfd = -1;
225
226 static int modlist_modentry(char *module, char *description, int usecnt, char *like)
227 {
228         /* Comparing the like with the module */
229         if ( strstr(module,like) != NULL) {
230                 ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
231                 return 1;
232                 
233         } 
234         return 0;
235 }
236
237 static char modlist_help[] =
238 "Usage: show modules [like keyword]\n"
239 "       Shows Asterisk modules currently in use, and usage statistics.\n";
240
241 static char version_help[] =
242 "Usage: show version\n"
243 "       Shows Asterisk version information.\n ";
244
245 static char *format_uptimestr(time_t timeval)
246 {
247         int years = 0, weeks = 0, days = 0, hours = 0, mins = 0, secs = 0;
248         char timestr[256]="";
249         int bytes = 0;
250         int maxbytes = 0;
251         int offset = 0;
252 #define SECOND (1)
253 #define MINUTE (SECOND*60)
254 #define HOUR (MINUTE*60)
255 #define DAY (HOUR*24)
256 #define WEEK (DAY*7)
257 #define YEAR (DAY*365)
258 #define ESS(x) ((x == 1) ? "" : "s")
259
260         maxbytes = sizeof(timestr);
261         if (timeval < 0)
262                 return NULL;
263         if (timeval > YEAR) {
264                 years = (timeval / YEAR);
265                 timeval -= (years * YEAR);
266                 if (years > 0) {
267                         snprintf(timestr + offset, maxbytes, "%d year%s, ", years, ESS(years));
268                         bytes = strlen(timestr + offset);
269                         offset += bytes;
270                         maxbytes -= bytes;
271                 }
272         }
273         if (timeval > WEEK) {
274                 weeks = (timeval / WEEK);
275                 timeval -= (weeks * WEEK);
276                 if (weeks > 0) {
277                         snprintf(timestr + offset, maxbytes, "%d week%s, ", weeks, ESS(weeks));
278                         bytes = strlen(timestr + offset);
279                         offset += bytes;
280                         maxbytes -= bytes;
281                 }
282         }
283         if (timeval > DAY) {
284                 days = (timeval / DAY);
285                 timeval -= (days * DAY);
286                 if (days > 0) {
287                         snprintf(timestr + offset, maxbytes, "%d day%s, ", days, ESS(days));
288                         bytes = strlen(timestr + offset);
289                         offset += bytes;
290                         maxbytes -= bytes;
291                 }
292         }
293         if (timeval > HOUR) {
294                 hours = (timeval / HOUR);
295                 timeval -= (hours * HOUR);
296                 if (hours > 0) {
297                         snprintf(timestr + offset, maxbytes, "%d hour%s, ", hours, ESS(hours));
298                         bytes = strlen(timestr + offset);
299                         offset += bytes;
300                         maxbytes -= bytes;
301                 }
302         }
303         if (timeval > MINUTE) {
304                 mins = (timeval / MINUTE);
305                 timeval -= (mins * MINUTE);
306                 if (mins > 0) {
307                         snprintf(timestr + offset, maxbytes, "%d minute%s, ", mins, ESS(mins));
308                         bytes = strlen(timestr + offset);
309                         offset += bytes;
310                         maxbytes -= bytes;
311                 }
312         }
313         secs = timeval;
314
315         if (secs > 0) {
316                 snprintf(timestr + offset, maxbytes, "%d second%s", secs, ESS(secs));
317         }
318
319         return timestr ? strdup(timestr) : NULL;
320 }
321
322 static int handle_showuptime(int fd, int argc, char *argv[])
323 {
324         time_t curtime, tmptime;
325         char *timestr;
326
327         time(&curtime);
328         if (ast_startuptime) {
329                 tmptime = curtime - ast_startuptime;
330                 timestr = format_uptimestr(tmptime);
331                 if (timestr) {
332                         ast_cli(fd, "System uptime: %s\n", timestr);
333                         free(timestr);
334                 }
335         }               
336         if (ast_lastreloadtime) {
337                 tmptime = curtime - ast_lastreloadtime;
338                 timestr = format_uptimestr(tmptime);
339                 if (timestr) {
340                         ast_cli(fd, "Last reload: %s\n", timestr);
341                         free(timestr);
342                 }
343         }
344         return RESULT_SUCCESS;
345 }
346
347 static int handle_modlist(int fd, int argc, char *argv[])
348 {
349         char *like = "";
350         if (argc == 3)
351                 return RESULT_SHOWUSAGE;
352         else if (argc >= 4) {
353                 if ( strcmp(argv[2],"like") ) 
354                         return RESULT_SHOWUSAGE;
355                 like = argv[3];
356         }
357                 
358         ast_mutex_lock(&climodentrylock);
359         climodentryfd = fd;
360         ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
361         ast_cli(fd,"%d modules loaded\n",ast_update_module_list(modlist_modentry,like));
362         climodentryfd = -1;
363         ast_mutex_unlock(&climodentrylock);
364         return RESULT_SUCCESS;
365 }
366
367 static int handle_version(int fd, int argc, char *argv[])
368 {
369         if (argc != 2)
370                 return RESULT_SHOWUSAGE;
371         ast_cli(fd, "%s\n", VERSION_INFO);
372         return RESULT_SUCCESS;
373 }
374 static int handle_chanlist(int fd, int argc, char *argv[])
375 {
376 #define FORMAT_STRING  "%15s  (%-10s %-12s %-4d) %7s %-12s  %-15s\n"
377 #define FORMAT_STRING2 "%15s  (%-10s %-12s %-4s) %7s %-12s  %-15s\n"
378 #define CONCISE_FORMAT_STRING  "%s:%s:%s:%d:%s:%s:%s:%s:%s:%d\n"
379
380         struct ast_channel *c=NULL;
381         int numchans = 0;
382         int concise = 0;
383         if (argc < 2 || argc > 3)
384                 return RESULT_SHOWUSAGE;
385         
386         concise = (argc == 3 && (!strcasecmp(argv[2],"concise")));
387         c = ast_channel_walk_locked(NULL);
388         if(!concise)
389                 ast_cli(fd, FORMAT_STRING2, "Channel", "Context", "Extension", "Pri", "State", "Appl.", "Data");
390         while(c) {
391                 if(concise)
392                         ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
393                                         c->appl ? c->appl : "(None)", c->data ? ( !ast_strlen_zero(c->data) ? c->data : "" ): "",
394                                         (c->cid.cid_num && !ast_strlen_zero(c->cid.cid_num)) ? c->cid.cid_num : "",
395                                         (c->accountcode && !ast_strlen_zero(c->accountcode)) ? c->accountcode : "",c->amaflags);
396                 else
397                         ast_cli(fd, FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
398                                         c->appl ? c->appl : "(None)", c->data ? ( !ast_strlen_zero(c->data) ? c->data : "(Empty)" ): "(None)");
399
400                 numchans++;
401                 ast_mutex_unlock(&c->lock);
402                 c = ast_channel_walk_locked(c);
403         }
404         if(!concise)
405                 ast_cli(fd, "%d active channel(s)\n", numchans);
406         return RESULT_SUCCESS;
407 }
408
409 static char showchan_help[] = 
410 "Usage: show channel <channel>\n"
411 "       Shows lots of information about the specified channel.\n";
412
413 static char debugchan_help[] = 
414 "Usage: debug channel <channel>\n"
415 "       Enables debugging on a specific channel.\n";
416
417 static char nodebugchan_help[] = 
418 "Usage: no debug channel <channel>\n"
419 "       Disables debugging on a specific channel.\n";
420
421 static char commandcomplete_help[] = 
422 "Usage: _command complete \"<line>\" text state\n"
423 "       This function is used internally to help with command completion and should.\n"
424 "       never be called by the user directly.\n";
425
426 static char commandnummatches_help[] = 
427 "Usage: _command nummatches \"<line>\" text \n"
428 "       This function is used internally to help with command completion and should.\n"
429 "       never be called by the user directly.\n";
430
431 static char commandmatchesarray_help[] = 
432 "Usage: _command matchesarray \"<line>\" text \n"
433 "       This function is used internally to help with command completion and should.\n"
434 "       never be called by the user directly.\n";
435
436 static int handle_softhangup(int fd, int argc, char *argv[])
437 {
438         struct ast_channel *c=NULL;
439         if (argc != 3)
440                 return RESULT_SHOWUSAGE;
441         c = ast_channel_walk_locked(NULL);
442         while(c) {
443                 if (!strcasecmp(c->name, argv[2])) {
444                         ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name);
445                         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
446                         ast_mutex_unlock(&c->lock);
447                         break;
448                 }
449                 ast_mutex_unlock(&c->lock);
450                 c = ast_channel_walk_locked(c);
451         }
452         if (!c) 
453                 ast_cli(fd, "%s is not a known channel\n", argv[2]);
454         return RESULT_SUCCESS;
455 }
456
457 static char *__ast_cli_generator(char *text, char *word, int state, int lock);
458
459 static int handle_commandmatchesarray(int fd, int argc, char *argv[])
460 {
461         char *buf, *obuf;
462         int buflen = 2048;
463         int len = 0;
464         char **matches;
465         int x, matchlen;
466
467         if (argc != 4)
468                 return RESULT_SHOWUSAGE;
469         buf = malloc(buflen);
470         if (!buf)
471                 return RESULT_FAILURE;
472         buf[len] = '\0';
473         matches = ast_cli_completion_matches(argv[2], argv[3]);
474         if (matches) {
475                 for (x=0; matches[x]; x++) {
476 #if 0
477                         printf("command matchesarray for '%s' %s got '%s'\n", argv[2], argv[3], matches[x]);
478 #endif
479                         matchlen = strlen(matches[x]) + 1;
480                         if (len + matchlen >= buflen) {
481                                 buflen += matchlen * 3;
482                                 obuf = buf;
483                                 buf = realloc(obuf, buflen);
484                                 if (!buf) 
485                                         /* Out of memory...  Just free old buffer and be done */
486                                         free(obuf);
487                         }
488                         if (buf)
489                                 len += sprintf( buf + len, "%s ", matches[x]);
490                         free(matches[x]);
491                         matches[x] = NULL;
492                 }
493                 free(matches);
494         }
495 #if 0
496         printf("array for '%s' %s got '%s'\n", argv[2], argv[3], buf);
497 #endif
498         
499         if (buf) {
500                 ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
501                 free(buf);
502         } else
503                 ast_cli(fd, "NULL\n");
504
505         return RESULT_SUCCESS;
506 }
507
508
509
510 static int handle_commandnummatches(int fd, int argc, char *argv[])
511 {
512         int matches = 0;
513
514         if (argc != 4)
515                 return RESULT_SHOWUSAGE;
516
517         matches = ast_cli_generatornummatches(argv[2], argv[3]);
518
519 #if 0
520         printf("Search for '%s' %s got '%d'\n", argv[2], argv[3], matches);
521 #endif
522         ast_cli(fd, "%d", matches);
523
524         return RESULT_SUCCESS;
525 }
526
527 static int handle_commandcomplete(int fd, int argc, char *argv[])
528 {
529         char *buf;
530 #if 0
531         printf("Search for %d args: '%s', '%s', '%s', '%s'\n", argc, argv[0], argv[1], argv[2], argv[3]);
532 #endif  
533         if (argc != 5)
534                 return RESULT_SHOWUSAGE;
535         buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0);
536 #if 0
537         printf("Search for '%s' %s %d got '%s'\n", argv[2], argv[3], atoi(argv[4]), buf);
538 #endif  
539         if (buf) {
540                 ast_cli(fd, buf);
541                 free(buf);
542         } else
543                 ast_cli(fd, "NULL\n");
544         return RESULT_SUCCESS;
545 }
546
547 static int handle_debugchan(int fd, int argc, char *argv[])
548 {
549         struct ast_channel *c=NULL;
550         if (argc != 3)
551                 return RESULT_SHOWUSAGE;
552         c = ast_channel_walk_locked(NULL);
553         while(c) {
554                 if (!strcasecmp(c->name, argv[2])) {
555                         c->fin |= 0x80000000;
556                         c->fout |= 0x80000000;
557                         break;
558                 }
559                 ast_mutex_unlock(&c->lock);
560                 c = ast_channel_walk_locked(c);
561         }
562         if (c) {
563                 ast_cli(fd, "Debugging enabled on channel %s\n", c->name);
564                 ast_mutex_unlock(&c->lock);
565         }
566         else
567                 ast_cli(fd, "No such channel %s\n", argv[2]);
568         return RESULT_SUCCESS;
569 }
570
571 static int handle_nodebugchan(int fd, int argc, char *argv[])
572 {
573         struct ast_channel *c=NULL;
574         if (argc != 4)
575                 return RESULT_SHOWUSAGE;
576         c = ast_channel_walk_locked(NULL);
577         while(c) {
578                 if (!strcasecmp(c->name, argv[3])) {
579                         c->fin &= 0x7fffffff;
580                         c->fout &= 0x7fffffff;
581                         break;
582                 }
583                 ast_mutex_unlock(&c->lock);
584                 c = ast_channel_walk_locked(c);
585         }
586         if (c) {
587                 ast_cli(fd, "Debugging disabled on channel %s\n", c->name);
588                 ast_mutex_unlock(&c->lock);
589         } else
590                 ast_cli(fd, "No such channel %s\n", argv[2]);
591         return RESULT_SUCCESS;
592 }
593                 
594         
595
596 static int handle_showchan(int fd, int argc, char *argv[])
597 {
598         struct ast_channel *c=NULL;
599         struct timeval now;
600         long elapsed_seconds=0;
601         int hour=0, min=0, sec=0;
602         if (argc != 3)
603                 return RESULT_SHOWUSAGE;
604         gettimeofday(&now, NULL);
605         c = ast_channel_walk_locked(NULL);
606         while(c) {
607                 if (!strcasecmp(c->name, argv[2])) {
608                         if(c->cdr) {
609                                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
610                                 hour = elapsed_seconds / 3600;
611                                 min = (elapsed_seconds % 3600) / 60;
612                                 sec = elapsed_seconds % 60;
613                         }
614                         ast_cli(fd, 
615         " -- General --\n"
616         "           Name: %s\n"
617         "           Type: %s\n"
618         "       UniqueID: %s\n"
619         "      Caller ID: %s\n"
620         " Caller ID Name: %s\n"
621         "    DNID Digits: %s\n"
622         "          State: %s (%d)\n"
623         "          Rings: %d\n"
624         "   NativeFormat: %d\n"
625         "    WriteFormat: %d\n"
626         "     ReadFormat: %d\n"
627         "1st File Descriptor: %d\n"
628         "      Frames in: %d%s\n"
629         "     Frames out: %d%s\n"
630         " Time to Hangup: %ld\n"
631         "   Elapsed Time: %dh%dm%ds\n"
632         " --   PBX   --\n"
633         "        Context: %s\n"
634         "      Extension: %s\n"
635         "       Priority: %d\n"
636         "     Call Group: %d\n"
637         "   Pickup Group: %d\n"
638         "    Application: %s\n"
639         "           Data: %s\n"
640         "          Stack: %d\n"
641         "    Blocking in: %s\n",
642         c->name, c->type, c->uniqueid,
643         (c->cid.cid_num ? c->cid.cid_num : "(N/A)"),
644         (c->cid.cid_name ? c->cid.cid_name : "(N/A)"),
645         (c->cid.cid_dnid ? c->cid.cid_dnid : "(N/A)" ), ast_state2str(c->_state), c->_state, c->rings, c->nativeformats, c->writeformat, c->readformat,
646         c->fds[0], c->fin & 0x7fffffff, (c->fin & 0x80000000) ? " (DEBUGGED)" : "",
647         c->fout & 0x7fffffff, (c->fout & 0x80000000) ? " (DEBUGGED)" : "", (long)c->whentohangup,
648         hour, min, sec, 
649         c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
650         ( c-> data ? (!ast_strlen_zero(c->data) ? c->data : "(Empty)") : "(None)"),
651         c->stack, (c->blocking ? c->blockproc : "(Not Blocking)"));
652                 ast_mutex_unlock(&c->lock);
653                 break;
654                 }
655                 ast_mutex_unlock(&c->lock);
656                 c = ast_channel_walk_locked(c);
657         }
658         if (!c) 
659                 ast_cli(fd, "%s is not a known channel\n", argv[2]);
660         return RESULT_SUCCESS;
661 }
662
663 static char *complete_ch_helper(char *line, char *word, int pos, int state, int rpos)
664 {
665         struct ast_channel *c;
666         int which=0;
667         char *ret;
668         if (pos != rpos)
669                 return NULL;
670         c = ast_channel_walk_locked(NULL);
671         while(c) {
672                 if (!strncasecmp(word, c->name, strlen(word))) {
673                         if (++which > state)
674                                 break;
675                 }
676                 ast_mutex_unlock(&c->lock);
677                 c = ast_channel_walk_locked(c);
678         }
679         if (c) {
680                 ret = strdup(c->name);
681                 ast_mutex_unlock(&c->lock);
682         } else
683                 ret = NULL;
684         return ret;
685 }
686
687 static char *complete_ch_3(char *line, char *word, int pos, int state)
688 {
689         return complete_ch_helper(line, word, pos, state, 2);
690 }
691
692 static char *complete_ch_4(char *line, char *word, int pos, int state)
693 {
694         return complete_ch_helper(line, word, pos, state, 3);
695 }
696
697 static char *complete_mod_2(char *line, char *word, int pos, int state)
698 {
699         return ast_module_helper(line, word, pos, state, 1, 1);
700 }
701
702 static char *complete_mod_4(char *line, char *word, int pos, int state)
703 {
704         return ast_module_helper(line, word, pos, state, 3, 0);
705 }
706
707 static char *complete_fn(char *line, char *word, int pos, int state)
708 {
709         char *c;
710         char filename[256];
711         if (pos != 1)
712                 return NULL;
713         if (word[0] == '/')
714                 strncpy(filename, word, sizeof(filename)-1);
715         else
716                 snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_MODULE_DIR, word);
717         c = (char*)filename_completion_function(filename, state);
718         if (c && word[0] != '/')
719                 c += (strlen((char*)ast_config_AST_MODULE_DIR) + 1);
720         return c ? strdup(c) : c;
721 }
722
723 static int handle_help(int fd, int argc, char *argv[]);
724
725 static struct ast_cli_entry builtins[] = {
726         /* Keep alphabetized, with longer matches first (example: abcd before abc) */
727         { { "_command", "complete", NULL }, handle_commandcomplete, "Command complete", commandcomplete_help },
728         { { "_command", "nummatches", NULL }, handle_commandnummatches, "Returns number of command matches", commandnummatches_help },
729         { { "_command", "matchesarray", NULL }, handle_commandmatchesarray, "Returns command matches array", commandmatchesarray_help },
730         { { "debug", "channel", NULL }, handle_debugchan, "Enable debugging on a channel", debugchan_help, complete_ch_3 },
731         { { "help", NULL }, handle_help, "Display help list, or specific help on a command", help_help },
732         { { "load", NULL }, handle_load, "Load a dynamic module by name", load_help, complete_fn },
733         { { "no", "debug", "channel", NULL }, handle_nodebugchan, "Disable debugging on a channel", nodebugchan_help, complete_ch_4 },
734         { { "reload", NULL }, handle_reload, "Reload configuration", reload_help, complete_mod_2 },
735         { { "set", "debug", NULL }, handle_set_debug, "Set level of debug chattiness", set_debug_help },
736         { { "set", "verbose", NULL }, handle_set_verbose, "Set level of verboseness", set_verbose_help },
737         { { "show", "channels", NULL }, handle_chanlist, "Display information on channels", chanlist_help },
738         { { "show", "channel", NULL }, handle_showchan, "Display information on a specific channel", showchan_help, complete_ch_3 },
739         { { "show", "modules", NULL }, handle_modlist, "List modules and info", modlist_help },
740         { { "show", "modules", "like", NULL }, handle_modlist, "List modules and info", modlist_help, complete_mod_4 },
741         { { "show", "uptime", NULL }, handle_showuptime, "Show uptime information", modlist_help },
742         { { "show", "version", NULL }, handle_version, "Display version info", version_help },
743         { { "soft", "hangup", NULL }, handle_softhangup, "Request a hangup on a given channel", softhangup_help, complete_ch_3 },
744         { { "unload", NULL }, handle_unload, "Unload a dynamic module by name", unload_help, complete_fn },
745         { { NULL }, NULL, NULL, NULL }
746 };
747
748 static struct ast_cli_entry *find_cli(char *cmds[], int exact)
749 {
750         int x;
751         int y;
752         int match;
753         struct ast_cli_entry *e=NULL;
754         for (x=0;builtins[x].cmda[0];x++) {
755                 /* start optimistic */
756                 match = 1;
757                 for (y=0;match && cmds[y]; y++) {
758                         /* If there are no more words in the candidate command, then we're
759                            there.  */
760                         if (!builtins[x].cmda[y] && !exact)
761                                 break;
762                         /* If there are no more words in the command (and we're looking for
763                            an exact match) or there is a difference between the two words,
764                            then this is not a match */
765                         if (!builtins[x].cmda[y] || strcasecmp(builtins[x].cmda[y], cmds[y]))
766                                 match = 0;
767                 }
768                 /* If more words are needed to complete the command then this is not
769                    a candidate (unless we're looking for a really inexact answer  */
770                 if ((exact > -1) && builtins[x].cmda[y])
771                         match = 0;
772                 if (match)
773                         return &builtins[x];
774         }
775         for (e=helpers;e;e=e->next) {
776                 match = 1;
777                 for (y=0;match && cmds[y]; y++) {
778                         if (!e->cmda[y] && !exact)
779                                 break;
780                         if (!e->cmda[y] || strcasecmp(e->cmda[y], cmds[y]))
781                                 match = 0;
782                 }
783                 if ((exact > -1) && e->cmda[y])
784                         match = 0;
785                 if (match)
786                         break;
787         }
788         return e;
789 }
790
791 static void join(char *dest, size_t destsize, char *w[])
792 {
793         int x;
794         /* Join words into a string */
795         if (!dest || destsize < 1) {
796                 return;
797         }
798         dest[0] = '\0';
799         for (x=0;w[x];x++) {
800                 if (x)
801                         strncat(dest, " ", destsize - strlen(dest) - 1);
802                 strncat(dest, w[x], destsize - strlen(dest) - 1);
803         }
804 }
805
806 static void join2(char *dest, size_t destsize, char *w[])
807 {
808         int x;
809         /* Join words into a string */
810         if (!dest || destsize < 1) {
811                 return;
812         }
813         dest[0] = '\0';
814         for (x=0;w[x];x++) {
815                 strncat(dest, w[x], destsize - strlen(dest) - 1);
816         }
817 }
818
819 static char *find_best(char *argv[])
820 {
821         static char cmdline[80];
822         int x;
823         /* See how close we get, then print the  */
824         char *myargv[AST_MAX_CMD_LEN];
825         for (x=0;x<AST_MAX_CMD_LEN;x++)
826                 myargv[x]=NULL;
827         for (x=0;argv[x];x++) {
828                 myargv[x] = argv[x];
829                 if (!find_cli(myargv, -1))
830                         break;
831         }
832         join(cmdline, sizeof(cmdline), myargv);
833         return cmdline;
834 }
835
836 int ast_cli_unregister(struct ast_cli_entry *e)
837 {
838         struct ast_cli_entry *cur, *l=NULL;
839         ast_mutex_lock(&clilock);
840         cur = helpers;
841         while(cur) {
842                 if (e == cur) {
843                         if (e->inuse) {
844                                 ast_log(LOG_WARNING, "Can't remove command that is in use\n");
845                         } else {
846                                 /* Rewrite */
847                                 if (l)
848                                         l->next = e->next;
849                                 else
850                                         helpers = e->next;
851                                 e->next = NULL;
852                                 break;
853                         }
854                 }
855                 l = cur;
856                 cur = cur->next;
857         }
858         ast_mutex_unlock(&clilock);
859         return 0;
860 }
861
862 int ast_cli_register(struct ast_cli_entry *e)
863 {
864         struct ast_cli_entry *cur, *l=NULL;
865         char fulle[80] ="", fulltst[80] ="";
866         static int len;
867         ast_mutex_lock(&clilock);
868         join2(fulle, sizeof(fulle), e->cmda);
869         if (find_cli(e->cmda, -1)) {
870                 ast_mutex_unlock(&clilock);
871                 ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
872                 return -1;
873         }
874         cur = helpers;
875         while(cur) {
876                 join2(fulltst, sizeof(fulltst), cur->cmda);
877                 len = strlen(fulltst);
878                 if (strlen(fulle) < len)
879                         len = strlen(fulle);
880                 if (strncasecmp(fulle, fulltst, len) < 0) {
881                         if (l) {
882                                 e->next = l->next;
883                                 l->next = e;
884                         } else {
885                                 e->next = helpers;
886                                 helpers = e;
887                         }
888                         break;
889                 }
890                 l = cur;
891                 cur = cur->next;
892         }
893         if (!cur) {
894                 if (l)
895                         l->next = e;
896                 else
897                         helpers = e;
898                 e->next = NULL;
899         }
900         ast_mutex_unlock(&clilock);
901         return 0;
902 }
903
904 static int help_workhorse(int fd, char *match[])
905 {
906         char fullcmd1[80];
907         char fullcmd2[80];
908         char matchstr[80];
909         char *fullcmd;
910         struct ast_cli_entry *e, *e1, *e2;
911         e1 = builtins;
912         e2 = helpers;
913         if (match)
914                 join(matchstr, sizeof(matchstr), match);
915         while(e1->cmda[0] || e2) {
916                 if (e2)
917                         join(fullcmd2, sizeof(fullcmd2), e2->cmda);
918                 if (e1->cmda[0])
919                         join(fullcmd1, sizeof(fullcmd1), e1->cmda);
920                 if (!e1->cmda[0] || 
921                                 (e2 && (strcmp(fullcmd2, fullcmd1) < 0))) {
922                         /* Use e2 */
923                         e = e2;
924                         fullcmd = fullcmd2;
925                         /* Increment by going to next */
926                         e2 = e2->next;
927                 } else {
928                         /* Use e1 */
929                         e = e1;
930                         fullcmd = fullcmd1;
931                         e1++;
932                 }
933                 /* Hide commands that start with '_' */
934                 if (fullcmd[0] == '_')
935                         continue;
936                 if (match) {
937                         if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
938                                 continue;
939                         }
940                 }
941                 ast_cli(fd, "%25.25s  %s\n", fullcmd, e->summary);
942         }
943         return 0;
944 }
945
946 static int handle_help(int fd, int argc, char *argv[]) {
947         struct ast_cli_entry *e;
948         char fullcmd[80];
949         if ((argc < 1))
950                 return RESULT_SHOWUSAGE;
951         if (argc > 1) {
952                 e = find_cli(argv + 1, 1);
953                 if (e) 
954                         ast_cli(fd, e->usage);
955                 else {
956                         if (find_cli(argv + 1, -1)) {
957                                 return help_workhorse(fd, argv + 1);
958                         } else {
959                                 join(fullcmd, sizeof(fullcmd), argv+1);
960                                 ast_cli(fd, "No such command '%s'.\n", fullcmd);
961                         }
962                 }
963         } else {
964                 return help_workhorse(fd, NULL);
965         }
966         return RESULT_SUCCESS;
967 }
968
969 static char *parse_args(char *s, int *max, char *argv[])
970 {
971         char *dup, *cur;
972         int x=0;
973         int quoted=0;
974         int escaped=0;
975         int whitespace=1;
976
977         dup = strdup(s);
978         if (dup) {
979                 cur = dup;
980                 while(*s) {
981                         switch(*s) {
982                         case '"':
983                                 /* If it's escaped, put a literal quote */
984                                 if (escaped) 
985                                         goto normal;
986                                 else 
987                                         quoted = !quoted;
988                                 if (quoted && whitespace) {
989                                         /* If we're starting a quote, coming off white space start a new word, too */
990                                         argv[x++] = cur;
991                                         whitespace=0;
992                                 }
993                                 escaped = 0;
994                                 break;
995                         case ' ':
996                         case '\t':
997                                 if (!quoted && !escaped) {
998                                         /* If we're not quoted, mark this as whitespace, and
999                                            end the previous argument */
1000                                         whitespace = 1;
1001                                         *(cur++) = '\0';
1002                                 } else
1003                                         /* Otherwise, just treat it as anything else */ 
1004                                         goto normal;
1005                                 break;
1006                         case '\\':
1007                                 /* If we're escaped, print a literal, otherwise enable escaping */
1008                                 if (escaped) {
1009                                         goto normal;
1010                                 } else {
1011                                         escaped=1;
1012                                 }
1013                                 break;
1014                         default:
1015 normal:
1016                                 if (whitespace) {
1017                                         if (x >= AST_MAX_ARGS -1) {
1018                                                 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
1019                                                 break;
1020                                         }
1021                                         /* Coming off of whitespace, start the next argument */
1022                                         argv[x++] = cur;
1023                                         whitespace=0;
1024                                 }
1025                                 *(cur++) = *s;
1026                                 escaped=0;
1027                         }
1028                         s++;
1029                 }
1030                 /* Null terminate */
1031                 *(cur++) = '\0';
1032                 argv[x] = NULL;
1033                 *max = x;
1034         }
1035         return dup;
1036 }
1037
1038 /* This returns the number of unique matches for the generator */
1039 int ast_cli_generatornummatches(char *text, char *word)
1040 {
1041         int matches = 0, i = 0;
1042         char *buf, *oldbuf = NULL;
1043
1044
1045         while ( (buf = ast_cli_generator(text, word, i)) ) {
1046                 if (++i > 1 && strcmp(buf,oldbuf) == 0)  {
1047                                 continue;
1048                 }
1049                 oldbuf = buf;
1050                 matches++;
1051         }
1052
1053         return matches;
1054 }
1055
1056 char **ast_cli_completion_matches(char *text, char *word)
1057 {
1058         char **match_list = NULL, *retstr, *prevstr;
1059         size_t match_list_len, max_equal, which, i;
1060         int matches = 0;
1061
1062         match_list_len = 1;
1063         while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
1064                 if (matches + 1 >= match_list_len) {
1065                         match_list_len <<= 1;
1066                         match_list = realloc(match_list, match_list_len * sizeof(char *));
1067                 }
1068                 match_list[++matches] = retstr;
1069         }
1070
1071         if (!match_list)
1072                 return (char **) NULL;
1073
1074         which = 2;
1075         prevstr = match_list[1];
1076         max_equal = strlen(prevstr);
1077         for (; which <= matches; which++) {
1078                 for (i = 0; i < max_equal && prevstr[i] == match_list[which][i]; i++)
1079                         continue;
1080                 max_equal = i;
1081         }
1082
1083         retstr = malloc(max_equal + 1);
1084         (void) strncpy(retstr, match_list[1], max_equal);
1085         retstr[max_equal] = '\0';
1086         match_list[0] = retstr;
1087
1088         if (matches + 1 >= match_list_len)
1089                 match_list = realloc(match_list, (match_list_len + 1) * sizeof(char *));
1090         match_list[matches + 1] = (char *) NULL;
1091
1092         return (match_list);
1093 }
1094
1095 static char *__ast_cli_generator(char *text, char *word, int state, int lock)
1096 {
1097         char *argv[AST_MAX_ARGS];
1098         struct ast_cli_entry *e, *e1, *e2;
1099         int x;
1100         int matchnum=0;
1101         char *dup, *res;
1102         char fullcmd1[80];
1103         char fullcmd2[80];
1104         char matchstr[80];
1105         char *fullcmd;
1106
1107         if ((dup = parse_args(text, &x, argv))) {
1108                 join(matchstr, sizeof(matchstr), argv);
1109                 if (lock)
1110                         ast_mutex_lock(&clilock);
1111                 e1 = builtins;
1112                 e2 = helpers;
1113                 while(e1->cmda[0] || e2) {
1114                         if (e2)
1115                                 join(fullcmd2, sizeof(fullcmd2), e2->cmda);
1116                         if (e1->cmda[0])
1117                                 join(fullcmd1, sizeof(fullcmd1), e1->cmda);
1118                         if (!e1->cmda[0] || 
1119                                         (e2 && (strcmp(fullcmd2, fullcmd1) < 0))) {
1120                                 /* Use e2 */
1121                                 e = e2;
1122                                 fullcmd = fullcmd2;
1123                                 /* Increment by going to next */
1124                                 e2 = e2->next;
1125                         } else {
1126                                 /* Use e1 */
1127                                 e = e1;
1128                                 fullcmd = fullcmd1;
1129                                 e1++;
1130                         }
1131                         if ((fullcmd[0] != '_') && !strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
1132                                 /* We contain the first part of one or more commands */
1133                                 matchnum++;
1134                                 if (matchnum > state) {
1135                                         /* Now, what we're supposed to return is the next word... */
1136                                         if (!ast_strlen_zero(word) && x>0) {
1137                                                 res = e->cmda[x-1];
1138                                         } else {
1139                                                 res = e->cmda[x];
1140                                         }
1141                                         if (res) {
1142                                                 if (lock)
1143                                                         ast_mutex_unlock(&clilock);
1144                                                 free(dup);
1145                                                 return res ? strdup(res) : NULL;
1146                                         }
1147                                 }
1148                         }
1149                         if (e->generator && !strncasecmp(matchstr, fullcmd, strlen(fullcmd))) {
1150                                 /* We have a command in its entirity within us -- theoretically only one
1151                                    command can have this occur */
1152                                 fullcmd = e->generator(matchstr, word, (!ast_strlen_zero(word) ? (x - 1) : (x)), state);
1153                                 if (lock)
1154                                         ast_mutex_unlock(&clilock);
1155                                 free(dup);
1156                                 return fullcmd;
1157                         }
1158                         
1159                 }
1160                 if (lock)
1161                         ast_mutex_unlock(&clilock);
1162                 free(dup);
1163         }
1164         return NULL;
1165 }
1166
1167 char *ast_cli_generator(char *text, char *word, int state)
1168 {
1169         return __ast_cli_generator(text, word, state, 1);
1170 }
1171
1172 int ast_cli_command(int fd, char *s)
1173 {
1174         char *argv[AST_MAX_ARGS];
1175         struct ast_cli_entry *e;
1176         int x;
1177         char *dup;
1178         x = AST_MAX_ARGS;
1179         if ((dup = parse_args(s, &x, argv))) {
1180                 /* We need at least one entry, or ignore */
1181                 if (x > 0) {
1182                         ast_mutex_lock(&clilock);
1183                         e = find_cli(argv, 0);
1184                         if (e)
1185                                 e->inuse++;
1186                         ast_mutex_unlock(&clilock);
1187                         if (e) {
1188                                 switch(e->handler(fd, x, argv)) {
1189                                 case RESULT_SHOWUSAGE:
1190                                         ast_cli(fd, e->usage);
1191                                         break;
1192                                 }
1193                         } else 
1194                                 ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv));
1195                         if (e) {
1196                                 ast_mutex_lock(&clilock);
1197                                 e->inuse--;
1198                                 ast_mutex_unlock(&clilock);
1199                         }
1200                 }
1201                 free(dup);
1202         } else {
1203                 ast_log(LOG_WARNING, "Out of memory\n");        
1204                 return -1;
1205         }
1206         return 0;
1207 }