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