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
, ast_cli_buf_init
);
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_deprecated(int fd
, int argc
, char *argv
[])
276 int oldval
= option_debug
;
278 /* "set debug [atleast] N" */
280 option_debug
= atoi(argv
[2]);
281 else if (argc
== 4) {
282 if (strcasecmp(argv
[2], "atleast"))
283 return RESULT_SHOWUSAGE
;
285 if (val
> option_debug
)
288 return RESULT_SHOWUSAGE
;
290 if (oldval
!= option_debug
&& option_debug
> 0)
291 ast_cli(fd
, "Core debug was %d and is now %d\n", oldval
, option_debug
);
292 else if (oldval
> 0 && option_debug
> 0)
293 ast_cli(fd
, "Core debug is at least %d\n", option_debug
);
294 else if (oldval
> 0 && option_debug
== 0)
295 ast_cli(fd
, "Core debug is now OFF\n");
297 return RESULT_SUCCESS
;
300 static int handle_set_debug(int fd
, int argc
, char *argv
[])
302 int oldval
= option_debug
;
305 char *filename
= '\0';
307 /* 'core set debug <level>'
308 * 'core set debug <level> <fn>'
309 * 'core set debug atleast <level>'
310 * 'core set debug atleast <level> <fn>'
312 if ((argc
< 4) || (argc
> 6))
313 return RESULT_SHOWUSAGE
;
315 if (!strcasecmp(argv
[3], "atleast"))
320 return RESULT_SHOWUSAGE
;
322 if (sscanf(argv
[3], "%d", &newlevel
) != 1)
323 return RESULT_SHOWUSAGE
;
326 debug_filename
[0] = '\0';
329 ast_copy_string(debug_filename
, filename
, sizeof(debug_filename
));
332 option_debug
= newlevel
;
334 if (argc
< 5 || argc
> 6)
335 return RESULT_SHOWUSAGE
;
337 if (sscanf(argv
[4], "%d", &newlevel
) != 1)
338 return RESULT_SHOWUSAGE
;
341 debug_filename
[0] = '\0';
344 ast_copy_string(debug_filename
, filename
, sizeof(debug_filename
));
347 if (newlevel
> option_debug
)
348 option_debug
= newlevel
;
351 if (oldval
> 0 && option_debug
== 0)
352 ast_cli(fd
, "Core debug is now OFF\n");
353 else if (option_debug
> 0) {
355 if (oldval
== option_debug
)
356 ast_cli(fd
, "Core debug is at least %d, file '%s'\n", option_debug
, filename
);
358 ast_cli(fd
, "Core debug was %d and is now %d, file '%s'\n", oldval
, option_debug
, filename
);
360 if (oldval
== option_debug
)
361 ast_cli(fd
, "Core debug is at least %d\n", option_debug
);
363 ast_cli(fd
, "Core debug was %d and is now %d\n", oldval
, option_debug
);
367 return RESULT_SUCCESS
;
370 static int handle_nodebug(int fd
, int argc
, char *argv
[])
372 int oldval
= option_debug
;
374 return RESULT_SHOWUSAGE
;
377 debug_filename
[0] = '\0';
380 ast_cli(fd
, "Core debug is now OFF\n");
381 return RESULT_SUCCESS
;
384 static int handle_debuglevel_deprecated(int fd
, int argc
, char *argv
[])
387 char *filename
= "<any>";
388 if ((argc
< 3) || (argc
> 4))
389 return RESULT_SHOWUSAGE
;
390 if (sscanf(argv
[2], "%d", &newlevel
) != 1)
391 return RESULT_SHOWUSAGE
;
392 option_debug
= newlevel
;
395 ast_copy_string(debug_filename
, filename
, sizeof(debug_filename
));
397 debug_filename
[0] = '\0';
399 ast_cli(fd
, "Debugging level set to %d, file '%s'\n", newlevel
, filename
);
400 return RESULT_SUCCESS
;
403 static int handle_logger_mute(int fd
, int argc
, char *argv
[])
406 return RESULT_SHOWUSAGE
;
407 ast_console_toggle_mute(fd
);
408 return RESULT_SUCCESS
;
411 static int handle_unload_deprecated(int fd
, int argc
, char *argv
[])
414 int force
= AST_FORCE_SOFT
;
416 return RESULT_SHOWUSAGE
;
417 for (x
= 1; x
< argc
; x
++) {
418 if (argv
[x
][0] == '-') {
421 force
= AST_FORCE_FIRM
;
424 force
= AST_FORCE_HARD
;
427 return RESULT_SHOWUSAGE
;
429 } else if (x
!= argc
- 1)
430 return RESULT_SHOWUSAGE
;
431 else if (ast_unload_resource(argv
[x
], force
)) {
432 ast_cli(fd
, "Unable to unload resource %s\n", argv
[x
]);
433 return RESULT_FAILURE
;
436 return RESULT_SUCCESS
;
439 static int handle_unload(int fd
, int argc
, char *argv
[])
442 int force
= AST_FORCE_SOFT
;
444 return RESULT_SHOWUSAGE
;
445 for (x
= 2; x
< argc
; x
++) {
446 if (argv
[x
][0] == '-') {
449 force
= AST_FORCE_FIRM
;
452 force
= AST_FORCE_HARD
;
455 return RESULT_SHOWUSAGE
;
457 } else if (x
!= argc
- 1)
458 return RESULT_SHOWUSAGE
;
459 else if (ast_unload_resource(argv
[x
], force
)) {
460 ast_cli(fd
, "Unable to unload resource %s\n", argv
[x
]);
461 return RESULT_FAILURE
;
464 return RESULT_SUCCESS
;
467 #define MODLIST_FORMAT "%-30s %-40.40s %-10d\n"
468 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
470 AST_MUTEX_DEFINE_STATIC(climodentrylock
);
471 static int climodentryfd
= -1;
473 static int modlist_modentry(const char *module
, const char *description
, int usecnt
, const char *like
)
475 /* Comparing the like with the module */
476 if (strcasestr(module
, like
) ) {
477 ast_cli(climodentryfd
, MODLIST_FORMAT
, module
, description
, usecnt
);
483 static char modlist_help
[] =
484 "Usage: module show [like <keyword>]\n"
485 " Shows Asterisk modules currently in use, and usage statistics.\n";
487 static char uptime_help
[] =
488 "Usage: core show uptime [seconds]\n"
489 " Shows Asterisk uptime information.\n"
490 " The seconds word returns the uptime in seconds only.\n";
492 static void print_uptimestr(int fd
, time_t timeval
, const char *prefix
, int printsec
)
494 int x
; /* the main part - years, weeks, etc. */
495 char timestr
[256]="", *s
= timestr
;
496 size_t maxbytes
= sizeof(timestr
);
499 #define MINUTE (SECOND*60)
500 #define HOUR (MINUTE*60)
501 #define DAY (HOUR*24)
503 #define YEAR (DAY*365)
504 #define ESS(x) ((x == 1) ? "" : "s") /* plural suffix */
505 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
506 if (timeval
< 0) /* invalid, nothing to show */
508 if (printsec
) { /* plain seconds output */
509 ast_build_string(&s
, &maxbytes
, "%lu", (u_long
)timeval
);
510 timeval
= 0; /* bypass the other cases */
512 if (timeval
> YEAR
) {
513 x
= (timeval
/ YEAR
);
514 timeval
-= (x
* YEAR
);
515 ast_build_string(&s
, &maxbytes
, "%d year%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
517 if (timeval
> WEEK
) {
518 x
= (timeval
/ WEEK
);
519 timeval
-= (x
* WEEK
);
520 ast_build_string(&s
, &maxbytes
, "%d week%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
524 timeval
-= (x
* DAY
);
525 ast_build_string(&s
, &maxbytes
, "%d day%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
527 if (timeval
> HOUR
) {
528 x
= (timeval
/ HOUR
);
529 timeval
-= (x
* HOUR
);
530 ast_build_string(&s
, &maxbytes
, "%d hour%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
532 if (timeval
> MINUTE
) {
533 x
= (timeval
/ MINUTE
);
534 timeval
-= (x
* MINUTE
);
535 ast_build_string(&s
, &maxbytes
, "%d minute%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
539 ast_build_string(&s
, &maxbytes
, "%d second%s ", x
, ESS(x
));
540 if (timestr
[0] != '\0')
541 ast_cli(fd
, "%s: %s\n", prefix
, timestr
);
544 static int handle_showuptime(int fd
, int argc
, char *argv
[])
546 /* 'show uptime [seconds]' */
547 time_t curtime
= time(NULL
);
548 int printsec
= (argc
== 4 && !strcasecmp(argv
[3],"seconds"));
550 if (argc
!= 3 && !printsec
)
551 return RESULT_SHOWUSAGE
;
553 print_uptimestr(fd
, curtime
- ast_startuptime
, "System uptime", printsec
);
554 if (ast_lastreloadtime
)
555 print_uptimestr(fd
, curtime
- ast_lastreloadtime
, "Last reload", printsec
);
556 return RESULT_SUCCESS
;
559 static int handle_modlist(int fd
, int argc
, char *argv
[])
563 return RESULT_SHOWUSAGE
;
564 else if (argc
>= 4) {
565 if (strcmp(argv
[2],"like"))
566 return RESULT_SHOWUSAGE
;
570 ast_mutex_lock(&climodentrylock
);
571 climodentryfd
= fd
; /* global, protected by climodentrylock */
572 ast_cli(fd
, MODLIST_FORMAT2
, "Module", "Description", "Use Count");
573 ast_cli(fd
,"%d modules loaded\n", ast_update_module_list(modlist_modentry
, like
));
575 ast_mutex_unlock(&climodentrylock
);
576 return RESULT_SUCCESS
;
578 #undef MODLIST_FORMAT
579 #undef MODLIST_FORMAT2
581 #define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
582 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
583 #define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n"
584 #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"
585 #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"
587 static int handle_chanlist_deprecated(int fd
, int argc
, char *argv
[])
589 struct ast_channel
*c
= NULL
;
590 char durbuf
[10] = "-";
594 int durh
, durm
, durs
;
595 int numchans
= 0, concise
= 0, verbose
= 0;
597 concise
= (argc
== 3 && (!strcasecmp(argv
[2],"concise")));
598 verbose
= (argc
== 3 && (!strcasecmp(argv
[2],"verbose")));
600 if (argc
< 2 || argc
> 3 || (argc
== 3 && !concise
&& !verbose
))
601 return RESULT_SHOWUSAGE
;
603 if (!concise
&& !verbose
)
604 ast_cli(fd
, FORMAT_STRING2
, "Channel", "Location", "State", "Application(Data)");
606 ast_cli(fd
, VERBOSE_FORMAT_STRING2
, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
607 "CallerID", "Duration", "Accountcode", "BridgedTo");
609 while ((c
= ast_channel_walk_locked(c
)) != NULL
) {
610 struct ast_channel
*bc
= ast_bridged_channel(c
);
611 if ((concise
|| verbose
) && c
->cdr
&& !ast_tvzero(c
->cdr
->start
)) {
612 duration
= (int)(ast_tvdiff_ms(ast_tvnow(), c
->cdr
->start
) / 1000);
614 durh
= duration
/ 3600;
615 durm
= (duration
% 3600) / 60;
616 durs
= duration
% 60;
617 snprintf(durbuf
, sizeof(durbuf
), "%02d:%02d:%02d", durh
, durm
, durs
);
619 snprintf(durbuf
, sizeof(durbuf
), "%d", duration
);
625 ast_cli(fd
, CONCISE_FORMAT_STRING
, c
->name
, c
->context
, c
->exten
, c
->priority
, ast_state2str(c
->_state
),
626 c
->appl
? c
->appl
: "(None)",
627 S_OR(c
->data
, ""), /* XXX different from verbose ? */
628 S_OR(c
->cid
.cid_num
, ""),
629 S_OR(c
->accountcode
, ""),
632 bc
? bc
->name
: "(None)");
633 } else if (verbose
) {
634 ast_cli(fd
, VERBOSE_FORMAT_STRING
, c
->name
, c
->context
, c
->exten
, c
->priority
, ast_state2str(c
->_state
),
635 c
->appl
? c
->appl
: "(None)",
636 c
->data
? S_OR(c
->data
, "(Empty)" ): "(None)",
637 S_OR(c
->cid
.cid_num
, ""),
639 S_OR(c
->accountcode
, ""),
640 bc
? bc
->name
: "(None)");
642 if (!ast_strlen_zero(c
->context
) && !ast_strlen_zero(c
->exten
))
643 snprintf(locbuf
, sizeof(locbuf
), "%s@%s:%d", c
->exten
, c
->context
, c
->priority
);
645 strcpy(locbuf
, "(None)");
647 snprintf(appdata
, sizeof(appdata
), "%s(%s)", c
->appl
, c
->data
? c
->data
: "");
649 strcpy(appdata
, "(None)");
650 ast_cli(fd
, FORMAT_STRING
, c
->name
, locbuf
, ast_state2str(c
->_state
), appdata
);
653 ast_channel_unlock(c
);
656 ast_cli(fd
, "%d active channel%s\n", numchans
, ESS(numchans
));
658 ast_cli(fd
, "%d of %d max active call%s (%5.2f%% of capacity)\n",
659 ast_active_calls(), option_maxcalls
, ESS(ast_active_calls()),
660 ((double)ast_active_calls() / (double)option_maxcalls
) * 100.0);
662 ast_cli(fd
, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
664 return RESULT_SUCCESS
;
667 static int handle_chanlist(int fd
, int argc
, char *argv
[])
669 struct ast_channel
*c
= NULL
;
670 char durbuf
[10] = "-";
674 int durh
, durm
, durs
;
675 int numchans
= 0, concise
= 0, verbose
= 0;
677 concise
= (argc
== 4 && (!strcasecmp(argv
[3],"concise")));
678 verbose
= (argc
== 4 && (!strcasecmp(argv
[3],"verbose")));
680 if (argc
< 3 || argc
> 4 || (argc
== 4 && !concise
&& !verbose
))
681 return RESULT_SHOWUSAGE
;
683 if (!concise
&& !verbose
)
684 ast_cli(fd
, FORMAT_STRING2
, "Channel", "Location", "State", "Application(Data)");
686 ast_cli(fd
, VERBOSE_FORMAT_STRING2
, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
687 "CallerID", "Duration", "Accountcode", "BridgedTo");
689 while ((c
= ast_channel_walk_locked(c
)) != NULL
) {
690 struct ast_channel
*bc
= ast_bridged_channel(c
);
691 if ((concise
|| verbose
) && c
->cdr
&& !ast_tvzero(c
->cdr
->start
)) {
692 duration
= (int)(ast_tvdiff_ms(ast_tvnow(), c
->cdr
->start
) / 1000);
694 durh
= duration
/ 3600;
695 durm
= (duration
% 3600) / 60;
696 durs
= duration
% 60;
697 snprintf(durbuf
, sizeof(durbuf
), "%02d:%02d:%02d", durh
, durm
, durs
);
699 snprintf(durbuf
, sizeof(durbuf
), "%d", duration
);
705 ast_cli(fd
, CONCISE_FORMAT_STRING
, c
->name
, c
->context
, c
->exten
, c
->priority
, ast_state2str(c
->_state
),
706 c
->appl
? c
->appl
: "(None)",
707 S_OR(c
->data
, ""), /* XXX different from verbose ? */
708 S_OR(c
->cid
.cid_num
, ""),
709 S_OR(c
->accountcode
, ""),
712 bc
? bc
->name
: "(None)");
713 } else if (verbose
) {
714 ast_cli(fd
, VERBOSE_FORMAT_STRING
, c
->name
, c
->context
, c
->exten
, c
->priority
, ast_state2str(c
->_state
),
715 c
->appl
? c
->appl
: "(None)",
716 c
->data
? S_OR(c
->data
, "(Empty)" ): "(None)",
717 S_OR(c
->cid
.cid_num
, ""),
719 S_OR(c
->accountcode
, ""),
720 bc
? bc
->name
: "(None)");
722 if (!ast_strlen_zero(c
->context
) && !ast_strlen_zero(c
->exten
))
723 snprintf(locbuf
, sizeof(locbuf
), "%s@%s:%d", c
->exten
, c
->context
, c
->priority
);
725 strcpy(locbuf
, "(None)");
727 snprintf(appdata
, sizeof(appdata
), "%s(%s)", c
->appl
, c
->data
? c
->data
: "");
729 strcpy(appdata
, "(None)");
730 ast_cli(fd
, FORMAT_STRING
, c
->name
, locbuf
, ast_state2str(c
->_state
), appdata
);
733 ast_channel_unlock(c
);
736 ast_cli(fd
, "%d active channel%s\n", numchans
, ESS(numchans
));
738 ast_cli(fd
, "%d of %d max active call%s (%5.2f%% of capacity)\n",
739 ast_active_calls(), option_maxcalls
, ESS(ast_active_calls()),
740 ((double)ast_active_calls() / (double)option_maxcalls
) * 100.0);
742 ast_cli(fd
, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
744 return RESULT_SUCCESS
;
748 #undef FORMAT_STRING2
749 #undef CONCISE_FORMAT_STRING
750 #undef VERBOSE_FORMAT_STRING
751 #undef VERBOSE_FORMAT_STRING2
753 static char showchan_help
[] =
754 "Usage: core show channel <channel>\n"
755 " Shows lots of information about the specified channel.\n";
757 static char debugchan_help
[] =
758 "Usage: core set debug channel <channel> [off]\n"
759 " Enables/disables debugging on a specific channel.\n";
761 static char commandcomplete_help
[] =
762 "Usage: _command complete \"<line>\" text state\n"
763 " This function is used internally to help with command completion and should.\n"
764 " never be called by the user directly.\n";
766 static char commandnummatches_help
[] =
767 "Usage: _command nummatches \"<line>\" text \n"
768 " This function is used internally to help with command completion and should.\n"
769 " never be called by the user directly.\n";
771 static char commandmatchesarray_help
[] =
772 "Usage: _command matchesarray \"<line>\" text \n"
773 " This function is used internally to help with command completion and should.\n"
774 " never be called by the user directly.\n";
776 static int handle_softhangup(int fd
, int argc
, char *argv
[])
778 struct ast_channel
*c
=NULL
;
780 return RESULT_SHOWUSAGE
;
781 c
= ast_get_channel_by_name_locked(argv
[2]);
783 ast_cli(fd
, "Requested Hangup on channel '%s'\n", c
->name
);
784 ast_softhangup(c
, AST_SOFTHANGUP_EXPLICIT
);
785 ast_channel_unlock(c
);
787 ast_cli(fd
, "%s is not a known channel\n", argv
[2]);
788 return RESULT_SUCCESS
;
791 static char *__ast_cli_generator(const char *text
, const char *word
, int state
, int lock
);
793 static int handle_commandmatchesarray(int fd
, int argc
, char *argv
[])
802 return RESULT_SHOWUSAGE
;
803 if (!(buf
= ast_malloc(buflen
)))
804 return RESULT_FAILURE
;
806 matches
= ast_cli_completion_matches(argv
[2], argv
[3]);
808 for (x
=0; matches
[x
]; x
++) {
809 matchlen
= strlen(matches
[x
]) + 1;
810 if (len
+ matchlen
>= buflen
) {
811 buflen
+= matchlen
* 3;
813 if (!(buf
= ast_realloc(obuf
, buflen
)))
814 /* Memory allocation failure... Just free old buffer and be done */
818 len
+= sprintf( buf
+ len
, "%s ", matches
[x
]);
826 ast_cli(fd
, "%s%s",buf
, AST_CLI_COMPLETE_EOF
);
829 ast_cli(fd
, "NULL\n");
831 return RESULT_SUCCESS
;
836 static int handle_commandnummatches(int fd
, int argc
, char *argv
[])
841 return RESULT_SHOWUSAGE
;
843 matches
= ast_cli_generatornummatches(argv
[2], argv
[3]);
845 ast_cli(fd
, "%d", matches
);
847 return RESULT_SUCCESS
;
850 static int handle_commandcomplete(int fd
, int argc
, char *argv
[])
855 return RESULT_SHOWUSAGE
;
856 buf
= __ast_cli_generator(argv
[2], argv
[3], atoi(argv
[4]), 0);
861 ast_cli(fd
, "NULL\n");
862 return RESULT_SUCCESS
;
865 static int handle_debugchan_deprecated(int fd
, int argc
, char *argv
[])
867 struct ast_channel
*c
=NULL
;
870 /* 'debug channel {all|chan_id}' */
872 return RESULT_SHOWUSAGE
;
874 is_all
= !strcasecmp("all", argv
[3]);
876 global_fin
|= DEBUGCHAN_FLAG
;
877 global_fout
|= DEBUGCHAN_FLAG
;
878 c
= ast_channel_walk_locked(NULL
);
880 c
= ast_get_channel_by_name_locked(argv
[3]);
882 ast_cli(fd
, "No such channel %s\n", argv
[3]);
885 if (!(c
->fin
& DEBUGCHAN_FLAG
) || !(c
->fout
& DEBUGCHAN_FLAG
)) {
886 c
->fin
|= DEBUGCHAN_FLAG
;
887 c
->fout
|= DEBUGCHAN_FLAG
;
888 ast_cli(fd
, "Debugging enabled on channel %s\n", c
->name
);
890 ast_channel_unlock(c
);
893 c
= ast_channel_walk_locked(c
);
895 ast_cli(fd
, "Debugging on new channels is enabled\n");
896 return RESULT_SUCCESS
;
899 static int handle_core_set_debug_channel(int fd
, int argc
, char *argv
[])
901 struct ast_channel
*c
= NULL
;
902 int is_all
, is_off
= 0;
904 /* 'core set debug channel {all|chan_id}' */
905 if (argc
== 6 && strcmp(argv
[5], "off") == 0)
908 return RESULT_SHOWUSAGE
;
910 is_all
= !strcasecmp("all", argv
[4]);
913 global_fin
&= ~DEBUGCHAN_FLAG
;
914 global_fout
&= ~DEBUGCHAN_FLAG
;
916 global_fin
|= DEBUGCHAN_FLAG
;
917 global_fout
|= DEBUGCHAN_FLAG
;
919 c
= ast_channel_walk_locked(NULL
);
921 c
= ast_get_channel_by_name_locked(argv
[4]);
923 ast_cli(fd
, "No such channel %s\n", argv
[4]);
926 if (!(c
->fin
& DEBUGCHAN_FLAG
) || !(c
->fout
& DEBUGCHAN_FLAG
)) {
928 c
->fin
&= ~DEBUGCHAN_FLAG
;
929 c
->fout
&= ~DEBUGCHAN_FLAG
;
931 c
->fin
|= DEBUGCHAN_FLAG
;
932 c
->fout
|= DEBUGCHAN_FLAG
;
934 ast_cli(fd
, "Debugging %s on channel %s\n", is_off
? "disabled" : "enabled", c
->name
);
936 ast_channel_unlock(c
);
939 c
= ast_channel_walk_locked(c
);
941 ast_cli(fd
, "Debugging on new channels is %s\n", is_off
? "disabled" : "enabled");
942 return RESULT_SUCCESS
;
945 static int handle_nodebugchan_deprecated(int fd
, int argc
, char *argv
[])
947 struct ast_channel
*c
=NULL
;
949 /* 'no debug channel {all|chan_id}' */
951 return RESULT_SHOWUSAGE
;
952 is_all
= !strcasecmp("all", argv
[3]);
954 global_fin
&= ~DEBUGCHAN_FLAG
;
955 global_fout
&= ~DEBUGCHAN_FLAG
;
956 c
= ast_channel_walk_locked(NULL
);
958 c
= ast_get_channel_by_name_locked(argv
[3]);
960 ast_cli(fd
, "No such channel %s\n", argv
[3]);
963 if ((c
->fin
& DEBUGCHAN_FLAG
) || (c
->fout
& DEBUGCHAN_FLAG
)) {
964 c
->fin
&= ~DEBUGCHAN_FLAG
;
965 c
->fout
&= ~DEBUGCHAN_FLAG
;
966 ast_cli(fd
, "Debugging disabled on channel %s\n", c
->name
);
968 ast_channel_unlock(c
);
971 c
= ast_channel_walk_locked(c
);
973 ast_cli(fd
, "Debugging on new channels is disabled\n");
974 return RESULT_SUCCESS
;
977 static int handle_showchan_deprecated(int fd
, int argc
, char *argv
[])
979 struct ast_channel
*c
=NULL
;
983 char nf
[256], wf
[256], rf
[256];
984 long elapsed_seconds
=0;
985 int hour
=0, min
=0, sec
=0;
988 return RESULT_SHOWUSAGE
;
990 c
= ast_get_channel_by_name_locked(argv
[2]);
992 ast_cli(fd
, "%s is not a known channel\n", argv
[2]);
993 return RESULT_SUCCESS
;
996 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
997 hour
= elapsed_seconds
/ 3600;
998 min
= (elapsed_seconds
% 3600) / 60;
999 sec
= elapsed_seconds
% 60;
1000 snprintf(cdrtime
, sizeof(cdrtime
), "%dh%dm%ds", hour
, min
, sec
);
1002 strcpy(cdrtime
, "N/A");
1009 " Caller ID Name: %s\n"
1010 " DNID Digits: %s\n"
1013 " NativeFormats: %s\n"
1014 " WriteFormat: %s\n"
1016 " WriteTranscode: %s\n"
1017 " ReadTranscode: %s\n"
1018 "1st File Descriptor: %d\n"
1019 " Frames in: %d%s\n"
1020 " Frames out: %d%s\n"
1021 " Time to Hangup: %ld\n"
1022 " Elapsed Time: %s\n"
1023 " Direct Bridge: %s\n"
1024 "Indirect Bridge: %s\n"
1029 " Call Group: %llu\n"
1030 " Pickup Group: %llu\n"
1031 " Application: %s\n"
1033 " Blocking in: %s\n",
1034 c
->name
, c
->tech
->type
, c
->uniqueid
,
1035 S_OR(c
->cid
.cid_num
, "(N/A)"),
1036 S_OR(c
->cid
.cid_name
, "(N/A)"),
1037 S_OR(c
->cid
.cid_dnid
, "(N/A)"), ast_state2str(c
->_state
), c
->_state
, c
->rings
,
1038 ast_getformatname_multiple(nf
, sizeof(nf
), c
->nativeformats
),
1039 ast_getformatname_multiple(wf
, sizeof(wf
), c
->writeformat
),
1040 ast_getformatname_multiple(rf
, sizeof(rf
), c
->readformat
),
1041 c
->writetrans
? "Yes" : "No",
1042 c
->readtrans
? "Yes" : "No",
1044 c
->fin
& ~DEBUGCHAN_FLAG
, (c
->fin
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
1045 c
->fout
& ~DEBUGCHAN_FLAG
, (c
->fout
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
1046 (long)c
->whentohangup
,
1047 cdrtime
, c
->_bridge
? c
->_bridge
->name
: "<none>", ast_bridged_channel(c
) ? ast_bridged_channel(c
)->name
: "<none>",
1048 c
->context
, c
->exten
, c
->priority
, c
->callgroup
, c
->pickupgroup
, ( c
->appl
? c
->appl
: "(N/A)" ),
1049 ( c
-> data
? S_OR(c
->data
, "(Empty)") : "(None)"),
1050 (ast_test_flag(c
, AST_FLAG_BLOCKING
) ? c
->blockproc
: "(Not Blocking)"));
1052 if(pbx_builtin_serialize_variables(c
,buf
,sizeof(buf
)))
1053 ast_cli(fd
," Variables:\n%s\n",buf
);
1054 if(c
->cdr
&& ast_cdr_serialize_variables(c
->cdr
,buf
, sizeof(buf
), '=', '\n', 1))
1055 ast_cli(fd
," CDR Variables:\n%s\n",buf
);
1057 ast_channel_unlock(c
);
1058 return RESULT_SUCCESS
;
1061 static int handle_showchan(int fd
, int argc
, char *argv
[])
1063 struct ast_channel
*c
=NULL
;
1067 char nf
[256], wf
[256], rf
[256];
1068 long elapsed_seconds
=0;
1069 int hour
=0, min
=0, sec
=0;
1072 return RESULT_SHOWUSAGE
;
1074 c
= ast_get_channel_by_name_locked(argv
[3]);
1076 ast_cli(fd
, "%s is not a known channel\n", argv
[3]);
1077 return RESULT_SUCCESS
;
1080 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
1081 hour
= elapsed_seconds
/ 3600;
1082 min
= (elapsed_seconds
% 3600) / 60;
1083 sec
= elapsed_seconds
% 60;
1084 snprintf(cdrtime
, sizeof(cdrtime
), "%dh%dm%ds", hour
, min
, sec
);
1086 strcpy(cdrtime
, "N/A");
1093 " Caller ID Name: %s\n"
1094 " DNID Digits: %s\n"
1097 " NativeFormats: %s\n"
1098 " WriteFormat: %s\n"
1100 " WriteTranscode: %s\n"
1101 " ReadTranscode: %s\n"
1102 "1st File Descriptor: %d\n"
1103 " Frames in: %d%s\n"
1104 " Frames out: %d%s\n"
1105 " Time to Hangup: %ld\n"
1106 " Elapsed Time: %s\n"
1107 " Direct Bridge: %s\n"
1108 "Indirect Bridge: %s\n"
1113 " Call Group: %llu\n"
1114 " Pickup Group: %llu\n"
1115 " Application: %s\n"
1117 " Blocking in: %s\n",
1118 c
->name
, c
->tech
->type
, c
->uniqueid
,
1119 S_OR(c
->cid
.cid_num
, "(N/A)"),
1120 S_OR(c
->cid
.cid_name
, "(N/A)"),
1121 S_OR(c
->cid
.cid_dnid
, "(N/A)"), ast_state2str(c
->_state
), c
->_state
, c
->rings
,
1122 ast_getformatname_multiple(nf
, sizeof(nf
), c
->nativeformats
),
1123 ast_getformatname_multiple(wf
, sizeof(wf
), c
->writeformat
),
1124 ast_getformatname_multiple(rf
, sizeof(rf
), c
->readformat
),
1125 c
->writetrans
? "Yes" : "No",
1126 c
->readtrans
? "Yes" : "No",
1128 c
->fin
& ~DEBUGCHAN_FLAG
, (c
->fin
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
1129 c
->fout
& ~DEBUGCHAN_FLAG
, (c
->fout
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
1130 (long)c
->whentohangup
,
1131 cdrtime
, c
->_bridge
? c
->_bridge
->name
: "<none>", ast_bridged_channel(c
) ? ast_bridged_channel(c
)->name
: "<none>",
1132 c
->context
, c
->exten
, c
->priority
, c
->callgroup
, c
->pickupgroup
, ( c
->appl
? c
->appl
: "(N/A)" ),
1133 ( c
-> data
? S_OR(c
->data
, "(Empty)") : "(None)"),
1134 (ast_test_flag(c
, AST_FLAG_BLOCKING
) ? c
->blockproc
: "(Not Blocking)"));
1136 if(pbx_builtin_serialize_variables(c
,buf
,sizeof(buf
)))
1137 ast_cli(fd
," Variables:\n%s\n",buf
);
1138 if(c
->cdr
&& ast_cdr_serialize_variables(c
->cdr
,buf
, sizeof(buf
), '=', '\n', 1))
1139 ast_cli(fd
," CDR Variables:\n%s\n",buf
);
1141 ast_channel_unlock(c
);
1142 return RESULT_SUCCESS
;
1146 * helper function to generate CLI matches from a fixed set of values.
1147 * A NULL word is acceptable.
1149 char *ast_cli_complete(const char *word
, char *const choices
[], int state
)
1151 int i
, which
= 0, len
;
1152 len
= ast_strlen_zero(word
) ? 0 : strlen(word
);
1154 for (i
= 0; choices
[i
]; i
++) {
1155 if ((!len
|| !strncasecmp(word
, choices
[i
], len
)) && ++which
> state
)
1156 return ast_strdup(choices
[i
]);
1161 static char *complete_show_channels_deprecated(const char *line
, const char *word
, int pos
, int state
)
1163 static char *choices
[] = { "concise", "verbose", NULL
};
1165 return (pos
!= 2) ? NULL
: ast_cli_complete(word
, choices
, state
);
1168 static char *complete_show_channels(const char *line
, const char *word
, int pos
, int state
)
1170 static char *choices
[] = { "concise", "verbose", NULL
};
1172 return (pos
!= 3) ? NULL
: ast_cli_complete(word
, choices
, state
);
1175 char *ast_complete_channels(const char *line
, const char *word
, int pos
, int state
, int rpos
)
1177 struct ast_channel
*c
= NULL
;
1180 char notfound
= '\0';
1181 char *ret
= ¬found
; /* so NULL can break the loop */
1186 wordlen
= strlen(word
);
1188 while (ret
== ¬found
&& (c
= ast_channel_walk_locked(c
))) {
1189 if (!strncasecmp(word
, c
->name
, wordlen
) && ++which
> state
)
1190 ret
= ast_strdup(c
->name
);
1191 ast_channel_unlock(c
);
1193 return ret
== ¬found
? NULL
: ret
;
1196 static char *complete_ch_3(const char *line
, const char *word
, int pos
, int state
)
1198 return ast_complete_channels(line
, word
, pos
, state
, 2);
1201 static char *complete_ch_4(const char *line
, const char *word
, int pos
, int state
)
1203 return ast_complete_channels(line
, word
, pos
, state
, 3);
1206 static char *complete_ch_5(const char *line
, const char *word
, int pos
, int state
)
1208 return ast_complete_channels(line
, word
, pos
, state
, 4);
1211 static char *complete_mod_2(const char *line
, const char *word
, int pos
, int state
)
1213 return ast_module_helper(line
, word
, pos
, state
, 1, 1);
1216 static char *complete_mod_3_nr(const char *line
, const char *word
, int pos
, int state
)
1218 return ast_module_helper(line
, word
, pos
, state
, 2, 0);
1221 static char *complete_mod_3(const char *line
, const char *word
, int pos
, int state
)
1223 return ast_module_helper(line
, word
, pos
, state
, 2, 1);
1226 static char *complete_mod_4(const char *line
, const char *word
, int pos
, int state
)
1228 return ast_module_helper(line
, word
, pos
, state
, 3, 0);
1231 static char *complete_fn(const char *line
, const char *word
, int pos
, int state
)
1240 ast_copy_string(filename
, word
, sizeof(filename
));
1242 snprintf(filename
, sizeof(filename
), "%s/%s", ast_config_AST_MODULE_DIR
, word
);
1244 c
= filename_completion_function(filename
, state
);
1246 if (c
&& word
[0] != '/')
1247 c
+= (strlen(ast_config_AST_MODULE_DIR
) + 1);
1249 return c
? strdup(c
) : c
;
1252 static int group_show_channels(int fd
, int argc
, char *argv
[])
1254 #define FORMAT_STRING "%-25s %-20s %-20s\n"
1256 struct ast_channel
*c
= NULL
;
1258 struct ast_var_t
*current
;
1259 struct varshead
*headp
;
1261 int havepattern
= 0;
1263 if (argc
< 3 || argc
> 4)
1264 return RESULT_SHOWUSAGE
;
1267 if (regcomp(®exbuf
, argv
[3], REG_EXTENDED
| REG_NOSUB
))
1268 return RESULT_SHOWUSAGE
;
1272 ast_cli(fd
, FORMAT_STRING
, "Channel", "Group", "Category");
1273 while ( (c
= ast_channel_walk_locked(c
)) != NULL
) {
1275 AST_LIST_TRAVERSE(headp
,current
,entries
) {
1276 if (!strncmp(ast_var_name(current
), GROUP_CATEGORY_PREFIX
"_", strlen(GROUP_CATEGORY_PREFIX
) + 1)) {
1277 if (!havepattern
|| !regexec(®exbuf
, ast_var_value(current
), 0, NULL
, 0)) {
1278 ast_cli(fd
, FORMAT_STRING
, c
->name
, ast_var_value(current
),
1279 (ast_var_name(current
) + strlen(GROUP_CATEGORY_PREFIX
) + 1));
1282 } else if (!strcmp(ast_var_name(current
), GROUP_CATEGORY_PREFIX
)) {
1283 if (!havepattern
|| !regexec(®exbuf
, ast_var_value(current
), 0, NULL
, 0)) {
1284 ast_cli(fd
, FORMAT_STRING
, c
->name
, ast_var_value(current
), "(default)");
1290 ast_channel_unlock(c
);
1296 ast_cli(fd
, "%d active channel%s\n", numchans
, (numchans
!= 1) ? "s" : "");
1297 return RESULT_SUCCESS
;
1298 #undef FORMAT_STRING
1301 static int handle_help(int fd
, int argc
, char *argv
[]);
1303 static char * complete_help(const char *text
, const char *word
, int pos
, int state
)
1305 /* skip first 4 or 5 chars, "help "*/
1306 int l
= strlen(text
);
1311 /* XXX watch out, should stop to the non-generator parts */
1312 return __ast_cli_generator(text
, word
, state
, 0);
1315 /* XXX Nothing in this array can currently be deprecated...
1316 You have to change the way find_cli works in order to remove this array
1317 I recommend doing this eventually...
1319 static struct ast_cli_entry builtins
[] = {
1320 /* Keep alphabetized, with longer matches first (example: abcd before abc) */
1321 { { "_command", "complete", NULL
},
1322 handle_commandcomplete
, "Command complete",
1323 commandcomplete_help
},
1325 { { "_command", "nummatches", NULL
},
1326 handle_commandnummatches
, "Returns number of command matches",
1327 commandnummatches_help
},
1329 { { "_command", "matchesarray", NULL
},
1330 handle_commandmatchesarray
, "Returns command matches array",
1331 commandmatchesarray_help
},
1333 { { NULL
}, NULL
, NULL
, NULL
}
1336 static struct ast_cli_entry cli_debug_channel_deprecated
= {
1337 { "debug", "channel", NULL
},
1338 handle_debugchan_deprecated
, NULL
,
1339 NULL
, complete_ch_3
};
1341 static struct ast_cli_entry cli_debug_level_deprecated
= {
1342 { "debug", "level", NULL
},
1343 handle_debuglevel_deprecated
, NULL
,
1346 static struct ast_cli_entry cli_set_debug_deprecated
= {
1347 { "set", "debug", NULL
},
1348 handle_set_debug_deprecated
, NULL
,
1349 NULL
, NULL
, &cli_debug_level_deprecated
};
1351 static struct ast_cli_entry cli_set_verbose_deprecated
= {
1352 { "set", "verbose", NULL
},
1353 handle_set_verbose_deprecated
, NULL
,
1356 static struct ast_cli_entry cli_show_channel_deprecated
= {
1357 { "show", "channel", NULL
},
1358 handle_showchan_deprecated
, NULL
,
1359 NULL
, complete_ch_3
};
1361 static struct ast_cli_entry cli_show_channels_deprecated
= {
1362 { "show", "channels", NULL
},
1363 handle_chanlist_deprecated
, NULL
,
1364 NULL
, complete_show_channels_deprecated
};
1366 static struct ast_cli_entry cli_show_modules_deprecated
= {
1367 { "show", "modules", NULL
},
1368 handle_modlist
, NULL
,
1371 static struct ast_cli_entry cli_show_modules_like_deprecated
= {
1372 { "show", "modules", "like", NULL
},
1373 handle_modlist
, NULL
,
1374 NULL
, complete_mod_4
};
1376 static struct ast_cli_entry cli_module_load_deprecated
= {
1378 handle_load_deprecated
, NULL
,
1379 NULL
, complete_fn
};
1381 static struct ast_cli_entry cli_module_reload_deprecated
= {
1383 handle_reload_deprecated
, NULL
,
1384 NULL
, complete_mod_2
};
1386 static struct ast_cli_entry cli_module_unload_deprecated
= {
1388 handle_unload_deprecated
, NULL
,
1389 NULL
, complete_mod_2
};
1391 static struct ast_cli_entry cli_cli
[] = {
1392 /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
1393 { { "no", "debug", "channel", NULL
},
1394 handle_nodebugchan_deprecated
, NULL
,
1395 NULL
, complete_ch_4
},
1397 { { "core", "show", "channels", NULL
},
1398 handle_chanlist
, "Display information on channels",
1399 chanlist_help
, complete_show_channels
, &cli_show_channels_deprecated
},
1401 { { "core", "show", "channel", NULL
},
1402 handle_showchan
, "Display information on a specific channel",
1403 showchan_help
, complete_ch_4
, &cli_show_channel_deprecated
},
1405 { { "core", "set", "debug", "channel", NULL
},
1406 handle_core_set_debug_channel
, "Enable/disable debugging on a channel",
1407 debugchan_help
, complete_ch_5
, &cli_debug_channel_deprecated
},
1409 { { "core", "set", "debug", NULL
},
1410 handle_set_debug
, "Set level of debug chattiness",
1411 debug_help
, NULL
, &cli_set_debug_deprecated
},
1413 { { "core", "set", "debug", "off", NULL
},
1414 handle_nodebug
, "Turns off debug chattiness",
1417 { { "core", "set", "verbose", NULL
},
1418 handle_verbose
, "Set level of verboseness",
1419 verbose_help
, NULL
, &cli_set_verbose_deprecated
},
1421 { { "group", "show", "channels", NULL
},
1422 group_show_channels
, "Display active channels with group(s)",
1423 group_show_channels_help
},
1426 handle_help
, "Display help list, or specific help on a command",
1427 help_help
, complete_help
},
1429 { { "logger", "mute", NULL
},
1430 handle_logger_mute
, "Toggle logging output to a console",
1433 { { "module", "show", NULL
},
1434 handle_modlist
, "List modules and info",
1435 modlist_help
, NULL
, &cli_show_modules_deprecated
},
1437 { { "module", "show", "like", NULL
},
1438 handle_modlist
, "List modules and info",
1439 modlist_help
, complete_mod_4
, &cli_show_modules_like_deprecated
},
1441 { { "module", "load", NULL
},
1442 handle_load
, "Load a module by name",
1443 load_help
, complete_fn
, &cli_module_load_deprecated
},
1445 { { "module", "reload", NULL
},
1446 handle_reload
, "Reload configuration",
1447 reload_help
, complete_mod_3
, &cli_module_reload_deprecated
},
1449 { { "module", "unload", NULL
},
1450 handle_unload
, "Unload a module by name",
1451 unload_help
, complete_mod_3_nr
, &cli_module_unload_deprecated
},
1453 { { "core", "show", "uptime", NULL
},
1454 handle_showuptime
, "Show uptime information",
1457 { { "soft", "hangup", NULL
},
1458 handle_softhangup
, "Request a hangup on a given channel",
1459 softhangup_help
, complete_ch_3
},
1462 /*! \brief initialize the _full_cmd string in * each of the builtins. */
1463 void ast_builtins_init(void)
1465 struct ast_cli_entry
*e
;
1467 for (e
= builtins
; e
->cmda
[0] != NULL
; e
++) {
1469 ast_join(buf
, sizeof(buf
), e
->cmda
);
1470 e
->_full_cmd
= strdup(buf
);
1472 ast_log(LOG_WARNING
, "-- cannot allocate <%s>\n", buf
);
1475 ast_cli_register_multiple(cli_cli
, sizeof(cli_cli
) / sizeof(struct ast_cli_entry
));
1479 * We have two sets of commands: builtins are stored in a
1480 * NULL-terminated array of ast_cli_entry, whereas external
1481 * commands are in a list.
1482 * When navigating, we need to keep two pointers and get
1483 * the next one in lexicographic order. For the purpose,
1484 * we use a structure.
1487 struct cli_iterator
{
1488 struct ast_cli_entry
*builtins
;
1489 struct ast_cli_entry
*helpers
;
1492 static struct ast_cli_entry
*cli_next(struct cli_iterator
*i
)
1494 struct ast_cli_entry
*e
;
1496 if (i
->builtins
== NULL
&& i
->helpers
== NULL
) {
1498 i
->builtins
= builtins
;
1499 i
->helpers
= AST_LIST_FIRST(&helpers
);
1501 e
= i
->builtins
; /* temporary */
1502 if (!e
->cmda
[0] || (i
->helpers
&&
1503 strcmp(i
->helpers
->_full_cmd
, e
->_full_cmd
) < 0)) {
1507 i
->helpers
= AST_LIST_NEXT(e
, list
);
1508 } else { /* use builtin. e is already set */
1509 (i
->builtins
)++; /* move to next */
1515 * \brief locate a cli command in the 'helpers' list (which must be locked).
1516 * exact has 3 values:
1517 * 0 returns if the search key is equal or longer than the entry.
1518 * -1 true if the mismatch is on the last word XXX not true!
1519 * 1 true only on complete, exact match.
1521 static struct ast_cli_entry
*find_cli(char *const cmds
[], int match_type
)
1523 int matchlen
= -1; /* length of longest match so far */
1524 struct ast_cli_entry
*cand
= NULL
, *e
=NULL
;
1525 struct cli_iterator i
= { NULL
, NULL
};
1527 while( (e
= cli_next(&i
)) ) {
1529 for (y
= 0 ; cmds
[y
] && e
->cmda
[y
]; y
++) {
1530 if (strcasecmp(e
->cmda
[y
], cmds
[y
]))
1533 if (e
->cmda
[y
] == NULL
) { /* no more words in candidate */
1534 if (cmds
[y
] == NULL
) /* this is an exact match, cannot do better */
1536 /* here the search key is longer than the candidate */
1537 if (match_type
!= 0) /* but we look for almost exact match... */
1538 continue; /* so we skip this one. */
1539 /* otherwise we like it (case 0) */
1540 } else { /* still words in candidate */
1541 if (cmds
[y
] == NULL
) /* search key is shorter, not good */
1543 /* if we get here, both words exist but there is a mismatch */
1544 if (match_type
== 0) /* not the one we look for */
1546 if (match_type
== 1) /* not the one we look for */
1548 if (cmds
[y
+1] != NULL
|| e
->cmda
[y
+1] != NULL
) /* not the one we look for */
1550 /* we are in case match_type == -1 and mismatch on last word */
1552 if (y
> matchlen
) { /* remember the candidate */
1557 return e
? e
: cand
;
1560 static char *find_best(char *argv
[])
1562 static char cmdline
[80];
1564 /* See how close we get, then print the candidate */
1565 char *myargv
[AST_MAX_CMD_LEN
];
1566 for (x
=0;x
<AST_MAX_CMD_LEN
;x
++)
1568 AST_LIST_LOCK(&helpers
);
1569 for (x
=0;argv
[x
];x
++) {
1570 myargv
[x
] = argv
[x
];
1571 if (!find_cli(myargv
, -1))
1574 AST_LIST_UNLOCK(&helpers
);
1575 ast_join(cmdline
, sizeof(cmdline
), myargv
);
1579 static int __ast_cli_unregister(struct ast_cli_entry
*e
, struct ast_cli_entry
*ed
)
1581 if (e
->deprecate_cmd
) {
1582 __ast_cli_unregister(e
->deprecate_cmd
, e
);
1585 ast_log(LOG_WARNING
, "Can't remove command that is in use\n");
1587 AST_LIST_LOCK(&helpers
);
1588 AST_LIST_REMOVE(&helpers
, e
, list
);
1589 AST_LIST_UNLOCK(&helpers
);
1595 static int __ast_cli_register(struct ast_cli_entry
*e
, struct ast_cli_entry
*ed
)
1597 struct ast_cli_entry
*cur
;
1601 ast_join(fulle
, sizeof(fulle
), e
->cmda
);
1602 AST_LIST_LOCK(&helpers
);
1604 if (find_cli(e
->cmda
, 1)) {
1605 ast_log(LOG_WARNING
, "Command '%s' already registered (or something close enough)\n", fulle
);
1608 e
->_full_cmd
= ast_strdup(fulle
);
1614 e
->summary
= ed
->summary
;
1615 e
->usage
= ed
->usage
;
1616 /* XXX If command A deprecates command B, and command B deprecates command C...
1617 Do we want to show command A or command B when telling the user to use new syntax?
1618 This currently would show command A.
1619 To show command B, you just need to always use ed->_full_cmd.
1621 e
->_deprecated_by
= S_OR(ed
->_deprecated_by
, ed
->_full_cmd
);
1627 AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers
, cur
, list
) {
1628 int len
= strlen(cur
->_full_cmd
);
1631 if (strncasecmp(fulle
, cur
->_full_cmd
, len
) < 0) {
1632 AST_LIST_INSERT_BEFORE_CURRENT(&helpers
, e
, list
);
1636 AST_LIST_TRAVERSE_SAFE_END
;
1639 AST_LIST_INSERT_TAIL(&helpers
, e
, list
);
1640 ret
= 0; /* success */
1643 AST_LIST_UNLOCK(&helpers
);
1645 if (e
->deprecate_cmd
) {
1646 /* This command deprecates another command. Register that one also. */
1647 __ast_cli_register(e
->deprecate_cmd
, e
);
1653 /* wrapper function, so we can unregister deprecated commands recursively */
1654 int ast_cli_unregister(struct ast_cli_entry
*e
)
1656 return __ast_cli_unregister(e
, NULL
);
1659 /* wrapper function, so we can register deprecated commands recursively */
1660 int ast_cli_register(struct ast_cli_entry
*e
)
1662 return __ast_cli_register(e
, NULL
);
1666 * register/unregister an array of entries.
1668 void ast_cli_register_multiple(struct ast_cli_entry
*e
, int len
)
1672 for (i
= 0; i
< len
; i
++)
1673 ast_cli_register(e
+ i
);
1676 void ast_cli_unregister_multiple(struct ast_cli_entry
*e
, int len
)
1680 for (i
= 0; i
< len
; i
++)
1681 ast_cli_unregister(e
+ i
);
1685 /*! \brief helper for help_workhorse and final part of
1686 * handle_help. if locked = 0 it's just help_workhorse,
1687 * otherwise assume the list is already locked and print
1688 * an error message if not found.
1690 static int help1(int fd
, char *match
[], int locked
)
1692 char matchstr
[80] = "";
1693 struct ast_cli_entry
*e
;
1696 struct cli_iterator i
= { NULL
, NULL
};
1699 ast_join(matchstr
, sizeof(matchstr
), match
);
1700 len
= strlen(matchstr
);
1703 AST_LIST_LOCK(&helpers
);
1704 while ( (e
= cli_next(&i
)) ) {
1705 /* Hide commands that start with '_' */
1706 if (e
->_full_cmd
[0] == '_')
1708 /* Hide commands that are marked as deprecated. */
1711 if (match
&& strncasecmp(matchstr
, e
->_full_cmd
, len
))
1713 ast_cli(fd
, "%25.25s %s\n", e
->_full_cmd
, e
->summary
);
1716 AST_LIST_UNLOCK(&helpers
);
1717 if (!locked
&& !found
&& matchstr
[0])
1718 ast_cli(fd
, "No such command '%s'.\n", matchstr
);
1722 static int help_workhorse(int fd
, char *match
[])
1724 return help1(fd
, match
, 0 /* do not print errors */);
1727 static int handle_help(int fd
, int argc
, char *argv
[])
1730 struct ast_cli_entry
*e
;
1733 return RESULT_SHOWUSAGE
;
1735 return help_workhorse(fd
, NULL
);
1737 AST_LIST_LOCK(&helpers
);
1738 e
= find_cli(argv
+ 1, 1); /* try exact match first */
1740 return help1(fd
, argv
+ 1, 1 /* locked */);
1742 ast_cli(fd
, "%s", e
->usage
);
1744 ast_join(fullcmd
, sizeof(fullcmd
), argv
+1);
1745 ast_cli(fd
, "No help text available for '%s'.\n", fullcmd
);
1747 AST_LIST_UNLOCK(&helpers
);
1748 return RESULT_SUCCESS
;
1751 static char *parse_args(const char *s
, int *argc
, char *argv
[], int max
, int *trailingwhitespace
)
1759 *trailingwhitespace
= 0;
1760 if (s
== NULL
) /* invalid, though! */
1762 /* make a copy to store the parsed string */
1763 if (!(dup
= ast_strdup(s
)))
1767 /* scan the original string copying into cur when needed */
1770 ast_log(LOG_WARNING
, "Too many arguments, truncating at %s\n", s
);
1773 if (*s
== '"' && !escaped
) {
1775 if (quoted
&& whitespace
) {
1776 /* start a quoted string from previous whitespace: new argument */
1780 } else if ((*s
== ' ' || *s
== '\t') && !(quoted
|| escaped
)) {
1781 /* If we are not already in whitespace, and not in a quoted string or
1782 processing an escape sequence, and just entered whitespace, then
1783 finalize the previous argument and remember that we are in whitespace
1789 } else if (*s
== '\\' && !escaped
) {
1793 /* we leave whitespace, and are not quoted. So it's a new argument */
1801 /* Null terminate */
1803 /* XXX put a NULL in the last argument, because some functions that take
1804 * the array may want a null-terminated array.
1805 * argc still reflects the number of non-NULL entries.
1809 *trailingwhitespace
= whitespace
;
1813 /*! \brief Return the number of unique matches for the generator */
1814 int ast_cli_generatornummatches(const char *text
, const char *word
)
1816 int matches
= 0, i
= 0;
1817 char *buf
= NULL
, *oldbuf
= NULL
;
1819 while ((buf
= ast_cli_generator(text
, word
, i
++))) {
1820 if (!oldbuf
|| strcmp(buf
,oldbuf
))
1831 char **ast_cli_completion_matches(const char *text
, const char *word
)
1833 char **match_list
= NULL
, *retstr
, *prevstr
;
1834 size_t match_list_len
, max_equal
, which
, i
;
1837 /* leave entry 0 free for the longest common substring */
1839 while ((retstr
= ast_cli_generator(text
, word
, matches
)) != NULL
) {
1840 if (matches
+ 1 >= match_list_len
) {
1841 match_list_len
<<= 1;
1842 if (!(match_list
= ast_realloc(match_list
, match_list_len
* sizeof(*match_list
))))
1845 match_list
[++matches
] = retstr
;
1849 return match_list
; /* NULL */
1851 /* Find the longest substring that is common to all results
1852 * (it is a candidate for completion), and store a copy in entry 0.
1854 prevstr
= match_list
[1];
1855 max_equal
= strlen(prevstr
);
1856 for (which
= 2; which
<= matches
; which
++) {
1857 for (i
= 0; i
< max_equal
&& toupper(prevstr
[i
]) == toupper(match_list
[which
][i
]); i
++)
1862 if (!(retstr
= ast_malloc(max_equal
+ 1)))
1865 ast_copy_string(retstr
, match_list
[1], max_equal
+ 1);
1866 match_list
[0] = retstr
;
1868 /* ensure that the array is NULL terminated */
1869 if (matches
+ 1 >= match_list_len
) {
1870 if (!(match_list
= ast_realloc(match_list
, (match_list_len
+ 1) * sizeof(*match_list
))))
1873 match_list
[matches
+ 1] = NULL
;
1878 static char *__ast_cli_generator(const char *text
, const char *word
, int state
, int lock
)
1880 char *argv
[AST_MAX_ARGS
];
1881 struct ast_cli_entry
*e
;
1882 struct cli_iterator i
= { NULL
, NULL
};
1883 int x
= 0, argindex
, matchlen
;
1886 char matchstr
[80] = "";
1888 char *dup
= parse_args(text
, &x
, argv
, sizeof(argv
) / sizeof(argv
[0]), &tws
);
1890 if (!dup
) /* error */
1892 argindex
= (!ast_strlen_zero(word
) && x
>0) ? x
-1 : x
;
1893 /* rebuild the command, ignore tws */
1894 ast_join(matchstr
, sizeof(matchstr
)-1, argv
);
1895 matchlen
= strlen(matchstr
);
1897 strcat(matchstr
, " "); /* XXX */
1902 AST_LIST_LOCK(&helpers
);
1903 while( !ret
&& (e
= cli_next(&i
)) ) {
1904 int lc
= strlen(e
->_full_cmd
);
1905 if (e
->_full_cmd
[0] != '_' && lc
> 0 && matchlen
<= lc
&&
1906 !strncasecmp(matchstr
, e
->_full_cmd
, matchlen
)) {
1907 /* Found initial part, return a copy of the next word... */
1908 if (e
->cmda
[argindex
] && ++matchnum
> state
)
1909 ret
= strdup(e
->cmda
[argindex
]); /* we need a malloced string */
1910 } else if (e
->generator
&& !strncasecmp(matchstr
, e
->_full_cmd
, lc
) && matchstr
[lc
] < 33) {
1911 /* We have a command in its entirity within us -- theoretically only one
1912 command can have this occur */
1913 ret
= e
->generator(matchstr
, word
, argindex
, state
);
1917 AST_LIST_UNLOCK(&helpers
);
1922 char *ast_cli_generator(const char *text
, const char *word
, int state
)
1924 return __ast_cli_generator(text
, word
, state
, 1);
1927 int ast_cli_command(int fd
, const char *s
)
1929 char *argv
[AST_MAX_ARGS
];
1930 struct ast_cli_entry
*e
;
1935 if (!(dup
= parse_args(s
, &x
, argv
, sizeof(argv
) / sizeof(argv
[0]), &tws
)))
1938 /* We need at least one entry, or ignore */
1940 AST_LIST_LOCK(&helpers
);
1941 e
= find_cli(argv
, 0);
1944 AST_LIST_UNLOCK(&helpers
);
1946 switch(e
->handler(fd
, x
, argv
)) {
1947 case RESULT_SHOWUSAGE
:
1949 ast_cli(fd
, "%s", e
->usage
);
1951 ast_cli(fd
, "Invalid usage, but no usage information available.\n");
1954 AST_LIST_LOCK(&helpers
);
1955 if (e
->deprecated
== 1) {
1956 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
);
1959 AST_LIST_UNLOCK(&helpers
);
1963 ast_cli(fd
, "No such command '%s' (type 'help' for help)\n", find_best(argv
));
1965 ast_atomic_fetchadd_int(&e
->inuse
, -1);