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