2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief Standard Command Line Interface
23 * \author Mark Spencer <markster@digium.com>
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <sys/signal.h>
39 #include "asterisk/logger.h"
40 #include "asterisk/options.h"
41 #include "asterisk/cli.h"
42 #include "asterisk/linkedlists.h"
43 #include "asterisk/module.h"
44 #include "asterisk/pbx.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/utils.h"
47 #include "asterisk/app.h"
48 #include "asterisk/lock.h"
49 #include "editline/readline/readline.h"
50 #include "asterisk/threadstorage.h"
52 extern unsigned long global_fin, global_fout;
54 AST_THREADSTORAGE(ast_cli_buf);
56 /*! \brief Initial buffer size for resulting strings in ast_cli() */
57 #define AST_CLI_INITLEN 256
59 void ast_cli(int fd, char *fmt, ...)
62 struct ast_dynamic_str *buf;
65 if (!(buf = ast_dynamic_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
69 res = ast_dynamic_str_thread_set_va(&buf, 0, &ast_cli_buf, fmt, ap);
72 if (res != AST_DYNSTR_BUILD_FAILED)
73 ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
76 static AST_LIST_HEAD_STATIC(helpers, ast_cli_entry);
78 static char load_help[] =
79 "Usage: module load <module name>\n"
80 " Loads the specified module into Asterisk.\n";
82 static char unload_help[] =
83 "Usage: module unload [-f|-h] <module name>\n"
84 " Unloads the specified module from Asterisk. The -f\n"
85 " option causes the module to be unloaded even if it is\n"
86 " in use (may cause a crash) and the -h module causes the\n"
87 " module to be unloaded even if the module says it cannot, \n"
88 " which almost always will cause a crash.\n";
90 static char help_help[] =
91 "Usage: help [topic]\n"
92 " When called with a topic as an argument, displays usage\n"
93 " information on the given command. If called without a\n"
94 " topic, it provides a list of commands.\n";
96 static char chanlist_help[] =
97 "Usage: core show channels [concise|verbose]\n"
98 " Lists currently defined channels and some information about them. If\n"
99 " 'concise' is specified, the format is abridged and in a more easily\n"
100 " machine parsable format. If 'verbose' is specified, the output includes\n"
101 " more and longer fields.\n";
103 static char reload_help[] =
104 "Usage: module reload [module ...]\n"
105 " Reloads configuration files for all listed modules which support\n"
106 " reloading, or for all supported modules if none are listed.\n";
108 static char verbose_help[] =
109 "Usage: core set verbose <level>\n"
110 " Sets level of verbose messages to be displayed. 0 means\n"
111 " no messages should be displayed. Equivalent to -v[v[v...]]\n"
114 static char debug_help[] =
115 "Usage: core set debug <level> [filename]\n"
116 " Sets level of core debug messages to be displayed. 0 means\n"
117 " no messages should be displayed. Equivalent to -d[d[d...]]\n"
118 " on startup. If filename is specified, debugging will be\n"
119 " limited to just that file.\n";
121 static char nodebug_help[] =
122 "Usage: core set debug off\n"
123 " Turns off core debug messages.\n";
125 static char logger_mute_help[] =
126 "Usage: logger mute\n"
127 " Disables logging output to the current console, making it possible to\n"
128 " gather information without being disturbed by scrolling lines.\n";
130 static char softhangup_help[] =
131 "Usage: soft hangup <channel>\n"
132 " Request that a channel be hung up. The hangup takes effect\n"
133 " the next time the driver reads or writes from the channel\n";
135 static char group_show_channels_help[] =
136 "Usage: group show channels [pattern]\n"
137 " Lists all currently active channels with channel group(s) specified.\n"
138 " Optional regular expression pattern is matched to group names for each\n"
141 static int handle_load_deprecated(int fd, int argc, char *argv[])
144 return RESULT_SHOWUSAGE;
145 if (ast_load_resource(argv[1])) {
146 ast_cli(fd, "Unable to load module %s\n", argv[1]);
147 return RESULT_FAILURE;
149 return RESULT_SUCCESS;
152 static int handle_load(int fd, int argc, char *argv[])
155 return RESULT_SHOWUSAGE;
156 if (ast_load_resource(argv[2])) {
157 ast_cli(fd, "Unable to load module %s\n", argv[2]);
158 return RESULT_FAILURE;
160 return RESULT_SUCCESS;
163 static int handle_reload_deprecated(int fd, int argc, char *argv[])
168 return RESULT_SHOWUSAGE;
170 for (x = 1; x < argc; x++) {
171 res = ast_module_reload(argv[x]);
174 ast_cli(fd, "No such module '%s'\n", argv[x]);
177 ast_cli(fd, "Module '%s' does not support reload\n", argv[x]);
182 ast_module_reload(NULL);
183 return RESULT_SUCCESS;
186 static int handle_reload(int fd, int argc, char *argv[])
191 return RESULT_SHOWUSAGE;
193 for (x = 2; x < argc; x++) {
194 res = ast_module_reload(argv[x]);
197 ast_cli(fd, "No such module '%s'\n", argv[x]);
200 ast_cli(fd, "Module '%s' does not support reload\n", argv[x]);
205 ast_module_reload(NULL);
206 return RESULT_SUCCESS;
209 static int handle_set_verbose_deprecated(int fd, int argc, char *argv[])
212 int oldval = option_verbose;
214 /* "set verbose [atleast] N" */
216 option_verbose = atoi(argv[2]);
217 else if (argc == 4) {
218 if (strcasecmp(argv[2], "atleast"))
219 return RESULT_SHOWUSAGE;
221 if (val > option_verbose)
222 option_verbose = val;
224 return RESULT_SHOWUSAGE;
226 if (oldval != option_verbose && option_verbose > 0)
227 ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
228 else if (oldval > 0 && option_verbose > 0)
229 ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
230 else if (oldval > 0 && option_verbose == 0)
231 ast_cli(fd, "Verbosity is now OFF\n");
233 return RESULT_SUCCESS;
236 static int handle_verbose(int fd, int argc, char *argv[])
238 int oldval = option_verbose;
242 if ((argc < 4) || (argc > 5))
243 return RESULT_SHOWUSAGE;
245 if (!strcasecmp(argv[3], "atleast"))
250 return RESULT_SHOWUSAGE;
252 option_verbose = atoi(argv[3]);
255 return RESULT_SHOWUSAGE;
257 newlevel = atoi(argv[4]);
258 if (newlevel > option_verbose)
259 option_verbose = newlevel;
261 if (oldval > 0 && option_verbose == 0)
262 ast_cli(fd, "Verbosity is now OFF\n");
263 else if (option_verbose > 0) {
264 if (oldval == option_verbose)
265 ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
267 ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
270 return RESULT_SUCCESS;
273 static int handle_set_debug(int fd, int argc, char *argv[])
275 int oldval = option_debug;
278 char *filename = '\0';
280 /* 'core set debug <level>'
281 * 'core set debug <level> <fn>'
282 * 'core set debug atleast <level>'
283 * 'core set debug atleast <level> <fn>'
285 if ((argc < 4) || (argc > 6))
286 return RESULT_SHOWUSAGE;
288 if (!strcasecmp(argv[3], "atleast"))
293 return RESULT_SHOWUSAGE;
295 if (sscanf(argv[3], "%d", &newlevel) != 1)
296 return RESULT_SHOWUSAGE;
299 debug_filename[0] = '\0';
302 ast_copy_string(debug_filename, filename, sizeof(debug_filename));
305 option_debug = newlevel;
307 if (argc < 5 || argc > 6)
308 return RESULT_SHOWUSAGE;
310 if (sscanf(argv[4], "%d", &newlevel) != 1)
311 return RESULT_SHOWUSAGE;
314 debug_filename[0] = '\0';
317 ast_copy_string(debug_filename, filename, sizeof(debug_filename));
320 if (newlevel > option_debug)
321 option_debug = newlevel;
324 if (oldval > 0 && option_debug == 0)
325 ast_cli(fd, "Core debug is now OFF\n");
326 else if (option_debug > 0) {
328 if (oldval == option_debug)
329 ast_cli(fd, "Core debug is at least %d, file '%s'\n", option_debug, filename);
331 ast_cli(fd, "Core debug was %d and is now %d, file '%s'\n", oldval, option_debug, filename);
333 if (oldval == option_debug)
334 ast_cli(fd, "Core debug is at least %d\n", option_debug);
336 ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug);
340 return RESULT_SUCCESS;
343 static int handle_nodebug(int fd, int argc, char *argv[])
345 int oldval = option_debug;
347 return RESULT_SHOWUSAGE;
350 debug_filename[0] = '\0';
353 ast_cli(fd, "Core debug is now OFF\n");
354 return RESULT_SUCCESS;
357 static int handle_logger_mute(int fd, int argc, char *argv[])
360 return RESULT_SHOWUSAGE;
361 ast_console_toggle_mute(fd);
362 return RESULT_SUCCESS;
365 static int handle_unload_deprecated(int fd, int argc, char *argv[])
368 int force = AST_FORCE_SOFT;
370 return RESULT_SHOWUSAGE;
371 for (x = 1; x < argc; x++) {
372 if (argv[x][0] == '-') {
375 force = AST_FORCE_FIRM;
378 force = AST_FORCE_HARD;
381 return RESULT_SHOWUSAGE;
383 } else if (x != argc - 1)
384 return RESULT_SHOWUSAGE;
385 else if (ast_unload_resource(argv[x], force)) {
386 ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
387 return RESULT_FAILURE;
390 return RESULT_SUCCESS;
393 static int handle_unload(int fd, int argc, char *argv[])
396 int force = AST_FORCE_SOFT;
398 return RESULT_SHOWUSAGE;
399 for (x = 2; x < argc; x++) {
400 if (argv[x][0] == '-') {
403 force = AST_FORCE_FIRM;
406 force = AST_FORCE_HARD;
409 return RESULT_SHOWUSAGE;
411 } else if (x != argc - 1)
412 return RESULT_SHOWUSAGE;
413 else if (ast_unload_resource(argv[x], force)) {
414 ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
415 return RESULT_FAILURE;
418 return RESULT_SUCCESS;
421 #define MODLIST_FORMAT "%-30s %-40.40s %-10d\n"
422 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
424 AST_MUTEX_DEFINE_STATIC(climodentrylock);
425 static int climodentryfd = -1;
427 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
429 /* Comparing the like with the module */
430 if (strcasestr(module, like) ) {
431 ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
437 static char modlist_help[] =
438 "Usage: core show modules [like keyword]\n"
439 " Shows Asterisk modules currently in use, and usage statistics.\n";
441 static char uptime_help[] =
442 "Usage: core show uptime [seconds]\n"
443 " Shows Asterisk uptime information.\n"
444 " The seconds word returns the uptime in seconds only.\n";
446 static void print_uptimestr(int fd, time_t timeval, const char *prefix, int printsec)
448 int x; /* the main part - years, weeks, etc. */
449 char timestr[256]="", *s = timestr;
450 size_t maxbytes = sizeof(timestr);
453 #define MINUTE (SECOND*60)
454 #define HOUR (MINUTE*60)
455 #define DAY (HOUR*24)
457 #define YEAR (DAY*365)
458 #define ESS(x) ((x == 1) ? "" : "s") /* plural suffix */
459 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
460 if (timeval < 0) /* invalid, nothing to show */
462 if (printsec) { /* plain seconds output */
463 ast_build_string(&s, &maxbytes, "%lu", (u_long)timeval);
464 timeval = 0; /* bypass the other cases */
466 if (timeval > YEAR) {
467 x = (timeval / YEAR);
468 timeval -= (x * YEAR);
469 ast_build_string(&s, &maxbytes, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval));
471 if (timeval > WEEK) {
472 x = (timeval / WEEK);
473 timeval -= (x * WEEK);
474 ast_build_string(&s, &maxbytes, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval));
478 timeval -= (x * DAY);
479 ast_build_string(&s, &maxbytes, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval));
481 if (timeval > HOUR) {
482 x = (timeval / HOUR);
483 timeval -= (x * HOUR);
484 ast_build_string(&s, &maxbytes, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval));
486 if (timeval > MINUTE) {
487 x = (timeval / MINUTE);
488 timeval -= (x * MINUTE);
489 ast_build_string(&s, &maxbytes, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval));
493 ast_build_string(&s, &maxbytes, "%d second%s ", x, ESS(x));
494 if (timestr[0] != '\0')
495 ast_cli(fd, "%s: %s\n", prefix, timestr);
498 static int handle_showuptime(int fd, int argc, char *argv[])
500 /* 'show uptime [seconds]' */
501 time_t curtime = time(NULL);
502 int printsec = (argc == 4 && !strcasecmp(argv[3],"seconds"));
504 if (argc != 3 && !printsec)
505 return RESULT_SHOWUSAGE;
507 print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
508 if (ast_lastreloadtime)
509 print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
510 return RESULT_SUCCESS;
513 /* core show modules [like keyword] */
514 static int handle_modlist(int fd, int argc, char *argv[])
517 if (argc != 3 && argc != 5)
518 return RESULT_SHOWUSAGE;
519 else if (argc == 5) {
520 if (strcmp(argv[3],"like"))
521 return RESULT_SHOWUSAGE;
525 ast_mutex_lock(&climodentrylock);
526 climodentryfd = fd; /* global, protected by climodentrylock */
527 ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
528 ast_cli(fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
530 ast_mutex_unlock(&climodentrylock);
531 return RESULT_SUCCESS;
533 #undef MODLIST_FORMAT
534 #undef MODLIST_FORMAT2
536 /* core show channels [concise|verbose] */
537 static int handle_chanlist(int fd, int argc, char *argv[])
539 #define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
540 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
541 #define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n"
542 #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"
543 #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"
545 struct ast_channel *c = NULL;
546 char durbuf[10] = "-";
550 int durh, durm, durs;
551 int numchans = 0, concise = 0, verbose = 0;
553 concise = (argc == 4 && (!strcasecmp(argv[3],"concise")));
554 verbose = (argc == 4 && (!strcasecmp(argv[3],"verbose")));
556 if (argc < 3 || argc > 4 || (argc == 4 && !concise && !verbose))
557 return RESULT_SHOWUSAGE;
559 if (!concise && !verbose)
560 ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
562 ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
563 "CallerID", "Duration", "Accountcode", "BridgedTo");
565 while ((c = ast_channel_walk_locked(c)) != NULL) {
566 struct ast_channel *bc = ast_bridged_channel(c);
567 if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) {
568 duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
570 durh = duration / 3600;
571 durm = (duration % 3600) / 60;
572 durs = duration % 60;
573 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
575 snprintf(durbuf, sizeof(durbuf), "%d", duration);
581 ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
582 c->appl ? c->appl : "(None)",
583 S_OR(c->data, ""), /* XXX different from verbose ? */
584 S_OR(c->cid.cid_num, ""),
585 S_OR(c->accountcode, ""),
588 bc ? bc->name : "(None)");
589 } else if (verbose) {
590 ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
591 c->appl ? c->appl : "(None)",
592 c->data ? S_OR(c->data, "(Empty)" ): "(None)",
593 S_OR(c->cid.cid_num, ""),
595 S_OR(c->accountcode, ""),
596 bc ? bc->name : "(None)");
598 if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten))
599 snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
601 strcpy(locbuf, "(None)");
603 snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, c->data ? c->data : "");
605 strcpy(appdata, "(None)");
606 ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
609 ast_channel_unlock(c);
612 ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
614 ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
615 ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
616 ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
618 ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
620 return RESULT_SUCCESS;
623 #undef FORMAT_STRING2
624 #undef CONCISE_FORMAT_STRING
625 #undef VERBOSE_FORMAT_STRING
626 #undef VERBOSE_FORMAT_STRING2
629 static char showchan_help[] =
630 "Usage: core show channel <channel>\n"
631 " Shows lots of information about the specified channel.\n";
633 static char debugchan_help[] =
634 "Usage: core set debug channel <channel> [off]\n"
635 " Enables/disables debugging on a specific channel.\n";
637 static char commandcomplete_help[] =
638 "Usage: _command complete \"<line>\" text state\n"
639 " This function is used internally to help with command completion and should.\n"
640 " never be called by the user directly.\n";
642 static char commandnummatches_help[] =
643 "Usage: _command nummatches \"<line>\" text \n"
644 " This function is used internally to help with command completion and should.\n"
645 " never be called by the user directly.\n";
647 static char commandmatchesarray_help[] =
648 "Usage: _command matchesarray \"<line>\" text \n"
649 " This function is used internally to help with command completion and should.\n"
650 " never be called by the user directly.\n";
652 static int handle_softhangup(int fd, int argc, char *argv[])
654 struct ast_channel *c=NULL;
656 return RESULT_SHOWUSAGE;
657 c = ast_get_channel_by_name_locked(argv[2]);
659 ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name);
660 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
661 ast_channel_unlock(c);
663 ast_cli(fd, "%s is not a known channel\n", argv[2]);
664 return RESULT_SUCCESS;
667 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
669 static int handle_commandmatchesarray(int fd, int argc, char *argv[])
678 return RESULT_SHOWUSAGE;
679 if (!(buf = ast_malloc(buflen)))
680 return RESULT_FAILURE;
682 matches = ast_cli_completion_matches(argv[2], argv[3]);
684 for (x=0; matches[x]; x++) {
685 matchlen = strlen(matches[x]) + 1;
686 if (len + matchlen >= buflen) {
687 buflen += matchlen * 3;
689 if (!(buf = ast_realloc(obuf, buflen)))
690 /* Memory allocation failure... Just free old buffer and be done */
694 len += sprintf( buf + len, "%s ", matches[x]);
702 ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
705 ast_cli(fd, "NULL\n");
707 return RESULT_SUCCESS;
712 static int handle_commandnummatches(int fd, int argc, char *argv[])
717 return RESULT_SHOWUSAGE;
719 matches = ast_cli_generatornummatches(argv[2], argv[3]);
721 ast_cli(fd, "%d", matches);
723 return RESULT_SUCCESS;
726 static int handle_commandcomplete(int fd, int argc, char *argv[])
731 return RESULT_SHOWUSAGE;
732 buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0);
737 ast_cli(fd, "NULL\n");
738 return RESULT_SUCCESS;
741 static int handle_debugchan_deprecated(int fd, int argc, char *argv[])
743 struct ast_channel *c=NULL;
746 /* 'debug channel {all|chan_id}' */
748 return RESULT_SHOWUSAGE;
750 is_all = !strcasecmp("all", argv[3]);
752 global_fin |= DEBUGCHAN_FLAG;
753 global_fout |= DEBUGCHAN_FLAG;
754 c = ast_channel_walk_locked(NULL);
756 c = ast_get_channel_by_name_locked(argv[3]);
758 ast_cli(fd, "No such channel %s\n", argv[3]);
761 if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
762 c->fin |= DEBUGCHAN_FLAG;
763 c->fout |= DEBUGCHAN_FLAG;
764 ast_cli(fd, "Debugging enabled on channel %s\n", c->name);
766 ast_channel_unlock(c);
769 c = ast_channel_walk_locked(c);
771 ast_cli(fd, "Debugging on new channels is enabled\n");
772 return RESULT_SUCCESS;
775 static int handle_core_set_debug_channel(int fd, int argc, char *argv[])
777 struct ast_channel *c = NULL;
778 int is_all, is_off = 0;
780 /* 'core set debug channel {all|chan_id}' */
781 if (argc == 6 && strcmp(argv[5], "off") == 0)
784 return RESULT_SHOWUSAGE;
786 is_all = !strcasecmp("all", argv[4]);
789 global_fin &= ~DEBUGCHAN_FLAG;
790 global_fout &= ~DEBUGCHAN_FLAG;
792 global_fin |= DEBUGCHAN_FLAG;
793 global_fout |= DEBUGCHAN_FLAG;
795 c = ast_channel_walk_locked(NULL);
797 c = ast_get_channel_by_name_locked(argv[4]);
799 ast_cli(fd, "No such channel %s\n", argv[4]);
802 if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
804 c->fin &= ~DEBUGCHAN_FLAG;
805 c->fout &= ~DEBUGCHAN_FLAG;
807 c->fin |= DEBUGCHAN_FLAG;
808 c->fout |= DEBUGCHAN_FLAG;
810 ast_cli(fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
812 ast_channel_unlock(c);
815 c = ast_channel_walk_locked(c);
817 ast_cli(fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
818 return RESULT_SUCCESS;
821 static int handle_nodebugchan_deprecated(int fd, int argc, char *argv[])
823 struct ast_channel *c=NULL;
825 /* 'no debug channel {all|chan_id}' */
827 return RESULT_SHOWUSAGE;
828 is_all = !strcasecmp("all", argv[3]);
830 global_fin &= ~DEBUGCHAN_FLAG;
831 global_fout &= ~DEBUGCHAN_FLAG;
832 c = ast_channel_walk_locked(NULL);
834 c = ast_get_channel_by_name_locked(argv[3]);
836 ast_cli(fd, "No such channel %s\n", argv[3]);
839 if ((c->fin & DEBUGCHAN_FLAG) || (c->fout & DEBUGCHAN_FLAG)) {
840 c->fin &= ~DEBUGCHAN_FLAG;
841 c->fout &= ~DEBUGCHAN_FLAG;
842 ast_cli(fd, "Debugging disabled on channel %s\n", c->name);
844 ast_channel_unlock(c);
847 c = ast_channel_walk_locked(c);
849 ast_cli(fd, "Debugging on new channels is disabled\n");
850 return RESULT_SUCCESS;
853 static int handle_showchan(int fd, int argc, char *argv[])
855 struct ast_channel *c=NULL;
859 char nf[256], wf[256], rf[256];
860 long elapsed_seconds=0;
861 int hour=0, min=0, sec=0;
864 return RESULT_SHOWUSAGE;
866 c = ast_get_channel_by_name_locked(argv[3]);
868 ast_cli(fd, "%s is not a known channel\n", argv[3]);
869 return RESULT_SUCCESS;
872 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
873 hour = elapsed_seconds / 3600;
874 min = (elapsed_seconds % 3600) / 60;
875 sec = elapsed_seconds % 60;
876 snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
878 strcpy(cdrtime, "N/A");
885 " Caller ID Name: %s\n"
889 " NativeFormats: %s\n"
892 " WriteTranscode: %s\n"
893 " ReadTranscode: %s\n"
894 "1st File Descriptor: %d\n"
896 " Frames out: %d%s\n"
897 " Time to Hangup: %ld\n"
898 " Elapsed Time: %s\n"
899 " Direct Bridge: %s\n"
900 "Indirect Bridge: %s\n"
905 " Call Group: %llu\n"
906 " Pickup Group: %llu\n"
909 " Blocking in: %s\n",
910 c->name, c->tech->type, c->uniqueid,
911 S_OR(c->cid.cid_num, "(N/A)"),
912 S_OR(c->cid.cid_name, "(N/A)"),
913 S_OR(c->cid.cid_dnid, "(N/A)"), ast_state2str(c->_state), c->_state, c->rings,
914 ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats),
915 ast_getformatname_multiple(wf, sizeof(wf), c->writeformat),
916 ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
917 c->writetrans ? "Yes" : "No",
918 c->readtrans ? "Yes" : "No",
920 c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
921 c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
922 (long)c->whentohangup,
923 cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>",
924 c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
925 ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
926 (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
928 if(pbx_builtin_serialize_variables(c,buf,sizeof(buf)))
929 ast_cli(fd," Variables:\n%s\n",buf);
930 if(c->cdr && ast_cdr_serialize_variables(c->cdr,buf, sizeof(buf), '=', '\n', 1))
931 ast_cli(fd," CDR Variables:\n%s\n",buf);
933 ast_channel_unlock(c);
934 return RESULT_SUCCESS;
938 * helper function to generate CLI matches from a fixed set of values.
939 * A NULL word is acceptable.
941 char *ast_cli_complete(const char *word, char *const choices[], int state)
943 int i, which = 0, len;
944 len = ast_strlen_zero(word) ? 0 : strlen(word);
946 for (i = 0; choices[i]; i++) {
947 if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
948 return ast_strdup(choices[i]);
953 static char *complete_show_channels(const char *line, const char *word, int pos, int state)
955 static char *choices[] = { "concise", "verbose", NULL };
957 return (pos != 3) ? NULL : ast_cli_complete(word, choices, state);
960 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
962 struct ast_channel *c = NULL;
965 char notfound = '\0';
966 char *ret = ¬found; /* so NULL can break the loop */
971 wordlen = strlen(word);
973 while (ret == ¬found && (c = ast_channel_walk_locked(c))) {
974 if (!strncasecmp(word, c->name, wordlen) && ++which > state)
975 ret = ast_strdup(c->name);
976 ast_channel_unlock(c);
978 return ret == ¬found ? NULL : ret;
981 static char *complete_ch_3(const char *line, const char *word, int pos, int state)
983 return ast_complete_channels(line, word, pos, state, 2);
986 static char *complete_ch_4(const char *line, const char *word, int pos, int state)
988 return ast_complete_channels(line, word, pos, state, 3);
991 static char *complete_ch_5(const char *line, const char *word, int pos, int state)
993 return ast_complete_channels(line, word, pos, state, 4);
996 static char *complete_mod_2(const char *line, const char *word, int pos, int state)
998 return ast_module_helper(line, word, pos, state, 1, 1);
1001 static char *complete_mod_3_nr(const char *line, const char *word, int pos, int state)
1003 return ast_module_helper(line, word, pos, state, 2, 0);
1006 static char *complete_mod_3(const char *line, const char *word, int pos, int state)
1008 return ast_module_helper(line, word, pos, state, 2, 1);
1011 static char *complete_mod_4(const char *line, const char *word, int pos, int state)
1013 return ast_module_helper(line, word, pos, state, 3, 0);
1016 static char *complete_fn(const char *line, const char *word, int pos, int state)
1025 ast_copy_string(filename, word, sizeof(filename));
1027 snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
1029 c = filename_completion_function(filename, state);
1031 if (c && word[0] != '/')
1032 c += (strlen(ast_config_AST_MODULE_DIR) + 1);
1034 return c ? strdup(c) : c;
1037 static int group_show_channels(int fd, int argc, char *argv[])
1039 #define FORMAT_STRING "%-25s %-20s %-20s\n"
1041 struct ast_channel *c = NULL;
1043 struct ast_var_t *current;
1044 struct varshead *headp;
1046 int havepattern = 0;
1048 if (argc < 3 || argc > 4)
1049 return RESULT_SHOWUSAGE;
1052 if (regcomp(®exbuf, argv[3], REG_EXTENDED | REG_NOSUB))
1053 return RESULT_SHOWUSAGE;
1057 ast_cli(fd, FORMAT_STRING, "Channel", "Group", "Category");
1058 while ( (c = ast_channel_walk_locked(c)) != NULL) {
1060 AST_LIST_TRAVERSE(headp,current,entries) {
1061 if (!strncmp(ast_var_name(current), GROUP_CATEGORY_PREFIX "_", strlen(GROUP_CATEGORY_PREFIX) + 1)) {
1062 if (!havepattern || !regexec(®exbuf, ast_var_value(current), 0, NULL, 0)) {
1063 ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current),
1064 (ast_var_name(current) + strlen(GROUP_CATEGORY_PREFIX) + 1));
1067 } else if (!strcmp(ast_var_name(current), GROUP_CATEGORY_PREFIX)) {
1068 if (!havepattern || !regexec(®exbuf, ast_var_value(current), 0, NULL, 0)) {
1069 ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current), "(default)");
1075 ast_channel_unlock(c);
1081 ast_cli(fd, "%d active channel%s\n", numchans, (numchans != 1) ? "s" : "");
1082 return RESULT_SUCCESS;
1083 #undef FORMAT_STRING
1086 static int handle_help(int fd, int argc, char *argv[]);
1088 static char * complete_help(const char *text, const char *word, int pos, int state)
1090 /* skip first 4 or 5 chars, "help "*/
1091 int l = strlen(text);
1096 /* XXX watch out, should stop to the non-generator parts */
1097 return __ast_cli_generator(text, word, state, 0);
1100 /* XXX Nothing in this array can currently be deprecated...
1101 You have to change the way find_cli works in order to remove this array
1102 I recommend doing this eventually...
1104 static struct ast_cli_entry builtins[] = {
1105 /* Keep alphabetized, with longer matches first (example: abcd before abc) */
1106 { { "_command", "complete", NULL },
1107 handle_commandcomplete, "Command complete",
1108 commandcomplete_help },
1110 { { "_command", "nummatches", NULL },
1111 handle_commandnummatches, "Returns number of command matches",
1112 commandnummatches_help },
1114 { { "_command", "matchesarray", NULL },
1115 handle_commandmatchesarray, "Returns command matches array",
1116 commandmatchesarray_help },
1118 { { NULL }, NULL, NULL, NULL }
1121 static struct ast_cli_entry cli_debug_channel_deprecated = {
1122 { "debug", "channel", NULL },
1123 handle_debugchan_deprecated, NULL,
1124 NULL, complete_ch_3 };
1126 static struct ast_cli_entry cli_debug_level_deprecated = {
1127 { "debug", "level", NULL },
1128 handle_debuglevel_deprecated, NULL,
1131 static struct ast_cli_entry cli_set_debug_deprecated = {
1132 { "set", "debug", NULL },
1133 handle_set_debug_deprecated, NULL,
1134 NULL, NULL, &cli_debug_level_deprecated };
1136 static struct ast_cli_entry cli_set_verbose_deprecated = {
1137 { "set", "verbose", NULL },
1138 handle_set_verbose_deprecated, NULL,
1141 static struct ast_cli_entry cli_show_channel_deprecated = {
1142 { "show", "channel", NULL },
1143 handle_showchan, NULL,
1144 NULL, complete_ch_3 };
1146 static struct ast_cli_entry cli_show_channels_deprecated = {
1147 { "show", "channels", NULL },
1148 handle_chanlist, NULL,
1149 NULL, complete_show_channels };
1151 static struct ast_cli_entry cli_show_modules_deprecated = {
1152 { "show", "modules", NULL },
1153 handle_modlist, NULL,
1156 static struct ast_cli_entry cli_show_modules_like_deprecated = {
1157 { "show", "modules", "like", NULL },
1158 handle_modlist, NULL,
1159 NULL, complete_mod_4 };
1161 static struct ast_cli_entry cli_module_load_deprecated = {
1163 handle_load_deprecated, NULL,
1164 NULL, complete_fn };
1166 static struct ast_cli_entry cli_module_reload_deprecated = {
1168 handle_reload_deprecated, NULL,
1169 NULL, complete_mod_2 };
1171 static struct ast_cli_entry cli_module_unload_deprecated = {
1173 handle_unload_deprecated, NULL,
1174 NULL, complete_mod_2 };
1176 static struct ast_cli_entry cli_cli[] = {
1177 /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
1178 { { "no", "debug", "channel", NULL },
1179 handle_nodebugchan_deprecated, NULL,
1180 NULL, complete_ch_4 },
1182 { { "core", "show", "channels", NULL },
1183 handle_chanlist, "Display information on channels",
1184 chanlist_help, complete_show_channels },
1186 { { "core", "show", "channel", NULL },
1187 handle_showchan, "Display information on a specific channel",
1188 showchan_help, complete_ch_4 },
1190 { { "core", "set", "debug", "channel", NULL },
1191 handle_core_set_debug_channel, "Enable/disable debugging on a channel",
1192 debugchan_help, complete_ch_5, &cli_debug_channel_deprecated },
1194 { { "core", "set", "debug", NULL },
1195 handle_set_debug, "Set level of debug chattiness",
1198 { { "core", "set", "debug", "off", NULL },
1199 handle_nodebug, "Turns off debug chattiness",
1202 { { "core", "set", "verbose", NULL },
1203 handle_verbose, "Set level of verboseness",
1206 { { "group", "show", "channels", NULL },
1207 group_show_channels, "Display active channels with group(s)",
1208 group_show_channels_help },
1211 handle_help, "Display help list, or specific help on a command",
1212 help_help, complete_help },
1214 { { "logger", "mute", NULL },
1215 handle_logger_mute, "Toggle logging output to a console",
1218 { { "module", "show", NULL },
1219 handle_modlist, "List modules and info",
1222 { { "module", "show", "like", NULL },
1223 handle_modlist, "List modules and info",
1224 modlist_help, complete_mod_4 },
1226 { { "module", "load", NULL },
1227 handle_load, "Load a module by name",
1228 load_help, complete_fn, &cli_module_load_deprecated },
1230 { { "module", "reload", NULL },
1231 handle_reload, "Reload configuration",
1232 reload_help, complete_mod_3, &cli_module_reload_deprecated },
1234 { { "module", "unload", NULL },
1235 handle_unload, "Unload a module by name",
1236 unload_help, complete_mod_3_nr, &cli_module_unload_deprecated },
1238 { { "core", "show", "uptime", NULL },
1239 handle_showuptime, "Show uptime information",
1242 { { "soft", "hangup", NULL },
1243 handle_softhangup, "Request a hangup on a given channel",
1244 softhangup_help, complete_ch_3 },
1247 /*! \brief initialize the _full_cmd string in * each of the builtins. */
1248 void ast_builtins_init(void)
1250 struct ast_cli_entry *e;
1252 for (e = builtins; e->cmda[0] != NULL; e++) {
1254 ast_join(buf, sizeof(buf), e->cmda);
1255 e->_full_cmd = strdup(buf);
1257 ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
1260 ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
1264 * We have two sets of commands: builtins are stored in a
1265 * NULL-terminated array of ast_cli_entry, whereas external
1266 * commands are in a list.
1267 * When navigating, we need to keep two pointers and get
1268 * the next one in lexicographic order. For the purpose,
1269 * we use a structure.
1272 struct cli_iterator {
1273 struct ast_cli_entry *builtins;
1274 struct ast_cli_entry *helpers;
1277 static struct ast_cli_entry *cli_next(struct cli_iterator *i)
1279 struct ast_cli_entry *e;
1281 if (i->builtins == NULL && i->helpers == NULL) {
1283 i->builtins = builtins;
1284 i->helpers = AST_LIST_FIRST(&helpers);
1286 e = i->builtins; /* temporary */
1287 if (!e->cmda[0] || (i->helpers &&
1288 strcmp(i->helpers->_full_cmd, e->_full_cmd) < 0)) {
1292 i->helpers = AST_LIST_NEXT(e, list);
1293 } else { /* use builtin. e is already set */
1294 (i->builtins)++; /* move to next */
1300 * \brief locate a cli command in the 'helpers' list (which must be locked).
1301 * exact has 3 values:
1302 * 0 returns if the search key is equal or longer than the entry.
1303 * -1 true if the mismatch is on the last word XXX not true!
1304 * 1 true only on complete, exact match.
1306 static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
1308 int matchlen = -1; /* length of longest match so far */
1309 struct ast_cli_entry *cand = NULL, *e=NULL;
1310 struct cli_iterator i = { NULL, NULL};
1312 while( (e = cli_next(&i)) ) {
1314 for (y = 0 ; cmds[y] && e->cmda[y]; y++) {
1315 if (strcasecmp(e->cmda[y], cmds[y]))
1318 if (e->cmda[y] == NULL) { /* no more words in candidate */
1319 if (cmds[y] == NULL) /* this is an exact match, cannot do better */
1321 /* here the search key is longer than the candidate */
1322 if (match_type != 0) /* but we look for almost exact match... */
1323 continue; /* so we skip this one. */
1324 /* otherwise we like it (case 0) */
1325 } else { /* still words in candidate */
1326 if (cmds[y] == NULL) /* search key is shorter, not good */
1328 /* if we get here, both words exist but there is a mismatch */
1329 if (match_type == 0) /* not the one we look for */
1331 if (match_type == 1) /* not the one we look for */
1333 if (cmds[y+1] != NULL || e->cmda[y+1] != NULL) /* not the one we look for */
1335 /* we are in case match_type == -1 and mismatch on last word */
1337 if (cand == NULL || y > matchlen) /* remember the candidate */
1340 return e ? e : cand;
1343 static char *find_best(char *argv[])
1345 static char cmdline[80];
1347 /* See how close we get, then print the candidate */
1348 char *myargv[AST_MAX_CMD_LEN];
1349 for (x=0;x<AST_MAX_CMD_LEN;x++)
1351 AST_LIST_LOCK(&helpers);
1352 for (x=0;argv[x];x++) {
1353 myargv[x] = argv[x];
1354 if (!find_cli(myargv, -1))
1357 AST_LIST_UNLOCK(&helpers);
1358 ast_join(cmdline, sizeof(cmdline), myargv);
1362 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
1364 if (e->deprecate_cmd) {
1365 __ast_cli_unregister(e->deprecate_cmd, e);
1368 ast_log(LOG_WARNING, "Can't remove command that is in use\n");
1370 AST_LIST_LOCK(&helpers);
1371 AST_LIST_REMOVE(&helpers, e, list);
1372 AST_LIST_UNLOCK(&helpers);
1377 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
1379 struct ast_cli_entry *cur;
1383 ast_join(fulle, sizeof(fulle), e->cmda);
1384 AST_LIST_LOCK(&helpers);
1386 if (find_cli(e->cmda, 1)) {
1387 AST_LIST_UNLOCK(&helpers);
1388 ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
1391 e->_full_cmd = ast_strdup(fulle);
1397 e->summary = ed->summary;
1398 e->usage = ed->usage;
1399 /* XXX If command A deprecates command B, and command B deprecates command C...
1400 Do we want to show command A or command B when telling the user to use new syntax?
1401 This currently would show command A.
1402 To show command B, you just need to always use ed->_full_cmd.
1404 e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
1410 AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
1411 int len = strlen(cur->_full_cmd);
1414 if (strncasecmp(fulle, cur->_full_cmd, len) < 0) {
1415 AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list);
1419 AST_LIST_TRAVERSE_SAFE_END;
1422 AST_LIST_INSERT_TAIL(&helpers, e, list);
1423 ret = 0; /* success */
1426 AST_LIST_UNLOCK(&helpers);
1428 if (e->deprecate_cmd) {
1429 /* This command deprecates another command. Register that one also. */
1430 __ast_cli_register(e->deprecate_cmd, e);
1436 /* wrapper function, so we can unregister deprecated commands recursively */
1437 int ast_cli_unregister(struct ast_cli_entry *e)
1439 return __ast_cli_unregister(e, NULL);
1442 /* wrapper function, so we can register deprecated commands recursively */
1443 int ast_cli_register(struct ast_cli_entry *e)
1445 return __ast_cli_register(e, NULL);
1449 * register/unregister an array of entries.
1451 void ast_cli_register_multiple(struct ast_cli_entry *e, int len)
1455 for (i = 0; i < len; i++)
1456 ast_cli_register(e + i);
1459 void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
1463 for (i = 0; i < len; i++)
1464 ast_cli_unregister(e + i);
1468 /*! \brief helper for help_workhorse and final part of
1469 * handle_help. if locked = 0 it's just help_workhorse,
1470 * otherwise assume the list is already locked and print
1471 * an error message if not found.
1473 static int help1(int fd, char *match[], int locked)
1475 char matchstr[80] = "";
1476 struct ast_cli_entry *e;
1479 struct cli_iterator i = { NULL, NULL};
1482 ast_join(matchstr, sizeof(matchstr), match);
1483 len = strlen(matchstr);
1486 AST_LIST_LOCK(&helpers);
1487 while ( (e = cli_next(&i)) ) {
1488 /* Hide commands that start with '_' */
1489 if (e->_full_cmd[0] == '_')
1491 /* Hide commands that are marked as deprecated. */
1494 if (match && strncasecmp(matchstr, e->_full_cmd, len))
1496 ast_cli(fd, "%25.25s %s\n", e->_full_cmd, e->summary);
1499 AST_LIST_UNLOCK(&helpers);
1500 if (!locked && !found && matchstr[0])
1501 ast_cli(fd, "No such command '%s'.\n", matchstr);
1505 static int help_workhorse(int fd, char *match[])
1507 return help1(fd, match, 0 /* do not print errors */);
1510 static int handle_help(int fd, int argc, char *argv[])
1513 struct ast_cli_entry *e;
1516 return RESULT_SHOWUSAGE;
1518 return help_workhorse(fd, NULL);
1520 AST_LIST_LOCK(&helpers);
1521 e = find_cli(argv + 1, 1); /* try exact match first */
1523 return help1(fd, argv + 1, 1 /* locked */);
1525 ast_cli(fd, "%s", e->usage);
1527 ast_join(fullcmd, sizeof(fullcmd), argv+1);
1528 ast_cli(fd, "No help text available for '%s'.\n", fullcmd);
1530 AST_LIST_UNLOCK(&helpers);
1531 return RESULT_SUCCESS;
1534 static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
1542 *trailingwhitespace = 0;
1543 if (s == NULL) /* invalid, though! */
1545 /* make a copy to store the parsed string */
1546 if (!(dup = ast_strdup(s)))
1550 /* scan the original string copying into cur when needed */
1553 ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
1556 if (*s == '"' && !escaped) {
1558 if (quoted && whitespace) {
1559 /* start a quoted string from previous whitespace: new argument */
1563 } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
1564 /* If we are not already in whitespace, and not in a quoted string or
1565 processing an escape sequence, and just entered whitespace, then
1566 finalize the previous argument and remember that we are in whitespace
1572 } else if (*s == '\\' && !escaped) {
1576 /* we leave whitespace, and are not quoted. So it's a new argument */
1584 /* Null terminate */
1586 /* XXX put a NULL in the last argument, because some functions that take
1587 * the array may want a null-terminated array.
1588 * argc still reflects the number of non-NULL entries.
1592 *trailingwhitespace = whitespace;
1596 /*! \brief Return the number of unique matches for the generator */
1597 int ast_cli_generatornummatches(const char *text, const char *word)
1599 int matches = 0, i = 0;
1600 char *buf = NULL, *oldbuf = NULL;
1602 while ((buf = ast_cli_generator(text, word, i++))) {
1603 if (!oldbuf || strcmp(buf,oldbuf))
1614 char **ast_cli_completion_matches(const char *text, const char *word)
1616 char **match_list = NULL, *retstr, *prevstr;
1617 size_t match_list_len, max_equal, which, i;
1620 /* leave entry 0 free for the longest common substring */
1622 while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
1623 if (matches + 1 >= match_list_len) {
1624 match_list_len <<= 1;
1625 if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
1628 match_list[++matches] = retstr;
1632 return match_list; /* NULL */
1634 /* Find the longest substring that is common to all results
1635 * (it is a candidate for completion), and store a copy in entry 0.
1637 prevstr = match_list[1];
1638 max_equal = strlen(prevstr);
1639 for (which = 2; which <= matches; which++) {
1640 for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
1645 if (!(retstr = ast_malloc(max_equal + 1)))
1648 ast_copy_string(retstr, match_list[1], max_equal + 1);
1649 match_list[0] = retstr;
1651 /* ensure that the array is NULL terminated */
1652 if (matches + 1 >= match_list_len) {
1653 if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
1656 match_list[matches + 1] = NULL;
1661 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
1663 char *argv[AST_MAX_ARGS];
1664 struct ast_cli_entry *e;
1665 struct cli_iterator i = { NULL, NULL };
1666 int x = 0, argindex, matchlen;
1669 char matchstr[80] = "";
1671 char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
1673 if (!dup) /* error */
1675 argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
1676 /* rebuild the command, ignore tws */
1677 ast_join(matchstr, sizeof(matchstr)-1, argv);
1678 matchlen = strlen(matchstr);
1680 strcat(matchstr, " "); /* XXX */
1685 AST_LIST_LOCK(&helpers);
1686 while( !ret && (e = cli_next(&i)) ) {
1687 int lc = strlen(e->_full_cmd);
1688 if (e->_full_cmd[0] != '_' && lc > 0 && matchlen <= lc &&
1689 !strncasecmp(matchstr, e->_full_cmd, matchlen)) {
1690 /* Found initial part, return a copy of the next word... */
1691 if (e->cmda[argindex] && ++matchnum > state)
1692 ret = strdup(e->cmda[argindex]); /* we need a malloced string */
1693 } else if (e->generator && !strncasecmp(matchstr, e->_full_cmd, lc) && matchstr[lc] < 33) {
1694 /* We have a command in its entirity within us -- theoretically only one
1695 command can have this occur */
1696 ret = e->generator(matchstr, word, argindex, state);
1700 AST_LIST_UNLOCK(&helpers);
1705 char *ast_cli_generator(const char *text, const char *word, int state)
1707 return __ast_cli_generator(text, word, state, 1);
1710 int ast_cli_command(int fd, const char *s)
1712 char *argv[AST_MAX_ARGS];
1713 struct ast_cli_entry *e;
1718 if (!(dup = parse_args(s, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws)))
1721 /* We need at least one entry, or ignore */
1723 AST_LIST_LOCK(&helpers);
1724 e = find_cli(argv, 0);
1727 AST_LIST_UNLOCK(&helpers);
1729 switch(e->handler(fd, x, argv)) {
1730 case RESULT_SHOWUSAGE:
1732 ast_cli(fd, "%s", e->usage);
1734 ast_cli(fd, "Invalid usage, but no usage information available.\n");
1737 AST_LIST_LOCK(&helpers);
1738 if (e->deprecated == 1) {
1739 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);
1742 AST_LIST_UNLOCK(&helpers);
1746 ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv));
1748 ast_atomic_fetchadd_int(&e->inuse, -1);