00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "asterisk.h"
00027
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 160234 $")
00029
00030 #include "asterisk/_private.h"
00031 #include "asterisk/paths.h"
00032 #include <sys/signal.h>
00033 #include <signal.h>
00034 #include <ctype.h>
00035 #include <regex.h>
00036
00037 #include "asterisk/cli.h"
00038 #include "asterisk/linkedlists.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/lock.h"
00045 #include "editline/readline/readline.h"
00046 #include "asterisk/threadstorage.h"
00047
00048
00049
00050
00051 struct ast_debug_file {
00052 unsigned int level;
00053 AST_RWLIST_ENTRY(ast_debug_file) entry;
00054 char filename[0];
00055 };
00056
00057 AST_RWLIST_HEAD(debug_file_list, ast_debug_file);
00058
00059
00060 static struct debug_file_list debug_files;
00061
00062 static struct debug_file_list verbose_files;
00063
00064 AST_THREADSTORAGE(ast_cli_buf);
00065
00066
00067 #define AST_CLI_INITLEN 256
00068
00069 void ast_cli(int fd, const char *fmt, ...)
00070 {
00071 int res;
00072 struct ast_str *buf;
00073 va_list ap;
00074
00075 if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
00076 return;
00077
00078 va_start(ap, fmt);
00079 res = ast_str_set_va(&buf, 0, fmt, ap);
00080 va_end(ap);
00081
00082 if (res != AST_DYNSTR_BUILD_FAILED)
00083 ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
00084 }
00085
00086 unsigned int ast_debug_get_by_file(const char *file)
00087 {
00088 struct ast_debug_file *adf;
00089 unsigned int res = 0;
00090
00091 AST_RWLIST_RDLOCK(&debug_files);
00092 AST_LIST_TRAVERSE(&debug_files, adf, entry) {
00093 if (!strncasecmp(adf->filename, file, strlen(adf->filename))) {
00094 res = adf->level;
00095 break;
00096 }
00097 }
00098 AST_RWLIST_UNLOCK(&debug_files);
00099
00100 return res;
00101 }
00102
00103 unsigned int ast_verbose_get_by_file(const char *file)
00104 {
00105 struct ast_debug_file *adf;
00106 unsigned int res = 0;
00107
00108 AST_RWLIST_RDLOCK(&verbose_files);
00109 AST_LIST_TRAVERSE(&verbose_files, adf, entry) {
00110 if (!strncasecmp(adf->filename, file, strlen(file))) {
00111 res = adf->level;
00112 break;
00113 }
00114 }
00115 AST_RWLIST_UNLOCK(&verbose_files);
00116
00117 return res;
00118 }
00119
00120 static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
00121
00122 static char *complete_fn(const char *word, int state)
00123 {
00124 char *c, *d;
00125 char filename[PATH_MAX];
00126
00127 if (word[0] == '/')
00128 ast_copy_string(filename, word, sizeof(filename));
00129 else
00130 snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
00131
00132 c = d = filename_completion_function(filename, state);
00133
00134 if (c && word[0] != '/')
00135 c += (strlen(ast_config_AST_MODULE_DIR) + 1);
00136 if (c)
00137 c = ast_strdup(c);
00138 if (d)
00139 free(d);
00140
00141 return c;
00142 }
00143
00144 static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00145 {
00146
00147 switch (cmd) {
00148 case CLI_INIT:
00149 e->command = "module load";
00150 e->usage =
00151 "Usage: module load <module name>\n"
00152 " Loads the specified module into Asterisk.\n";
00153 return NULL;
00154
00155 case CLI_GENERATE:
00156 if (a->pos != e->args)
00157 return NULL;
00158 return complete_fn(a->word, a->n);
00159 }
00160 if (a->argc != e->args + 1)
00161 return CLI_SHOWUSAGE;
00162 if (ast_load_resource(a->argv[e->args])) {
00163 ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]);
00164 return CLI_FAILURE;
00165 }
00166 return CLI_SUCCESS;
00167 }
00168
00169 static char *handle_load_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00170 {
00171 char *res = handle_load(e, cmd, a);
00172 if (cmd == CLI_INIT)
00173 e->command = "load";
00174 return res;
00175 }
00176
00177 static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00178 {
00179 int x;
00180
00181 switch (cmd) {
00182 case CLI_INIT:
00183 e->command = "module reload";
00184 e->usage =
00185 "Usage: module reload [module ...]\n"
00186 " Reloads configuration files for all listed modules which support\n"
00187 " reloading, or for all supported modules if none are listed.\n";
00188 return NULL;
00189
00190 case CLI_GENERATE:
00191 return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1);
00192 }
00193 if (a->argc == e->args) {
00194 ast_module_reload(NULL);
00195 return CLI_SUCCESS;
00196 }
00197 for (x = e->args; x < a->argc; x++) {
00198 int res = ast_module_reload(a->argv[x]);
00199
00200 switch (res) {
00201 case 0:
00202 ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
00203 break;
00204 case 1:
00205 ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
00206 break;
00207 }
00208 }
00209 return CLI_SUCCESS;
00210 }
00211
00212 static char *handle_reload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00213 {
00214 char *s = handle_reload(e, cmd, a);
00215 if (cmd == CLI_INIT)
00216 e->command = "reload";
00217 return s;
00218 }
00219
00220
00221
00222
00223
00224 static struct ast_debug_file *find_debug_file(const char *fn, unsigned int debug)
00225 {
00226 struct ast_debug_file *df = NULL;
00227 struct debug_file_list *dfl = debug ? &debug_files : &verbose_files;
00228
00229 AST_LIST_TRAVERSE(dfl, df, entry) {
00230 if (!strcasecmp(df->filename, fn))
00231 break;
00232 }
00233
00234 return df;
00235 }
00236
00237 static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00238 {
00239 int oldval;
00240 int newlevel;
00241 int atleast = 0;
00242 int fd = a->fd;
00243 int argc = a->argc;
00244 char **argv = a->argv;
00245 int *dst;
00246 char *what;
00247 struct debug_file_list *dfl;
00248 struct ast_debug_file *adf;
00249 char *fn;
00250
00251 switch (cmd) {
00252 case CLI_INIT:
00253 e->command = "core set {debug|verbose} [off|atleast]";
00254 e->usage =
00255 "Usage: core set {debug|verbose} [atleast] <level> [filename]\n"
00256 " core set {debug|verbose} off\n"
00257 " Sets level of debug or verbose messages to be displayed or \n"
00258 " sets a filename to display debug messages from.\n"
00259 " 0 or off means no messages should be displayed.\n"
00260 " Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
00261 return NULL;
00262
00263 case CLI_GENERATE:
00264 return NULL;
00265 }
00266
00267
00268
00269
00270 if (argc < e->args)
00271 return CLI_SHOWUSAGE;
00272 if (!strcasecmp(argv[e->args - 2], "debug")) {
00273 dst = &option_debug;
00274 oldval = option_debug;
00275 what = "Core debug";
00276 } else {
00277 dst = &option_verbose;
00278 oldval = option_verbose;
00279 what = "Verbosity";
00280 }
00281 if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) {
00282 unsigned int debug = (*what == 'C');
00283 newlevel = 0;
00284
00285 dfl = debug ? &debug_files : &verbose_files;
00286
00287 AST_RWLIST_WRLOCK(dfl);
00288 while ((adf = AST_RWLIST_REMOVE_HEAD(dfl, entry)))
00289 ast_free(adf);
00290 ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00291 AST_RWLIST_UNLOCK(dfl);
00292
00293 goto done;
00294 }
00295 if (!strcasecmp(argv[e->args-1], "atleast"))
00296 atleast = 1;
00297 if (argc != e->args + atleast && argc != e->args + atleast + 1)
00298 return CLI_SHOWUSAGE;
00299 if (sscanf(argv[e->args + atleast - 1], "%d", &newlevel) != 1)
00300 return CLI_SHOWUSAGE;
00301 if (argc == e->args + atleast + 1) {
00302 unsigned int debug = (*what == 'C');
00303 dfl = debug ? &debug_files : &verbose_files;
00304
00305 fn = argv[e->args + atleast];
00306
00307 AST_RWLIST_WRLOCK(dfl);
00308
00309 if ((adf = find_debug_file(fn, debug)) && !newlevel) {
00310 AST_RWLIST_REMOVE(dfl, adf, entry);
00311 if (AST_RWLIST_EMPTY(dfl))
00312 ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00313 AST_RWLIST_UNLOCK(dfl);
00314 ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, adf->level, fn);
00315 ast_free(adf);
00316 return CLI_SUCCESS;
00317 }
00318
00319 if (adf) {
00320 if ((atleast && newlevel < adf->level) || adf->level == newlevel) {
00321 ast_cli(fd, "%s is %d for '%s'\n", what, adf->level, fn);
00322 AST_RWLIST_UNLOCK(dfl);
00323 return CLI_SUCCESS;
00324 }
00325 } else if (!(adf = ast_calloc(1, sizeof(*adf) + strlen(fn) + 1))) {
00326 AST_RWLIST_UNLOCK(dfl);
00327 return CLI_FAILURE;
00328 }
00329
00330 oldval = adf->level;
00331 adf->level = newlevel;
00332 strcpy(adf->filename, fn);
00333
00334 ast_set_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00335
00336 AST_RWLIST_INSERT_TAIL(dfl, adf, entry);
00337 AST_RWLIST_UNLOCK(dfl);
00338
00339 ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, adf->level, adf->filename);
00340
00341 return CLI_SUCCESS;
00342 }
00343
00344 done:
00345 if (!atleast || newlevel > *dst)
00346 *dst = newlevel;
00347 if (oldval > 0 && *dst == 0)
00348 ast_cli(fd, "%s is now OFF\n", what);
00349 else if (*dst > 0) {
00350 if (oldval == *dst)
00351 ast_cli(fd, "%s is at least %d\n", what, *dst);
00352 else
00353 ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst);
00354 }
00355
00356 return CLI_SUCCESS;
00357 }
00358
00359 static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00360 {
00361 switch (cmd) {
00362 case CLI_INIT:
00363 e->command = "logger mute";
00364 e->usage =
00365 "Usage: logger mute\n"
00366 " Disables logging output to the current console, making it possible to\n"
00367 " gather information without being disturbed by scrolling lines.\n";
00368 return NULL;
00369 case CLI_GENERATE:
00370 return NULL;
00371 }
00372
00373 if (a->argc < 2 || a->argc > 3)
00374 return CLI_SHOWUSAGE;
00375
00376 if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
00377 ast_console_toggle_mute(a->fd, 1);
00378 else
00379 ast_console_toggle_mute(a->fd, 0);
00380
00381 return CLI_SUCCESS;
00382 }
00383
00384 static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00385 {
00386
00387 int x;
00388 int force = AST_FORCE_SOFT;
00389 char *s;
00390
00391 switch (cmd) {
00392 case CLI_INIT:
00393 e->command = "module unload";
00394 e->usage =
00395 "Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
00396 " Unloads the specified module from Asterisk. The -f\n"
00397 " option causes the module to be unloaded even if it is\n"
00398 " in use (may cause a crash) and the -h module causes the\n"
00399 " module to be unloaded even if the module says it cannot, \n"
00400 " which almost always will cause a crash.\n";
00401 return NULL;
00402
00403 case CLI_GENERATE:
00404 return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
00405 }
00406 if (a->argc < e->args + 1)
00407 return CLI_SHOWUSAGE;
00408 x = e->args;
00409 s = a->argv[x];
00410 if (s[0] == '-') {
00411 if (s[1] == 'f')
00412 force = AST_FORCE_FIRM;
00413 else if (s[1] == 'h')
00414 force = AST_FORCE_HARD;
00415 else
00416 return CLI_SHOWUSAGE;
00417 if (a->argc < e->args + 2)
00418 return CLI_SHOWUSAGE;
00419 x++;
00420 }
00421
00422 for (; x < a->argc; x++) {
00423 if (ast_unload_resource(a->argv[x], force)) {
00424 ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
00425 return CLI_FAILURE;
00426 }
00427 }
00428 return CLI_SUCCESS;
00429 }
00430
00431 static char *handle_unload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00432 {
00433 char *res = handle_unload(e, cmd, a);
00434 if (cmd == CLI_INIT)
00435 e->command = "unload";
00436 return res;
00437 }
00438
00439 #define MODLIST_FORMAT "%-30s %-40.40s %-10d\n"
00440 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
00441
00442 AST_MUTEX_DEFINE_STATIC(climodentrylock);
00443 static int climodentryfd = -1;
00444
00445 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
00446 {
00447
00448 if (strcasestr(module, like) ) {
00449 ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
00450 return 1;
00451 }
00452 return 0;
00453 }
00454
00455 static void print_uptimestr(int fd, struct timeval timeval, const char *prefix, int printsec)
00456 {
00457 int x;
00458 struct ast_str *out;
00459
00460 #define SECOND (1)
00461 #define MINUTE (SECOND*60)
00462 #define HOUR (MINUTE*60)
00463 #define DAY (HOUR*24)
00464 #define WEEK (DAY*7)
00465 #define YEAR (DAY*365)
00466 #define NEEDCOMMA(x) ((x)? ",": "")
00467 if (timeval.tv_sec < 0)
00468 return;
00469
00470 if (printsec) {
00471 ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval.tv_sec);
00472 return;
00473 }
00474 out = ast_str_alloca(256);
00475 if (timeval.tv_sec > YEAR) {
00476 x = (timeval.tv_sec / YEAR);
00477 timeval.tv_sec -= (x * YEAR);
00478 ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00479 }
00480 if (timeval.tv_sec > WEEK) {
00481 x = (timeval.tv_sec / WEEK);
00482 timeval.tv_sec -= (x * WEEK);
00483 ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00484 }
00485 if (timeval.tv_sec > DAY) {
00486 x = (timeval.tv_sec / DAY);
00487 timeval.tv_sec -= (x * DAY);
00488 ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00489 }
00490 if (timeval.tv_sec > HOUR) {
00491 x = (timeval.tv_sec / HOUR);
00492 timeval.tv_sec -= (x * HOUR);
00493 ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00494 }
00495 if (timeval.tv_sec > MINUTE) {
00496 x = (timeval.tv_sec / MINUTE);
00497 timeval.tv_sec -= (x * MINUTE);
00498 ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00499 }
00500 x = timeval.tv_sec;
00501 if (x > 0 || out->used == 0)
00502 ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
00503 ast_cli(fd, "%s: %s\n", prefix, out->str);
00504 }
00505
00506 static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00507 {
00508 struct timeval curtime = ast_tvnow();
00509 int printsec;
00510
00511 switch (cmd) {
00512 case CLI_INIT:
00513 e->command = "core show uptime [seconds]";
00514 e->usage =
00515 "Usage: core show uptime [seconds]\n"
00516 " Shows Asterisk uptime information.\n"
00517 " The seconds word returns the uptime in seconds only.\n";
00518 return NULL;
00519
00520 case CLI_GENERATE:
00521 return NULL;
00522 }
00523
00524 if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
00525 printsec = 1;
00526 else if (a->argc == e->args-1)
00527 printsec = 0;
00528 else
00529 return CLI_SHOWUSAGE;
00530 if (ast_startuptime.tv_sec)
00531 print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
00532 if (ast_lastreloadtime.tv_sec)
00533 print_uptimestr(a->fd, ast_tvsub(curtime, ast_lastreloadtime), "Last reload", printsec);
00534 return CLI_SUCCESS;
00535 }
00536
00537 static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00538 {
00539 char *like;
00540
00541 switch (cmd) {
00542 case CLI_INIT:
00543 e->command = "module show [like]";
00544 e->usage =
00545 "Usage: module show [like keyword]\n"
00546 " Shows Asterisk modules currently in use, and usage statistics.\n";
00547 return NULL;
00548
00549 case CLI_GENERATE:
00550 if (a->pos == e->args)
00551 return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
00552 else
00553 return NULL;
00554 }
00555
00556
00557
00558 if (a->argc == e->args - 1)
00559 like = "";
00560 else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
00561 like = a->argv[e->args];
00562 else
00563 return CLI_SHOWUSAGE;
00564
00565 ast_mutex_lock(&climodentrylock);
00566 climodentryfd = a->fd;
00567 ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
00568 ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
00569 climodentryfd = -1;
00570 ast_mutex_unlock(&climodentrylock);
00571 return CLI_SUCCESS;
00572 }
00573 #undef MODLIST_FORMAT
00574 #undef MODLIST_FORMAT2
00575
00576 static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00577 {
00578 struct timeval curtime = ast_tvnow();
00579 int showuptime, printsec;
00580
00581 switch (cmd) {
00582 case CLI_INIT:
00583 e->command = "core show calls [uptime]";
00584 e->usage =
00585 "Usage: core show calls [uptime] [seconds]\n"
00586 " Lists number of currently active calls and total number of calls\n"
00587 " processed through PBX since last restart. If 'uptime' is specified\n"
00588 " the system uptime is also displayed. If 'seconds' is specified in\n"
00589 " addition to 'uptime', the system uptime is displayed in seconds.\n";
00590 return NULL;
00591
00592 case CLI_GENERATE:
00593 if (a->pos != e->args)
00594 return NULL;
00595 return a->n == 0 ? ast_strdup("seconds") : NULL;
00596 }
00597
00598
00599 if (a->argc >= e->args && !strcasecmp(a->argv[e->args-1],"uptime")) {
00600 showuptime = 1;
00601
00602 if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
00603 printsec = 1;
00604 else if (a->argc == e->args)
00605 printsec = 0;
00606 else
00607 return CLI_SHOWUSAGE;
00608 } else if (a->argc == e->args-1) {
00609 showuptime = 0;
00610 printsec = 0;
00611 } else
00612 return CLI_SHOWUSAGE;
00613
00614 if (option_maxcalls) {
00615 ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00616 ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00617 ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00618 } else {
00619 ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00620 }
00621
00622 ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
00623
00624 if (ast_startuptime.tv_sec && showuptime) {
00625 print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
00626 }
00627
00628 return RESULT_SUCCESS;
00629 }
00630
00631 static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00632 {
00633 #define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00634 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00635 #define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
00636 #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"
00637 #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"
00638
00639 struct ast_channel *c = NULL;
00640 int numchans = 0, concise = 0, verbose = 0, count = 0;
00641 int fd, argc;
00642 char **argv;
00643
00644 switch (cmd) {
00645 case CLI_INIT:
00646 e->command = "core show channels [concise|verbose|count]";
00647 e->usage =
00648 "Usage: core show channels [concise|verbose|count]\n"
00649 " Lists currently defined channels and some information about them. If\n"
00650 " 'concise' is specified, the format is abridged and in a more easily\n"
00651 " machine parsable format. If 'verbose' is specified, the output includes\n"
00652 " more and longer fields. If 'count' is specified only the channel and call\n"
00653 " count is output.\n"
00654 " The 'concise' option is deprecated and will be removed from future versions\n"
00655 " of Asterisk.\n";
00656 return NULL;
00657
00658 case CLI_GENERATE:
00659 return NULL;
00660 }
00661 fd = a->fd;
00662 argc = a->argc;
00663 argv = a->argv;
00664
00665 if (a->argc == e->args) {
00666 if (!strcasecmp(argv[e->args-1],"concise"))
00667 concise = 1;
00668 else if (!strcasecmp(argv[e->args-1],"verbose"))
00669 verbose = 1;
00670 else if (!strcasecmp(argv[e->args-1],"count"))
00671 count = 1;
00672 else
00673 return CLI_SHOWUSAGE;
00674 } else if (a->argc != e->args - 1)
00675 return CLI_SHOWUSAGE;
00676
00677 if (!count) {
00678 if (!concise && !verbose)
00679 ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
00680 else if (verbose)
00681 ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
00682 "CallerID", "Duration", "Accountcode", "BridgedTo");
00683 }
00684
00685 while ((c = ast_channel_walk_locked(c)) != NULL) {
00686 struct ast_channel *bc = ast_bridged_channel(c);
00687 char durbuf[10] = "-";
00688
00689 if (!count) {
00690 if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) {
00691 int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
00692 if (verbose) {
00693 int durh = duration / 3600;
00694 int durm = (duration % 3600) / 60;
00695 int durs = duration % 60;
00696 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
00697 } else {
00698 snprintf(durbuf, sizeof(durbuf), "%d", duration);
00699 }
00700 }
00701 if (concise) {
00702 ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00703 c->appl ? c->appl : "(None)",
00704 S_OR(c->data, ""),
00705 S_OR(c->cid.cid_num, ""),
00706 S_OR(c->accountcode, ""),
00707 c->amaflags,
00708 durbuf,
00709 bc ? bc->name : "(None)",
00710 c->uniqueid);
00711 } else if (verbose) {
00712 ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00713 c->appl ? c->appl : "(None)",
00714 c->data ? S_OR(c->data, "(Empty)" ): "(None)",
00715 S_OR(c->cid.cid_num, ""),
00716 durbuf,
00717 S_OR(c->accountcode, ""),
00718 bc ? bc->name : "(None)");
00719 } else {
00720 char locbuf[40] = "(None)";
00721 char appdata[40] = "(None)";
00722
00723 if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten))
00724 snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
00725 if (c->appl)
00726 snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, S_OR(c->data, ""));
00727 ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
00728 }
00729 }
00730 numchans++;
00731 ast_channel_unlock(c);
00732 }
00733 if (!concise) {
00734 ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
00735 if (option_maxcalls)
00736 ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00737 ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00738 ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00739 else
00740 ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00741
00742 ast_cli(fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
00743 }
00744 return CLI_SUCCESS;
00745
00746 #undef FORMAT_STRING
00747 #undef FORMAT_STRING2
00748 #undef CONCISE_FORMAT_STRING
00749 #undef VERBOSE_FORMAT_STRING
00750 #undef VERBOSE_FORMAT_STRING2
00751 }
00752
00753 static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00754 {
00755 struct ast_channel *c=NULL;
00756
00757 switch (cmd) {
00758 case CLI_INIT:
00759 e->command = "soft hangup";
00760 e->usage =
00761 "Usage: soft hangup <channel>\n"
00762 " Request that a channel be hung up. The hangup takes effect\n"
00763 " the next time the driver reads or writes from the channel\n";
00764 return NULL;
00765 case CLI_GENERATE:
00766 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00767 }
00768 if (a->argc != 3)
00769 return CLI_SHOWUSAGE;
00770 c = ast_get_channel_by_name_locked(a->argv[2]);
00771 if (c) {
00772 ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
00773 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
00774 ast_channel_unlock(c);
00775 } else
00776 ast_cli(a->fd, "%s is not a known channel\n", a->argv[2]);
00777 return CLI_SUCCESS;
00778 }
00779
00780 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
00781
00782 static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00783 {
00784 char *buf, *obuf;
00785 int buflen = 2048;
00786 int len = 0;
00787 char **matches;
00788 int x, matchlen;
00789
00790 switch (cmd) {
00791 case CLI_INIT:
00792 e->command = "_command matchesarray";
00793 e->usage =
00794 "Usage: _command matchesarray \"<line>\" text \n"
00795 " This function is used internally to help with command completion and should.\n"
00796 " never be called by the user directly.\n";
00797 return NULL;
00798 case CLI_GENERATE:
00799 return NULL;
00800 }
00801
00802 if (a->argc != 4)
00803 return CLI_SHOWUSAGE;
00804 if (!(buf = ast_malloc(buflen)))
00805 return CLI_FAILURE;
00806 buf[len] = '\0';
00807 matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
00808 if (matches) {
00809 for (x=0; matches[x]; x++) {
00810 matchlen = strlen(matches[x]) + 1;
00811 if (len + matchlen >= buflen) {
00812 buflen += matchlen * 3;
00813 obuf = buf;
00814 if (!(buf = ast_realloc(obuf, buflen)))
00815
00816 ast_free(obuf);
00817 }
00818 if (buf)
00819 len += sprintf( buf + len, "%s ", matches[x]);
00820 ast_free(matches[x]);
00821 matches[x] = NULL;
00822 }
00823 ast_free(matches);
00824 }
00825
00826 if (buf) {
00827 ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
00828 ast_free(buf);
00829 } else
00830 ast_cli(a->fd, "NULL\n");
00831
00832 return CLI_SUCCESS;
00833 }
00834
00835
00836
00837 static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00838 {
00839 int matches = 0;
00840
00841 switch (cmd) {
00842 case CLI_INIT:
00843 e->command = "_command nummatches";
00844 e->usage =
00845 "Usage: _command nummatches \"<line>\" text \n"
00846 " This function is used internally to help with command completion and should.\n"
00847 " never be called by the user directly.\n";
00848 return NULL;
00849 case CLI_GENERATE:
00850 return NULL;
00851 }
00852
00853 if (a->argc != 4)
00854 return CLI_SHOWUSAGE;
00855
00856 matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
00857
00858 ast_cli(a->fd, "%d", matches);
00859
00860 return CLI_SUCCESS;
00861 }
00862
00863 static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00864 {
00865 char *buf;
00866 switch (cmd) {
00867 case CLI_INIT:
00868 e->command = "_command complete";
00869 e->usage =
00870 "Usage: _command complete \"<line>\" text state\n"
00871 " This function is used internally to help with command completion and should.\n"
00872 " never be called by the user directly.\n";
00873 return NULL;
00874 case CLI_GENERATE:
00875 return NULL;
00876 }
00877 if (a->argc != 5)
00878 return CLI_SHOWUSAGE;
00879 buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
00880 if (buf) {
00881 ast_cli(a->fd, "%s", buf);
00882 ast_free(buf);
00883 } else
00884 ast_cli(a->fd, "NULL\n");
00885 return CLI_SUCCESS;
00886 }
00887
00888 static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00889 {
00890 struct ast_channel *c = NULL;
00891 int is_all, is_off = 0;
00892
00893 switch (cmd) {
00894 case CLI_INIT:
00895 e->command = "core set debug channel";
00896 e->usage =
00897 "Usage: core set debug channel <all|channel> [off]\n"
00898 " Enables/disables debugging on all or on a specific channel.\n";
00899 return NULL;
00900
00901 case CLI_GENERATE:
00902
00903 if (a->pos != e->args)
00904 return NULL;
00905 return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
00906 }
00907
00908 if (a->argc == e->args + 2) {
00909 if (!strcasecmp(a->argv[e->args + 1], "off"))
00910 is_off = 1;
00911 else
00912 return CLI_SHOWUSAGE;
00913 } else if (a->argc != e->args + 1)
00914 return CLI_SHOWUSAGE;
00915
00916 is_all = !strcasecmp("all", a->argv[e->args]);
00917 if (is_all) {
00918 if (is_off) {
00919 global_fin &= ~DEBUGCHAN_FLAG;
00920 global_fout &= ~DEBUGCHAN_FLAG;
00921 } else {
00922 global_fin |= DEBUGCHAN_FLAG;
00923 global_fout |= DEBUGCHAN_FLAG;
00924 }
00925 c = ast_channel_walk_locked(NULL);
00926 } else {
00927 c = ast_get_channel_by_name_locked(a->argv[e->args]);
00928 if (c == NULL)
00929 ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
00930 }
00931 while (c) {
00932 if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
00933 if (is_off) {
00934 c->fin &= ~DEBUGCHAN_FLAG;
00935 c->fout &= ~DEBUGCHAN_FLAG;
00936 } else {
00937 c->fin |= DEBUGCHAN_FLAG;
00938 c->fout |= DEBUGCHAN_FLAG;
00939 }
00940 ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
00941 }
00942 ast_channel_unlock(c);
00943 if (!is_all)
00944 break;
00945 c = ast_channel_walk_locked(c);
00946 }
00947 ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
00948 return CLI_SUCCESS;
00949 }
00950
00951 static char *handle_debugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00952 {
00953 char *res;
00954
00955 if (cmd == CLI_HANDLER && a->argc != e->args + 1)
00956 return CLI_SHOWUSAGE;
00957 res = handle_core_set_debug_channel(e, cmd, a);
00958 if (cmd == CLI_INIT)
00959 e->command = "debug channel";
00960 return res;
00961 }
00962
00963 static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00964 {
00965 char *res;
00966 if (cmd == CLI_HANDLER) {
00967 if (a->argc != e->args + 1)
00968 return CLI_SHOWUSAGE;
00969
00970
00971
00972 a->argv[e->args+1] = "off";
00973 a->argc++;
00974 }
00975 res = handle_core_set_debug_channel(e, cmd, a);
00976 if (cmd == CLI_INIT)
00977 e->command = "no debug channel";
00978 return res;
00979 }
00980
00981 static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00982 {
00983 struct ast_channel *c=NULL;
00984 struct timeval now;
00985 struct ast_str *out = ast_str_alloca(2048);
00986 char cdrtime[256];
00987 char nf[256], wf[256], rf[256];
00988 long elapsed_seconds=0;
00989 int hour=0, min=0, sec=0;
00990 #ifdef CHANNEL_TRACE
00991 int trace_enabled;
00992 #endif
00993
00994 switch (cmd) {
00995 case CLI_INIT:
00996 e->command = "core show channel";
00997 e->usage =
00998 "Usage: core show channel <channel>\n"
00999 " Shows lots of information about the specified channel.\n";
01000 return NULL;
01001 case CLI_GENERATE:
01002 return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
01003 }
01004
01005 if (a->argc != 4)
01006 return CLI_SHOWUSAGE;
01007 now = ast_tvnow();
01008 c = ast_get_channel_by_name_locked(a->argv[3]);
01009 if (!c) {
01010 ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
01011 return CLI_SUCCESS;
01012 }
01013 if (c->cdr) {
01014 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01015 hour = elapsed_seconds / 3600;
01016 min = (elapsed_seconds % 3600) / 60;
01017 sec = elapsed_seconds % 60;
01018 snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
01019 } else
01020 strcpy(cdrtime, "N/A");
01021 ast_cli(a->fd,
01022 " -- General --\n"
01023 " Name: %s\n"
01024 " Type: %s\n"
01025 " UniqueID: %s\n"
01026 " Caller ID: %s\n"
01027 " Caller ID Name: %s\n"
01028 " DNID Digits: %s\n"
01029 " Language: %s\n"
01030 " State: %s (%d)\n"
01031 " Rings: %d\n"
01032 " NativeFormats: %s\n"
01033 " WriteFormat: %s\n"
01034 " ReadFormat: %s\n"
01035 " WriteTranscode: %s\n"
01036 " ReadTranscode: %s\n"
01037 "1st File Descriptor: %d\n"
01038 " Frames in: %d%s\n"
01039 " Frames out: %d%s\n"
01040 " Time to Hangup: %ld\n"
01041 " Elapsed Time: %s\n"
01042 " Direct Bridge: %s\n"
01043 "Indirect Bridge: %s\n"
01044 " -- PBX --\n"
01045 " Context: %s\n"
01046 " Extension: %s\n"
01047 " Priority: %d\n"
01048 " Call Group: %llu\n"
01049 " Pickup Group: %llu\n"
01050 " Application: %s\n"
01051 " Data: %s\n"
01052 " Blocking in: %s\n",
01053 c->name, c->tech->type, c->uniqueid,
01054 S_OR(c->cid.cid_num, "(N/A)"),
01055 S_OR(c->cid.cid_name, "(N/A)"),
01056 S_OR(c->cid.cid_dnid, "(N/A)"),
01057 c->language,
01058 ast_state2str(c->_state), c->_state, c->rings,
01059 ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats),
01060 ast_getformatname_multiple(wf, sizeof(wf), c->writeformat),
01061 ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
01062 c->writetrans ? "Yes" : "No",
01063 c->readtrans ? "Yes" : "No",
01064 c->fds[0],
01065 c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01066 c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01067 (long)c->whentohangup.tv_sec,
01068 cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>",
01069 c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
01070 ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
01071 (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
01072
01073 if (pbx_builtin_serialize_variables(c, &out))
01074 ast_cli(a->fd," Variables:\n%s\n", out->str);
01075 if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
01076 ast_cli(a->fd," CDR Variables:\n%s\n", out->str);
01077 #ifdef CHANNEL_TRACE
01078 trace_enabled = ast_channel_trace_is_enabled(c);
01079 ast_cli(a->fd, " Context Trace: %s\n", trace_enabled ? "Enabled" : "Disabled");
01080 if (trace_enabled && ast_channel_trace_serialize(c, &out))
01081 ast_cli(a->fd, " Trace:\n%s\n", out->str);
01082 #endif
01083 ast_channel_unlock(c);
01084 return CLI_SUCCESS;
01085 }
01086
01087
01088
01089
01090
01091 char *ast_cli_complete(const char *word, char *const choices[], int state)
01092 {
01093 int i, which = 0, len;
01094 len = ast_strlen_zero(word) ? 0 : strlen(word);
01095
01096 for (i = 0; choices[i]; i++) {
01097 if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
01098 return ast_strdup(choices[i]);
01099 }
01100 return NULL;
01101 }
01102
01103 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
01104 {
01105 struct ast_channel *c = NULL;
01106 int which = 0;
01107 int wordlen;
01108 char notfound = '\0';
01109 char *ret = ¬found;
01110
01111 if (pos != rpos)
01112 return NULL;
01113
01114 wordlen = strlen(word);
01115
01116 while (ret == ¬found && (c = ast_channel_walk_locked(c))) {
01117 if (!strncasecmp(word, c->name, wordlen) && ++which > state)
01118 ret = ast_strdup(c->name);
01119 ast_channel_unlock(c);
01120 }
01121 return ret == ¬found ? NULL : ret;
01122 }
01123
01124 static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01125 {
01126 #define FORMAT_STRING "%-25s %-20s %-20s\n"
01127
01128 struct ast_group_info *gi = NULL;
01129 int numchans = 0;
01130 regex_t regexbuf;
01131 int havepattern = 0;
01132
01133 switch (cmd) {
01134 case CLI_INIT:
01135 e->command = "group show channels";
01136 e->usage =
01137 "Usage: group show channels [pattern]\n"
01138 " Lists all currently active channels with channel group(s) specified.\n"
01139 " Optional regular expression pattern is matched to group names for each\n"
01140 " channel.\n";
01141 return NULL;
01142 case CLI_GENERATE:
01143 return NULL;
01144 }
01145
01146 if (a->argc < 3 || a->argc > 4)
01147 return CLI_SHOWUSAGE;
01148
01149 if (a->argc == 4) {
01150 if (regcomp(®exbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
01151 return CLI_SHOWUSAGE;
01152 havepattern = 1;
01153 }
01154
01155 ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
01156
01157 ast_app_group_list_rdlock();
01158
01159 gi = ast_app_group_list_head();
01160 while (gi) {
01161 if (!havepattern || !regexec(®exbuf, gi->group, 0, NULL, 0)) {
01162 ast_cli(a->fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
01163 numchans++;
01164 }
01165 gi = AST_LIST_NEXT(gi, list);
01166 }
01167
01168 ast_app_group_list_unlock();
01169
01170 if (havepattern)
01171 regfree(®exbuf);
01172
01173 ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
01174 return CLI_SUCCESS;
01175 #undef FORMAT_STRING
01176 }
01177
01178 static struct ast_cli_entry cli_debug_channel_deprecated = AST_CLI_DEFINE(handle_debugchan_deprecated, "Enable debugging on channel");
01179 static struct ast_cli_entry cli_module_load_deprecated = AST_CLI_DEFINE(handle_load_deprecated, "Load a module");
01180 static struct ast_cli_entry cli_module_reload_deprecated = AST_CLI_DEFINE(handle_reload_deprecated, "reload modules by name");
01181 static struct ast_cli_entry cli_module_unload_deprecated = AST_CLI_DEFINE(handle_unload_deprecated, "unload modules by name");
01182
01183 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
01184
01185 static struct ast_cli_entry cli_cli[] = {
01186
01187 AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
01188 AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
01189 AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
01190
01191 AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
01192
01193 AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
01194
01195 AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
01196
01197 AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
01198
01199 AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel",
01200 .deprecate_cmd = &cli_debug_channel_deprecated),
01201
01202 AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
01203
01204 AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
01205
01206 AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
01207
01208 AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
01209
01210 AST_CLI_DEFINE(handle_modlist, "List modules and info"),
01211
01212 AST_CLI_DEFINE(handle_load, "Load a module by name", .deprecate_cmd = &cli_module_load_deprecated),
01213
01214 AST_CLI_DEFINE(handle_reload, "Reload configuration", .deprecate_cmd = &cli_module_reload_deprecated),
01215
01216 AST_CLI_DEFINE(handle_unload, "Unload a module by name", .deprecate_cmd = &cli_module_unload_deprecated ),
01217
01218 AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
01219
01220 AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
01221 };
01222
01223
01224
01225
01226 static const char cli_rsvd[] = "[]{}|*%";
01227
01228
01229
01230
01231
01232 static int set_full_cmd(struct ast_cli_entry *e)
01233 {
01234 int i;
01235 char buf[80];
01236
01237 ast_join(buf, sizeof(buf), e->cmda);
01238 e->_full_cmd = ast_strdup(buf);
01239 if (!e->_full_cmd) {
01240 ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
01241 return -1;
01242 }
01243 e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
01244 for (i = 0; e->cmda[i]; i++)
01245 ;
01246 e->args = i;
01247 return 0;
01248 }
01249
01250
01251 void ast_builtins_init(void)
01252 {
01253 ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
01254 }
01255
01256 static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
01257 {
01258 if (e) {
01259 return AST_LIST_NEXT(e, list);
01260 } else {
01261 return AST_LIST_FIRST(&helpers);
01262 }
01263 }
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276 static int word_match(const char *cmd, const char *cli_word)
01277 {
01278 int l;
01279 char *pos;
01280
01281 if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
01282 return -1;
01283 if (!strchr(cli_rsvd, cli_word[0]))
01284 return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
01285
01286 l = strlen(cmd);
01287
01288 if (l > 0 && cli_word[0] == '%') {
01289 return 1;
01290 }
01291 pos = strcasestr(cli_word, cmd);
01292 if (pos == NULL)
01293 return cli_word[0] == '[' ? 0 : -1;
01294 if (pos == cli_word)
01295 return -1;
01296 if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
01297 return 1;
01298 return -1;
01299 }
01300
01301
01302
01303
01304
01305 static char *is_prefix(const char *word, const char *token,
01306 int pos, int *actual)
01307 {
01308 int lw;
01309 char *s, *t1;
01310
01311 *actual = 0;
01312 if (ast_strlen_zero(token))
01313 return NULL;
01314 if (ast_strlen_zero(word))
01315 word = "";
01316 lw = strlen(word);
01317 if (strcspn(word, cli_rsvd) != lw)
01318 return NULL;
01319 if (strchr(cli_rsvd, token[0]) == NULL) {
01320 if (strncasecmp(token, word, lw))
01321 return NULL;
01322 *actual = 1;
01323 return (pos != 0) ? NULL : ast_strdup(token);
01324 }
01325
01326
01327
01328
01329 t1 = ast_strdupa(token + 1);
01330 while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
01331 if (*s == '%')
01332 continue;
01333 if (strncasecmp(s, word, lw))
01334 continue;
01335 (*actual)++;
01336 if (pos-- == 0)
01337 return ast_strdup(s);
01338 }
01339 return NULL;
01340 }
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352 static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
01353 {
01354 int matchlen = -1;
01355 struct ast_cli_entry *cand = NULL, *e=NULL;
01356
01357 while ( (e = cli_next(e)) ) {
01358
01359 char * const *src = cmds;
01360 char * const *dst = e->cmda;
01361 int n = 0;
01362 for (;; dst++, src += n) {
01363 n = word_match(*src, *dst);
01364 if (n < 0)
01365 break;
01366 }
01367 if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
01368
01369 if (ast_strlen_zero(*src))
01370 break;
01371
01372 if (match_type != 0)
01373 continue;
01374
01375 } else {
01376 if (ast_strlen_zero(*src))
01377 continue;
01378
01379
01380
01381
01382 if (match_type != -1 || !ast_strlen_zero(src[1]) ||
01383 !ast_strlen_zero(dst[1]))
01384 continue;
01385
01386 }
01387 if (src - cmds > matchlen) {
01388 matchlen = src - cmds;
01389 cand = e;
01390 }
01391 }
01392 return e ? e : cand;
01393 }
01394
01395 static char *find_best(char *argv[])
01396 {
01397 static char cmdline[80];
01398 int x;
01399
01400 char *myargv[AST_MAX_CMD_LEN];
01401 for (x=0;x<AST_MAX_CMD_LEN;x++)
01402 myargv[x]=NULL;
01403 AST_RWLIST_RDLOCK(&helpers);
01404 for (x=0;argv[x];x++) {
01405 myargv[x] = argv[x];
01406 if (!find_cli(myargv, -1))
01407 break;
01408 }
01409 AST_RWLIST_UNLOCK(&helpers);
01410 ast_join(cmdline, sizeof(cmdline), myargv);
01411 return cmdline;
01412 }
01413
01414 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01415 {
01416 if (e->deprecate_cmd) {
01417 __ast_cli_unregister(e->deprecate_cmd, e);
01418 }
01419 if (e->inuse) {
01420 ast_log(LOG_WARNING, "Can't remove command that is in use\n");
01421 } else {
01422 AST_RWLIST_WRLOCK(&helpers);
01423 AST_RWLIST_REMOVE(&helpers, e, list);
01424 AST_RWLIST_UNLOCK(&helpers);
01425 ast_free(e->_full_cmd);
01426 e->_full_cmd = NULL;
01427 if (e->handler) {
01428
01429 char *cmda = (char *) e->cmda;
01430 memset(cmda, '\0', sizeof(e->cmda));
01431 ast_free(e->command);
01432 e->command = NULL;
01433 e->usage = NULL;
01434 }
01435 }
01436 return 0;
01437 }
01438
01439 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01440 {
01441 struct ast_cli_entry *cur;
01442 int i, lf, ret = -1;
01443
01444 struct ast_cli_args a;
01445 char **dst = (char **)e->cmda;
01446 char *s;
01447
01448 memset(&a, '\0', sizeof(a));
01449 e->handler(e, CLI_INIT, &a);
01450
01451 s = ast_skip_blanks(e->command);
01452 s = e->command = ast_strdup(s);
01453 for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
01454 *dst++ = s;
01455 s = ast_skip_nonblanks(s);
01456 if (*s == '\0')
01457 break;
01458 *s++ = '\0';
01459 s = ast_skip_blanks(s);
01460 }
01461 *dst++ = NULL;
01462
01463 AST_RWLIST_WRLOCK(&helpers);
01464
01465 if (find_cli(e->cmda, 1)) {
01466 ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", e->_full_cmd);
01467 goto done;
01468 }
01469 if (set_full_cmd(e))
01470 goto done;
01471 if (!ed) {
01472 e->deprecated = 0;
01473 } else {
01474 e->deprecated = 1;
01475 e->summary = ed->summary;
01476 e->usage = ed->usage;
01477
01478
01479
01480
01481
01482 e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
01483 }
01484
01485 lf = e->cmdlen;
01486 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
01487 int len = cur->cmdlen;
01488 if (lf < len)
01489 len = lf;
01490 if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
01491 AST_RWLIST_INSERT_BEFORE_CURRENT(e, list);
01492 break;
01493 }
01494 }
01495 AST_RWLIST_TRAVERSE_SAFE_END;
01496
01497 if (!cur)
01498 AST_RWLIST_INSERT_TAIL(&helpers, e, list);
01499 ret = 0;
01500
01501 done:
01502 AST_RWLIST_UNLOCK(&helpers);
01503
01504 if (e->deprecate_cmd) {
01505
01506 __ast_cli_register(e->deprecate_cmd, e);
01507 }
01508
01509 return ret;
01510 }
01511
01512
01513 int ast_cli_unregister(struct ast_cli_entry *e)
01514 {
01515 return __ast_cli_unregister(e, NULL);
01516 }
01517
01518
01519 int ast_cli_register(struct ast_cli_entry *e)
01520 {
01521 return __ast_cli_register(e, NULL);
01522 }
01523
01524
01525
01526
01527 int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
01528 {
01529 int i, res = 0;
01530
01531 for (i = 0; i < len; i++)
01532 res |= ast_cli_register(e + i);
01533
01534 return res;
01535 }
01536
01537 int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
01538 {
01539 int i, res = 0;
01540
01541 for (i = 0; i < len; i++)
01542 res |= ast_cli_unregister(e + i);
01543
01544 return res;
01545 }
01546
01547
01548
01549
01550
01551 static char *help1(int fd, char *match[], int locked)
01552 {
01553 char matchstr[80] = "";
01554 struct ast_cli_entry *e = NULL;
01555 int len = 0;
01556 int found = 0;
01557
01558 if (match) {
01559 ast_join(matchstr, sizeof(matchstr), match);
01560 len = strlen(matchstr);
01561 }
01562 if (!locked)
01563 AST_RWLIST_RDLOCK(&helpers);
01564 while ( (e = cli_next(e)) ) {
01565
01566 if (e->_full_cmd[0] == '_')
01567 continue;
01568
01569 if (e->deprecated)
01570 continue;
01571 if (match && strncasecmp(matchstr, e->_full_cmd, len))
01572 continue;
01573 ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
01574 found++;
01575 }
01576 if (!locked)
01577 AST_RWLIST_UNLOCK(&helpers);
01578 if (!found && matchstr[0])
01579 ast_cli(fd, "No such command '%s'.\n", matchstr);
01580 return CLI_SUCCESS;
01581 }
01582
01583 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01584 {
01585 char fullcmd[80];
01586 struct ast_cli_entry *my_e;
01587 char *res = CLI_SUCCESS;
01588
01589 if (cmd == CLI_INIT) {
01590 e->command = "help";
01591 e->usage =
01592 "Usage: help [topic]\n"
01593 " When called with a topic as an argument, displays usage\n"
01594 " information on the given command. If called without a\n"
01595 " topic, it provides a list of commands.\n";
01596 return NULL;
01597
01598 } else if (cmd == CLI_GENERATE) {
01599
01600 int l = strlen(a->line);
01601
01602 if (l > 5)
01603 l = 5;
01604
01605 return __ast_cli_generator(a->line + l, a->word, a->n, 0);
01606 }
01607 if (a->argc == 1)
01608 return help1(a->fd, NULL, 0);
01609
01610 AST_RWLIST_RDLOCK(&helpers);
01611 my_e = find_cli(a->argv + 1, 1);
01612 if (!my_e) {
01613 res = help1(a->fd, a->argv + 1, 1 );
01614 AST_RWLIST_UNLOCK(&helpers);
01615 return res;
01616 }
01617 if (my_e->usage)
01618 ast_cli(a->fd, "%s", my_e->usage);
01619 else {
01620 ast_join(fullcmd, sizeof(fullcmd), a->argv+1);
01621 ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd);
01622 }
01623 AST_RWLIST_UNLOCK(&helpers);
01624 return res;
01625 }
01626
01627 static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
01628 {
01629 char *duplicate, *cur;
01630 int x = 0;
01631 int quoted = 0;
01632 int escaped = 0;
01633 int whitespace = 1;
01634 int dummy = 0;
01635
01636 if (trailingwhitespace == NULL)
01637 trailingwhitespace = &dummy;
01638 *trailingwhitespace = 0;
01639 if (s == NULL)
01640 return NULL;
01641
01642 if (!(duplicate = ast_strdup(s)))
01643 return NULL;
01644
01645 cur = duplicate;
01646
01647 for (; *s ; s++) {
01648 if (x >= max - 1) {
01649 ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
01650 break;
01651 }
01652 if (*s == '"' && !escaped) {
01653 quoted = !quoted;
01654 if (quoted && whitespace) {
01655
01656 argv[x++] = cur;
01657 whitespace = 0;
01658 }
01659 } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
01660
01661
01662
01663
01664 if (!whitespace) {
01665 *cur++ = '\0';
01666 whitespace = 1;
01667 }
01668 } else if (*s == '\\' && !escaped) {
01669 escaped = 1;
01670 } else {
01671 if (whitespace) {
01672
01673 argv[x++] = cur;
01674 whitespace = 0;
01675 }
01676 *cur++ = *s;
01677 escaped = 0;
01678 }
01679 }
01680
01681 *cur++ = '\0';
01682
01683
01684
01685
01686 argv[x] = NULL;
01687 *argc = x;
01688 *trailingwhitespace = whitespace;
01689 return duplicate;
01690 }
01691
01692
01693 int ast_cli_generatornummatches(const char *text, const char *word)
01694 {
01695 int matches = 0, i = 0;
01696 char *buf = NULL, *oldbuf = NULL;
01697
01698 while ((buf = ast_cli_generator(text, word, i++))) {
01699 if (!oldbuf || strcmp(buf,oldbuf))
01700 matches++;
01701 if (oldbuf)
01702 ast_free(oldbuf);
01703 oldbuf = buf;
01704 }
01705 if (oldbuf)
01706 ast_free(oldbuf);
01707 return matches;
01708 }
01709
01710 char **ast_cli_completion_matches(const char *text, const char *word)
01711 {
01712 char **match_list = NULL, *retstr, *prevstr;
01713 size_t match_list_len, max_equal, which, i;
01714 int matches = 0;
01715
01716
01717 match_list_len = 1;
01718 while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
01719 if (matches + 1 >= match_list_len) {
01720 match_list_len <<= 1;
01721 if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
01722 return NULL;
01723 }
01724 match_list[++matches] = retstr;
01725 }
01726
01727 if (!match_list)
01728 return match_list;
01729
01730
01731
01732
01733 prevstr = match_list[1];
01734 max_equal = strlen(prevstr);
01735 for (which = 2; which <= matches; which++) {
01736 for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
01737 continue;
01738 max_equal = i;
01739 }
01740
01741 if (!(retstr = ast_malloc(max_equal + 1)))
01742 return NULL;
01743
01744 ast_copy_string(retstr, match_list[1], max_equal + 1);
01745 match_list[0] = retstr;
01746
01747
01748 if (matches + 1 >= match_list_len) {
01749 if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
01750 return NULL;
01751 }
01752 match_list[matches + 1] = NULL;
01753
01754 return match_list;
01755 }
01756
01757
01758 static int more_words (char * const *dst)
01759 {
01760 int i;
01761 for (i = 0; dst[i]; i++) {
01762 if (dst[i][0] != '[')
01763 return -1;
01764 }
01765 return 0;
01766 }
01767
01768
01769
01770
01771 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
01772 {
01773 char *argv[AST_MAX_ARGS];
01774 struct ast_cli_entry *e = NULL;
01775 int x = 0, argindex, matchlen;
01776 int matchnum=0;
01777 char *ret = NULL;
01778 char matchstr[80] = "";
01779 int tws = 0;
01780
01781 char *duplicate = parse_args(text, &x, argv, ARRAY_LEN(argv), &tws);
01782
01783 if (!duplicate)
01784 return NULL;
01785
01786
01787 argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
01788
01789
01790 ast_join(matchstr, sizeof(matchstr)-1, argv);
01791 matchlen = strlen(matchstr);
01792 if (tws) {
01793 strcat(matchstr, " ");
01794 if (matchlen)
01795 matchlen++;
01796 }
01797 if (lock)
01798 AST_RWLIST_RDLOCK(&helpers);
01799 while ( (e = cli_next(e)) ) {
01800
01801 int src = 0, dst = 0, n = 0;
01802
01803 if (e->command[0] == '_')
01804 continue;
01805
01806
01807
01808
01809
01810 for (;src < argindex; dst++, src += n) {
01811 n = word_match(argv[src], e->cmda[dst]);
01812 if (n < 0)
01813 break;
01814 }
01815
01816 if (src != argindex && more_words(e->cmda + dst))
01817 continue;
01818 ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n);
01819 matchnum += n;
01820 if (ret) {
01821
01822
01823
01824
01825 if (matchnum > state)
01826 break;
01827 ast_free(ret);
01828 ret = NULL;
01829 } else if (ast_strlen_zero(e->cmda[dst])) {
01830
01831
01832
01833
01834
01835 if (e->handler) {
01836 struct ast_cli_args a = {
01837 .line = matchstr, .word = word,
01838 .pos = argindex,
01839 .n = state - matchnum };
01840 ret = e->handler(e, CLI_GENERATE, &a);
01841 }
01842 if (ret)
01843 break;
01844 }
01845 }
01846 if (lock)
01847 AST_RWLIST_UNLOCK(&helpers);
01848 ast_free(duplicate);
01849 return ret;
01850 }
01851
01852 char *ast_cli_generator(const char *text, const char *word, int state)
01853 {
01854 return __ast_cli_generator(text, word, state, 1);
01855 }
01856
01857 int ast_cli_command(int fd, const char *s)
01858 {
01859 char *args[AST_MAX_ARGS + 1];
01860 struct ast_cli_entry *e;
01861 int x;
01862 char *duplicate = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
01863 char *retval = NULL;
01864 struct ast_cli_args a = {
01865 .fd = fd, .argc = x, .argv = args+1 };
01866
01867 if (duplicate == NULL)
01868 return -1;
01869
01870 if (x < 1)
01871 goto done;
01872
01873 AST_RWLIST_RDLOCK(&helpers);
01874 e = find_cli(args + 1, 0);
01875 if (e)
01876 ast_atomic_fetchadd_int(&e->inuse, 1);
01877 AST_RWLIST_UNLOCK(&helpers);
01878 if (e == NULL) {
01879 ast_cli(fd, "No such command '%s' (type 'help %s' for other possible commands)\n", s, find_best(args + 1));
01880 goto done;
01881 }
01882
01883
01884
01885
01886 args[0] = (char *)e;
01887
01888 retval = e->handler(e, CLI_HANDLER, &a);
01889
01890 if (retval == CLI_SHOWUSAGE) {
01891 ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
01892 AST_RWLIST_RDLOCK(&helpers);
01893 if (e->deprecated)
01894 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);
01895 AST_RWLIST_UNLOCK(&helpers);
01896 } else {
01897 if (retval == CLI_FAILURE)
01898 ast_cli(fd, "Command '%s' failed.\n", s);
01899 AST_RWLIST_RDLOCK(&helpers);
01900 if (e->deprecated == 1) {
01901 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);
01902 e->deprecated = 2;
01903 }
01904 AST_RWLIST_UNLOCK(&helpers);
01905 }
01906 ast_atomic_fetchadd_int(&e->inuse, -1);
01907 done:
01908 ast_free(duplicate);
01909 return 0;
01910 }
01911
01912 int ast_cli_command_multiple(int fd, size_t size, const char *s)
01913 {
01914 char cmd[512];
01915 int x, y = 0, count = 0;
01916
01917 for (x = 0; x < size; x++) {
01918 cmd[y] = s[x];
01919 y++;
01920 if (s[x] == '\0') {
01921 ast_cli_command(fd, cmd);
01922 y = 0;
01923 count++;
01924 }
01925 }
01926 return count;
01927 }