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