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