Implement flags for AGI in the channel structure so taht "show channels" and
[asterisk/asterisk.git] / main / 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 "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/_private.h"
31 #include "asterisk/paths.h"     /* use ast_config_AST_MODULE_DIR */
32 #include <sys/signal.h>
33 #include <signal.h>
34 #include <ctype.h>
35 #include <regex.h>
36
37 #include "asterisk/cli.h"
38 #include "asterisk/linkedlists.h"
39 #include "asterisk/module.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/app.h"
44 #include "asterisk/lock.h"
45 #include "editline/readline/readline.h"
46 #include "asterisk/threadstorage.h"
47
48 /*!
49  * \brief map a debug or verbose value to a filename
50  */
51 struct ast_debug_file {
52         unsigned int level;
53         AST_RWLIST_ENTRY(ast_debug_file) entry;
54         char filename[0];
55 };
56
57 AST_RWLIST_HEAD(debug_file_list, ast_debug_file);
58
59 /*! list of filenames and their debug settings */
60 static struct debug_file_list debug_files;
61 /*! list of filenames and their verbose settings */
62 static struct debug_file_list verbose_files;
63
64 AST_THREADSTORAGE(ast_cli_buf);
65
66 /*! \brief Initial buffer size for resulting strings in ast_cli() */
67 #define AST_CLI_INITLEN   256
68
69 void ast_cli(int fd, const char *fmt, ...)
70 {
71         int res;
72         struct ast_str *buf;
73         va_list ap;
74
75         if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
76                 return;
77
78         va_start(ap, fmt);
79         res = ast_str_set_va(&buf, 0, fmt, ap);
80         va_end(ap);
81
82         if (res != AST_DYNSTR_BUILD_FAILED)
83                 ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
84 }
85
86 unsigned int ast_debug_get_by_file(const char *file) 
87 {
88         struct ast_debug_file *adf;
89         unsigned int res = 0;
90
91         AST_RWLIST_RDLOCK(&debug_files);
92         AST_LIST_TRAVERSE(&debug_files, adf, entry) {
93                 if (!strncasecmp(adf->filename, file, strlen(adf->filename))) {
94                         res = adf->level;
95                         break;
96                 }
97         }
98         AST_RWLIST_UNLOCK(&debug_files);
99
100         return res;
101 }
102
103 unsigned int ast_verbose_get_by_file(const char *file) 
104 {
105         struct ast_debug_file *adf;
106         unsigned int res = 0;
107
108         AST_RWLIST_RDLOCK(&verbose_files);
109         AST_LIST_TRAVERSE(&verbose_files, adf, entry) {
110                 if (!strncasecmp(adf->filename, file, strlen(file))) {
111                         res = adf->level;
112                         break;
113                 }
114         }
115         AST_RWLIST_UNLOCK(&verbose_files);
116
117         return res;
118 }
119
120 static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
121
122 static char *complete_fn(const char *word, int state)
123 {
124         char *c, *d;
125         char filename[256];
126
127         if (word[0] == '/')
128                 ast_copy_string(filename, word, sizeof(filename));
129         else
130                 snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
131
132         c = d = filename_completion_function(filename, state);
133         
134         if (c && word[0] != '/')
135                 c += (strlen(ast_config_AST_MODULE_DIR) + 1);
136         if (c)
137                 c = ast_strdup(c);
138         if (d)
139                 free(d);
140         
141         return c;
142 }
143
144 static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
145 {
146         /* "module load <mod>" */
147         switch (cmd) {
148         case CLI_INIT:
149                 e->command = "module load";
150                 e->usage =
151                         "Usage: module load <module name>\n"
152                         "       Loads the specified module into Asterisk.\n";
153                 return NULL;
154
155         case CLI_GENERATE:
156                 if (a->pos != e->args)
157                         return NULL;
158                 return complete_fn(a->word, a->n);
159         }
160         if (a->argc != e->args + 1)
161                 return CLI_SHOWUSAGE;
162         if (ast_load_resource(a->argv[e->args])) {
163                 ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]);
164                 return CLI_FAILURE;
165         }
166         return CLI_SUCCESS;
167 }
168
169 static char *handle_load_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
170 {
171         char *res = handle_load(e, cmd, a);
172         if (cmd == CLI_INIT)
173                 e->command = "load";
174         return res;
175 }
176
177 static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
178 {
179         int x;
180
181         switch (cmd) {
182         case CLI_INIT:
183                 e->command = "module reload";
184                 e->usage =
185                         "Usage: module reload [module ...]\n"
186                         "       Reloads configuration files for all listed modules which support\n"
187                         "       reloading, or for all supported modules if none are listed.\n";
188                 return NULL;
189
190         case CLI_GENERATE:
191                 return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1);
192         }
193         if (a->argc == e->args) {
194                 ast_module_reload(NULL);
195                 return CLI_SUCCESS;
196         }
197         for (x = e->args; x < a->argc; x++) {
198                 int res = ast_module_reload(a->argv[x]);
199                 /* XXX reload has multiple error returns, including -1 on error and 2 on success */
200                 switch (res) {
201                 case 0:
202                         ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
203                         break;
204                 case 1:
205                         ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
206                         break;
207                 }
208         }
209         return CLI_SUCCESS;
210 }
211
212 static char *handle_reload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
213 {
214         char *s = handle_reload(e, cmd, a);
215         if (cmd == CLI_INIT)            /* override command name */
216                 e->command = "reload";
217         return s;
218 }
219
220 /*! 
221  * \brief Find the debug or verbose file setting 
222  * \arg debug 1 for debug, 0 for verbose
223  */
224 static struct ast_debug_file *find_debug_file(const char *fn, unsigned int debug)
225 {
226         struct ast_debug_file *df = NULL;
227         struct debug_file_list *dfl = debug ? &debug_files : &verbose_files;
228
229         AST_LIST_TRAVERSE(dfl, df, entry) {
230                 if (!strcasecmp(df->filename, fn))
231                         break;
232         }
233
234         return df;
235 }
236
237 static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
238 {
239         int oldval;
240         int newlevel;
241         int atleast = 0;
242         int fd = a->fd;
243         int argc = a->argc;
244         char **argv = a->argv;
245         int *dst;
246         char *what;
247         struct debug_file_list *dfl;
248         struct ast_debug_file *adf;
249         char *fn;
250
251         switch (cmd) {
252         case CLI_INIT:
253                 e->command = "core set {debug|verbose} [off|atleast]";
254                 e->usage =
255                         "Usage: core set {debug|verbose} [atleast] <level> [filename]\n"
256                         "       core set {debug|verbose} off\n"
257                         "       Sets level of debug or verbose messages to be displayed or \n"
258                         "       sets a filename to display debug messages from.\n"
259                         "       0 or off means no messages should be displayed.\n"
260                         "       Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
261                 return NULL;
262
263         case CLI_GENERATE:
264                 return NULL;
265         }
266         /* all the above return, so we proceed with the handler.
267          * we are guaranteed to be called with argc >= e->args;
268          */
269
270         if (argc < e->args)
271                 return CLI_SHOWUSAGE;
272         if (!strcasecmp(argv[e->args - 2], "debug")) {
273                 dst = &option_debug;
274                 oldval = option_debug;
275                 what = "Core debug";
276         } else {
277                 dst = &option_verbose;
278                 oldval = option_verbose;
279                 what = "Verbosity";
280         }
281         if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) {
282                 unsigned int debug = (*what == 'C');
283                 newlevel = 0;
284
285                 dfl = debug ? &debug_files : &verbose_files;
286
287                 AST_RWLIST_WRLOCK(dfl);
288                 while ((adf = AST_RWLIST_REMOVE_HEAD(dfl, entry)))
289                         ast_free(adf);
290                 ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
291                 AST_RWLIST_UNLOCK(dfl);
292
293                 goto done;
294         }
295         if (!strcasecmp(argv[e->args-1], "atleast"))
296                 atleast = 1;
297         if (argc != e->args + atleast && argc != e->args + atleast + 1)
298                 return CLI_SHOWUSAGE;
299         if (sscanf(argv[e->args + atleast - 1], "%d", &newlevel) != 1)
300                 return CLI_SHOWUSAGE;
301         if (argc == e->args + atleast + 1) {
302                 unsigned int debug = (*what == 'C');
303                 dfl = debug ? &debug_files : &verbose_files;
304
305                 fn = argv[e->args + atleast];
306
307                 AST_RWLIST_WRLOCK(dfl);
308
309                 if ((adf = find_debug_file(fn, debug)) && !newlevel) {
310                         AST_RWLIST_REMOVE(dfl, adf, entry);
311                         if (AST_RWLIST_EMPTY(dfl))
312                                 ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
313                         AST_RWLIST_UNLOCK(dfl);
314                         ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, adf->level, fn);
315                         ast_free(adf);
316                         return CLI_SUCCESS;
317                 }
318
319                 if (adf) {
320                         if ((atleast && newlevel < adf->level) || adf->level == newlevel) {
321                                 ast_cli(fd, "%s is %d for '%s'\n", what, adf->level, fn);
322                                 AST_RWLIST_UNLOCK(dfl);
323                                 return CLI_SUCCESS;
324                         }
325                 } else if (!(adf = ast_calloc(1, sizeof(*adf) + strlen(fn) + 1))) {
326                         AST_RWLIST_UNLOCK(dfl);
327                         return CLI_FAILURE;
328                 }
329
330                 oldval = adf->level;
331                 adf->level = newlevel;
332                 strcpy(adf->filename, fn);
333
334                 ast_set_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
335
336                 AST_RWLIST_INSERT_TAIL(dfl, adf, entry);
337                 AST_RWLIST_UNLOCK(dfl);
338
339                 ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, adf->level, adf->filename);
340
341                 return CLI_SUCCESS;
342         }
343
344 done:
345         if (!atleast || newlevel > *dst)
346                 *dst = newlevel;
347         if (oldval > 0 && *dst == 0)
348                 ast_cli(fd, "%s is now OFF\n", what);
349         else if (*dst > 0) {
350                 if (oldval == *dst)
351                         ast_cli(fd, "%s is at least %d\n", what, *dst);
352                 else
353                         ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst);
354         }
355
356         return CLI_SUCCESS;
357 }
358
359 static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
360 {
361         switch (cmd) {
362         case CLI_INIT:
363                 e->command = "logger mute";
364                 e->usage = 
365                         "Usage: logger mute\n"
366                         "       Disables logging output to the current console, making it possible to\n"
367                         "       gather information without being disturbed by scrolling lines.\n";
368                 return NULL;
369         case CLI_GENERATE:
370                 return NULL;
371         }
372
373         if (a->argc < 2 || a->argc > 3)
374                 return CLI_SHOWUSAGE;
375
376         if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
377                 ast_console_toggle_mute(a->fd, 1);
378         else
379                 ast_console_toggle_mute(a->fd, 0);
380
381         return CLI_SUCCESS;
382 }
383
384 static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
385 {
386         /* "module unload mod_1 [mod_2 .. mod_N]" */
387         int x;
388         int force = AST_FORCE_SOFT;
389         char *s;
390
391         switch (cmd) {
392         case CLI_INIT:
393                 e->command = "module unload";
394                 e->usage =
395                         "Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
396                         "       Unloads the specified module from Asterisk. The -f\n"
397                         "       option causes the module to be unloaded even if it is\n"
398                         "       in use (may cause a crash) and the -h module causes the\n"
399                         "       module to be unloaded even if the module says it cannot, \n"
400                         "       which almost always will cause a crash.\n";
401                 return NULL;
402
403         case CLI_GENERATE:
404                 return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
405         }
406         if (a->argc < e->args + 1)
407                 return CLI_SHOWUSAGE;
408         x = e->args;    /* first argument */
409         s = a->argv[x];
410         if (s[0] == '-') {
411                 if (s[1] == 'f')
412                         force = AST_FORCE_FIRM;
413                 else if (s[1] == 'h')
414                         force = AST_FORCE_HARD;
415                 else
416                         return CLI_SHOWUSAGE;
417                 if (a->argc < e->args + 2)      /* need at least one module name */
418                         return CLI_SHOWUSAGE;
419                 x++;    /* skip this argument */
420         }
421
422         for (; x < a->argc; x++) {
423                 if (ast_unload_resource(a->argv[x], force)) {
424                         ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
425                         return CLI_FAILURE;
426                 }
427         }
428         return CLI_SUCCESS;
429 }
430
431 static char *handle_unload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
432 {
433         char *res = handle_unload(e, cmd, a);
434         if (cmd == CLI_INIT)
435                 e->command = "unload";  /* XXX override */
436         return res;
437 }
438
439 #define MODLIST_FORMAT  "%-30s %-40.40s %-10d\n"
440 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
441
442 AST_MUTEX_DEFINE_STATIC(climodentrylock);
443 static int climodentryfd = -1;
444
445 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
446 {
447         /* Comparing the like with the module */
448         if (strcasestr(module, like) ) {
449                 ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
450                 return 1;
451         } 
452         return 0;
453 }
454
455 static void print_uptimestr(int fd, struct timeval timeval, const char *prefix, int printsec)
456 {
457         int x; /* the main part - years, weeks, etc. */
458         struct ast_str *out;
459
460 #define SECOND (1)
461 #define MINUTE (SECOND*60)
462 #define HOUR (MINUTE*60)
463 #define DAY (HOUR*24)
464 #define WEEK (DAY*7)
465 #define YEAR (DAY*365)
466 #define NEEDCOMMA(x) ((x)? ",": "")     /* define if we need a comma */
467         if (timeval.tv_sec < 0) /* invalid, nothing to show */
468                 return;
469
470         if (printsec)  {        /* plain seconds output */
471                 ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval.tv_sec);
472                 return;
473         }
474         out = ast_str_alloca(256);
475         if (timeval.tv_sec > YEAR) {
476                 x = (timeval.tv_sec / YEAR);
477                 timeval.tv_sec -= (x * YEAR);
478                 ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
479         }
480         if (timeval.tv_sec > WEEK) {
481                 x = (timeval.tv_sec / WEEK);
482                 timeval.tv_sec -= (x * WEEK);
483                 ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
484         }
485         if (timeval.tv_sec > DAY) {
486                 x = (timeval.tv_sec / DAY);
487                 timeval.tv_sec -= (x * DAY);
488                 ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
489         }
490         if (timeval.tv_sec > HOUR) {
491                 x = (timeval.tv_sec / HOUR);
492                 timeval.tv_sec -= (x * HOUR);
493                 ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
494         }
495         if (timeval.tv_sec > MINUTE) {
496                 x = (timeval.tv_sec / MINUTE);
497                 timeval.tv_sec -= (x * MINUTE);
498                 ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
499         }
500         x = timeval.tv_sec;
501         if (x > 0 || out->used == 0)    /* if there is nothing, print 0 seconds */
502                 ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
503         ast_cli(fd, "%s: %s\n", prefix, out->str);
504 }
505
506 static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
507 {
508         struct timeval curtime = ast_tvnow();
509         int printsec;
510
511         switch (cmd) {
512         case CLI_INIT:
513                 e->command = "core show uptime [seconds]";
514                 e->usage =
515                         "Usage: core show uptime [seconds]\n"
516                         "       Shows Asterisk uptime information.\n"
517                         "       The seconds word returns the uptime in seconds only.\n";
518                 return NULL;
519
520         case CLI_GENERATE:
521                 return NULL;
522         }
523         /* regular handler */
524         if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
525                 printsec = 1;
526         else if (a->argc == e->args-1)
527                 printsec = 0;
528         else
529                 return CLI_SHOWUSAGE;
530         if (ast_startuptime.tv_sec)
531                 print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
532         if (ast_lastreloadtime.tv_sec)
533                 print_uptimestr(a->fd, ast_tvsub(curtime, ast_lastreloadtime), "Last reload", printsec);
534         return CLI_SUCCESS;
535 }
536
537 static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
538 {
539         char *like;
540
541         switch (cmd) {
542         case CLI_INIT:
543                 e->command = "module show [like]";
544                 e->usage =
545                         "Usage: module show [like keyword]\n"
546                         "       Shows Asterisk modules currently in use, and usage statistics.\n";
547                 return NULL;
548
549         case CLI_GENERATE:
550                 if (a->pos == e->args)
551                         return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
552                 else
553                         return NULL;
554         }
555         /* all the above return, so we proceed with the handler.
556          * we are guaranteed to have argc >= e->args
557          */
558         if (a->argc == e->args - 1)
559                 like = "";
560         else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
561                 like = a->argv[e->args];
562         else
563                 return CLI_SHOWUSAGE;
564                 
565         ast_mutex_lock(&climodentrylock);
566         climodentryfd = a->fd; /* global, protected by climodentrylock */
567         ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
568         ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
569         climodentryfd = -1;
570         ast_mutex_unlock(&climodentrylock);
571         return CLI_SUCCESS;
572 }
573 #undef MODLIST_FORMAT
574 #undef MODLIST_FORMAT2
575
576 static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
577 {
578         struct timeval curtime = ast_tvnow();
579         int showuptime, printsec;
580
581         switch (cmd) {
582         case CLI_INIT:
583                 e->command = "core show calls [uptime]";
584                 e->usage =
585                         "Usage: core show calls [uptime] [seconds]\n"
586                         "       Lists number of currently active calls and total number of calls\n"
587                         "       processed through PBX since last restart. If 'uptime' is specified\n"
588                         "       the system uptime is also displayed. If 'seconds' is specified in\n"
589                         "       addition to 'uptime', the system uptime is displayed in seconds.\n";
590                 return NULL;
591
592         case CLI_GENERATE:
593                 if (a->pos != e->args)
594                         return NULL;
595                 return a->n == 0  ? ast_strdup("seconds") : NULL;
596         }
597
598         /* regular handler */
599         if (a->argc >= e->args && !strcasecmp(a->argv[e->args-1],"uptime")) {
600                 showuptime = 1;
601
602                 if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
603                         printsec = 1;
604                 else if (a->argc == e->args)
605                         printsec = 0;
606                 else
607                         return CLI_SHOWUSAGE;
608         } else if (a->argc == e->args-1) {
609                 showuptime = 0;
610                 printsec = 0;
611         } else
612                 return CLI_SHOWUSAGE;
613
614         if (option_maxcalls) {
615                 ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
616                    ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
617                    ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
618         } else {
619                 ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
620         }
621    
622         ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
623
624         if (ast_startuptime.tv_sec && showuptime) {
625                 print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
626         }
627
628         return RESULT_SUCCESS;
629 }
630
631 /*! \brief Add a marker before the app if the channel is controlled by AGI/FastAGI or AsyncAGI 
632         Used for "show channels"
633 */
634 static const char *agi_flag(struct ast_channel *chan)
635 {
636         if (ast_test_flag(chan, AST_FLAG_AGI))
637                 return "[AGI]  ";
638         if (ast_test_flag(chan, AST_FLAG_FASTAGI))
639                 return "[FAGI] ";
640         if (ast_test_flag(chan, AST_FLAG_ASYNCAGI))
641                 return "[AAGI] ";
642         return "";
643 }
644
645 static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
646 {
647 #define FORMAT_STRING  "%-20.20s %-20.20s %-7.7s %-30.30s\n"
648 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
649 #define CONCISE_FORMAT_STRING  "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
650 #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"
651 #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"
652
653         struct ast_channel *c = NULL;
654         int numchans = 0, concise = 0, verbose = 0, count = 0;
655         int fd, argc;
656         char **argv;
657
658         switch (cmd) {
659         case CLI_INIT:
660                 e->command = "core show channels [concise|verbose|count]";
661                 e->usage =
662                         "Usage: core show channels [concise|verbose|count]\n"
663                         "       Lists currently defined channels and some information about them. If\n"
664                         "       'concise' is specified, the format is abridged and in a more easily\n"
665                         "       machine parsable format. If 'verbose' is specified, the output includes\n"
666                         "       more and longer fields. If 'count' is specified only the channel and call\n"
667                         "       count is output.\n"
668                         "       The 'concise' option is deprecated and will be removed from future versions\n"
669                         "       of Asterisk.\n";
670                 return NULL;
671
672         case CLI_GENERATE:
673                 return NULL;
674         }
675         fd = a->fd;
676         argc = a->argc;
677         argv = a->argv;
678
679         if (a->argc == e->args) {
680                 if (!strcasecmp(argv[e->args-1],"concise"))
681                         concise = 1;
682                 else if (!strcasecmp(argv[e->args-1],"verbose"))
683                         verbose = 1;
684                 else if (!strcasecmp(argv[e->args-1],"count"))
685                         count = 1;
686                 else
687                         return CLI_SHOWUSAGE;
688         } else if (a->argc != e->args - 1)
689                 return CLI_SHOWUSAGE;
690
691         if (!count) {
692                 if (!concise && !verbose)
693                         ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
694                 else if (verbose)
695                         ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
696                                 "CallerID", "Duration", "Accountcode", "BridgedTo");
697         }
698
699         while ((c = ast_channel_walk_locked(c)) != NULL) {
700                 struct ast_channel *bc = ast_bridged_channel(c);
701                 char durbuf[10] = "-";
702
703                 if (!count) {
704                         if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
705                                 int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
706                                 if (verbose) {
707                                         int durh = duration / 3600;
708                                         int durm = (duration % 3600) / 60;
709                                         int durs = duration % 60;
710                                         snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
711                                 } else {
712                                         snprintf(durbuf, sizeof(durbuf), "%d", duration);
713                                 }                               
714                         }
715                         if (concise) {
716                                 ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
717                                         c->appl ? c->appl : "(None)",
718                                         S_OR(c->data, ""),      /* XXX different from verbose ? */
719                                         S_OR(c->cid.cid_num, ""),
720                                         S_OR(c->accountcode, ""),
721                                         c->amaflags, 
722                                         durbuf,
723                                         bc ? bc->name : "(None)",
724                                         c->uniqueid);
725                         } else if (verbose) {
726                                 ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
727                                         c->appl ? c->appl : "(None)",
728                                         c->data ? S_OR(c->data, "(Empty)" ): "(None)",
729                                         S_OR(c->cid.cid_num, ""),
730                                         durbuf,
731                                         S_OR(c->accountcode, ""),
732                                         bc ? bc->name : "(None)");
733                         } else {
734                                 char locbuf[40] = "(None)";
735                                 char appdata[40] = "(None)";
736                                 
737                                 if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
738                                         snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
739                                 if (c->appl)
740                                         snprintf(appdata, sizeof(appdata), "%s%s(%s)", agi_flag(c), c->appl, S_OR(c->data, ""));
741                                 ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
742                         }
743                 }
744                 numchans++;
745                 ast_channel_unlock(c);
746         }
747         if (!concise) {
748                 ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
749                 if (option_maxcalls)
750                         ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
751                                 ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
752                                 ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
753                 else
754                         ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
755
756                 ast_cli(fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
757         }
758         return CLI_SUCCESS;
759         
760 #undef FORMAT_STRING
761 #undef FORMAT_STRING2
762 #undef CONCISE_FORMAT_STRING
763 #undef VERBOSE_FORMAT_STRING
764 #undef VERBOSE_FORMAT_STRING2
765 }
766
767 static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
768 {
769         struct ast_channel *c=NULL;
770
771         switch (cmd) {
772         case CLI_INIT:
773                 e->command = "soft hangup";
774                 e->usage =
775                         "Usage: soft hangup <channel>\n"
776                         "       Request that a channel be hung up. The hangup takes effect\n"
777                         "       the next time the driver reads or writes from the channel\n";
778                 return NULL;
779         case CLI_GENERATE:
780                 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
781         }
782         if (a->argc != 3)
783                 return CLI_SHOWUSAGE;
784         c = ast_get_channel_by_name_locked(a->argv[2]);
785         if (c) {
786                 ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
787                 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
788                 ast_channel_unlock(c);
789         } else
790                 ast_cli(a->fd, "%s is not a known channel\n", a->argv[2]);
791         return CLI_SUCCESS;
792 }
793
794 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
795
796 static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
797 {
798         char *buf, *obuf;
799         int buflen = 2048;
800         int len = 0;
801         char **matches;
802         int x, matchlen;
803         
804         switch (cmd) {
805         case CLI_INIT:
806                 e->command = "_command matchesarray";
807                 e->usage = 
808                         "Usage: _command matchesarray \"<line>\" text \n"
809                         "       This function is used internally to help with command completion and should.\n"
810                         "       never be called by the user directly.\n";
811                 return NULL;
812         case CLI_GENERATE:
813                 return NULL;
814         }
815
816         if (a->argc != 4)
817                 return CLI_SHOWUSAGE;
818         if (!(buf = ast_malloc(buflen)))
819                 return CLI_FAILURE;
820         buf[len] = '\0';
821         matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
822         if (matches) {
823                 for (x=0; matches[x]; x++) {
824                         matchlen = strlen(matches[x]) + 1;
825                         if (len + matchlen >= buflen) {
826                                 buflen += matchlen * 3;
827                                 obuf = buf;
828                                 if (!(buf = ast_realloc(obuf, buflen))) 
829                                         /* Memory allocation failure...  Just free old buffer and be done */
830                                         ast_free(obuf);
831                         }
832                         if (buf)
833                                 len += sprintf( buf + len, "%s ", matches[x]);
834                         ast_free(matches[x]);
835                         matches[x] = NULL;
836                 }
837                 ast_free(matches);
838         }
839
840         if (buf) {
841                 ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
842                 ast_free(buf);
843         } else
844                 ast_cli(a->fd, "NULL\n");
845
846         return CLI_SUCCESS;
847 }
848
849
850
851 static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
852 {
853         int matches = 0;
854
855         switch (cmd) {
856         case CLI_INIT:
857                 e->command = "_command nummatches";
858                 e->usage = 
859                         "Usage: _command nummatches \"<line>\" text \n"
860                         "       This function is used internally to help with command completion and should.\n"
861                         "       never be called by the user directly.\n";
862                 return NULL;
863         case CLI_GENERATE:
864                 return NULL;
865         }
866
867         if (a->argc != 4)
868                 return CLI_SHOWUSAGE;
869
870         matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
871
872         ast_cli(a->fd, "%d", matches);
873
874         return CLI_SUCCESS;
875 }
876
877 static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
878 {
879         char *buf;
880         switch (cmd) {
881         case CLI_INIT:
882                 e->command = "_command complete";
883                 e->usage = 
884                         "Usage: _command complete \"<line>\" text state\n"
885                         "       This function is used internally to help with command completion and should.\n"
886                         "       never be called by the user directly.\n";
887                 return NULL;
888         case CLI_GENERATE:
889                 return NULL;
890         }
891         if (a->argc != 5)
892                 return CLI_SHOWUSAGE;
893         buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
894         if (buf) {
895                 ast_cli(a->fd, "%s", buf);
896                 ast_free(buf);
897         } else
898                 ast_cli(a->fd, "NULL\n");
899         return CLI_SUCCESS;
900 }
901
902 static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
903 {
904         struct ast_channel *c = NULL;
905         int is_all, is_off = 0;
906
907         switch (cmd) {
908         case CLI_INIT:
909                 e->command = "core set debug channel";
910                 e->usage =
911                         "Usage: core set debug channel <all|channel> [off]\n"
912                         "       Enables/disables debugging on all or on a specific channel.\n";
913                 return NULL;
914
915         case CLI_GENERATE:
916                 /* XXX remember to handle the optional "off" */
917                 if (a->pos != e->args)
918                         return NULL;
919                 return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
920         }
921         /* 'core set debug channel {all|chan_id}' */
922         if (a->argc == e->args + 2) {
923                 if (!strcasecmp(a->argv[e->args + 1], "off"))
924                         is_off = 1;
925                 else
926                         return CLI_SHOWUSAGE;
927         } else if (a->argc != e->args + 1)
928                 return CLI_SHOWUSAGE;
929
930         is_all = !strcasecmp("all", a->argv[e->args]);
931         if (is_all) {
932                 if (is_off) {
933                         global_fin &= ~DEBUGCHAN_FLAG;
934                         global_fout &= ~DEBUGCHAN_FLAG;
935                 } else {
936                         global_fin |= DEBUGCHAN_FLAG;
937                         global_fout |= DEBUGCHAN_FLAG;
938                 }
939                 c = ast_channel_walk_locked(NULL);
940         } else {
941                 c = ast_get_channel_by_name_locked(a->argv[e->args]);
942                 if (c == NULL)
943                         ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
944         }
945         while (c) {
946                 if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
947                         if (is_off) {
948                                 c->fin &= ~DEBUGCHAN_FLAG;
949                                 c->fout &= ~DEBUGCHAN_FLAG;
950                         } else {
951                                 c->fin |= DEBUGCHAN_FLAG;
952                                 c->fout |= DEBUGCHAN_FLAG;
953                         }
954                         ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
955                 }
956                 ast_channel_unlock(c);
957                 if (!is_all)
958                         break;
959                 c = ast_channel_walk_locked(c);
960         }
961         ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
962         return CLI_SUCCESS;
963 }
964
965 static char *handle_debugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
966 {
967         char *res;
968
969         if (cmd == CLI_HANDLER && a->argc != e->args + 1)
970                 return CLI_SHOWUSAGE;
971         res = handle_core_set_debug_channel(e, cmd, a);
972         if (cmd == CLI_INIT)
973                 e->command = "debug channel";
974         return res;
975 }
976
977 static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
978 {
979         char *res;
980         if (cmd == CLI_HANDLER) {
981                 if (a->argc != e->args + 1)
982                         return CLI_SHOWUSAGE;
983                 /* pretend we have an extra "off" at the end. We can do this as the array
984                  * is NULL terminated so we overwrite that entry.
985                  */
986                 a->argv[e->args+1] = "off";
987                 a->argc++;
988         }
989         res = handle_core_set_debug_channel(e, cmd, a);
990         if (cmd == CLI_INIT)
991                 e->command = "no debug channel";
992         return res;
993 }
994                 
995 static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
996 {
997         struct ast_channel *c=NULL;
998         struct timeval now;
999         struct ast_str *out = ast_str_alloca(2048);
1000         char cdrtime[256];
1001         char nf[256], wf[256], rf[256];
1002         long elapsed_seconds=0;
1003         int hour=0, min=0, sec=0;
1004 #ifdef CHANNEL_TRACE
1005         int trace_enabled;
1006 #endif
1007
1008         switch (cmd) {
1009         case CLI_INIT:
1010                 e->command = "core show channel";
1011                 e->usage = 
1012                         "Usage: core show channel <channel>\n"
1013                         "       Shows lots of information about the specified channel.\n";
1014                 return NULL;
1015         case CLI_GENERATE:
1016                 return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
1017         }
1018         
1019         if (a->argc != 4)
1020                 return CLI_SHOWUSAGE;
1021         now = ast_tvnow();
1022         c = ast_get_channel_by_name_locked(a->argv[3]);
1023         if (!c) {
1024                 ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
1025                 return CLI_SUCCESS;
1026         }
1027         if (c->cdr) {
1028                 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
1029                 hour = elapsed_seconds / 3600;
1030                 min = (elapsed_seconds % 3600) / 60;
1031                 sec = elapsed_seconds % 60;
1032                 snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
1033         } else
1034                 strcpy(cdrtime, "N/A");
1035         ast_cli(a->fd, 
1036                 " -- General --\n"
1037                 "           Name: %s\n"
1038                 "           Type: %s\n"
1039                 "       UniqueID: %s\n"
1040                 "      Caller ID: %s\n"
1041                 " Caller ID Name: %s\n"
1042                 "    DNID Digits: %s\n"
1043                 "       Language: %s\n"
1044                 "          State: %s (%d)\n"
1045                 "          Rings: %d\n"
1046                 "  NativeFormats: %s\n"
1047                 "    WriteFormat: %s\n"
1048                 "     ReadFormat: %s\n"
1049                 " WriteTranscode: %s\n"
1050                 "  ReadTranscode: %s\n"
1051                 "1st File Descriptor: %d\n"
1052                 "      Frames in: %d%s\n"
1053                 "     Frames out: %d%s\n"
1054                 " Time to Hangup: %ld\n"
1055                 "   Elapsed Time: %s\n"
1056                 "  Direct Bridge: %s\n"
1057                 "Indirect Bridge: %s\n"
1058                 " --   PBX   --\n"
1059                 "        Context: %s\n"
1060                 "      Extension: %s\n"
1061                 "       Priority: %d\n"
1062                 "     Call Group: %llu\n"
1063                 "   Pickup Group: %llu\n"
1064                 "    Application: %s\n"
1065                 "           Data: %s\n"
1066                 "    Blocking in: %s\n",
1067                 c->name, c->tech->type, c->uniqueid,
1068                 S_OR(c->cid.cid_num, "(N/A)"),
1069                 S_OR(c->cid.cid_name, "(N/A)"),
1070                 S_OR(c->cid.cid_dnid, "(N/A)"), 
1071                 c->language,    
1072                 ast_state2str(c->_state), c->_state, c->rings, 
1073                 ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
1074                 ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
1075                 ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
1076                 c->writetrans ? "Yes" : "No",
1077                 c->readtrans ? "Yes" : "No",
1078                 c->fds[0],
1079                 c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
1080                 c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
1081                 (long)c->whentohangup.tv_sec,
1082                 cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
1083                 c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
1084                 ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
1085                 (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
1086         
1087         if (pbx_builtin_serialize_variables(c, &out))
1088                 ast_cli(a->fd,"      Variables:\n%s\n", out->str);
1089         if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
1090                 ast_cli(a->fd,"  CDR Variables:\n%s\n", out->str);
1091 #ifdef CHANNEL_TRACE
1092         trace_enabled = ast_channel_trace_is_enabled(c);
1093         ast_cli(a->fd, "  Context Trace: %s\n", trace_enabled ? "Enabled" : "Disabled");
1094         if (trace_enabled && ast_channel_trace_serialize(c, &out))
1095                 ast_cli(a->fd, "          Trace:\n%s\n", out->str);
1096 #endif
1097         ast_channel_unlock(c);
1098         return CLI_SUCCESS;
1099 }
1100
1101 /*
1102  * helper function to generate CLI matches from a fixed set of values.
1103  * A NULL word is acceptable.
1104  */
1105 char *ast_cli_complete(const char *word, char *const choices[], int state)
1106 {
1107         int i, which = 0, len;
1108         len = ast_strlen_zero(word) ? 0 : strlen(word);
1109
1110         for (i = 0; choices[i]; i++) {
1111                 if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
1112                         return ast_strdup(choices[i]);
1113         }
1114         return NULL;
1115 }
1116
1117 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
1118 {
1119         struct ast_channel *c = NULL;
1120         int which = 0;
1121         int wordlen;
1122         char notfound = '\0';
1123         char *ret = &notfound; /* so NULL can break the loop */
1124
1125         if (pos != rpos)
1126                 return NULL;
1127
1128         wordlen = strlen(word); 
1129
1130         while (ret == &notfound && (c = ast_channel_walk_locked(c))) {
1131                 if (!strncasecmp(word, c->name, wordlen) && ++which > state)
1132                         ret = ast_strdup(c->name);
1133                 ast_channel_unlock(c);
1134         }
1135         return ret == &notfound ? NULL : ret;
1136 }
1137
1138 static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1139 {
1140 #define FORMAT_STRING  "%-25s  %-20s  %-20s\n"
1141
1142         struct ast_group_info *gi = NULL;
1143         int numchans = 0;
1144         regex_t regexbuf;
1145         int havepattern = 0;
1146
1147         switch (cmd) {
1148         case CLI_INIT:
1149                 e->command = "group show channels";
1150                 e->usage = 
1151                         "Usage: group show channels [pattern]\n"
1152                         "       Lists all currently active channels with channel group(s) specified.\n"
1153                         "       Optional regular expression pattern is matched to group names for each\n"
1154                         "       channel.\n";
1155                 return NULL;
1156         case CLI_GENERATE:
1157                 return NULL;
1158         }
1159
1160         if (a->argc < 3 || a->argc > 4)
1161                 return CLI_SHOWUSAGE;
1162         
1163         if (a->argc == 4) {
1164                 if (regcomp(&regexbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
1165                         return CLI_SHOWUSAGE;
1166                 havepattern = 1;
1167         }
1168
1169         ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
1170
1171         ast_app_group_list_rdlock();
1172         
1173         gi = ast_app_group_list_head();
1174         while (gi) {
1175                 if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
1176                         ast_cli(a->fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
1177                         numchans++;
1178                 }
1179                 gi = AST_LIST_NEXT(gi, list);
1180         }
1181         
1182         ast_app_group_list_unlock();
1183         
1184         if (havepattern)
1185                 regfree(&regexbuf);
1186
1187         ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
1188         return CLI_SUCCESS;
1189 #undef FORMAT_STRING
1190 }
1191
1192 static struct ast_cli_entry cli_debug_channel_deprecated = AST_CLI_DEFINE(handle_debugchan_deprecated, "Enable debugging on channel");
1193 static struct ast_cli_entry cli_module_load_deprecated = AST_CLI_DEFINE(handle_load_deprecated, "Load a module");
1194 static struct ast_cli_entry cli_module_reload_deprecated = AST_CLI_DEFINE(handle_reload_deprecated, "reload modules by name");
1195 static struct ast_cli_entry cli_module_unload_deprecated = AST_CLI_DEFINE(handle_unload_deprecated, "unload modules by name");
1196
1197 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
1198
1199 static struct ast_cli_entry cli_cli[] = {
1200         /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
1201         AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
1202         AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
1203         AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
1204
1205         AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
1206
1207         AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
1208
1209         AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
1210
1211         AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
1212
1213         AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel",
1214                 .deprecate_cmd = &cli_debug_channel_deprecated),
1215
1216         AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
1217
1218         AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
1219
1220         AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
1221
1222         AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
1223
1224         AST_CLI_DEFINE(handle_modlist, "List modules and info"),
1225
1226         AST_CLI_DEFINE(handle_load, "Load a module by name", .deprecate_cmd = &cli_module_load_deprecated),
1227
1228         AST_CLI_DEFINE(handle_reload, "Reload configuration", .deprecate_cmd = &cli_module_reload_deprecated),
1229
1230         AST_CLI_DEFINE(handle_unload, "Unload a module by name", .deprecate_cmd = &cli_module_unload_deprecated ),
1231
1232         AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
1233
1234         AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
1235 };
1236
1237 /*!
1238  * Some regexp characters in cli arguments are reserved and used as separators.
1239  */
1240 static const char cli_rsvd[] = "[]{}|*%";
1241
1242 /*!
1243  * initialize the _full_cmd string and related parameters,
1244  * return 0 on success, -1 on error.
1245  */
1246 static int set_full_cmd(struct ast_cli_entry *e)
1247 {
1248         int i;
1249         char buf[80];
1250
1251         ast_join(buf, sizeof(buf), e->cmda);
1252         e->_full_cmd = ast_strdup(buf);
1253         if (!e->_full_cmd) {
1254                 ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
1255                 return -1;
1256         }
1257         e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
1258         for (i = 0; e->cmda[i]; i++)
1259                 ;
1260         e->args = i;
1261         return 0;
1262 }
1263
1264 /*! \brief initialize the _full_cmd string in * each of the builtins. */
1265 void ast_builtins_init(void)
1266 {
1267         ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
1268 }
1269
1270 static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
1271 {
1272         if (e == NULL)
1273                 e = AST_LIST_FIRST(&helpers);
1274         if (e) 
1275                 e = AST_LIST_NEXT(e, list);
1276         return e;
1277 }
1278
1279 /*!
1280  * match a word in the CLI entry.
1281  * returns -1 on mismatch, 0 on match of an optional word,
1282  * 1 on match of a full word.
1283  *
1284  * The pattern can be
1285  *   any_word           match for equal
1286  *   [foo|bar|baz]      optionally, one of these words
1287  *   {foo|bar|baz}      exactly, one of these words
1288  *   %                  any word
1289  */
1290 static int word_match(const char *cmd, const char *cli_word)
1291 {
1292         int l;
1293         char *pos;
1294
1295         if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
1296                 return -1;
1297         if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
1298                 return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
1299         /* regexp match, takes [foo|bar] or {foo|bar} */
1300         l = strlen(cmd);
1301         /* wildcard match - will extend in the future */
1302         if (l > 0 && cli_word[0] == '%') {
1303                 return 1;       /* wildcard */
1304         }
1305         pos = strcasestr(cli_word, cmd);
1306         if (pos == NULL) /* not found, say ok if optional */
1307                 return cli_word[0] == '[' ? 0 : -1;
1308         if (pos == cli_word)    /* no valid match at the beginning */
1309                 return -1;
1310         if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
1311                 return 1;       /* valid match */
1312         return -1;      /* not found */
1313 }
1314
1315 /*! \brief if word is a valid prefix for token, returns the pos-th
1316  * match as a malloced string, or NULL otherwise.
1317  * Always tell in *actual how many matches we got.
1318  */
1319 static char *is_prefix(const char *word, const char *token,
1320         int pos, int *actual)
1321 {
1322         int lw;
1323         char *s, *t1;
1324
1325         *actual = 0;
1326         if (ast_strlen_zero(token))
1327                 return NULL;
1328         if (ast_strlen_zero(word))
1329                 word = "";      /* dummy */
1330         lw = strlen(word);
1331         if (strcspn(word, cli_rsvd) != lw)
1332                 return NULL;    /* no match if word has reserved chars */
1333         if (strchr(cli_rsvd, token[0]) == NULL) {       /* regular match */
1334                 if (strncasecmp(token, word, lw))       /* no match */
1335                         return NULL;
1336                 *actual = 1;
1337                 return (pos != 0) ? NULL : ast_strdup(token);
1338         }
1339         /* now handle regexp match */
1340
1341         /* Wildcard always matches, so we never do is_prefix on them */
1342
1343         t1 = ast_strdupa(token + 1);    /* copy, skipping first char */
1344         while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
1345                 if (*s == '%')  /* wildcard */
1346                         continue;
1347                 if (strncasecmp(s, word, lw))   /* no match */
1348                         continue;
1349                 (*actual)++;
1350                 if (pos-- == 0)
1351                         return ast_strdup(s);
1352         }
1353         return NULL;
1354 }
1355
1356 /*!
1357  * \brief locate a cli command in the 'helpers' list (which must be locked).
1358  * exact has 3 values:
1359  *      0       returns if the search key is equal or longer than the entry.
1360  *              note that trailing optional arguments are skipped.
1361  *      -1      true if the mismatch is on the last word XXX not true!
1362  *      1       true only on complete, exact match.
1363  *
1364  * The search compares word by word taking care of regexps in e->cmda
1365  */
1366 static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
1367 {
1368         int matchlen = -1;      /* length of longest match so far */
1369         struct ast_cli_entry *cand = NULL, *e=NULL;
1370
1371         while ( (e = cli_next(e)) ) {
1372                 /* word-by word regexp comparison */
1373                 char * const *src = cmds;
1374                 char * const *dst = e->cmda;
1375                 int n = 0;
1376                 for (;; dst++, src += n) {
1377                         n = word_match(*src, *dst);
1378                         if (n < 0)
1379                                 break;
1380                 }
1381                 if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
1382                         /* no more words in 'e' */
1383                         if (ast_strlen_zero(*src))      /* exact match, cannot do better */
1384                                 break;
1385                         /* Here, cmds has more words than the entry 'e' */
1386                         if (match_type != 0)    /* but we look for almost exact match... */
1387                                 continue;       /* so we skip this one. */
1388                         /* otherwise we like it (case 0) */
1389                 } else {        /* still words in 'e' */
1390                         if (ast_strlen_zero(*src))
1391                                 continue; /* cmds is shorter than 'e', not good */
1392                         /* Here we have leftover words in cmds and 'e',
1393                          * but there is a mismatch. We only accept this one if match_type == -1
1394                          * and this is the last word for both.
1395                          */
1396                         if (match_type != -1 || !ast_strlen_zero(src[1]) ||
1397                             !ast_strlen_zero(dst[1]))   /* not the one we look for */
1398                                 continue;
1399                         /* good, we are in case match_type == -1 and mismatch on last word */
1400                 }
1401                 if (src - cmds > matchlen) {    /* remember the candidate */
1402                         matchlen = src - cmds;
1403                         cand = e;
1404                 }
1405         }
1406         return e ? e : cand;
1407 }
1408
1409 static char *find_best(char *argv[])
1410 {
1411         static char cmdline[80];
1412         int x;
1413         /* See how close we get, then print the candidate */
1414         char *myargv[AST_MAX_CMD_LEN];
1415         for (x=0;x<AST_MAX_CMD_LEN;x++)
1416                 myargv[x]=NULL;
1417         AST_RWLIST_RDLOCK(&helpers);
1418         for (x=0;argv[x];x++) {
1419                 myargv[x] = argv[x];
1420                 if (!find_cli(myargv, -1))
1421                         break;
1422         }
1423         AST_RWLIST_UNLOCK(&helpers);
1424         ast_join(cmdline, sizeof(cmdline), myargv);
1425         return cmdline;
1426 }
1427
1428 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
1429 {
1430         if (e->deprecate_cmd) {
1431                 __ast_cli_unregister(e->deprecate_cmd, e);
1432         }
1433         if (e->inuse) {
1434                 ast_log(LOG_WARNING, "Can't remove command that is in use\n");
1435         } else {
1436                 AST_RWLIST_WRLOCK(&helpers);
1437                 AST_RWLIST_REMOVE(&helpers, e, list);
1438                 AST_RWLIST_UNLOCK(&helpers);
1439                 ast_free(e->_full_cmd);
1440                 e->_full_cmd = NULL;
1441                 if (e->handler) {
1442                         /* this is a new-style entry. Reset fields and free memory. */
1443                         bzero((char **)(e->cmda), sizeof(e->cmda));
1444                         ast_free(e->command);
1445                         e->command = NULL;
1446                         e->usage = NULL;
1447                 }
1448         }
1449         return 0;
1450 }
1451
1452 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
1453 {
1454         struct ast_cli_entry *cur;
1455         int i, lf, ret = -1;
1456
1457         struct ast_cli_args a;  /* fake argument */
1458         char **dst = (char **)e->cmda;  /* need to cast as the entry is readonly */
1459         char *s;
1460
1461         bzero (&a, sizeof(a));
1462         e->handler(e, CLI_INIT, &a);
1463         /* XXX check that usage and command are filled up */
1464         s = ast_skip_blanks(e->command);
1465         s = e->command = ast_strdup(s);
1466         for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
1467                 *dst++ = s;     /* store string */
1468                 s = ast_skip_nonblanks(s);
1469                 if (*s == '\0') /* we are done */
1470                         break;
1471                 *s++ = '\0';
1472                 s = ast_skip_blanks(s);
1473         }
1474         *dst++ = NULL;
1475         
1476         AST_RWLIST_WRLOCK(&helpers);
1477         
1478         if (find_cli(e->cmda, 1)) {
1479                 ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", e->_full_cmd);
1480                 goto done;
1481         }
1482         if (set_full_cmd(e))
1483                 goto done;
1484         if (!ed) {
1485                 e->deprecated = 0;
1486         } else {
1487                 e->deprecated = 1;
1488                 e->summary = ed->summary;
1489                 e->usage = ed->usage;
1490                 /* XXX If command A deprecates command B, and command B deprecates command C...
1491                    Do we want to show command A or command B when telling the user to use new syntax?
1492                    This currently would show command A.
1493                    To show command B, you just need to always use ed->_full_cmd.
1494                  */
1495                 e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
1496         }
1497
1498         lf = e->cmdlen;
1499         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
1500                 int len = cur->cmdlen;
1501                 if (lf < len)
1502                         len = lf;
1503                 if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
1504                         AST_RWLIST_INSERT_BEFORE_CURRENT(e, list); 
1505                         break;
1506                 }
1507         }
1508         AST_RWLIST_TRAVERSE_SAFE_END;
1509
1510         if (!cur)
1511                 AST_RWLIST_INSERT_TAIL(&helpers, e, list); 
1512         ret = 0;        /* success */
1513
1514 done:
1515         AST_RWLIST_UNLOCK(&helpers);
1516
1517         if (e->deprecate_cmd) {
1518                 /* This command deprecates another command.  Register that one also. */
1519                 __ast_cli_register(e->deprecate_cmd, e);
1520         }
1521         
1522         return ret;
1523 }
1524
1525 /* wrapper function, so we can unregister deprecated commands recursively */
1526 int ast_cli_unregister(struct ast_cli_entry *e)
1527 {
1528         return __ast_cli_unregister(e, NULL);
1529 }
1530
1531 /* wrapper function, so we can register deprecated commands recursively */
1532 int ast_cli_register(struct ast_cli_entry *e)
1533 {
1534         return __ast_cli_register(e, NULL);
1535 }
1536
1537 /*
1538  * register/unregister an array of entries.
1539  */
1540 int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
1541 {
1542         int i, res = 0;
1543
1544         for (i = 0; i < len; i++)
1545                 res |= ast_cli_register(e + i);
1546
1547         return res;
1548 }
1549
1550 int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
1551 {
1552         int i, res = 0;
1553
1554         for (i = 0; i < len; i++)
1555                 res |= ast_cli_unregister(e + i);
1556
1557         return res;
1558 }
1559
1560
1561 /*! \brief helper for final part of handle_help
1562  *  if locked = 1, assume the list is already locked
1563  */
1564 static char *help1(int fd, char *match[], int locked)
1565 {
1566         char matchstr[80] = "";
1567         struct ast_cli_entry *e = NULL;
1568         int len = 0;
1569         int found = 0;
1570
1571         if (match) {
1572                 ast_join(matchstr, sizeof(matchstr), match);
1573                 len = strlen(matchstr);
1574         }
1575         if (!locked)
1576                 AST_RWLIST_RDLOCK(&helpers);
1577         while ( (e = cli_next(e)) ) {
1578                 /* Hide commands that start with '_' */
1579                 if (e->_full_cmd[0] == '_')
1580                         continue;
1581                 /* Hide commands that are marked as deprecated. */
1582                 if (e->deprecated)
1583                         continue;
1584                 if (match && strncasecmp(matchstr, e->_full_cmd, len))
1585                         continue;
1586                 ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
1587                 found++;
1588         }
1589         if (!locked)
1590                 AST_RWLIST_UNLOCK(&helpers);
1591         if (!found && matchstr[0])
1592                 ast_cli(fd, "No such command '%s'.\n", matchstr);
1593         return CLI_SUCCESS;
1594 }
1595
1596 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1597 {
1598         char fullcmd[80];
1599         struct ast_cli_entry *my_e;
1600         char *res = CLI_SUCCESS;
1601
1602         if (cmd == CLI_INIT) {
1603                 e->command = "help";
1604                 e->usage =
1605                         "Usage: help [topic]\n"
1606                         "       When called with a topic as an argument, displays usage\n"
1607                         "       information on the given command. If called without a\n"
1608                         "       topic, it provides a list of commands.\n";
1609                 return NULL;
1610
1611         } else if (cmd == CLI_GENERATE) {
1612                 /* skip first 4 or 5 chars, "help " */
1613                 int l = strlen(a->line);
1614
1615                 if (l > 5)
1616                         l = 5;
1617                 /* XXX watch out, should stop to the non-generator parts */
1618                 return __ast_cli_generator(a->line + l, a->word, a->n, 0);
1619         }
1620         if (a->argc == 1)
1621                 return help1(a->fd, NULL, 0);
1622
1623         AST_RWLIST_RDLOCK(&helpers);
1624         my_e = find_cli(a->argv + 1, 1);        /* try exact match first */
1625         if (!my_e) {
1626                 res = help1(a->fd, a->argv + 1, 1 /* locked */);
1627                 AST_RWLIST_UNLOCK(&helpers);
1628                 return res;
1629         }
1630         if (my_e->usage)
1631                 ast_cli(a->fd, "%s", my_e->usage);
1632         else {
1633                 ast_join(fullcmd, sizeof(fullcmd), a->argv+1);
1634                 ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd);
1635         }
1636         AST_RWLIST_UNLOCK(&helpers);
1637         return res;
1638 }
1639
1640 static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
1641 {
1642         char *dup, *cur;
1643         int x = 0;
1644         int quoted = 0;
1645         int escaped = 0;
1646         int whitespace = 1;
1647         int dummy = 0;
1648
1649         if (trailingwhitespace == NULL)
1650                 trailingwhitespace = &dummy;
1651         *trailingwhitespace = 0;
1652         if (s == NULL)  /* invalid, though! */
1653                 return NULL;
1654         /* make a copy to store the parsed string */
1655         if (!(dup = ast_strdup(s)))
1656                 return NULL;
1657
1658         cur = dup;
1659         /* scan the original string copying into cur when needed */
1660         for (; *s ; s++) {
1661                 if (x >= max - 1) {
1662                         ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
1663                         break;
1664                 }
1665                 if (*s == '"' && !escaped) {
1666                         quoted = !quoted;
1667                         if (quoted && whitespace) {
1668                                 /* start a quoted string from previous whitespace: new argument */
1669                                 argv[x++] = cur;
1670                                 whitespace = 0;
1671                         }
1672                 } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
1673                         /* If we are not already in whitespace, and not in a quoted string or
1674                            processing an escape sequence, and just entered whitespace, then
1675                            finalize the previous argument and remember that we are in whitespace
1676                         */
1677                         if (!whitespace) {
1678                                 *cur++ = '\0';
1679                                 whitespace = 1;
1680                         }
1681                 } else if (*s == '\\' && !escaped) {
1682                         escaped = 1;
1683                 } else {
1684                         if (whitespace) {
1685                                 /* we leave whitespace, and are not quoted. So it's a new argument */
1686                                 argv[x++] = cur;
1687                                 whitespace = 0;
1688                         }
1689                         *cur++ = *s;
1690                         escaped = 0;
1691                 }
1692         }
1693         /* Null terminate */
1694         *cur++ = '\0';
1695         /* XXX put a NULL in the last argument, because some functions that take
1696          * the array may want a null-terminated array.
1697          * argc still reflects the number of non-NULL entries.
1698          */
1699         argv[x] = NULL;
1700         *argc = x;
1701         *trailingwhitespace = whitespace;
1702         return dup;
1703 }
1704
1705 /*! \brief Return the number of unique matches for the generator */
1706 int ast_cli_generatornummatches(const char *text, const char *word)
1707 {
1708         int matches = 0, i = 0;
1709         char *buf = NULL, *oldbuf = NULL;
1710
1711         while ((buf = ast_cli_generator(text, word, i++))) {
1712                 if (!oldbuf || strcmp(buf,oldbuf))
1713                         matches++;
1714                 if (oldbuf)
1715                         ast_free(oldbuf);
1716                 oldbuf = buf;
1717         }
1718         if (oldbuf)
1719                 ast_free(oldbuf);
1720         return matches;
1721 }
1722
1723 char **ast_cli_completion_matches(const char *text, const char *word)
1724 {
1725         char **match_list = NULL, *retstr, *prevstr;
1726         size_t match_list_len, max_equal, which, i;
1727         int matches = 0;
1728
1729         /* leave entry 0 free for the longest common substring */
1730         match_list_len = 1;
1731         while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
1732                 if (matches + 1 >= match_list_len) {
1733                         match_list_len <<= 1;
1734                         if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
1735                                 return NULL;
1736                 }
1737                 match_list[++matches] = retstr;
1738         }
1739
1740         if (!match_list)
1741                 return match_list; /* NULL */
1742
1743         /* Find the longest substring that is common to all results
1744          * (it is a candidate for completion), and store a copy in entry 0.
1745          */
1746         prevstr = match_list[1];
1747         max_equal = strlen(prevstr);
1748         for (which = 2; which <= matches; which++) {
1749                 for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
1750                         continue;
1751                 max_equal = i;
1752         }
1753
1754         if (!(retstr = ast_malloc(max_equal + 1)))
1755                 return NULL;
1756         
1757         ast_copy_string(retstr, match_list[1], max_equal + 1);
1758         match_list[0] = retstr;
1759
1760         /* ensure that the array is NULL terminated */
1761         if (matches + 1 >= match_list_len) {
1762                 if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
1763                         return NULL;
1764         }
1765         match_list[matches + 1] = NULL;
1766
1767         return match_list;
1768 }
1769
1770 /*! \brief returns true if there are more words to match */
1771 static int more_words (char * const *dst)
1772 {
1773         int i;
1774         for (i = 0; dst[i]; i++) {
1775                 if (dst[i][0] != '[')
1776                         return -1;
1777         }
1778         return 0;
1779 }
1780         
1781 /*
1782  * generate the entry at position 'state'
1783  */
1784 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
1785 {
1786         char *argv[AST_MAX_ARGS];
1787         struct ast_cli_entry *e = NULL;
1788         int x = 0, argindex, matchlen;
1789         int matchnum=0;
1790         char *ret = NULL;
1791         char matchstr[80] = "";
1792         int tws = 0;
1793         /* Split the argument into an array of words */
1794         char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
1795
1796         if (!dup)       /* malloc error */
1797                 return NULL;
1798
1799         /* Compute the index of the last argument (could be an empty string) */
1800         argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
1801
1802         /* rebuild the command, ignore terminating white space and flatten space */
1803         ast_join(matchstr, sizeof(matchstr)-1, argv);
1804         matchlen = strlen(matchstr);
1805         if (tws) {
1806                 strcat(matchstr, " "); /* XXX */
1807                 if (matchlen)
1808                         matchlen++;
1809         }
1810         if (lock)
1811                 AST_RWLIST_RDLOCK(&helpers);
1812         while ( (e = cli_next(e)) ) {
1813                 /* XXX repeated code */
1814                 int src = 0, dst = 0, n = 0;
1815
1816                 if (e->command[0] == '_')
1817                         continue;
1818
1819                 /*
1820                  * Try to match words, up to and excluding the last word, which
1821                  * is either a blank or something that we want to extend.
1822                  */
1823                 for (;src < argindex; dst++, src += n) {
1824                         n = word_match(argv[src], e->cmda[dst]);
1825                         if (n < 0)
1826                                 break;
1827                 }
1828
1829                 if (src != argindex && more_words(e->cmda + dst))       /* not a match */
1830                         continue;
1831                 ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n);
1832                 matchnum += n;  /* this many matches here */
1833                 if (ret) {
1834                         /*
1835                          * argv[src] is a valid prefix of the next word in this
1836                          * command. If this is also the correct entry, return it.
1837                          */
1838                         if (matchnum > state)
1839                                 break;
1840                         ast_free(ret);
1841                         ret = NULL;
1842                 } else if (ast_strlen_zero(e->cmda[dst])) {
1843                         /*
1844                          * This entry is a prefix of the command string entered
1845                          * (only one entry in the list should have this property).
1846                          * Run the generator if one is available. In any case we are done.
1847                          */
1848                         if (e->handler) {       /* new style command */
1849                                 struct ast_cli_args a = {
1850                                         .line = matchstr, .word = word,
1851                                         .pos = argindex,
1852                                         .n = state - matchnum };
1853                                 ret = e->handler(e, CLI_GENERATE, &a);
1854                         }
1855                         if (ret)
1856                                 break;
1857                 }
1858         }
1859         if (lock)
1860                 AST_RWLIST_UNLOCK(&helpers);
1861         ast_free(dup);
1862         return ret;
1863 }
1864
1865 char *ast_cli_generator(const char *text, const char *word, int state)
1866 {
1867         return __ast_cli_generator(text, word, state, 1);
1868 }
1869
1870 int ast_cli_command(int fd, const char *s)
1871 {
1872         char *args[AST_MAX_ARGS + 1];
1873         struct ast_cli_entry *e;
1874         int x;
1875         char *dup = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
1876         char *retval = NULL;
1877         struct ast_cli_args a = {
1878                 .fd = fd, .argc = x, .argv = args+1 };
1879
1880         if (dup == NULL)
1881                 return -1;
1882
1883         if (x < 1)      /* We need at least one entry, otherwise ignore */
1884                 goto done;
1885
1886         AST_RWLIST_RDLOCK(&helpers);
1887         e = find_cli(args + 1, 0);
1888         if (e)
1889                 ast_atomic_fetchadd_int(&e->inuse, 1);
1890         AST_RWLIST_UNLOCK(&helpers);
1891         if (e == NULL) {
1892                 ast_cli(fd, "No such command '%s' (type 'help %s' for other possible commands)\n", s, find_best(args + 1));
1893                 goto done;
1894         }
1895         /*
1896          * Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
1897          * Remember that the array returned by parse_args is NULL-terminated.
1898          */
1899         args[0] = (char *)e;
1900
1901         retval = e->handler(e, CLI_HANDLER, &a);
1902
1903         if (retval == CLI_SHOWUSAGE) {
1904                 ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
1905                 AST_RWLIST_RDLOCK(&helpers);
1906                 if (e->deprecated)
1907                         ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
1908                 AST_RWLIST_UNLOCK(&helpers);
1909         } else {
1910                 if (retval == CLI_FAILURE)
1911                         ast_cli(fd, "Command '%s' failed.\n", s);
1912                 AST_RWLIST_RDLOCK(&helpers);
1913                 if (e->deprecated == 1) {
1914                         ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
1915                         e->deprecated = 2;
1916                 }
1917                 AST_RWLIST_UNLOCK(&helpers);
1918         }
1919         ast_atomic_fetchadd_int(&e->inuse, -1);
1920 done:
1921         ast_free(dup);
1922         return 0;
1923 }
1924
1925 int ast_cli_command_multiple(int fd, size_t size, const char *s)
1926 {
1927         char cmd[512];
1928         int x, y = 0, count = 0;
1929
1930         for (x = 0; x < size; x++) {
1931                 cmd[y] = s[x];
1932                 y++;
1933                 if (s[x] == '\0') {
1934                         ast_cli_command(fd, cmd);
1935                         y = 0;
1936                         count++;
1937                 }
1938         }
1939         return count;
1940 }