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