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
[])
405 if (argc
< 2 || argc
> 3)
406 return RESULT_SHOWUSAGE
;
407 if (argc
== 3 && !strcasecmp(argv
[2], "silent"))
408 ast_console_toggle_mute(fd
, 1);
410 ast_console_toggle_mute(fd
, 0);
411 return RESULT_SUCCESS
;
414 static int handle_unload_deprecated(int fd
, int argc
, char *argv
[])
417 int force
= AST_FORCE_SOFT
;
419 return RESULT_SHOWUSAGE
;
420 for (x
= 1; x
< argc
; x
++) {
421 if (argv
[x
][0] == '-') {
424 force
= AST_FORCE_FIRM
;
427 force
= AST_FORCE_HARD
;
430 return RESULT_SHOWUSAGE
;
432 } else if (x
!= argc
- 1)
433 return RESULT_SHOWUSAGE
;
434 else if (ast_unload_resource(argv
[x
], force
)) {
435 ast_cli(fd
, "Unable to unload resource %s\n", argv
[x
]);
436 return RESULT_FAILURE
;
439 return RESULT_SUCCESS
;
442 static int handle_unload(int fd
, int argc
, char *argv
[])
445 int force
= AST_FORCE_SOFT
;
447 return RESULT_SHOWUSAGE
;
448 for (x
= 2; x
< argc
; x
++) {
449 if (argv
[x
][0] == '-') {
452 force
= AST_FORCE_FIRM
;
455 force
= AST_FORCE_HARD
;
458 return RESULT_SHOWUSAGE
;
460 } else if (x
!= argc
- 1)
461 return RESULT_SHOWUSAGE
;
462 else if (ast_unload_resource(argv
[x
], force
)) {
463 ast_cli(fd
, "Unable to unload resource %s\n", argv
[x
]);
464 return RESULT_FAILURE
;
467 return RESULT_SUCCESS
;
470 #define MODLIST_FORMAT "%-30s %-40.40s %-10d\n"
471 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
473 AST_MUTEX_DEFINE_STATIC(climodentrylock
);
474 static int climodentryfd
= -1;
476 static int modlist_modentry(const char *module
, const char *description
, int usecnt
, const char *like
)
478 /* Comparing the like with the module */
479 if (strcasestr(module
, like
) ) {
480 ast_cli(climodentryfd
, MODLIST_FORMAT
, module
, description
, usecnt
);
486 static char modlist_help
[] =
487 "Usage: module show [like <keyword>]\n"
488 " Shows Asterisk modules currently in use, and usage statistics.\n";
490 static char uptime_help
[] =
491 "Usage: core show uptime [seconds]\n"
492 " Shows Asterisk uptime information.\n"
493 " The seconds word returns the uptime in seconds only.\n";
495 static void print_uptimestr(int fd
, time_t timeval
, const char *prefix
, int printsec
)
497 int x
; /* the main part - years, weeks, etc. */
498 char timestr
[256]="", *s
= timestr
;
499 size_t maxbytes
= sizeof(timestr
);
502 #define MINUTE (SECOND*60)
503 #define HOUR (MINUTE*60)
504 #define DAY (HOUR*24)
506 #define YEAR (DAY*365)
507 #define ESS(x) ((x == 1) ? "" : "s") /* plural suffix */
508 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
509 if (timeval
< 0) /* invalid, nothing to show */
511 if (printsec
) { /* plain seconds output */
512 ast_build_string(&s
, &maxbytes
, "%lu", (u_long
)timeval
);
513 timeval
= 0; /* bypass the other cases */
515 if (timeval
> YEAR
) {
516 x
= (timeval
/ YEAR
);
517 timeval
-= (x
* YEAR
);
518 ast_build_string(&s
, &maxbytes
, "%d year%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
520 if (timeval
> WEEK
) {
521 x
= (timeval
/ WEEK
);
522 timeval
-= (x
* WEEK
);
523 ast_build_string(&s
, &maxbytes
, "%d week%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
527 timeval
-= (x
* DAY
);
528 ast_build_string(&s
, &maxbytes
, "%d day%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
530 if (timeval
> HOUR
) {
531 x
= (timeval
/ HOUR
);
532 timeval
-= (x
* HOUR
);
533 ast_build_string(&s
, &maxbytes
, "%d hour%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
535 if (timeval
> MINUTE
) {
536 x
= (timeval
/ MINUTE
);
537 timeval
-= (x
* MINUTE
);
538 ast_build_string(&s
, &maxbytes
, "%d minute%s%s ", x
, ESS(x
),NEEDCOMMA(timeval
));
542 ast_build_string(&s
, &maxbytes
, "%d second%s ", x
, ESS(x
));
543 if (timestr
[0] != '\0')
544 ast_cli(fd
, "%s: %s\n", prefix
, timestr
);
547 static int handle_showuptime_deprecated(int fd
, int argc
, char *argv
[])
549 /* 'show uptime [seconds]' */
550 time_t curtime
= time(NULL
);
551 int printsec
= (argc
== 3 && !strcasecmp(argv
[2],"seconds"));
553 if (argc
!= 2 && !printsec
)
554 return RESULT_SHOWUSAGE
;
556 print_uptimestr(fd
, curtime
- ast_startuptime
, "System uptime", printsec
);
557 if (ast_lastreloadtime
)
558 print_uptimestr(fd
, curtime
- ast_lastreloadtime
, "Last reload", printsec
);
559 return RESULT_SUCCESS
;
562 static int handle_showuptime(int fd
, int argc
, char *argv
[])
564 /* 'core show uptime [seconds]' */
565 time_t curtime
= time(NULL
);
566 int printsec
= (argc
== 4 && !strcasecmp(argv
[3],"seconds"));
568 if (argc
!= 3 && !printsec
)
569 return RESULT_SHOWUSAGE
;
571 print_uptimestr(fd
, curtime
- ast_startuptime
, "System uptime", printsec
);
572 if (ast_lastreloadtime
)
573 print_uptimestr(fd
, curtime
- ast_lastreloadtime
, "Last reload", printsec
);
574 return RESULT_SUCCESS
;
577 static int handle_modlist(int fd
, int argc
, char *argv
[])
581 return RESULT_SHOWUSAGE
;
582 else if (argc
>= 4) {
583 if (strcmp(argv
[2],"like"))
584 return RESULT_SHOWUSAGE
;
588 ast_mutex_lock(&climodentrylock
);
589 climodentryfd
= fd
; /* global, protected by climodentrylock */
590 ast_cli(fd
, MODLIST_FORMAT2
, "Module", "Description", "Use Count");
591 ast_cli(fd
,"%d modules loaded\n", ast_update_module_list(modlist_modentry
, like
));
593 ast_mutex_unlock(&climodentrylock
);
594 return RESULT_SUCCESS
;
596 #undef MODLIST_FORMAT
597 #undef MODLIST_FORMAT2
599 #define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
600 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
601 #define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n"
602 #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"
603 #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"
605 static int handle_chanlist_deprecated(int fd
, int argc
, char *argv
[])
607 struct ast_channel
*c
= NULL
;
608 char durbuf
[10] = "-";
612 int durh
, durm
, durs
;
613 int numchans
= 0, concise
= 0, verbose
= 0;
615 concise
= (argc
== 3 && (!strcasecmp(argv
[2],"concise")));
616 verbose
= (argc
== 3 && (!strcasecmp(argv
[2],"verbose")));
618 if (argc
< 2 || argc
> 3 || (argc
== 3 && !concise
&& !verbose
))
619 return RESULT_SHOWUSAGE
;
621 if (!concise
&& !verbose
)
622 ast_cli(fd
, FORMAT_STRING2
, "Channel", "Location", "State", "Application(Data)");
624 ast_cli(fd
, VERBOSE_FORMAT_STRING2
, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
625 "CallerID", "Duration", "Accountcode", "BridgedTo");
627 while ((c
= ast_channel_walk_locked(c
)) != NULL
) {
628 struct ast_channel
*bc
= ast_bridged_channel(c
);
629 if ((concise
|| verbose
) && c
->cdr
&& !ast_tvzero(c
->cdr
->start
)) {
630 duration
= (int)(ast_tvdiff_ms(ast_tvnow(), c
->cdr
->start
) / 1000);
632 durh
= duration
/ 3600;
633 durm
= (duration
% 3600) / 60;
634 durs
= duration
% 60;
635 snprintf(durbuf
, sizeof(durbuf
), "%02d:%02d:%02d", durh
, durm
, durs
);
637 snprintf(durbuf
, sizeof(durbuf
), "%d", duration
);
643 ast_cli(fd
, CONCISE_FORMAT_STRING
, c
->name
, c
->context
, c
->exten
, c
->priority
, ast_state2str(c
->_state
),
644 c
->appl
? c
->appl
: "(None)",
645 S_OR(c
->data
, ""), /* XXX different from verbose ? */
646 S_OR(c
->cid
.cid_num
, ""),
647 S_OR(c
->accountcode
, ""),
650 bc
? bc
->name
: "(None)");
651 } else if (verbose
) {
652 ast_cli(fd
, VERBOSE_FORMAT_STRING
, c
->name
, c
->context
, c
->exten
, c
->priority
, ast_state2str(c
->_state
),
653 c
->appl
? c
->appl
: "(None)",
654 c
->data
? S_OR(c
->data
, "(Empty)" ): "(None)",
655 S_OR(c
->cid
.cid_num
, ""),
657 S_OR(c
->accountcode
, ""),
658 bc
? bc
->name
: "(None)");
660 if (!ast_strlen_zero(c
->context
) && !ast_strlen_zero(c
->exten
))
661 snprintf(locbuf
, sizeof(locbuf
), "%s@%s:%d", c
->exten
, c
->context
, c
->priority
);
663 strcpy(locbuf
, "(None)");
665 snprintf(appdata
, sizeof(appdata
), "%s(%s)", c
->appl
, c
->data
? c
->data
: "");
667 strcpy(appdata
, "(None)");
668 ast_cli(fd
, FORMAT_STRING
, c
->name
, locbuf
, ast_state2str(c
->_state
), appdata
);
671 ast_channel_unlock(c
);
674 ast_cli(fd
, "%d active channel%s\n", numchans
, ESS(numchans
));
676 ast_cli(fd
, "%d of %d max active call%s (%5.2f%% of capacity)\n",
677 ast_active_calls(), option_maxcalls
, ESS(ast_active_calls()),
678 ((double)ast_active_calls() / (double)option_maxcalls
) * 100.0);
680 ast_cli(fd
, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
682 return RESULT_SUCCESS
;
685 static int handle_chanlist(int fd
, int argc
, char *argv
[])
687 struct ast_channel
*c
= NULL
;
688 char durbuf
[10] = "-";
692 int durh
, durm
, durs
;
693 int numchans
= 0, concise
= 0, verbose
= 0;
695 concise
= (argc
== 4 && (!strcasecmp(argv
[3],"concise")));
696 verbose
= (argc
== 4 && (!strcasecmp(argv
[3],"verbose")));
698 if (argc
< 3 || argc
> 4 || (argc
== 4 && !concise
&& !verbose
))
699 return RESULT_SHOWUSAGE
;
701 if (!concise
&& !verbose
)
702 ast_cli(fd
, FORMAT_STRING2
, "Channel", "Location", "State", "Application(Data)");
704 ast_cli(fd
, VERBOSE_FORMAT_STRING2
, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
705 "CallerID", "Duration", "Accountcode", "BridgedTo");
707 while ((c
= ast_channel_walk_locked(c
)) != NULL
) {
708 struct ast_channel
*bc
= ast_bridged_channel(c
);
709 if ((concise
|| verbose
) && c
->cdr
&& !ast_tvzero(c
->cdr
->start
)) {
710 duration
= (int)(ast_tvdiff_ms(ast_tvnow(), c
->cdr
->start
) / 1000);
712 durh
= duration
/ 3600;
713 durm
= (duration
% 3600) / 60;
714 durs
= duration
% 60;
715 snprintf(durbuf
, sizeof(durbuf
), "%02d:%02d:%02d", durh
, durm
, durs
);
717 snprintf(durbuf
, sizeof(durbuf
), "%d", duration
);
723 ast_cli(fd
, CONCISE_FORMAT_STRING
, c
->name
, c
->context
, c
->exten
, c
->priority
, ast_state2str(c
->_state
),
724 c
->appl
? c
->appl
: "(None)",
725 S_OR(c
->data
, ""), /* XXX different from verbose ? */
726 S_OR(c
->cid
.cid_num
, ""),
727 S_OR(c
->accountcode
, ""),
730 bc
? bc
->name
: "(None)");
731 } else if (verbose
) {
732 ast_cli(fd
, VERBOSE_FORMAT_STRING
, c
->name
, c
->context
, c
->exten
, c
->priority
, ast_state2str(c
->_state
),
733 c
->appl
? c
->appl
: "(None)",
734 c
->data
? S_OR(c
->data
, "(Empty)" ): "(None)",
735 S_OR(c
->cid
.cid_num
, ""),
737 S_OR(c
->accountcode
, ""),
738 bc
? bc
->name
: "(None)");
740 if (!ast_strlen_zero(c
->context
) && !ast_strlen_zero(c
->exten
))
741 snprintf(locbuf
, sizeof(locbuf
), "%s@%s:%d", c
->exten
, c
->context
, c
->priority
);
743 strcpy(locbuf
, "(None)");
745 snprintf(appdata
, sizeof(appdata
), "%s(%s)", c
->appl
, c
->data
? c
->data
: "");
747 strcpy(appdata
, "(None)");
748 ast_cli(fd
, FORMAT_STRING
, c
->name
, locbuf
, ast_state2str(c
->_state
), appdata
);
751 ast_channel_unlock(c
);
754 ast_cli(fd
, "%d active channel%s\n", numchans
, ESS(numchans
));
756 ast_cli(fd
, "%d of %d max active call%s (%5.2f%% of capacity)\n",
757 ast_active_calls(), option_maxcalls
, ESS(ast_active_calls()),
758 ((double)ast_active_calls() / (double)option_maxcalls
) * 100.0);
760 ast_cli(fd
, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
762 return RESULT_SUCCESS
;
766 #undef FORMAT_STRING2
767 #undef CONCISE_FORMAT_STRING
768 #undef VERBOSE_FORMAT_STRING
769 #undef VERBOSE_FORMAT_STRING2
771 static char showchan_help
[] =
772 "Usage: core show channel <channel>\n"
773 " Shows lots of information about the specified channel.\n";
775 static char debugchan_help
[] =
776 "Usage: core set debug channel <channel> [off]\n"
777 " Enables/disables debugging on a specific channel.\n";
779 static char commandcomplete_help
[] =
780 "Usage: _command complete \"<line>\" text state\n"
781 " This function is used internally to help with command completion and should.\n"
782 " never be called by the user directly.\n";
784 static char commandnummatches_help
[] =
785 "Usage: _command nummatches \"<line>\" text \n"
786 " This function is used internally to help with command completion and should.\n"
787 " never be called by the user directly.\n";
789 static char commandmatchesarray_help
[] =
790 "Usage: _command matchesarray \"<line>\" text \n"
791 " This function is used internally to help with command completion and should.\n"
792 " never be called by the user directly.\n";
794 static int handle_softhangup(int fd
, int argc
, char *argv
[])
796 struct ast_channel
*c
=NULL
;
798 return RESULT_SHOWUSAGE
;
799 c
= ast_get_channel_by_name_locked(argv
[2]);
801 ast_cli(fd
, "Requested Hangup on channel '%s'\n", c
->name
);
802 ast_softhangup(c
, AST_SOFTHANGUP_EXPLICIT
);
803 ast_channel_unlock(c
);
805 ast_cli(fd
, "%s is not a known channel\n", argv
[2]);
806 return RESULT_SUCCESS
;
809 static char *__ast_cli_generator(const char *text
, const char *word
, int state
, int lock
);
811 static int handle_commandmatchesarray(int fd
, int argc
, char *argv
[])
820 return RESULT_SHOWUSAGE
;
821 if (!(buf
= ast_malloc(buflen
)))
822 return RESULT_FAILURE
;
824 matches
= ast_cli_completion_matches(argv
[2], argv
[3]);
826 for (x
=0; matches
[x
]; x
++) {
827 matchlen
= strlen(matches
[x
]) + 1;
828 if (len
+ matchlen
>= buflen
) {
829 buflen
+= matchlen
* 3;
831 if (!(buf
= ast_realloc(obuf
, buflen
)))
832 /* Memory allocation failure... Just free old buffer and be done */
836 len
+= sprintf( buf
+ len
, "%s ", matches
[x
]);
844 ast_cli(fd
, "%s%s",buf
, AST_CLI_COMPLETE_EOF
);
847 ast_cli(fd
, "NULL\n");
849 return RESULT_SUCCESS
;
854 static int handle_commandnummatches(int fd
, int argc
, char *argv
[])
859 return RESULT_SHOWUSAGE
;
861 matches
= ast_cli_generatornummatches(argv
[2], argv
[3]);
863 ast_cli(fd
, "%d", matches
);
865 return RESULT_SUCCESS
;
868 static int handle_commandcomplete(int fd
, int argc
, char *argv
[])
873 return RESULT_SHOWUSAGE
;
874 buf
= __ast_cli_generator(argv
[2], argv
[3], atoi(argv
[4]), 0);
879 ast_cli(fd
, "NULL\n");
880 return RESULT_SUCCESS
;
883 static int handle_debugchan_deprecated(int fd
, int argc
, char *argv
[])
885 struct ast_channel
*c
=NULL
;
888 /* 'debug channel {all|chan_id}' */
890 return RESULT_SHOWUSAGE
;
892 is_all
= !strcasecmp("all", argv
[2]);
894 global_fin
|= DEBUGCHAN_FLAG
;
895 global_fout
|= DEBUGCHAN_FLAG
;
896 c
= ast_channel_walk_locked(NULL
);
898 c
= ast_get_channel_by_name_locked(argv
[2]);
900 ast_cli(fd
, "No such channel %s\n", argv
[2]);
903 if (!(c
->fin
& DEBUGCHAN_FLAG
) || !(c
->fout
& DEBUGCHAN_FLAG
)) {
904 c
->fin
|= DEBUGCHAN_FLAG
;
905 c
->fout
|= DEBUGCHAN_FLAG
;
906 ast_cli(fd
, "Debugging enabled on channel %s\n", c
->name
);
908 ast_channel_unlock(c
);
911 c
= ast_channel_walk_locked(c
);
913 ast_cli(fd
, "Debugging on new channels is enabled\n");
914 return RESULT_SUCCESS
;
917 static int handle_core_set_debug_channel(int fd
, int argc
, char *argv
[])
919 struct ast_channel
*c
= NULL
;
920 int is_all
, is_off
= 0;
922 /* 'core set debug channel {all|chan_id}' */
923 if (argc
== 6 && strcmp(argv
[5], "off") == 0)
926 return RESULT_SHOWUSAGE
;
928 is_all
= !strcasecmp("all", argv
[4]);
931 global_fin
&= ~DEBUGCHAN_FLAG
;
932 global_fout
&= ~DEBUGCHAN_FLAG
;
934 global_fin
|= DEBUGCHAN_FLAG
;
935 global_fout
|= DEBUGCHAN_FLAG
;
937 c
= ast_channel_walk_locked(NULL
);
939 c
= ast_get_channel_by_name_locked(argv
[4]);
941 ast_cli(fd
, "No such channel %s\n", argv
[4]);
944 if (!(c
->fin
& DEBUGCHAN_FLAG
) || !(c
->fout
& DEBUGCHAN_FLAG
)) {
946 c
->fin
&= ~DEBUGCHAN_FLAG
;
947 c
->fout
&= ~DEBUGCHAN_FLAG
;
949 c
->fin
|= DEBUGCHAN_FLAG
;
950 c
->fout
|= DEBUGCHAN_FLAG
;
952 ast_cli(fd
, "Debugging %s on channel %s\n", is_off
? "disabled" : "enabled", c
->name
);
954 ast_channel_unlock(c
);
957 c
= ast_channel_walk_locked(c
);
959 ast_cli(fd
, "Debugging on new channels is %s\n", is_off
? "disabled" : "enabled");
960 return RESULT_SUCCESS
;
963 static int handle_nodebugchan_deprecated(int fd
, int argc
, char *argv
[])
965 struct ast_channel
*c
=NULL
;
967 /* 'no debug channel {all|chan_id}' */
969 return RESULT_SHOWUSAGE
;
970 is_all
= !strcasecmp("all", argv
[3]);
972 global_fin
&= ~DEBUGCHAN_FLAG
;
973 global_fout
&= ~DEBUGCHAN_FLAG
;
974 c
= ast_channel_walk_locked(NULL
);
976 c
= ast_get_channel_by_name_locked(argv
[3]);
978 ast_cli(fd
, "No such channel %s\n", argv
[3]);
981 if ((c
->fin
& DEBUGCHAN_FLAG
) || (c
->fout
& DEBUGCHAN_FLAG
)) {
982 c
->fin
&= ~DEBUGCHAN_FLAG
;
983 c
->fout
&= ~DEBUGCHAN_FLAG
;
984 ast_cli(fd
, "Debugging disabled on channel %s\n", c
->name
);
986 ast_channel_unlock(c
);
989 c
= ast_channel_walk_locked(c
);
991 ast_cli(fd
, "Debugging on new channels is disabled\n");
992 return RESULT_SUCCESS
;
995 static int handle_showchan_deprecated(int fd
, int argc
, char *argv
[])
997 struct ast_channel
*c
=NULL
;
1001 char nf
[256], wf
[256], rf
[256];
1002 long elapsed_seconds
=0;
1003 int hour
=0, min
=0, sec
=0;
1006 return RESULT_SHOWUSAGE
;
1008 c
= ast_get_channel_by_name_locked(argv
[2]);
1010 ast_cli(fd
, "%s is not a known channel\n", argv
[2]);
1011 return RESULT_SUCCESS
;
1014 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
1015 hour
= elapsed_seconds
/ 3600;
1016 min
= (elapsed_seconds
% 3600) / 60;
1017 sec
= elapsed_seconds
% 60;
1018 snprintf(cdrtime
, sizeof(cdrtime
), "%dh%dm%ds", hour
, min
, sec
);
1020 strcpy(cdrtime
, "N/A");
1027 " Caller ID Name: %s\n"
1028 " DNID Digits: %s\n"
1031 " NativeFormats: %s\n"
1032 " WriteFormat: %s\n"
1034 " WriteTranscode: %s\n"
1035 " ReadTranscode: %s\n"
1036 "1st File Descriptor: %d\n"
1037 " Frames in: %d%s\n"
1038 " Frames out: %d%s\n"
1039 " Time to Hangup: %ld\n"
1040 " Elapsed Time: %s\n"
1041 " Direct Bridge: %s\n"
1042 "Indirect Bridge: %s\n"
1047 " Call Group: %llu\n"
1048 " Pickup Group: %llu\n"
1049 " Application: %s\n"
1051 " Blocking in: %s\n",
1052 c
->name
, c
->tech
->type
, c
->uniqueid
,
1053 S_OR(c
->cid
.cid_num
, "(N/A)"),
1054 S_OR(c
->cid
.cid_name
, "(N/A)"),
1055 S_OR(c
->cid
.cid_dnid
, "(N/A)"), ast_state2str(c
->_state
), c
->_state
, c
->rings
,
1056 ast_getformatname_multiple(nf
, sizeof(nf
), c
->nativeformats
),
1057 ast_getformatname_multiple(wf
, sizeof(wf
), c
->writeformat
),
1058 ast_getformatname_multiple(rf
, sizeof(rf
), c
->readformat
),
1059 c
->writetrans
? "Yes" : "No",
1060 c
->readtrans
? "Yes" : "No",
1062 c
->fin
& ~DEBUGCHAN_FLAG
, (c
->fin
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
1063 c
->fout
& ~DEBUGCHAN_FLAG
, (c
->fout
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
1064 (long)c
->whentohangup
,
1065 cdrtime
, c
->_bridge
? c
->_bridge
->name
: "<none>", ast_bridged_channel(c
) ? ast_bridged_channel(c
)->name
: "<none>",
1066 c
->context
, c
->exten
, c
->priority
, c
->callgroup
, c
->pickupgroup
, ( c
->appl
? c
->appl
: "(N/A)" ),
1067 ( c
-> data
? S_OR(c
->data
, "(Empty)") : "(None)"),
1068 (ast_test_flag(c
, AST_FLAG_BLOCKING
) ? c
->blockproc
: "(Not Blocking)"));
1070 if(pbx_builtin_serialize_variables(c
,buf
,sizeof(buf
)))
1071 ast_cli(fd
," Variables:\n%s\n",buf
);
1072 if(c
->cdr
&& ast_cdr_serialize_variables(c
->cdr
,buf
, sizeof(buf
), '=', '\n', 1))
1073 ast_cli(fd
," CDR Variables:\n%s\n",buf
);
1075 ast_channel_unlock(c
);
1076 return RESULT_SUCCESS
;
1079 static int handle_showchan(int fd
, int argc
, char *argv
[])
1081 struct ast_channel
*c
=NULL
;
1085 char nf
[256], wf
[256], rf
[256];
1086 long elapsed_seconds
=0;
1087 int hour
=0, min
=0, sec
=0;
1090 return RESULT_SHOWUSAGE
;
1092 c
= ast_get_channel_by_name_locked(argv
[3]);
1094 ast_cli(fd
, "%s is not a known channel\n", argv
[3]);
1095 return RESULT_SUCCESS
;
1098 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
1099 hour
= elapsed_seconds
/ 3600;
1100 min
= (elapsed_seconds
% 3600) / 60;
1101 sec
= elapsed_seconds
% 60;
1102 snprintf(cdrtime
, sizeof(cdrtime
), "%dh%dm%ds", hour
, min
, sec
);
1104 strcpy(cdrtime
, "N/A");
1111 " Caller ID Name: %s\n"
1112 " DNID Digits: %s\n"
1115 " NativeFormats: %s\n"
1116 " WriteFormat: %s\n"
1118 " WriteTranscode: %s\n"
1119 " ReadTranscode: %s\n"
1120 "1st File Descriptor: %d\n"
1121 " Frames in: %d%s\n"
1122 " Frames out: %d%s\n"
1123 " Time to Hangup: %ld\n"
1124 " Elapsed Time: %s\n"
1125 " Direct Bridge: %s\n"
1126 "Indirect Bridge: %s\n"
1131 " Call Group: %llu\n"
1132 " Pickup Group: %llu\n"
1133 " Application: %s\n"
1135 " Blocking in: %s\n",
1136 c
->name
, c
->tech
->type
, c
->uniqueid
,
1137 S_OR(c
->cid
.cid_num
, "(N/A)"),
1138 S_OR(c
->cid
.cid_name
, "(N/A)"),
1139 S_OR(c
->cid
.cid_dnid
, "(N/A)"), ast_state2str(c
->_state
), c
->_state
, c
->rings
,
1140 ast_getformatname_multiple(nf
, sizeof(nf
), c
->nativeformats
),
1141 ast_getformatname_multiple(wf
, sizeof(wf
), c
->writeformat
),
1142 ast_getformatname_multiple(rf
, sizeof(rf
), c
->readformat
),
1143 c
->writetrans
? "Yes" : "No",
1144 c
->readtrans
? "Yes" : "No",
1146 c
->fin
& ~DEBUGCHAN_FLAG
, (c
->fin
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
1147 c
->fout
& ~DEBUGCHAN_FLAG
, (c
->fout
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
1148 (long)c
->whentohangup
,
1149 cdrtime
, c
->_bridge
? c
->_bridge
->name
: "<none>", ast_bridged_channel(c
) ? ast_bridged_channel(c
)->name
: "<none>",
1150 c
->context
, c
->exten
, c
->priority
, c
->callgroup
, c
->pickupgroup
, ( c
->appl
? c
->appl
: "(N/A)" ),
1151 ( c
-> data
? S_OR(c
->data
, "(Empty)") : "(None)"),
1152 (ast_test_flag(c
, AST_FLAG_BLOCKING
) ? c
->blockproc
: "(Not Blocking)"));
1154 if(pbx_builtin_serialize_variables(c
,buf
,sizeof(buf
)))
1155 ast_cli(fd
," Variables:\n%s\n",buf
);
1156 if(c
->cdr
&& ast_cdr_serialize_variables(c
->cdr
,buf
, sizeof(buf
), '=', '\n', 1))
1157 ast_cli(fd
," CDR Variables:\n%s\n",buf
);
1159 ast_channel_unlock(c
);
1160 return RESULT_SUCCESS
;
1164 * helper function to generate CLI matches from a fixed set of values.
1165 * A NULL word is acceptable.
1167 char *ast_cli_complete(const char *word
, char *const choices
[], int state
)
1169 int i
, which
= 0, len
;
1170 len
= ast_strlen_zero(word
) ? 0 : strlen(word
);
1172 for (i
= 0; choices
[i
]; i
++) {
1173 if ((!len
|| !strncasecmp(word
, choices
[i
], len
)) && ++which
> state
)
1174 return ast_strdup(choices
[i
]);
1179 static char *complete_show_channels_deprecated(const char *line
, const char *word
, int pos
, int state
)
1181 static char *choices
[] = { "concise", "verbose", NULL
};
1183 return (pos
!= 2) ? NULL
: ast_cli_complete(word
, choices
, state
);
1186 static char *complete_show_channels(const char *line
, const char *word
, int pos
, int state
)
1188 static char *choices
[] = { "concise", "verbose", NULL
};
1190 return (pos
!= 3) ? NULL
: ast_cli_complete(word
, choices
, state
);
1193 char *ast_complete_channels(const char *line
, const char *word
, int pos
, int state
, int rpos
)
1195 struct ast_channel
*c
= NULL
;
1198 char notfound
= '\0';
1199 char *ret
= ¬found
; /* so NULL can break the loop */
1204 wordlen
= strlen(word
);
1206 while (ret
== ¬found
&& (c
= ast_channel_walk_locked(c
))) {
1207 if (!strncasecmp(word
, c
->name
, wordlen
) && ++which
> state
)
1208 ret
= ast_strdup(c
->name
);
1209 ast_channel_unlock(c
);
1211 return ret
== ¬found
? NULL
: ret
;
1214 static char *complete_ch_3(const char *line
, const char *word
, int pos
, int state
)
1216 return ast_complete_channels(line
, word
, pos
, state
, 2);
1219 static char *complete_ch_4(const char *line
, const char *word
, int pos
, int state
)
1221 return ast_complete_channels(line
, word
, pos
, state
, 3);
1224 static char *complete_ch_5(const char *line
, const char *word
, int pos
, int state
)
1226 return ast_complete_channels(line
, word
, pos
, state
, 4);
1229 static char *complete_mod_2(const char *line
, const char *word
, int pos
, int state
)
1231 return ast_module_helper(line
, word
, pos
, state
, 1, 1);
1234 static char *complete_mod_3_nr(const char *line
, const char *word
, int pos
, int state
)
1236 return ast_module_helper(line
, word
, pos
, state
, 2, 0);
1239 static char *complete_mod_3(const char *line
, const char *word
, int pos
, int state
)
1241 return ast_module_helper(line
, word
, pos
, state
, 2, 1);
1244 static char *complete_mod_4(const char *line
, const char *word
, int pos
, int state
)
1246 return ast_module_helper(line
, word
, pos
, state
, 3, 0);
1249 static char *complete_fn_2(const char *line
, const char *word
, int pos
, int state
)
1252 char filename
[PATH_MAX
];
1258 ast_copy_string(filename
, word
, sizeof(filename
));
1260 snprintf(filename
, sizeof(filename
), "%s/%s", ast_config_AST_MODULE_DIR
, word
);
1262 c
= d
= filename_completion_function(filename
, state
);
1264 if (c
&& word
[0] != '/')
1265 c
+= (strlen(ast_config_AST_MODULE_DIR
) + 1);
1273 static char *complete_fn_3(const char *line
, const char *word
, int pos
, int state
)
1276 char filename
[PATH_MAX
];
1282 ast_copy_string(filename
, word
, sizeof(filename
));
1284 snprintf(filename
, sizeof(filename
), "%s/%s", ast_config_AST_MODULE_DIR
, word
);
1286 c
= d
= filename_completion_function(filename
, state
);
1288 if (c
&& word
[0] != '/')
1289 c
+= (strlen(ast_config_AST_MODULE_DIR
) + 1);
1297 static int group_show_channels(int fd
, int argc
, char *argv
[])
1299 #define FORMAT_STRING "%-25s %-20s %-20s\n"
1301 struct ast_group_info
*gi
= NULL
;
1304 int havepattern
= 0;
1306 if (argc
< 3 || argc
> 4)
1307 return RESULT_SHOWUSAGE
;
1310 if (regcomp(®exbuf
, argv
[3], REG_EXTENDED
| REG_NOSUB
))
1311 return RESULT_SHOWUSAGE
;
1315 ast_cli(fd
, FORMAT_STRING
, "Channel", "Group", "Category");
1317 ast_app_group_list_lock();
1319 gi
= ast_app_group_list_head();
1321 if (!havepattern
|| !regexec(®exbuf
, gi
->group
, 0, NULL
, 0)) {
1322 ast_cli(fd
, FORMAT_STRING
, gi
->chan
->name
, gi
->group
, (ast_strlen_zero(gi
->category
) ? "(default)" : gi
->category
));
1325 gi
= AST_LIST_NEXT(gi
, list
);
1328 ast_app_group_list_unlock();
1333 ast_cli(fd
, "%d active channel%s\n", numchans
, (numchans
!= 1) ? "s" : "");
1334 return RESULT_SUCCESS
;
1335 #undef FORMAT_STRING
1338 static int handle_help(int fd
, int argc
, char *argv
[]);
1340 static char * complete_help(const char *text
, const char *word
, int pos
, int state
)
1342 /* skip first 4 or 5 chars, "help "*/
1343 int l
= strlen(text
);
1348 /* XXX watch out, should stop to the non-generator parts */
1349 return __ast_cli_generator(text
, word
, state
, 0);
1352 /* XXX Nothing in this array can currently be deprecated...
1353 You have to change the way find_cli works in order to remove this array
1354 I recommend doing this eventually...
1356 static struct ast_cli_entry builtins
[] = {
1357 /* Keep alphabetized, with longer matches first (example: abcd before abc) */
1358 { { "_command", "complete", NULL
},
1359 handle_commandcomplete
, "Command complete",
1360 commandcomplete_help
},
1362 { { "_command", "nummatches", NULL
},
1363 handle_commandnummatches
, "Returns number of command matches",
1364 commandnummatches_help
},
1366 { { "_command", "matchesarray", NULL
},
1367 handle_commandmatchesarray
, "Returns command matches array",
1368 commandmatchesarray_help
},
1370 { { NULL
}, NULL
, NULL
, NULL
}
1373 static struct ast_cli_entry cli_debug_channel_deprecated
= {
1374 { "debug", "channel", NULL
},
1375 handle_debugchan_deprecated
, NULL
,
1376 NULL
, complete_ch_3
};
1378 static struct ast_cli_entry cli_debug_level_deprecated
= {
1379 { "debug", "level", NULL
},
1380 handle_debuglevel_deprecated
, NULL
,
1383 static struct ast_cli_entry cli_set_debug_deprecated
= {
1384 { "set", "debug", NULL
},
1385 handle_set_debug_deprecated
, NULL
,
1386 NULL
, NULL
, &cli_debug_level_deprecated
};
1388 static struct ast_cli_entry cli_set_verbose_deprecated
= {
1389 { "set", "verbose", NULL
},
1390 handle_set_verbose_deprecated
, NULL
,
1393 static struct ast_cli_entry cli_show_channel_deprecated
= {
1394 { "show", "channel", NULL
},
1395 handle_showchan_deprecated
, NULL
,
1396 NULL
, complete_ch_3
};
1398 static struct ast_cli_entry cli_show_channels_deprecated
= {
1399 { "show", "channels", NULL
},
1400 handle_chanlist_deprecated
, NULL
,
1401 NULL
, complete_show_channels_deprecated
};
1403 static struct ast_cli_entry cli_show_modules_deprecated
= {
1404 { "show", "modules", NULL
},
1405 handle_modlist
, NULL
,
1408 static struct ast_cli_entry cli_show_modules_like_deprecated
= {
1409 { "show", "modules", "like", NULL
},
1410 handle_modlist
, NULL
,
1411 NULL
, complete_mod_4
};
1413 static struct ast_cli_entry cli_module_load_deprecated
= {
1415 handle_load_deprecated
, NULL
,
1416 NULL
, complete_fn_2
};
1418 static struct ast_cli_entry cli_module_reload_deprecated
= {
1420 handle_reload_deprecated
, NULL
,
1421 NULL
, complete_mod_2
};
1423 static struct ast_cli_entry cli_module_unload_deprecated
= {
1425 handle_unload_deprecated
, NULL
,
1426 NULL
, complete_mod_2
};
1428 static struct ast_cli_entry cli_show_uptime_deprecated
= {
1429 { "show", "uptime", NULL
},
1430 handle_showuptime_deprecated
, "Show uptime information",
1433 static struct ast_cli_entry cli_cli
[] = {
1434 /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
1435 { { "no", "debug", "channel", NULL
},
1436 handle_nodebugchan_deprecated
, NULL
,
1437 NULL
, complete_ch_4
},
1439 { { "core", "show", "channels", NULL
},
1440 handle_chanlist
, "Display information on channels",
1441 chanlist_help
, complete_show_channels
, &cli_show_channels_deprecated
},
1443 { { "core", "show", "channel", NULL
},
1444 handle_showchan
, "Display information on a specific channel",
1445 showchan_help
, complete_ch_4
, &cli_show_channel_deprecated
},
1447 { { "core", "set", "debug", "channel", NULL
},
1448 handle_core_set_debug_channel
, "Enable/disable debugging on a channel",
1449 debugchan_help
, complete_ch_5
, &cli_debug_channel_deprecated
},
1451 { { "core", "set", "debug", NULL
},
1452 handle_set_debug
, "Set level of debug chattiness",
1453 debug_help
, NULL
, &cli_set_debug_deprecated
},
1455 { { "core", "set", "debug", "off", NULL
},
1456 handle_nodebug
, "Turns off debug chattiness",
1459 { { "core", "set", "verbose", NULL
},
1460 handle_verbose
, "Set level of verboseness",
1461 verbose_help
, NULL
, &cli_set_verbose_deprecated
},
1463 { { "group", "show", "channels", NULL
},
1464 group_show_channels
, "Display active channels with group(s)",
1465 group_show_channels_help
},
1468 handle_help
, "Display help list, or specific help on a command",
1469 help_help
, complete_help
},
1471 { { "logger", "mute", NULL
},
1472 handle_logger_mute
, "Toggle logging output to a console",
1475 { { "module", "show", NULL
},
1476 handle_modlist
, "List modules and info",
1477 modlist_help
, NULL
, &cli_show_modules_deprecated
},
1479 { { "module", "show", "like", NULL
},
1480 handle_modlist
, "List modules and info",
1481 modlist_help
, complete_mod_4
, &cli_show_modules_like_deprecated
},
1483 { { "module", "load", NULL
},
1484 handle_load
, "Load a module by name",
1485 load_help
, complete_fn_3
, &cli_module_load_deprecated
},
1487 { { "module", "reload", NULL
},
1488 handle_reload
, "Reload configuration",
1489 reload_help
, complete_mod_3
, &cli_module_reload_deprecated
},
1491 { { "module", "unload", NULL
},
1492 handle_unload
, "Unload a module by name",
1493 unload_help
, complete_mod_3_nr
, &cli_module_unload_deprecated
},
1495 { { "core", "show", "uptime", NULL
},
1496 handle_showuptime
, "Show uptime information",
1497 uptime_help
, NULL
, &cli_show_uptime_deprecated
},
1499 { { "soft", "hangup", NULL
},
1500 handle_softhangup
, "Request a hangup on a given channel",
1501 softhangup_help
, complete_ch_3
},
1504 /*! \brief initialize the _full_cmd string in * each of the builtins. */
1505 void ast_builtins_init(void)
1507 struct ast_cli_entry
*e
;
1509 for (e
= builtins
; e
->cmda
[0] != NULL
; e
++) {
1511 ast_join(buf
, sizeof(buf
), e
->cmda
);
1512 e
->_full_cmd
= strdup(buf
);
1514 ast_log(LOG_WARNING
, "-- cannot allocate <%s>\n", buf
);
1517 ast_cli_register_multiple(cli_cli
, sizeof(cli_cli
) / sizeof(struct ast_cli_entry
));
1521 * We have two sets of commands: builtins are stored in a
1522 * NULL-terminated array of ast_cli_entry, whereas external
1523 * commands are in a list.
1524 * When navigating, we need to keep two pointers and get
1525 * the next one in lexicographic order. For the purpose,
1526 * we use a structure.
1529 struct cli_iterator
{
1530 struct ast_cli_entry
*builtins
;
1531 struct ast_cli_entry
*helpers
;
1534 static struct ast_cli_entry
*cli_next(struct cli_iterator
*i
)
1536 struct ast_cli_entry
*e
;
1538 if (i
->builtins
== NULL
&& i
->helpers
== NULL
) {
1540 i
->builtins
= builtins
;
1541 i
->helpers
= AST_LIST_FIRST(&helpers
);
1543 e
= i
->builtins
; /* temporary */
1544 if (!e
->cmda
[0] || (i
->helpers
&&
1545 strcmp(i
->helpers
->_full_cmd
, e
->_full_cmd
) < 0)) {
1549 i
->helpers
= AST_LIST_NEXT(e
, list
);
1550 } else { /* use builtin. e is already set */
1551 (i
->builtins
)++; /* move to next */
1557 * \brief locate a cli command in the 'helpers' list (which must be locked).
1558 * exact has 3 values:
1559 * 0 returns if the search key is equal or longer than the entry.
1560 * -1 true if the mismatch is on the last word XXX not true!
1561 * 1 true only on complete, exact match.
1563 static struct ast_cli_entry
*find_cli(char *const cmds
[], int match_type
)
1565 int matchlen
= -1; /* length of longest match so far */
1566 struct ast_cli_entry
*cand
= NULL
, *e
=NULL
;
1567 struct cli_iterator i
= { NULL
, NULL
};
1569 while( (e
= cli_next(&i
)) ) {
1571 for (y
= 0 ; cmds
[y
] && e
->cmda
[y
]; y
++) {
1572 if (strcasecmp(e
->cmda
[y
], cmds
[y
]))
1575 if (e
->cmda
[y
] == NULL
) { /* no more words in candidate */
1576 if (cmds
[y
] == NULL
) /* this is an exact match, cannot do better */
1578 /* here the search key is longer than the candidate */
1579 if (match_type
!= 0) /* but we look for almost exact match... */
1580 continue; /* so we skip this one. */
1581 /* otherwise we like it (case 0) */
1582 } else { /* still words in candidate */
1583 if (cmds
[y
] == NULL
) /* search key is shorter, not good */
1585 /* if we get here, both words exist but there is a mismatch */
1586 if (match_type
== 0) /* not the one we look for */
1588 if (match_type
== 1) /* not the one we look for */
1590 if (cmds
[y
+1] != NULL
|| e
->cmda
[y
+1] != NULL
) /* not the one we look for */
1592 /* we are in case match_type == -1 and mismatch on last word */
1594 if (y
> matchlen
) { /* remember the candidate */
1599 return e
? e
: cand
;
1602 static char *find_best(char *argv
[])
1604 static char cmdline
[80];
1606 /* See how close we get, then print the candidate */
1607 char *myargv
[AST_MAX_CMD_LEN
];
1608 for (x
=0;x
<AST_MAX_CMD_LEN
;x
++)
1610 AST_LIST_LOCK(&helpers
);
1611 for (x
=0;argv
[x
];x
++) {
1612 myargv
[x
] = argv
[x
];
1613 if (!find_cli(myargv
, -1))
1616 AST_LIST_UNLOCK(&helpers
);
1617 ast_join(cmdline
, sizeof(cmdline
), myargv
);
1621 static int __ast_cli_unregister(struct ast_cli_entry
*e
, struct ast_cli_entry
*ed
)
1623 if (e
->deprecate_cmd
) {
1624 __ast_cli_unregister(e
->deprecate_cmd
, e
);
1627 ast_log(LOG_WARNING
, "Can't remove command that is in use\n");
1629 AST_LIST_LOCK(&helpers
);
1630 AST_LIST_REMOVE(&helpers
, e
, list
);
1631 AST_LIST_UNLOCK(&helpers
);
1637 static int __ast_cli_register(struct ast_cli_entry
*e
, struct ast_cli_entry
*ed
)
1639 struct ast_cli_entry
*cur
;
1643 ast_join(fulle
, sizeof(fulle
), e
->cmda
);
1644 AST_LIST_LOCK(&helpers
);
1646 if (find_cli(e
->cmda
, 1)) {
1647 ast_log(LOG_WARNING
, "Command '%s' already registered (or something close enough)\n", fulle
);
1650 e
->_full_cmd
= ast_strdup(fulle
);
1656 e
->summary
= ed
->summary
;
1657 e
->usage
= ed
->usage
;
1658 /* XXX If command A deprecates command B, and command B deprecates command C...
1659 Do we want to show command A or command B when telling the user to use new syntax?
1660 This currently would show command A.
1661 To show command B, you just need to always use ed->_full_cmd.
1663 e
->_deprecated_by
= S_OR(ed
->_deprecated_by
, ed
->_full_cmd
);
1669 AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers
, cur
, list
) {
1670 int len
= strlen(cur
->_full_cmd
);
1673 if (strncasecmp(fulle
, cur
->_full_cmd
, len
) < 0) {
1674 AST_LIST_INSERT_BEFORE_CURRENT(&helpers
, e
, list
);
1678 AST_LIST_TRAVERSE_SAFE_END
;
1681 AST_LIST_INSERT_TAIL(&helpers
, e
, list
);
1682 ret
= 0; /* success */
1685 AST_LIST_UNLOCK(&helpers
);
1687 if (e
->deprecate_cmd
) {
1688 /* This command deprecates another command. Register that one also. */
1689 __ast_cli_register(e
->deprecate_cmd
, e
);
1695 /* wrapper function, so we can unregister deprecated commands recursively */
1696 int ast_cli_unregister(struct ast_cli_entry
*e
)
1698 return __ast_cli_unregister(e
, NULL
);
1701 /* wrapper function, so we can register deprecated commands recursively */
1702 int ast_cli_register(struct ast_cli_entry
*e
)
1704 return __ast_cli_register(e
, NULL
);
1708 * register/unregister an array of entries.
1710 void ast_cli_register_multiple(struct ast_cli_entry
*e
, int len
)
1714 for (i
= 0; i
< len
; i
++)
1715 ast_cli_register(e
+ i
);
1718 void ast_cli_unregister_multiple(struct ast_cli_entry
*e
, int len
)
1722 for (i
= 0; i
< len
; i
++)
1723 ast_cli_unregister(e
+ i
);
1727 /*! \brief helper for help_workhorse and final part of handle_help
1728 * if locked = 0 it's just help_workhorse, otherwise assume the
1729 * list is already locked.
1731 static int help1(int fd
, char *match
[], int locked
)
1733 char matchstr
[80] = "";
1734 struct ast_cli_entry
*e
;
1737 struct cli_iterator i
= { NULL
, NULL
};
1740 ast_join(matchstr
, sizeof(matchstr
), match
);
1741 len
= strlen(matchstr
);
1744 AST_LIST_LOCK(&helpers
);
1745 while ( (e
= cli_next(&i
)) ) {
1746 /* Hide commands that start with '_' */
1747 if (e
->_full_cmd
[0] == '_')
1749 /* Hide commands that are marked as deprecated. */
1752 if (match
&& strncasecmp(matchstr
, e
->_full_cmd
, len
))
1754 ast_cli(fd
, "%25.25s %s\n", e
->_full_cmd
, S_OR(e
->summary
, ""));
1758 AST_LIST_UNLOCK(&helpers
);
1759 if (!found
&& matchstr
[0])
1760 ast_cli(fd
, "No such command '%s'.\n", matchstr
);
1761 return RESULT_SUCCESS
;
1764 static int help_workhorse(int fd
, char *match
[])
1766 return help1(fd
, match
, 0 /* do not print errors */);
1769 static int handle_help(int fd
, int argc
, char *argv
[])
1772 struct ast_cli_entry
*e
;
1773 int res
= RESULT_SUCCESS
;
1776 return RESULT_SHOWUSAGE
;
1778 return help_workhorse(fd
, NULL
);
1780 AST_LIST_LOCK(&helpers
);
1781 e
= find_cli(argv
+ 1, 1); /* try exact match first */
1783 res
= help1(fd
, argv
+ 1, 1 /* locked */);
1784 AST_LIST_UNLOCK(&helpers
);
1788 ast_cli(fd
, "%s", e
->usage
);
1790 ast_join(fullcmd
, sizeof(fullcmd
), argv
+1);
1791 ast_cli(fd
, "No help text available for '%s'.\n", fullcmd
);
1793 AST_LIST_UNLOCK(&helpers
);
1797 static char *parse_args(const char *s
, int *argc
, char *argv
[], int max
, int *trailingwhitespace
)
1805 *trailingwhitespace
= 0;
1806 if (s
== NULL
) /* invalid, though! */
1808 /* make a copy to store the parsed string */
1809 if (!(dup
= ast_strdup(s
)))
1813 /* scan the original string copying into cur when needed */
1816 ast_log(LOG_WARNING
, "Too many arguments, truncating at %s\n", s
);
1819 if (*s
== '"' && !escaped
) {
1821 if (quoted
&& whitespace
) {
1822 /* start a quoted string from previous whitespace: new argument */
1826 } else if ((*s
== ' ' || *s
== '\t') && !(quoted
|| escaped
)) {
1827 /* If we are not already in whitespace, and not in a quoted string or
1828 processing an escape sequence, and just entered whitespace, then
1829 finalize the previous argument and remember that we are in whitespace
1835 } else if (*s
== '\\' && !escaped
) {
1839 /* we leave whitespace, and are not quoted. So it's a new argument */
1847 /* Null terminate */
1849 /* XXX put a NULL in the last argument, because some functions that take
1850 * the array may want a null-terminated array.
1851 * argc still reflects the number of non-NULL entries.
1855 *trailingwhitespace
= whitespace
;
1859 /*! \brief Return the number of unique matches for the generator */
1860 int ast_cli_generatornummatches(const char *text
, const char *word
)
1862 int matches
= 0, i
= 0;
1863 char *buf
= NULL
, *oldbuf
= NULL
;
1865 while ((buf
= ast_cli_generator(text
, word
, i
++))) {
1866 if (!oldbuf
|| strcmp(buf
,oldbuf
))
1877 char **ast_cli_completion_matches(const char *text
, const char *word
)
1879 char **match_list
= NULL
, *retstr
, *prevstr
;
1880 size_t match_list_len
, max_equal
, which
, i
;
1883 /* leave entry 0 free for the longest common substring */
1885 while ((retstr
= ast_cli_generator(text
, word
, matches
)) != NULL
) {
1886 if (matches
+ 1 >= match_list_len
) {
1887 match_list_len
<<= 1;
1888 if (!(match_list
= ast_realloc(match_list
, match_list_len
* sizeof(*match_list
))))
1891 match_list
[++matches
] = retstr
;
1895 return match_list
; /* NULL */
1897 /* Find the longest substring that is common to all results
1898 * (it is a candidate for completion), and store a copy in entry 0.
1900 prevstr
= match_list
[1];
1901 max_equal
= strlen(prevstr
);
1902 for (which
= 2; which
<= matches
; which
++) {
1903 for (i
= 0; i
< max_equal
&& toupper(prevstr
[i
]) == toupper(match_list
[which
][i
]); i
++)
1908 if (!(retstr
= ast_malloc(max_equal
+ 1)))
1911 ast_copy_string(retstr
, match_list
[1], max_equal
+ 1);
1912 match_list
[0] = retstr
;
1914 /* ensure that the array is NULL terminated */
1915 if (matches
+ 1 >= match_list_len
) {
1916 if (!(match_list
= ast_realloc(match_list
, (match_list_len
+ 1) * sizeof(*match_list
))))
1919 match_list
[matches
+ 1] = NULL
;
1924 static char *__ast_cli_generator(const char *text
, const char *word
, int state
, int lock
)
1926 char *argv
[AST_MAX_ARGS
];
1927 struct ast_cli_entry
*e
;
1928 struct cli_iterator i
= { NULL
, NULL
};
1929 int x
= 0, argindex
, matchlen
;
1932 char matchstr
[80] = "";
1934 char *dup
= parse_args(text
, &x
, argv
, sizeof(argv
) / sizeof(argv
[0]), &tws
);
1936 if (!dup
) /* error */
1938 argindex
= (!ast_strlen_zero(word
) && x
>0) ? x
-1 : x
;
1939 /* rebuild the command, ignore tws */
1940 ast_join(matchstr
, sizeof(matchstr
)-1, argv
);
1941 matchlen
= strlen(matchstr
);
1943 strcat(matchstr
, " "); /* XXX */
1948 AST_LIST_LOCK(&helpers
);
1949 while( !ret
&& (e
= cli_next(&i
)) ) {
1950 int lc
= strlen(e
->_full_cmd
);
1951 if (e
->_full_cmd
[0] != '_' && lc
> 0 && matchlen
<= lc
&&
1952 !strncasecmp(matchstr
, e
->_full_cmd
, matchlen
)) {
1953 /* Found initial part, return a copy of the next word... */
1954 if (e
->cmda
[argindex
] && ++matchnum
> state
)
1955 ret
= strdup(e
->cmda
[argindex
]); /* we need a malloced string */
1956 } else if (e
->generator
&& !strncasecmp(matchstr
, e
->_full_cmd
, lc
) && matchstr
[lc
] < 33) {
1957 /* We have a command in its entirity within us -- theoretically only one
1958 command can have this occur */
1959 ret
= e
->generator(matchstr
, word
, argindex
, state
);
1963 AST_LIST_UNLOCK(&helpers
);
1968 char *ast_cli_generator(const char *text
, const char *word
, int state
)
1970 return __ast_cli_generator(text
, word
, state
, 1);
1973 int ast_cli_command(int fd
, const char *s
)
1975 char *argv
[AST_MAX_ARGS
];
1976 struct ast_cli_entry
*e
;
1981 if (!(dup
= parse_args(s
, &x
, argv
, sizeof(argv
) / sizeof(argv
[0]), &tws
)))
1984 /* We need at least one entry, or ignore */
1986 AST_LIST_LOCK(&helpers
);
1987 e
= find_cli(argv
, 0);
1990 AST_LIST_UNLOCK(&helpers
);
1992 switch(e
->handler(fd
, x
, argv
)) {
1993 case RESULT_SHOWUSAGE
:
1995 ast_cli(fd
, "%s", e
->usage
);
1997 ast_cli(fd
, "Invalid usage, but no usage information available.\n");
1998 AST_LIST_LOCK(&helpers
);
2000 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
);
2001 AST_LIST_UNLOCK(&helpers
);
2004 AST_LIST_LOCK(&helpers
);
2005 if (e
->deprecated
== 1) {
2006 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
);
2009 AST_LIST_UNLOCK(&helpers
);
2013 ast_cli(fd
, "No such command '%s' (type 'help %s' for other possible commands)\n", s
, find_best(argv
));
2015 ast_atomic_fetchadd_int(&e
->inuse
, -1);
2022 int ast_cli_command_multiple(int fd
, size_t size
, const char *s
)
2025 int x
, y
= 0, count
= 0;
2027 for (x
= 0; x
< size
; x
++) {
2031 ast_cli_command(fd
, cmd
);