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 static int handle_chanlist(int fd
, int argc
, char *argv
[])
583 #define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
584 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
585 #define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n"
586 #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"
587 #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"
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
== 4 && (!strcasecmp(argv
[3],"concise")));
598 verbose
= (argc
== 4 && (!strcasecmp(argv
[3],"verbose")));
600 if (argc
< 3 || argc
> 4 || (argc
== 4 && !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 #undef FORMAT_STRING2
668 #undef CONCISE_FORMAT_STRING
669 #undef VERBOSE_FORMAT_STRING
670 #undef VERBOSE_FORMAT_STRING2
673 static char showchan_help
[] =
674 "Usage: core show channel <channel>\n"
675 " Shows lots of information about the specified channel.\n";
677 static char debugchan_help
[] =
678 "Usage: core set debug channel <channel> [off]\n"
679 " Enables/disables debugging on a specific channel.\n";
681 static char commandcomplete_help
[] =
682 "Usage: _command complete \"<line>\" text state\n"
683 " This function is used internally to help with command completion and should.\n"
684 " never be called by the user directly.\n";
686 static char commandnummatches_help
[] =
687 "Usage: _command nummatches \"<line>\" text \n"
688 " This function is used internally to help with command completion and should.\n"
689 " never be called by the user directly.\n";
691 static char commandmatchesarray_help
[] =
692 "Usage: _command matchesarray \"<line>\" text \n"
693 " This function is used internally to help with command completion and should.\n"
694 " never be called by the user directly.\n";
696 static int handle_softhangup(int fd
, int argc
, char *argv
[])
698 struct ast_channel
*c
=NULL
;
700 return RESULT_SHOWUSAGE
;
701 c
= ast_get_channel_by_name_locked(argv
[2]);
703 ast_cli(fd
, "Requested Hangup on channel '%s'\n", c
->name
);
704 ast_softhangup(c
, AST_SOFTHANGUP_EXPLICIT
);
705 ast_channel_unlock(c
);
707 ast_cli(fd
, "%s is not a known channel\n", argv
[2]);
708 return RESULT_SUCCESS
;
711 static char *__ast_cli_generator(const char *text
, const char *word
, int state
, int lock
);
713 static int handle_commandmatchesarray(int fd
, int argc
, char *argv
[])
722 return RESULT_SHOWUSAGE
;
723 if (!(buf
= ast_malloc(buflen
)))
724 return RESULT_FAILURE
;
726 matches
= ast_cli_completion_matches(argv
[2], argv
[3]);
728 for (x
=0; matches
[x
]; x
++) {
729 matchlen
= strlen(matches
[x
]) + 1;
730 if (len
+ matchlen
>= buflen
) {
731 buflen
+= matchlen
* 3;
733 if (!(buf
= ast_realloc(obuf
, buflen
)))
734 /* Memory allocation failure... Just free old buffer and be done */
738 len
+= sprintf( buf
+ len
, "%s ", matches
[x
]);
746 ast_cli(fd
, "%s%s",buf
, AST_CLI_COMPLETE_EOF
);
749 ast_cli(fd
, "NULL\n");
751 return RESULT_SUCCESS
;
756 static int handle_commandnummatches(int fd
, int argc
, char *argv
[])
761 return RESULT_SHOWUSAGE
;
763 matches
= ast_cli_generatornummatches(argv
[2], argv
[3]);
765 ast_cli(fd
, "%d", matches
);
767 return RESULT_SUCCESS
;
770 static int handle_commandcomplete(int fd
, int argc
, char *argv
[])
775 return RESULT_SHOWUSAGE
;
776 buf
= __ast_cli_generator(argv
[2], argv
[3], atoi(argv
[4]), 0);
781 ast_cli(fd
, "NULL\n");
782 return RESULT_SUCCESS
;
785 static int handle_debugchan_deprecated(int fd
, int argc
, char *argv
[])
787 struct ast_channel
*c
=NULL
;
790 /* 'debug channel {all|chan_id}' */
792 return RESULT_SHOWUSAGE
;
794 is_all
= !strcasecmp("all", argv
[3]);
796 global_fin
|= DEBUGCHAN_FLAG
;
797 global_fout
|= DEBUGCHAN_FLAG
;
798 c
= ast_channel_walk_locked(NULL
);
800 c
= ast_get_channel_by_name_locked(argv
[3]);
802 ast_cli(fd
, "No such channel %s\n", argv
[3]);
805 if (!(c
->fin
& DEBUGCHAN_FLAG
) || !(c
->fout
& DEBUGCHAN_FLAG
)) {
806 c
->fin
|= DEBUGCHAN_FLAG
;
807 c
->fout
|= DEBUGCHAN_FLAG
;
808 ast_cli(fd
, "Debugging enabled on channel %s\n", c
->name
);
810 ast_channel_unlock(c
);
813 c
= ast_channel_walk_locked(c
);
815 ast_cli(fd
, "Debugging on new channels is enabled\n");
816 return RESULT_SUCCESS
;
819 static int handle_core_set_debug_channel(int fd
, int argc
, char *argv
[])
821 struct ast_channel
*c
= NULL
;
822 int is_all
, is_off
= 0;
824 /* 'core set debug channel {all|chan_id}' */
825 if (argc
== 6 && strcmp(argv
[5], "off") == 0)
828 return RESULT_SHOWUSAGE
;
830 is_all
= !strcasecmp("all", argv
[4]);
833 global_fin
&= ~DEBUGCHAN_FLAG
;
834 global_fout
&= ~DEBUGCHAN_FLAG
;
836 global_fin
|= DEBUGCHAN_FLAG
;
837 global_fout
|= DEBUGCHAN_FLAG
;
839 c
= ast_channel_walk_locked(NULL
);
841 c
= ast_get_channel_by_name_locked(argv
[4]);
843 ast_cli(fd
, "No such channel %s\n", argv
[4]);
846 if (!(c
->fin
& DEBUGCHAN_FLAG
) || !(c
->fout
& DEBUGCHAN_FLAG
)) {
848 c
->fin
&= ~DEBUGCHAN_FLAG
;
849 c
->fout
&= ~DEBUGCHAN_FLAG
;
851 c
->fin
|= DEBUGCHAN_FLAG
;
852 c
->fout
|= DEBUGCHAN_FLAG
;
854 ast_cli(fd
, "Debugging %s on channel %s\n", is_off
? "disabled" : "enabled", c
->name
);
856 ast_channel_unlock(c
);
859 c
= ast_channel_walk_locked(c
);
861 ast_cli(fd
, "Debugging on new channels is %s\n", is_off
? "disabled" : "enabled");
862 return RESULT_SUCCESS
;
865 static int handle_nodebugchan_deprecated(int fd
, int argc
, char *argv
[])
867 struct ast_channel
*c
=NULL
;
869 /* 'no debug channel {all|chan_id}' */
871 return RESULT_SHOWUSAGE
;
872 is_all
= !strcasecmp("all", argv
[3]);
874 global_fin
&= ~DEBUGCHAN_FLAG
;
875 global_fout
&= ~DEBUGCHAN_FLAG
;
876 c
= ast_channel_walk_locked(NULL
);
878 c
= ast_get_channel_by_name_locked(argv
[3]);
880 ast_cli(fd
, "No such channel %s\n", argv
[3]);
883 if ((c
->fin
& DEBUGCHAN_FLAG
) || (c
->fout
& DEBUGCHAN_FLAG
)) {
884 c
->fin
&= ~DEBUGCHAN_FLAG
;
885 c
->fout
&= ~DEBUGCHAN_FLAG
;
886 ast_cli(fd
, "Debugging disabled on channel %s\n", c
->name
);
888 ast_channel_unlock(c
);
891 c
= ast_channel_walk_locked(c
);
893 ast_cli(fd
, "Debugging on new channels is disabled\n");
894 return RESULT_SUCCESS
;
897 static int handle_showchan(int fd
, int argc
, char *argv
[])
899 struct ast_channel
*c
=NULL
;
903 char nf
[256], wf
[256], rf
[256];
904 long elapsed_seconds
=0;
905 int hour
=0, min
=0, sec
=0;
908 return RESULT_SHOWUSAGE
;
910 c
= ast_get_channel_by_name_locked(argv
[3]);
912 ast_cli(fd
, "%s is not a known channel\n", argv
[3]);
913 return RESULT_SUCCESS
;
916 elapsed_seconds
= now
.tv_sec
- c
->cdr
->start
.tv_sec
;
917 hour
= elapsed_seconds
/ 3600;
918 min
= (elapsed_seconds
% 3600) / 60;
919 sec
= elapsed_seconds
% 60;
920 snprintf(cdrtime
, sizeof(cdrtime
), "%dh%dm%ds", hour
, min
, sec
);
922 strcpy(cdrtime
, "N/A");
929 " Caller ID Name: %s\n"
933 " NativeFormats: %s\n"
936 " WriteTranscode: %s\n"
937 " ReadTranscode: %s\n"
938 "1st File Descriptor: %d\n"
940 " Frames out: %d%s\n"
941 " Time to Hangup: %ld\n"
942 " Elapsed Time: %s\n"
943 " Direct Bridge: %s\n"
944 "Indirect Bridge: %s\n"
949 " Call Group: %llu\n"
950 " Pickup Group: %llu\n"
953 " Blocking in: %s\n",
954 c
->name
, c
->tech
->type
, c
->uniqueid
,
955 S_OR(c
->cid
.cid_num
, "(N/A)"),
956 S_OR(c
->cid
.cid_name
, "(N/A)"),
957 S_OR(c
->cid
.cid_dnid
, "(N/A)"), ast_state2str(c
->_state
), c
->_state
, c
->rings
,
958 ast_getformatname_multiple(nf
, sizeof(nf
), c
->nativeformats
),
959 ast_getformatname_multiple(wf
, sizeof(wf
), c
->writeformat
),
960 ast_getformatname_multiple(rf
, sizeof(rf
), c
->readformat
),
961 c
->writetrans
? "Yes" : "No",
962 c
->readtrans
? "Yes" : "No",
964 c
->fin
& ~DEBUGCHAN_FLAG
, (c
->fin
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
965 c
->fout
& ~DEBUGCHAN_FLAG
, (c
->fout
& DEBUGCHAN_FLAG
) ? " (DEBUGGED)" : "",
966 (long)c
->whentohangup
,
967 cdrtime
, c
->_bridge
? c
->_bridge
->name
: "<none>", ast_bridged_channel(c
) ? ast_bridged_channel(c
)->name
: "<none>",
968 c
->context
, c
->exten
, c
->priority
, c
->callgroup
, c
->pickupgroup
, ( c
->appl
? c
->appl
: "(N/A)" ),
969 ( c
-> data
? S_OR(c
->data
, "(Empty)") : "(None)"),
970 (ast_test_flag(c
, AST_FLAG_BLOCKING
) ? c
->blockproc
: "(Not Blocking)"));
972 if(pbx_builtin_serialize_variables(c
,buf
,sizeof(buf
)))
973 ast_cli(fd
," Variables:\n%s\n",buf
);
974 if(c
->cdr
&& ast_cdr_serialize_variables(c
->cdr
,buf
, sizeof(buf
), '=', '\n', 1))
975 ast_cli(fd
," CDR Variables:\n%s\n",buf
);
977 ast_channel_unlock(c
);
978 return RESULT_SUCCESS
;
982 * helper function to generate CLI matches from a fixed set of values.
983 * A NULL word is acceptable.
985 char *ast_cli_complete(const char *word
, char *const choices
[], int state
)
987 int i
, which
= 0, len
;
988 len
= ast_strlen_zero(word
) ? 0 : strlen(word
);
990 for (i
= 0; choices
[i
]; i
++) {
991 if ((!len
|| !strncasecmp(word
, choices
[i
], len
)) && ++which
> state
)
992 return ast_strdup(choices
[i
]);
997 static char *complete_show_channels(const char *line
, const char *word
, int pos
, int state
)
999 static char *choices
[] = { "concise", "verbose", NULL
};
1001 return (pos
!= 3) ? NULL
: ast_cli_complete(word
, choices
, state
);
1004 char *ast_complete_channels(const char *line
, const char *word
, int pos
, int state
, int rpos
)
1006 struct ast_channel
*c
= NULL
;
1009 char notfound
= '\0';
1010 char *ret
= ¬found
; /* so NULL can break the loop */
1015 wordlen
= strlen(word
);
1017 while (ret
== ¬found
&& (c
= ast_channel_walk_locked(c
))) {
1018 if (!strncasecmp(word
, c
->name
, wordlen
) && ++which
> state
)
1019 ret
= ast_strdup(c
->name
);
1020 ast_channel_unlock(c
);
1022 return ret
== ¬found
? NULL
: ret
;
1025 static char *complete_ch_3(const char *line
, const char *word
, int pos
, int state
)
1027 return ast_complete_channels(line
, word
, pos
, state
, 2);
1030 static char *complete_ch_4(const char *line
, const char *word
, int pos
, int state
)
1032 return ast_complete_channels(line
, word
, pos
, state
, 3);
1035 static char *complete_ch_5(const char *line
, const char *word
, int pos
, int state
)
1037 return ast_complete_channels(line
, word
, pos
, state
, 4);
1040 static char *complete_mod_2(const char *line
, const char *word
, int pos
, int state
)
1042 return ast_module_helper(line
, word
, pos
, state
, 1, 1);
1045 static char *complete_mod_3_nr(const char *line
, const char *word
, int pos
, int state
)
1047 return ast_module_helper(line
, word
, pos
, state
, 2, 0);
1050 static char *complete_mod_3(const char *line
, const char *word
, int pos
, int state
)
1052 return ast_module_helper(line
, word
, pos
, state
, 2, 1);
1055 static char *complete_mod_4(const char *line
, const char *word
, int pos
, int state
)
1057 return ast_module_helper(line
, word
, pos
, state
, 3, 0);
1060 static char *complete_fn(const char *line
, const char *word
, int pos
, int state
)
1069 ast_copy_string(filename
, word
, sizeof(filename
));
1071 snprintf(filename
, sizeof(filename
), "%s/%s", ast_config_AST_MODULE_DIR
, word
);
1073 c
= filename_completion_function(filename
, state
);
1075 if (c
&& word
[0] != '/')
1076 c
+= (strlen(ast_config_AST_MODULE_DIR
) + 1);
1078 return c
? strdup(c
) : c
;
1081 static int group_show_channels(int fd
, int argc
, char *argv
[])
1083 #define FORMAT_STRING "%-25s %-20s %-20s\n"
1085 struct ast_channel
*c
= NULL
;
1087 struct ast_var_t
*current
;
1088 struct varshead
*headp
;
1090 int havepattern
= 0;
1092 if (argc
< 3 || argc
> 4)
1093 return RESULT_SHOWUSAGE
;
1096 if (regcomp(®exbuf
, argv
[3], REG_EXTENDED
| REG_NOSUB
))
1097 return RESULT_SHOWUSAGE
;
1101 ast_cli(fd
, FORMAT_STRING
, "Channel", "Group", "Category");
1102 while ( (c
= ast_channel_walk_locked(c
)) != NULL
) {
1104 AST_LIST_TRAVERSE(headp
,current
,entries
) {
1105 if (!strncmp(ast_var_name(current
), GROUP_CATEGORY_PREFIX
"_", strlen(GROUP_CATEGORY_PREFIX
) + 1)) {
1106 if (!havepattern
|| !regexec(®exbuf
, ast_var_value(current
), 0, NULL
, 0)) {
1107 ast_cli(fd
, FORMAT_STRING
, c
->name
, ast_var_value(current
),
1108 (ast_var_name(current
) + strlen(GROUP_CATEGORY_PREFIX
) + 1));
1111 } else if (!strcmp(ast_var_name(current
), GROUP_CATEGORY_PREFIX
)) {
1112 if (!havepattern
|| !regexec(®exbuf
, ast_var_value(current
), 0, NULL
, 0)) {
1113 ast_cli(fd
, FORMAT_STRING
, c
->name
, ast_var_value(current
), "(default)");
1119 ast_channel_unlock(c
);
1125 ast_cli(fd
, "%d active channel%s\n", numchans
, (numchans
!= 1) ? "s" : "");
1126 return RESULT_SUCCESS
;
1127 #undef FORMAT_STRING
1130 static int handle_help(int fd
, int argc
, char *argv
[]);
1132 static char * complete_help(const char *text
, const char *word
, int pos
, int state
)
1134 /* skip first 4 or 5 chars, "help "*/
1135 int l
= strlen(text
);
1140 /* XXX watch out, should stop to the non-generator parts */
1141 return __ast_cli_generator(text
, word
, state
, 0);
1144 /* XXX Nothing in this array can currently be deprecated...
1145 You have to change the way find_cli works in order to remove this array
1146 I recommend doing this eventually...
1148 static struct ast_cli_entry builtins
[] = {
1149 /* Keep alphabetized, with longer matches first (example: abcd before abc) */
1150 { { "_command", "complete", NULL
},
1151 handle_commandcomplete
, "Command complete",
1152 commandcomplete_help
},
1154 { { "_command", "nummatches", NULL
},
1155 handle_commandnummatches
, "Returns number of command matches",
1156 commandnummatches_help
},
1158 { { "_command", "matchesarray", NULL
},
1159 handle_commandmatchesarray
, "Returns command matches array",
1160 commandmatchesarray_help
},
1162 { { NULL
}, NULL
, NULL
, NULL
}
1165 static struct ast_cli_entry cli_debug_channel_deprecated
= {
1166 { "debug", "channel", NULL
},
1167 handle_debugchan_deprecated
, NULL
,
1168 NULL
, complete_ch_3
};
1170 static struct ast_cli_entry cli_debug_level_deprecated
= {
1171 { "debug", "level", NULL
},
1172 handle_debuglevel_deprecated
, NULL
,
1175 static struct ast_cli_entry cli_set_debug_deprecated
= {
1176 { "set", "debug", NULL
},
1177 handle_set_debug_deprecated
, NULL
,
1178 NULL
, NULL
, &cli_debug_level_deprecated
};
1180 static struct ast_cli_entry cli_set_verbose_deprecated
= {
1181 { "set", "verbose", NULL
},
1182 handle_set_verbose_deprecated
, NULL
,
1185 static struct ast_cli_entry cli_show_channel_deprecated
= {
1186 { "show", "channel", NULL
},
1187 handle_showchan
, NULL
,
1188 NULL
, complete_ch_3
};
1190 static struct ast_cli_entry cli_show_channels_deprecated
= {
1191 { "show", "channels", NULL
},
1192 handle_chanlist
, NULL
,
1193 NULL
, complete_show_channels
};
1195 static struct ast_cli_entry cli_show_modules_deprecated
= {
1196 { "show", "modules", NULL
},
1197 handle_modlist
, NULL
,
1200 static struct ast_cli_entry cli_show_modules_like_deprecated
= {
1201 { "show", "modules", "like", NULL
},
1202 handle_modlist
, NULL
,
1203 NULL
, complete_mod_4
};
1205 static struct ast_cli_entry cli_module_load_deprecated
= {
1207 handle_load_deprecated
, NULL
,
1208 NULL
, complete_fn
};
1210 static struct ast_cli_entry cli_module_reload_deprecated
= {
1212 handle_reload_deprecated
, NULL
,
1213 NULL
, complete_mod_2
};
1215 static struct ast_cli_entry cli_module_unload_deprecated
= {
1217 handle_unload_deprecated
, NULL
,
1218 NULL
, complete_mod_2
};
1220 static struct ast_cli_entry cli_cli
[] = {
1221 /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
1222 { { "no", "debug", "channel", NULL
},
1223 handle_nodebugchan_deprecated
, NULL
,
1224 NULL
, complete_ch_4
},
1226 { { "core", "show", "channels", NULL
},
1227 handle_chanlist
, "Display information on channels",
1228 chanlist_help
, complete_show_channels
, &cli_show_channels_deprecated
},
1230 { { "core", "show", "channel", NULL
},
1231 handle_showchan
, "Display information on a specific channel",
1232 showchan_help
, complete_ch_4
, &cli_show_channel_deprecated
},
1234 { { "core", "set", "debug", "channel", NULL
},
1235 handle_core_set_debug_channel
, "Enable/disable debugging on a channel",
1236 debugchan_help
, complete_ch_5
, &cli_debug_channel_deprecated
},
1238 { { "core", "set", "debug", NULL
},
1239 handle_set_debug
, "Set level of debug chattiness",
1240 debug_help
, NULL
, &cli_set_debug_deprecated
},
1242 { { "core", "set", "debug", "off", NULL
},
1243 handle_nodebug
, "Turns off debug chattiness",
1246 { { "core", "set", "verbose", NULL
},
1247 handle_verbose
, "Set level of verboseness",
1248 verbose_help
, NULL
, &cli_set_verbose_deprecated
},
1250 { { "group", "show", "channels", NULL
},
1251 group_show_channels
, "Display active channels with group(s)",
1252 group_show_channels_help
},
1255 handle_help
, "Display help list, or specific help on a command",
1256 help_help
, complete_help
},
1258 { { "logger", "mute", NULL
},
1259 handle_logger_mute
, "Toggle logging output to a console",
1262 { { "module", "show", NULL
},
1263 handle_modlist
, "List modules and info",
1264 modlist_help
, NULL
, &cli_show_modules_deprecated
},
1266 { { "module", "show", "like", NULL
},
1267 handle_modlist
, "List modules and info",
1268 modlist_help
, complete_mod_4
, &cli_show_modules_like_deprecated
},
1270 { { "module", "load", NULL
},
1271 handle_load
, "Load a module by name",
1272 load_help
, complete_fn
, &cli_module_load_deprecated
},
1274 { { "module", "reload", NULL
},
1275 handle_reload
, "Reload configuration",
1276 reload_help
, complete_mod_3
, &cli_module_reload_deprecated
},
1278 { { "module", "unload", NULL
},
1279 handle_unload
, "Unload a module by name",
1280 unload_help
, complete_mod_3_nr
, &cli_module_unload_deprecated
},
1282 { { "core", "show", "uptime", NULL
},
1283 handle_showuptime
, "Show uptime information",
1286 { { "soft", "hangup", NULL
},
1287 handle_softhangup
, "Request a hangup on a given channel",
1288 softhangup_help
, complete_ch_3
},
1291 /*! \brief initialize the _full_cmd string in * each of the builtins. */
1292 void ast_builtins_init(void)
1294 struct ast_cli_entry
*e
;
1296 for (e
= builtins
; e
->cmda
[0] != NULL
; e
++) {
1298 ast_join(buf
, sizeof(buf
), e
->cmda
);
1299 e
->_full_cmd
= strdup(buf
);
1301 ast_log(LOG_WARNING
, "-- cannot allocate <%s>\n", buf
);
1304 ast_cli_register_multiple(cli_cli
, sizeof(cli_cli
) / sizeof(struct ast_cli_entry
));
1308 * We have two sets of commands: builtins are stored in a
1309 * NULL-terminated array of ast_cli_entry, whereas external
1310 * commands are in a list.
1311 * When navigating, we need to keep two pointers and get
1312 * the next one in lexicographic order. For the purpose,
1313 * we use a structure.
1316 struct cli_iterator
{
1317 struct ast_cli_entry
*builtins
;
1318 struct ast_cli_entry
*helpers
;
1321 static struct ast_cli_entry
*cli_next(struct cli_iterator
*i
)
1323 struct ast_cli_entry
*e
;
1325 if (i
->builtins
== NULL
&& i
->helpers
== NULL
) {
1327 i
->builtins
= builtins
;
1328 i
->helpers
= AST_LIST_FIRST(&helpers
);
1330 e
= i
->builtins
; /* temporary */
1331 if (!e
->cmda
[0] || (i
->helpers
&&
1332 strcmp(i
->helpers
->_full_cmd
, e
->_full_cmd
) < 0)) {
1336 i
->helpers
= AST_LIST_NEXT(e
, list
);
1337 } else { /* use builtin. e is already set */
1338 (i
->builtins
)++; /* move to next */
1344 * \brief locate a cli command in the 'helpers' list (which must be locked).
1345 * exact has 3 values:
1346 * 0 returns if the search key is equal or longer than the entry.
1347 * -1 true if the mismatch is on the last word XXX not true!
1348 * 1 true only on complete, exact match.
1350 static struct ast_cli_entry
*find_cli(char *const cmds
[], int match_type
)
1352 int matchlen
= -1; /* length of longest match so far */
1353 struct ast_cli_entry
*cand
= NULL
, *e
=NULL
;
1354 struct cli_iterator i
= { NULL
, NULL
};
1356 while( (e
= cli_next(&i
)) ) {
1358 for (y
= 0 ; cmds
[y
] && e
->cmda
[y
]; y
++) {
1359 if (strcasecmp(e
->cmda
[y
], cmds
[y
]))
1362 if (e
->cmda
[y
] == NULL
) { /* no more words in candidate */
1363 if (cmds
[y
] == NULL
) /* this is an exact match, cannot do better */
1365 /* here the search key is longer than the candidate */
1366 if (match_type
!= 0) /* but we look for almost exact match... */
1367 continue; /* so we skip this one. */
1368 /* otherwise we like it (case 0) */
1369 } else { /* still words in candidate */
1370 if (cmds
[y
] == NULL
) /* search key is shorter, not good */
1372 /* if we get here, both words exist but there is a mismatch */
1373 if (match_type
== 0) /* not the one we look for */
1375 if (match_type
== 1) /* not the one we look for */
1377 if (cmds
[y
+1] != NULL
|| e
->cmda
[y
+1] != NULL
) /* not the one we look for */
1379 /* we are in case match_type == -1 and mismatch on last word */
1381 if (y
> matchlen
) { /* remember the candidate */
1386 return e
? e
: cand
;
1389 static char *find_best(char *argv
[])
1391 static char cmdline
[80];
1393 /* See how close we get, then print the candidate */
1394 char *myargv
[AST_MAX_CMD_LEN
];
1395 for (x
=0;x
<AST_MAX_CMD_LEN
;x
++)
1397 AST_LIST_LOCK(&helpers
);
1398 for (x
=0;argv
[x
];x
++) {
1399 myargv
[x
] = argv
[x
];
1400 if (!find_cli(myargv
, -1))
1403 AST_LIST_UNLOCK(&helpers
);
1404 ast_join(cmdline
, sizeof(cmdline
), myargv
);
1408 static int __ast_cli_unregister(struct ast_cli_entry
*e
, struct ast_cli_entry
*ed
)
1410 if (e
->deprecate_cmd
) {
1411 __ast_cli_unregister(e
->deprecate_cmd
, e
);
1414 ast_log(LOG_WARNING
, "Can't remove command that is in use\n");
1416 AST_LIST_LOCK(&helpers
);
1417 AST_LIST_REMOVE(&helpers
, e
, list
);
1418 AST_LIST_UNLOCK(&helpers
);
1424 static int __ast_cli_register(struct ast_cli_entry
*e
, struct ast_cli_entry
*ed
)
1426 struct ast_cli_entry
*cur
;
1430 ast_join(fulle
, sizeof(fulle
), e
->cmda
);
1431 AST_LIST_LOCK(&helpers
);
1433 if (find_cli(e
->cmda
, 1)) {
1434 AST_LIST_UNLOCK(&helpers
);
1435 ast_log(LOG_WARNING
, "Command '%s' already registered (or something close enough)\n", fulle
);
1438 e
->_full_cmd
= ast_strdup(fulle
);
1444 e
->summary
= ed
->summary
;
1445 e
->usage
= ed
->usage
;
1446 /* XXX If command A deprecates command B, and command B deprecates command C...
1447 Do we want to show command A or command B when telling the user to use new syntax?
1448 This currently would show command A.
1449 To show command B, you just need to always use ed->_full_cmd.
1451 e
->_deprecated_by
= S_OR(ed
->_deprecated_by
, ed
->_full_cmd
);
1457 AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers
, cur
, list
) {
1458 int len
= strlen(cur
->_full_cmd
);
1461 if (strncasecmp(fulle
, cur
->_full_cmd
, len
) < 0) {
1462 AST_LIST_INSERT_BEFORE_CURRENT(&helpers
, e
, list
);
1466 AST_LIST_TRAVERSE_SAFE_END
;
1469 AST_LIST_INSERT_TAIL(&helpers
, e
, list
);
1470 ret
= 0; /* success */
1473 AST_LIST_UNLOCK(&helpers
);
1475 if (e
->deprecate_cmd
) {
1476 /* This command deprecates another command. Register that one also. */
1477 __ast_cli_register(e
->deprecate_cmd
, e
);
1483 /* wrapper function, so we can unregister deprecated commands recursively */
1484 int ast_cli_unregister(struct ast_cli_entry
*e
)
1486 return __ast_cli_unregister(e
, NULL
);
1489 /* wrapper function, so we can register deprecated commands recursively */
1490 int ast_cli_register(struct ast_cli_entry
*e
)
1492 return __ast_cli_register(e
, NULL
);
1496 * register/unregister an array of entries.
1498 void ast_cli_register_multiple(struct ast_cli_entry
*e
, int len
)
1502 for (i
= 0; i
< len
; i
++)
1503 ast_cli_register(e
+ i
);
1506 void ast_cli_unregister_multiple(struct ast_cli_entry
*e
, int len
)
1510 for (i
= 0; i
< len
; i
++)
1511 ast_cli_unregister(e
+ i
);
1515 /*! \brief helper for help_workhorse and final part of
1516 * handle_help. if locked = 0 it's just help_workhorse,
1517 * otherwise assume the list is already locked and print
1518 * an error message if not found.
1520 static int help1(int fd
, char *match
[], int locked
)
1522 char matchstr
[80] = "";
1523 struct ast_cli_entry
*e
;
1526 struct cli_iterator i
= { NULL
, NULL
};
1529 ast_join(matchstr
, sizeof(matchstr
), match
);
1530 len
= strlen(matchstr
);
1533 AST_LIST_LOCK(&helpers
);
1534 while ( (e
= cli_next(&i
)) ) {
1535 /* Hide commands that start with '_' */
1536 if (e
->_full_cmd
[0] == '_')
1538 /* Hide commands that are marked as deprecated. */
1541 if (match
&& strncasecmp(matchstr
, e
->_full_cmd
, len
))
1543 ast_cli(fd
, "%25.25s %s\n", e
->_full_cmd
, e
->summary
);
1546 AST_LIST_UNLOCK(&helpers
);
1547 if (!locked
&& !found
&& matchstr
[0])
1548 ast_cli(fd
, "No such command '%s'.\n", matchstr
);
1552 static int help_workhorse(int fd
, char *match
[])
1554 return help1(fd
, match
, 0 /* do not print errors */);
1557 static int handle_help(int fd
, int argc
, char *argv
[])
1560 struct ast_cli_entry
*e
;
1563 return RESULT_SHOWUSAGE
;
1565 return help_workhorse(fd
, NULL
);
1567 AST_LIST_LOCK(&helpers
);
1568 e
= find_cli(argv
+ 1, 1); /* try exact match first */
1570 return help1(fd
, argv
+ 1, 1 /* locked */);
1572 ast_cli(fd
, "%s", e
->usage
);
1574 ast_join(fullcmd
, sizeof(fullcmd
), argv
+1);
1575 ast_cli(fd
, "No help text available for '%s'.\n", fullcmd
);
1577 AST_LIST_UNLOCK(&helpers
);
1578 return RESULT_SUCCESS
;
1581 static char *parse_args(const char *s
, int *argc
, char *argv
[], int max
, int *trailingwhitespace
)
1589 *trailingwhitespace
= 0;
1590 if (s
== NULL
) /* invalid, though! */
1592 /* make a copy to store the parsed string */
1593 if (!(dup
= ast_strdup(s
)))
1597 /* scan the original string copying into cur when needed */
1600 ast_log(LOG_WARNING
, "Too many arguments, truncating at %s\n", s
);
1603 if (*s
== '"' && !escaped
) {
1605 if (quoted
&& whitespace
) {
1606 /* start a quoted string from previous whitespace: new argument */
1610 } else if ((*s
== ' ' || *s
== '\t') && !(quoted
|| escaped
)) {
1611 /* If we are not already in whitespace, and not in a quoted string or
1612 processing an escape sequence, and just entered whitespace, then
1613 finalize the previous argument and remember that we are in whitespace
1619 } else if (*s
== '\\' && !escaped
) {
1623 /* we leave whitespace, and are not quoted. So it's a new argument */
1631 /* Null terminate */
1633 /* XXX put a NULL in the last argument, because some functions that take
1634 * the array may want a null-terminated array.
1635 * argc still reflects the number of non-NULL entries.
1639 *trailingwhitespace
= whitespace
;
1643 /*! \brief Return the number of unique matches for the generator */
1644 int ast_cli_generatornummatches(const char *text
, const char *word
)
1646 int matches
= 0, i
= 0;
1647 char *buf
= NULL
, *oldbuf
= NULL
;
1649 while ((buf
= ast_cli_generator(text
, word
, i
++))) {
1650 if (!oldbuf
|| strcmp(buf
,oldbuf
))
1661 char **ast_cli_completion_matches(const char *text
, const char *word
)
1663 char **match_list
= NULL
, *retstr
, *prevstr
;
1664 size_t match_list_len
, max_equal
, which
, i
;
1667 /* leave entry 0 free for the longest common substring */
1669 while ((retstr
= ast_cli_generator(text
, word
, matches
)) != NULL
) {
1670 if (matches
+ 1 >= match_list_len
) {
1671 match_list_len
<<= 1;
1672 if (!(match_list
= ast_realloc(match_list
, match_list_len
* sizeof(*match_list
))))
1675 match_list
[++matches
] = retstr
;
1679 return match_list
; /* NULL */
1681 /* Find the longest substring that is common to all results
1682 * (it is a candidate for completion), and store a copy in entry 0.
1684 prevstr
= match_list
[1];
1685 max_equal
= strlen(prevstr
);
1686 for (which
= 2; which
<= matches
; which
++) {
1687 for (i
= 0; i
< max_equal
&& toupper(prevstr
[i
]) == toupper(match_list
[which
][i
]); i
++)
1692 if (!(retstr
= ast_malloc(max_equal
+ 1)))
1695 ast_copy_string(retstr
, match_list
[1], max_equal
+ 1);
1696 match_list
[0] = retstr
;
1698 /* ensure that the array is NULL terminated */
1699 if (matches
+ 1 >= match_list_len
) {
1700 if (!(match_list
= ast_realloc(match_list
, (match_list_len
+ 1) * sizeof(*match_list
))))
1703 match_list
[matches
+ 1] = NULL
;
1708 static char *__ast_cli_generator(const char *text
, const char *word
, int state
, int lock
)
1710 char *argv
[AST_MAX_ARGS
];
1711 struct ast_cli_entry
*e
;
1712 struct cli_iterator i
= { NULL
, NULL
};
1713 int x
= 0, argindex
, matchlen
;
1716 char matchstr
[80] = "";
1718 char *dup
= parse_args(text
, &x
, argv
, sizeof(argv
) / sizeof(argv
[0]), &tws
);
1720 if (!dup
) /* error */
1722 argindex
= (!ast_strlen_zero(word
) && x
>0) ? x
-1 : x
;
1723 /* rebuild the command, ignore tws */
1724 ast_join(matchstr
, sizeof(matchstr
)-1, argv
);
1725 matchlen
= strlen(matchstr
);
1727 strcat(matchstr
, " "); /* XXX */
1732 AST_LIST_LOCK(&helpers
);
1733 while( !ret
&& (e
= cli_next(&i
)) ) {
1734 int lc
= strlen(e
->_full_cmd
);
1735 if (e
->_full_cmd
[0] != '_' && lc
> 0 && matchlen
<= lc
&&
1736 !strncasecmp(matchstr
, e
->_full_cmd
, matchlen
)) {
1737 /* Found initial part, return a copy of the next word... */
1738 if (e
->cmda
[argindex
] && ++matchnum
> state
)
1739 ret
= strdup(e
->cmda
[argindex
]); /* we need a malloced string */
1740 } else if (e
->generator
&& !strncasecmp(matchstr
, e
->_full_cmd
, lc
) && matchstr
[lc
] < 33) {
1741 /* We have a command in its entirity within us -- theoretically only one
1742 command can have this occur */
1743 ret
= e
->generator(matchstr
, word
, argindex
, state
);
1747 AST_LIST_UNLOCK(&helpers
);
1752 char *ast_cli_generator(const char *text
, const char *word
, int state
)
1754 return __ast_cli_generator(text
, word
, state
, 1);
1757 int ast_cli_command(int fd
, const char *s
)
1759 char *argv
[AST_MAX_ARGS
];
1760 struct ast_cli_entry
*e
;
1765 if (!(dup
= parse_args(s
, &x
, argv
, sizeof(argv
) / sizeof(argv
[0]), &tws
)))
1768 /* We need at least one entry, or ignore */
1770 AST_LIST_LOCK(&helpers
);
1771 e
= find_cli(argv
, 0);
1774 AST_LIST_UNLOCK(&helpers
);
1776 switch(e
->handler(fd
, x
, argv
)) {
1777 case RESULT_SHOWUSAGE
:
1779 ast_cli(fd
, "%s", e
->usage
);
1781 ast_cli(fd
, "Invalid usage, but no usage information available.\n");
1784 AST_LIST_LOCK(&helpers
);
1785 if (e
->deprecated
== 1) {
1786 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
);
1789 AST_LIST_UNLOCK(&helpers
);
1793 ast_cli(fd
, "No such command '%s' (type 'help' for help)\n", find_best(argv
));
1795 ast_atomic_fetchadd_int(&e
->inuse
, -1);