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