![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
cli.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2006, Digium, Inc. 00005 * 00006 * Mark Spencer <markster@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! \file 00020 * 00021 * \brief Standard Command Line Interface 00022 * 00023 * \author Mark Spencer <markster@digium.com> 00024 */ 00025 00026 #include "asterisk.h" 00027 00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 52542 $") 00029 00030 #include <unistd.h> 00031 #include <stdlib.h> 00032 #include <sys/signal.h> 00033 #include <stdio.h> 00034 #include <signal.h> 00035 #include <string.h> 00036 #include <ctype.h> 00037 #include <regex.h> 00038 00039 #include "asterisk/logger.h" 00040 #include "asterisk/options.h" 00041 #include "asterisk/cli.h" 00042 #include "asterisk/linkedlists.h" 00043 #include "asterisk/module.h" 00044 #include "asterisk/pbx.h" 00045 #include "asterisk/channel.h" 00046 #include "asterisk/utils.h" 00047 #include "asterisk/app.h" 00048 #include "asterisk/lock.h" 00049 #include "editline/readline/readline.h" 00050 #include "asterisk/threadstorage.h" 00051 00052 AST_THREADSTORAGE(ast_cli_buf); 00053 00054 /*! \brief Initial buffer size for resulting strings in ast_cli() */ 00055 #define AST_CLI_INITLEN 256 00056 00057 void ast_cli(int fd, char *fmt, ...) 00058 { 00059 int res; 00060 struct ast_str *buf; 00061 va_list ap; 00062 00063 if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN))) 00064 return; 00065 00066 va_start(ap, fmt); 00067 res = ast_str_set_va(&buf, 0, fmt, ap); 00068 va_end(ap); 00069 00070 if (res != AST_DYNSTR_BUILD_FAILED) 00071 ast_carefulwrite(fd, buf->str, strlen(buf->str), 100); 00072 } 00073 00074 static AST_LIST_HEAD_STATIC(helpers, ast_cli_entry); 00075 00076 static const char logger_mute_help[] = 00077 "Usage: logger mute\n" 00078 " Disables logging output to the current console, making it possible to\n" 00079 " gather information without being disturbed by scrolling lines.\n"; 00080 00081 static const char softhangup_help[] = 00082 "Usage: soft hangup <channel>\n" 00083 " Request that a channel be hung up. The hangup takes effect\n" 00084 " the next time the driver reads or writes from the channel\n"; 00085 00086 static const char group_show_channels_help[] = 00087 "Usage: group show channels [pattern]\n" 00088 " Lists all currently active channels with channel group(s) specified.\n" 00089 " Optional regular expression pattern is matched to group names for each\n" 00090 " channel.\n"; 00091 00092 static char *complete_fn(const char *word, int state) 00093 { 00094 char *c; 00095 char filename[256]; 00096 00097 if (word[0] == '/') 00098 ast_copy_string(filename, word, sizeof(filename)); 00099 else 00100 snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word); 00101 00102 /* XXX the following function is not reentrant, so we better not use it */ 00103 c = filename_completion_function(filename, state); 00104 00105 if (c && word[0] != '/') 00106 c += (strlen(ast_config_AST_MODULE_DIR) + 1); 00107 00108 return c ? strdup(c) : c; 00109 } 00110 00111 static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00112 { 00113 /* "module load <mod>" */ 00114 switch (cmd) { 00115 case CLI_INIT: 00116 e->command = "module load"; 00117 e->usage = 00118 "Usage: module load <module name>\n" 00119 " Loads the specified module into Asterisk.\n"; 00120 return NULL; 00121 00122 case CLI_GENERATE: 00123 if (a->pos != e->args) 00124 return NULL; 00125 return complete_fn(a->word, a->n); 00126 } 00127 if (a->argc != e->args + 1) 00128 return CLI_SHOWUSAGE; 00129 if (ast_load_resource(a->argv[e->args])) { 00130 ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]); 00131 return CLI_FAILURE; 00132 } 00133 return CLI_SUCCESS; 00134 } 00135 00136 static char *handle_load_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00137 { 00138 char *res = handle_load(e, cmd, a); 00139 if (cmd == CLI_INIT) 00140 e->command = "load"; 00141 return res; 00142 } 00143 00144 static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00145 { 00146 int x; 00147 00148 switch (cmd) { 00149 case CLI_INIT: 00150 e->command = "module reload"; 00151 e->usage = 00152 "Usage: module reload [module ...]\n" 00153 " Reloads configuration files for all listed modules which support\n" 00154 " reloading, or for all supported modules if none are listed.\n"; 00155 return NULL; 00156 00157 case CLI_GENERATE: 00158 return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1); 00159 } 00160 if (a->argc == e->args) { 00161 ast_module_reload(NULL); 00162 return CLI_SUCCESS; 00163 } 00164 for (x = e->args; x < a->argc; x++) { 00165 int res = ast_module_reload(a->argv[x]); 00166 /* XXX reload has multiple error returns, including -1 on error and 2 on success */ 00167 switch (res) { 00168 case 0: 00169 ast_cli(a->fd, "No such module '%s'\n", a->argv[x]); 00170 break; 00171 case 1: 00172 ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]); 00173 break; 00174 } 00175 } 00176 return CLI_SUCCESS; 00177 } 00178 00179 static char *handle_reload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00180 { 00181 char *s = handle_reload(e, cmd, a); 00182 if (cmd == CLI_INIT) /* override command name */ 00183 e->command = "reload"; 00184 return s; 00185 } 00186 00187 static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00188 { 00189 int oldval = option_verbose; 00190 int newlevel; 00191 int atleast = 0; 00192 int fd = a->fd; 00193 int argc = a->argc; 00194 char **argv = a->argv; 00195 int *dst; 00196 char *what; 00197 00198 switch (cmd) { 00199 case CLI_INIT: 00200 e->command = "core set {debug|verbose} [off|atleast]"; 00201 e->usage = 00202 "Usage: core set {debug|verbose} [atleast] <level>\n" 00203 " core set {debug|verbose} off\n" 00204 " Sets level of debug or verbose messages to be displayed.\n" 00205 " 0 or off means no messages should be displayed.\n" 00206 " Equivalent to -d[d[...]] or -v[v[v...]] on startup\n"; 00207 return NULL; 00208 00209 case CLI_GENERATE: 00210 return NULL; 00211 } 00212 /* all the above return, so we proceed with the handler. 00213 * we are guaranteed to be called with argc >= e->args; 00214 */ 00215 00216 if (argc < e->args) 00217 return CLI_SHOWUSAGE; 00218 if (!strcasecmp(argv[e->args - 2], "debug")) { 00219 dst = &option_debug; 00220 what = "Core debug"; 00221 } else { 00222 dst = &option_verbose; 00223 what = "Verbosity"; 00224 } 00225 if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) { 00226 newlevel = 0; 00227 goto done; 00228 } 00229 if (!strcasecmp(argv[e->args-1], "atleast")) 00230 atleast = 1; 00231 if (argc != e->args + atleast) 00232 return CLI_SHOWUSAGE; 00233 if (sscanf(argv[e->args + atleast - 1], "%d", &newlevel) != 1) 00234 return CLI_SHOWUSAGE; 00235 00236 done: 00237 if (!atleast || newlevel > *dst) 00238 *dst = newlevel; 00239 if (oldval > 0 && *dst == 0) 00240 ast_cli(fd, "%s is now OFF\n", what); 00241 else if (*dst > 0) { 00242 if (oldval == *dst) 00243 ast_cli(fd, "%s is at least %d\n", what, *dst); 00244 else 00245 ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst); 00246 } 00247 00248 return CLI_SUCCESS; 00249 } 00250 00251 static int handle_logger_mute(int fd, int argc, char *argv[]) 00252 { 00253 if (argc != 2) 00254 return RESULT_SHOWUSAGE; 00255 ast_console_toggle_mute(fd); 00256 return RESULT_SUCCESS; 00257 } 00258 00259 static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00260 { 00261 /* "module unload mod_1 [mod_2 .. mod_N]" */ 00262 int x; 00263 int force = AST_FORCE_SOFT; 00264 char *s; 00265 00266 switch (cmd) { 00267 case CLI_INIT: 00268 e->command = "module unload"; 00269 e->usage = 00270 "Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n" 00271 " Unloads the specified module from Asterisk. The -f\n" 00272 " option causes the module to be unloaded even if it is\n" 00273 " in use (may cause a crash) and the -h module causes the\n" 00274 " module to be unloaded even if the module says it cannot, \n" 00275 " which almost always will cause a crash.\n"; 00276 return NULL; 00277 00278 case CLI_GENERATE: 00279 return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0); 00280 } 00281 if (a->argc < e->args + 1) 00282 return CLI_SHOWUSAGE; 00283 x = e->args; /* first argument */ 00284 s = a->argv[x]; 00285 if (s[0] == '-') { 00286 if (s[1] == 'f') 00287 force = AST_FORCE_FIRM; 00288 else if (s[1] == 'h') 00289 force = AST_FORCE_HARD; 00290 else 00291 return CLI_SHOWUSAGE; 00292 if (a->argc < e->args + 2) /* need at least one module name */ 00293 return CLI_SHOWUSAGE; 00294 x++; /* skip this argument */ 00295 } 00296 00297 for (; x < a->argc; x++) { 00298 if (ast_unload_resource(a->argv[x], force)) { 00299 ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]); 00300 return CLI_FAILURE; 00301 } 00302 } 00303 return CLI_SUCCESS; 00304 } 00305 00306 static char *handle_unload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00307 { 00308 char *res = handle_unload(e, cmd, a); 00309 if (cmd == CLI_INIT) 00310 e->command = "unload"; /* XXX override */ 00311 return res; 00312 } 00313 00314 #define MODLIST_FORMAT "%-30s %-40.40s %-10d\n" 00315 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n" 00316 00317 AST_MUTEX_DEFINE_STATIC(climodentrylock); 00318 static int climodentryfd = -1; 00319 00320 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like) 00321 { 00322 /* Comparing the like with the module */ 00323 if (strcasestr(module, like) ) { 00324 ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt); 00325 return 1; 00326 } 00327 return 0; 00328 } 00329 00330 static void print_uptimestr(int fd, time_t timeval, const char *prefix, int printsec) 00331 { 00332 int x; /* the main part - years, weeks, etc. */ 00333 struct ast_str *out; 00334 00335 #define SECOND (1) 00336 #define MINUTE (SECOND*60) 00337 #define HOUR (MINUTE*60) 00338 #define DAY (HOUR*24) 00339 #define WEEK (DAY*7) 00340 #define YEAR (DAY*365) 00341 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */ 00342 if (timeval < 0) /* invalid, nothing to show */ 00343 return; 00344 00345 if (printsec) { /* plain seconds output */ 00346 ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval); 00347 return; 00348 } 00349 out = ast_str_alloca(256); 00350 if (timeval > YEAR) { 00351 x = (timeval / YEAR); 00352 timeval -= (x * YEAR); 00353 ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval)); 00354 } 00355 if (timeval > WEEK) { 00356 x = (timeval / WEEK); 00357 timeval -= (x * WEEK); 00358 ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval)); 00359 } 00360 if (timeval > DAY) { 00361 x = (timeval / DAY); 00362 timeval -= (x * DAY); 00363 ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval)); 00364 } 00365 if (timeval > HOUR) { 00366 x = (timeval / HOUR); 00367 timeval -= (x * HOUR); 00368 ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval)); 00369 } 00370 if (timeval > MINUTE) { 00371 x = (timeval / MINUTE); 00372 timeval -= (x * MINUTE); 00373 ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval)); 00374 } 00375 x = timeval; 00376 if (x > 0 || out->used == 0) /* if there is nothing, print 0 seconds */ 00377 ast_str_append(&out, 0, "%d second%s ", x, ESS(x)); 00378 ast_cli(fd, "%s: %s\n", prefix, out->str); 00379 } 00380 00381 static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00382 { 00383 time_t curtime; 00384 int printsec; 00385 00386 switch (cmd) { 00387 case CLI_INIT: 00388 e->command = "core show uptime"; 00389 e->usage = 00390 "Usage: core show uptime [seconds]\n" 00391 " Shows Asterisk uptime information.\n" 00392 " The seconds word returns the uptime in seconds only.\n"; 00393 return NULL; 00394 00395 case CLI_GENERATE: 00396 return (a->pos > e->args || a->n > 0) ? NULL : "seconds"; 00397 } 00398 /* regular handler */ 00399 if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds")) 00400 printsec = 1; 00401 else if (a->argc == e->args) 00402 printsec = 0; 00403 else 00404 return CLI_SHOWUSAGE; 00405 curtime = time(NULL); 00406 if (ast_startuptime) 00407 print_uptimestr(a->fd, curtime - ast_startuptime, "System uptime", printsec); 00408 if (ast_lastreloadtime) 00409 print_uptimestr(a->fd, curtime - ast_lastreloadtime, "Last reload", printsec); 00410 return CLI_SUCCESS; 00411 } 00412 00413 static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00414 { 00415 char *like; 00416 00417 switch (cmd) { 00418 case CLI_INIT: 00419 e->command = "module show [like]"; 00420 e->usage = 00421 "Usage: module show [like keyword]\n" 00422 " Shows Asterisk modules currently in use, and usage statistics.\n"; 00423 return NULL; 00424 00425 case CLI_GENERATE: 00426 if (a->pos == e->args) 00427 return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0); 00428 else 00429 return NULL; 00430 } 00431 /* all the above return, so we proceed with the handler. 00432 * we are guaranteed to have argc >= e->args 00433 */ 00434 if (a->argc == e->args - 1) 00435 like = ""; 00436 else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") ) 00437 like = a->argv[e->args]; 00438 else 00439 return CLI_SHOWUSAGE; 00440 00441 ast_mutex_lock(&climodentrylock); 00442 climodentryfd = a->fd; /* global, protected by climodentrylock */ 00443 ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count"); 00444 ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like)); 00445 climodentryfd = -1; 00446 ast_mutex_unlock(&climodentrylock); 00447 return RESULT_SUCCESS; 00448 } 00449 #undef MODLIST_FORMAT 00450 #undef MODLIST_FORMAT2 00451 00452 static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00453 { 00454 #define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n" 00455 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n" 00456 #define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s\n" 00457 #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" 00458 #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" 00459 00460 struct ast_channel *c = NULL; 00461 int numchans = 0, concise = 0, verbose = 0, count = 0; 00462 int fd, argc; 00463 char **argv; 00464 00465 switch (cmd) { 00466 case CLI_INIT: 00467 e->command = "core show channels [concise|verbose|count]"; 00468 e->usage = 00469 "Usage: core show channels [concise|verbose|count]\n" 00470 " Lists currently defined channels and some information about them. If\n" 00471 " 'concise' is specified, the format is abridged and in a more easily\n" 00472 " machine parsable format. If 'verbose' is specified, the output includes\n" 00473 " more and longer fields. If 'count' is specified only the channel and call\n" 00474 " count is output.\n"; 00475 return NULL; 00476 00477 case CLI_GENERATE: 00478 return NULL; 00479 } 00480 fd = a->fd; 00481 argc = a->argc; 00482 argv = a->argv; 00483 00484 if (a->argc == e->args) { 00485 if (!strcasecmp(argv[e->args-1],"concise")) 00486 concise = 1; 00487 else if (!strcasecmp(argv[e->args-1],"verbose")) 00488 verbose = 1; 00489 else if (!strcasecmp(argv[e->args-1],"count")) 00490 count = 1; 00491 else 00492 return CLI_SHOWUSAGE; 00493 } else if (a->argc != e->args - 1) 00494 return CLI_SHOWUSAGE; 00495 00496 if (!count) { 00497 if (!concise && !verbose) 00498 ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)"); 00499 else if (verbose) 00500 ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 00501 "CallerID", "Duration", "Accountcode", "BridgedTo"); 00502 } 00503 00504 while ((c = ast_channel_walk_locked(c)) != NULL) { 00505 struct ast_channel *bc = ast_bridged_channel(c); 00506 char durbuf[10] = "-"; 00507 00508 if (!count) { 00509 if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) { 00510 int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000); 00511 if (verbose) { 00512 int durh = duration / 3600; 00513 int durm = (duration % 3600) / 60; 00514 int durs = duration % 60; 00515 snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs); 00516 } else { 00517 snprintf(durbuf, sizeof(durbuf), "%d", duration); 00518 } 00519 } 00520 if (concise) { 00521 ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state), 00522 c->appl ? c->appl : "(None)", 00523 S_OR(c->data, ""), /* XXX different from verbose ? */ 00524 S_OR(c->cid.cid_num, ""), 00525 S_OR(c->accountcode, ""), 00526 c->amaflags, 00527 durbuf, 00528 bc ? bc->name : "(None)"); 00529 } else if (verbose) { 00530 ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state), 00531 c->appl ? c->appl : "(None)", 00532 c->data ? S_OR(c->data, "(Empty)" ): "(None)", 00533 S_OR(c->cid.cid_num, ""), 00534 durbuf, 00535 S_OR(c->accountcode, ""), 00536 bc ? bc->name : "(None)"); 00537 } else { 00538 char locbuf[40] = "(None)"; 00539 char appdata[40] = "(None)"; 00540 00541 if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 00542 snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority); 00543 if (c->appl) 00544 snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, S_OR(c->data, "")); 00545 ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata); 00546 } 00547 } 00548 numchans++; 00549 ast_channel_unlock(c); 00550 } 00551 if (!concise) { 00552 ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans)); 00553 if (option_maxcalls) 00554 ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n", 00555 ast_active_calls(), option_maxcalls, ESS(ast_active_calls()), 00556 ((double)ast_active_calls() / (double)option_maxcalls) * 100.0); 00557 else 00558 ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls())); 00559 } 00560 return CLI_SUCCESS; 00561 00562 #undef FORMAT_STRING 00563 #undef FORMAT_STRING2 00564 #undef CONCISE_FORMAT_STRING 00565 #undef VERBOSE_FORMAT_STRING 00566 #undef VERBOSE_FORMAT_STRING2 00567 } 00568 00569 static const char showchan_help[] = 00570 "Usage: core show channel <channel>\n" 00571 " Shows lots of information about the specified channel.\n"; 00572 00573 static const char commandcomplete_help[] = 00574 "Usage: _command complete \"<line>\" text state\n" 00575 " This function is used internally to help with command completion and should.\n" 00576 " never be called by the user directly.\n"; 00577 00578 static const char commandnummatches_help[] = 00579 "Usage: _command nummatches \"<line>\" text \n" 00580 " This function is used internally to help with command completion and should.\n" 00581 " never be called by the user directly.\n"; 00582 00583 static const char commandmatchesarray_help[] = 00584 "Usage: _command matchesarray \"<line>\" text \n" 00585 " This function is used internally to help with command completion and should.\n" 00586 " never be called by the user directly.\n"; 00587 00588 static int handle_softhangup(int fd, int argc, char *argv[]) 00589 { 00590 struct ast_channel *c=NULL; 00591 if (argc != 3) 00592 return RESULT_SHOWUSAGE; 00593 c = ast_get_channel_by_name_locked(argv[2]); 00594 if (c) { 00595 ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name); 00596 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT); 00597 ast_channel_unlock(c); 00598 } else 00599 ast_cli(fd, "%s is not a known channel\n", argv[2]); 00600 return RESULT_SUCCESS; 00601 } 00602 00603 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock); 00604 00605 static int handle_commandmatchesarray(int fd, int argc, char *argv[]) 00606 { 00607 char *buf, *obuf; 00608 int buflen = 2048; 00609 int len = 0; 00610 char **matches; 00611 int x, matchlen; 00612 00613 if (argc != 4) 00614 return RESULT_SHOWUSAGE; 00615 if (!(buf = ast_malloc(buflen))) 00616 return RESULT_FAILURE; 00617 buf[len] = '\0'; 00618 matches = ast_cli_completion_matches(argv[2], argv[3]); 00619 if (matches) { 00620 for (x=0; matches[x]; x++) { 00621 matchlen = strlen(matches[x]) + 1; 00622 if (len + matchlen >= buflen) { 00623 buflen += matchlen * 3; 00624 obuf = buf; 00625 if (!(buf = ast_realloc(obuf, buflen))) 00626 /* Memory allocation failure... Just free old buffer and be done */ 00627 free(obuf); 00628 } 00629 if (buf) 00630 len += sprintf( buf + len, "%s ", matches[x]); 00631 free(matches[x]); 00632 matches[x] = NULL; 00633 } 00634 free(matches); 00635 } 00636 00637 if (buf) { 00638 ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF); 00639 free(buf); 00640 } else 00641 ast_cli(fd, "NULL\n"); 00642 00643 return RESULT_SUCCESS; 00644 } 00645 00646 00647 00648 static int handle_commandnummatches(int fd, int argc, char *argv[]) 00649 { 00650 int matches = 0; 00651 00652 if (argc != 4) 00653 return RESULT_SHOWUSAGE; 00654 00655 matches = ast_cli_generatornummatches(argv[2], argv[3]); 00656 00657 ast_cli(fd, "%d", matches); 00658 00659 return RESULT_SUCCESS; 00660 } 00661 00662 static int handle_commandcomplete(int fd, int argc, char *argv[]) 00663 { 00664 char *buf; 00665 00666 if (argc != 5) 00667 return RESULT_SHOWUSAGE; 00668 buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0); 00669 if (buf) { 00670 ast_cli(fd, buf); 00671 free(buf); 00672 } else 00673 ast_cli(fd, "NULL\n"); 00674 return RESULT_SUCCESS; 00675 } 00676 00677 static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00678 { 00679 struct ast_channel *c = NULL; 00680 int is_all, is_off = 0; 00681 00682 switch (cmd) { 00683 case CLI_INIT: 00684 e->command = "core set debug channel"; 00685 e->usage = 00686 "Usage: core set debug channel <all|channel> [off]\n" 00687 " Enables/disables debugging on all or on a specific channel.\n"; 00688 return NULL; 00689 00690 case CLI_GENERATE: 00691 /* XXX remember to handle the optional "off" */ 00692 if (a->pos != e->args) 00693 return NULL; 00694 return a->n == 0 ? strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args); 00695 } 00696 /* 'core set debug channel {all|chan_id}' */ 00697 if (a->argc == e->args + 2) { 00698 if (!strcasecmp(a->argv[e->args + 1], "off")) 00699 is_off = 1; 00700 else 00701 return CLI_SHOWUSAGE; 00702 } else if (a->argc != e->args + 1) 00703 return CLI_SHOWUSAGE; 00704 00705 is_all = !strcasecmp("all", a->argv[e->args]); 00706 if (is_all) { 00707 if (is_off) { 00708 global_fin &= ~DEBUGCHAN_FLAG; 00709 global_fout &= ~DEBUGCHAN_FLAG; 00710 } else { 00711 global_fin |= DEBUGCHAN_FLAG; 00712 global_fout |= DEBUGCHAN_FLAG; 00713 } 00714 c = ast_channel_walk_locked(NULL); 00715 } else { 00716 c = ast_get_channel_by_name_locked(a->argv[e->args]); 00717 if (c == NULL) 00718 ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]); 00719 } 00720 while (c) { 00721 if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) { 00722 if (is_off) { 00723 c->fin &= ~DEBUGCHAN_FLAG; 00724 c->fout &= ~DEBUGCHAN_FLAG; 00725 } else { 00726 c->fin |= DEBUGCHAN_FLAG; 00727 c->fout |= DEBUGCHAN_FLAG; 00728 } 00729 ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name); 00730 } 00731 ast_channel_unlock(c); 00732 if (!is_all) 00733 break; 00734 c = ast_channel_walk_locked(c); 00735 } 00736 ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled"); 00737 return RESULT_SUCCESS; 00738 } 00739 00740 static char *handle_debugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00741 { 00742 char *res; 00743 00744 if (cmd == CLI_HANDLER && a->argc != e->args + 1) 00745 return CLI_SHOWUSAGE; 00746 res = handle_core_set_debug_channel(e, cmd, a); 00747 if (cmd == CLI_INIT) 00748 e->command = "debug channel"; 00749 return res; 00750 } 00751 00752 static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 00753 { 00754 char *res; 00755 if (cmd == CLI_HANDLER) { 00756 if (a->argc != e->args + 1) 00757 return CLI_SHOWUSAGE; 00758 /* pretend we have an extra "off" at the end. We can do this as the array 00759 * is NULL terminated so we overwrite that entry. 00760 */ 00761 a->argv[e->args+1] = "off"; 00762 a->argc++; 00763 } 00764 res = handle_core_set_debug_channel(e, cmd, a); 00765 if (cmd == CLI_INIT) 00766 e->command = "no debug channel"; 00767 return res; 00768 } 00769 00770 static int handle_showchan(int fd, int argc, char *argv[]) 00771 { 00772 struct ast_channel *c=NULL; 00773 struct timeval now; 00774 struct ast_str *out = ast_str_alloca(2048); 00775 char cdrtime[256]; 00776 char nf[256], wf[256], rf[256]; 00777 long elapsed_seconds=0; 00778 int hour=0, min=0, sec=0; 00779 00780 if (argc != 4) 00781 return RESULT_SHOWUSAGE; 00782 now = ast_tvnow(); 00783 c = ast_get_channel_by_name_locked(argv[3]); 00784 if (!c) { 00785 ast_cli(fd, "%s is not a known channel\n", argv[3]); 00786 return RESULT_SUCCESS; 00787 } 00788 if (c->cdr) { 00789 elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec; 00790 hour = elapsed_seconds / 3600; 00791 min = (elapsed_seconds % 3600) / 60; 00792 sec = elapsed_seconds % 60; 00793 snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec); 00794 } else 00795 strcpy(cdrtime, "N/A"); 00796 ast_cli(fd, 00797 " -- General --\n" 00798 " Name: %s\n" 00799 " Type: %s\n" 00800 " UniqueID: %s\n" 00801 " Caller ID: %s\n" 00802 " Caller ID Name: %s\n" 00803 " DNID Digits: %s\n" 00804 " State: %s (%d)\n" 00805 " Rings: %d\n" 00806 " NativeFormats: %s\n" 00807 " WriteFormat: %s\n" 00808 " ReadFormat: %s\n" 00809 " WriteTranscode: %s\n" 00810 " ReadTranscode: %s\n" 00811 "1st File Descriptor: %d\n" 00812 " Frames in: %d%s\n" 00813 " Frames out: %d%s\n" 00814 " Time to Hangup: %ld\n" 00815 " Elapsed Time: %s\n" 00816 " Direct Bridge: %s\n" 00817 "Indirect Bridge: %s\n" 00818 " -- PBX --\n" 00819 " Context: %s\n" 00820 " Extension: %s\n" 00821 " Priority: %d\n" 00822 " Call Group: %llu\n" 00823 " Pickup Group: %llu\n" 00824 " Application: %s\n" 00825 " Data: %s\n" 00826 " Blocking in: %s\n", 00827 c->name, c->tech->type, c->uniqueid, 00828 S_OR(c->cid.cid_num, "(N/A)"), 00829 S_OR(c->cid.cid_name, "(N/A)"), 00830 S_OR(c->cid.cid_dnid, "(N/A)"), ast_state2str(c->_state), c->_state, c->rings, 00831 ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 00832 ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 00833 ast_getformatname_multiple(rf, sizeof(rf), c->readformat), 00834 c->writetrans ? "Yes" : "No", 00835 c->readtrans ? "Yes" : "No", 00836 c->fds[0], 00837 c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", 00838 c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", 00839 (long)c->whentohangup, 00840 cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 00841 c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ), 00842 ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"), 00843 (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)")); 00844 00845 if (pbx_builtin_serialize_variables(c, &out)) 00846 ast_cli(fd," Variables:\n%s\n", out->str); 00847 if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1)) 00848 ast_cli(fd," CDR Variables:\n%s\n", out->str); 00849 00850 ast_channel_unlock(c); 00851 return RESULT_SUCCESS; 00852 } 00853 00854 /* 00855 * helper function to generate CLI matches from a fixed set of values. 00856 * A NULL word is acceptable. 00857 */ 00858 char *ast_cli_complete(const char *word, char *const choices[], int state) 00859 { 00860 int i, which = 0, len; 00861 len = ast_strlen_zero(word) ? 0 : strlen(word); 00862 00863 for (i = 0; choices[i]; i++) { 00864 if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state) 00865 return ast_strdup(choices[i]); 00866 } 00867 return NULL; 00868 } 00869 00870 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos) 00871 { 00872 struct ast_channel *c = NULL; 00873 int which = 0; 00874 int wordlen; 00875 char notfound = '\0'; 00876 char *ret = ¬found; /* so NULL can break the loop */ 00877 00878 if (pos != rpos) 00879 return NULL; 00880 00881 wordlen = strlen(word); 00882 00883 while (ret == ¬found && (c = ast_channel_walk_locked(c))) { 00884 if (!strncasecmp(word, c->name, wordlen) && ++which > state) 00885 ret = ast_strdup(c->name); 00886 ast_channel_unlock(c); 00887 } 00888 return ret == ¬found ? NULL : ret; 00889 } 00890 00891 static char *complete_ch_3(const char *line, const char *word, int pos, int state) 00892 { 00893 return ast_complete_channels(line, word, pos, state, 2); 00894 } 00895 00896 static char *complete_ch_4(const char *line, const char *word, int pos, int state) 00897 { 00898 return ast_complete_channels(line, word, pos, state, 3); 00899 } 00900 00901 static int group_show_channels(int fd, int argc, char *argv[]) 00902 { 00903 #define FORMAT_STRING "%-25s %-20s %-20s\n" 00904 00905 struct ast_channel *c = NULL; 00906 int numchans = 0; 00907 struct ast_var_t *current; 00908 struct varshead *headp; 00909 regex_t regexbuf; 00910 int havepattern = 0; 00911 00912 if (argc < 3 || argc > 4) 00913 return RESULT_SHOWUSAGE; 00914 00915 if (argc == 4) { 00916 if (regcomp(®exbuf, argv[3], REG_EXTENDED | REG_NOSUB)) 00917 return RESULT_SHOWUSAGE; 00918 havepattern = 1; 00919 } 00920 00921 ast_cli(fd, FORMAT_STRING, "Channel", "Group", "Category"); 00922 while ( (c = ast_channel_walk_locked(c)) != NULL) { 00923 headp=&c->varshead; 00924 AST_LIST_TRAVERSE(headp,current,entries) { 00925 if (!strncmp(ast_var_name(current), GROUP_CATEGORY_PREFIX "_", strlen(GROUP_CATEGORY_PREFIX) + 1)) { 00926 if (!havepattern || !regexec(®exbuf, ast_var_value(current), 0, NULL, 0)) { 00927 ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current), 00928 (ast_var_name(current) + strlen(GROUP_CATEGORY_PREFIX) + 1)); 00929 numchans++; 00930 } 00931 } else if (!strcmp(ast_var_name(current), GROUP_CATEGORY_PREFIX)) { 00932 if (!havepattern || !regexec(®exbuf, ast_var_value(current), 0, NULL, 0)) { 00933 ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current), "(default)"); 00934 numchans++; 00935 } 00936 } 00937 } 00938 numchans++; 00939 ast_channel_unlock(c); 00940 } 00941 00942 if (havepattern) 00943 regfree(®exbuf); 00944 00945 ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans)); 00946 return RESULT_SUCCESS; 00947 #undef FORMAT_STRING 00948 } 00949 00950 /* XXX Nothing in this array can currently be deprecated... 00951 You have to change the way find_cli works in order to remove this array 00952 I recommend doing this eventually... 00953 */ 00954 static struct ast_cli_entry builtins[] = { 00955 /* Keep alphabetized, with longer matches first (example: abcd before abc) */ 00956 { { "_command", "complete", NULL }, 00957 handle_commandcomplete, "Command complete", 00958 commandcomplete_help }, 00959 00960 { { "_command", "nummatches", NULL }, 00961 handle_commandnummatches, "Returns number of command matches", 00962 commandnummatches_help }, 00963 00964 { { "_command", "matchesarray", NULL }, 00965 handle_commandmatchesarray, "Returns command matches array", 00966 commandmatchesarray_help }, 00967 00968 { { NULL }, NULL, NULL, NULL } 00969 }; 00970 00971 static struct ast_cli_entry cli_debug_channel_deprecated = NEW_CLI(handle_debugchan_deprecated, "Enable debugging on channel"); 00972 static struct ast_cli_entry cli_module_load_deprecated = NEW_CLI(handle_load_deprecated, "Load a module"); 00973 static struct ast_cli_entry cli_module_reload_deprecated = NEW_CLI(handle_reload_deprecated, "reload modules by name"); 00974 static struct ast_cli_entry cli_module_unload_deprecated = NEW_CLI(handle_unload_deprecated, "unload modules by name"); 00975 00976 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); 00977 00978 static struct ast_cli_entry cli_cli[] = { 00979 /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */ 00980 NEW_CLI(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"), 00981 00982 NEW_CLI(handle_chanlist, "Display information on channels"), 00983 00984 { { "core", "show", "channel", NULL }, 00985 handle_showchan, "Display information on a specific channel", 00986 showchan_help, complete_ch_4 }, 00987 00988 NEW_CLI(handle_core_set_debug_channel, "Enable/disable debugging on a channel", 00989 .deprecate_cmd = &cli_debug_channel_deprecated), 00990 00991 NEW_CLI(handle_verbose, "Set level of debug/verbose chattiness"), 00992 00993 { { "group", "show", "channels", NULL }, 00994 group_show_channels, "Display active channels with group(s)", 00995 group_show_channels_help }, 00996 00997 NEW_CLI(handle_help, "Display help list, or specific help on a command"), 00998 00999 { { "logger", "mute", NULL }, 01000 handle_logger_mute, "Toggle logging output to a console", 01001 logger_mute_help }, 01002 01003 NEW_CLI(handle_modlist, "List modules and info"), 01004 01005 NEW_CLI(handle_load, "Load a module by name", .deprecate_cmd = &cli_module_load_deprecated), 01006 01007 NEW_CLI(handle_reload, "Reload configuration", .deprecate_cmd = &cli_module_reload_deprecated), 01008 01009 NEW_CLI(handle_unload, "Unload a module by name", .deprecate_cmd = &cli_module_unload_deprecated ), 01010 01011 NEW_CLI(handle_showuptime, "Show uptime information"), 01012 01013 { { "soft", "hangup", NULL }, 01014 handle_softhangup, "Request a hangup on a given channel", 01015 softhangup_help, complete_ch_3 }, 01016 }; 01017 01018 /*! 01019 * Some regexp characters in cli arguments are reserved and used as separators. 01020 */ 01021 static const char cli_rsvd[] = "[]{}|*%"; 01022 01023 /*! 01024 * initialize the _full_cmd string and related parameters, 01025 * return 0 on success, -1 on error. 01026 */ 01027 static int set_full_cmd(struct ast_cli_entry *e) 01028 { 01029 int i; 01030 char buf[80]; 01031 01032 ast_join(buf, sizeof(buf), e->cmda); 01033 e->_full_cmd = strdup(buf); 01034 if (!e->_full_cmd) { 01035 ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf); 01036 return -1; 01037 } 01038 e->cmdlen = strcspn(e->_full_cmd, cli_rsvd); 01039 for (i = 0; e->cmda[i]; i++) 01040 ; 01041 e->args = i; 01042 return 0; 01043 } 01044 01045 /*! \brief initialize the _full_cmd string in * each of the builtins. */ 01046 void ast_builtins_init(void) 01047 { 01048 struct ast_cli_entry *e; 01049 01050 for (e = builtins; e->cmda[0] != NULL; e++) 01051 set_full_cmd(e); 01052 01053 ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry)); 01054 } 01055 01056 /* 01057 * We have two sets of commands: builtins are stored in a 01058 * NULL-terminated array of ast_cli_entry, whereas external 01059 * commands are in a list. 01060 * When navigating, we need to keep two pointers and get 01061 * the next one in lexicographic order. For the purpose, 01062 * we use a structure. 01063 */ 01064 01065 struct cli_iterator { 01066 struct ast_cli_entry *builtins; 01067 struct ast_cli_entry *helpers; 01068 }; 01069 01070 static struct ast_cli_entry *cli_next(struct cli_iterator *i) 01071 { 01072 struct ast_cli_entry *e; 01073 01074 if (i->builtins == NULL && i->helpers == NULL) { 01075 /* initialize */ 01076 i->builtins = builtins; 01077 i->helpers = AST_LIST_FIRST(&helpers); 01078 } 01079 e = i->builtins; /* temporary */ 01080 if (!e->cmda[0] || (i->helpers && 01081 strcmp(i->helpers->_full_cmd, e->_full_cmd) < 0)) { 01082 /* Use helpers */ 01083 e = i->helpers; 01084 if (e) 01085 i->helpers = AST_LIST_NEXT(e, list); 01086 } else { /* use builtin. e is already set */ 01087 (i->builtins)++; /* move to next */ 01088 } 01089 return e; 01090 } 01091 01092 /*! 01093 * match a word in the CLI entry. 01094 * returns -1 on mismatch, 0 on match of an optional word, 01095 * 1 on match of a full word. 01096 * 01097 * The pattern can be 01098 * any_word match for equal 01099 * [foo|bar|baz] optionally, one of these words 01100 * {foo|bar|baz} exactly, one of these words 01101 * % any word 01102 */ 01103 static int word_match(const char *cmd, const char *cli_word) 01104 { 01105 int l; 01106 char *pos; 01107 01108 if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word)) 01109 return -1; 01110 if (!strchr(cli_rsvd, cli_word[0])) /* normal match */ 01111 return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1; 01112 /* regexp match, takes [foo|bar] or {foo|bar} */ 01113 l = strlen(cmd); 01114 /* wildcard match - will extend in the future */ 01115 if (l > 0 && cli_word[0] == '%') { 01116 return 1; /* wildcard */ 01117 } 01118 pos = strcasestr(cli_word, cmd); 01119 if (pos == NULL) /* not found, say ok if optional */ 01120 return cli_word[0] == '[' ? 0 : -1; 01121 if (pos == cli_word) /* no valid match at the beginning */ 01122 return -1; 01123 if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l])) 01124 return 1; /* valid match */ 01125 return -1; /* not found */ 01126 } 01127 01128 /*! \brief if word is a valid prefix for token, returns the pos-th 01129 * match as a malloced string, or NULL otherwise. 01130 * Always tell in *actual how many matches we got. 01131 */ 01132 static char *is_prefix(const char *word, const char *token, 01133 int pos, int *actual) 01134 { 01135 int lw; 01136 char *s, *t1; 01137 01138 *actual = 0; 01139 if (ast_strlen_zero(token)) 01140 return NULL; 01141 if (ast_strlen_zero(word)) 01142 word = ""; /* dummy */ 01143 lw = strlen(word); 01144 if (strcspn(word, cli_rsvd) != lw) 01145 return NULL; /* no match if word has reserved chars */ 01146 if (strchr(cli_rsvd, token[0]) == NULL) { /* regular match */ 01147 if (strncasecmp(token, word, lw)) /* no match */ 01148 return NULL; 01149 *actual = 1; 01150 return (pos != 0) ? NULL : strdup(token); 01151 } 01152 /* now handle regexp match */ 01153 01154 /* Wildcard always matches, so we never do is_prefix on them */ 01155 01156 t1 = ast_strdupa(token + 1); /* copy, skipping first char */ 01157 while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) { 01158 if (*s == '%') /* wildcard */ 01159 continue; 01160 if (strncasecmp(s, word, lw)) /* no match */ 01161 continue; 01162 (*actual)++; 01163 if (pos-- == 0) 01164 return strdup(s); 01165 } 01166 return NULL; 01167 } 01168 01169 /*! 01170 * \brief locate a cli command in the 'helpers' list (which must be locked). 01171 * exact has 3 values: 01172 * 0 returns if the search key is equal or longer than the entry. 01173 * note that trailing optional arguments are skipped. 01174 * -1 true if the mismatch is on the last word XXX not true! 01175 * 1 true only on complete, exact match. 01176 * 01177 * The search compares word by word taking care of regexps in e->cmda 01178 */ 01179 static struct ast_cli_entry *find_cli(char *const cmds[], int match_type) 01180 { 01181 int matchlen = -1; /* length of longest match so far */ 01182 struct ast_cli_entry *cand = NULL, *e=NULL; 01183 struct cli_iterator i = { NULL, NULL}; 01184 01185 while ( (e = cli_next(&i)) ) { 01186 /* word-by word regexp comparison */ 01187 char * const *src = cmds; 01188 char * const *dst = e->cmda; 01189 int n = 0; 01190 for (;; dst++, src += n) { 01191 n = word_match(*src, *dst); 01192 if (n < 0) 01193 break; 01194 } 01195 if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) { 01196 /* no more words in 'e' */ 01197 if (ast_strlen_zero(*src)) /* exact match, cannot do better */ 01198 break; 01199 /* Here, cmds has more words than the entry 'e' */ 01200 if (match_type != 0) /* but we look for almost exact match... */ 01201 continue; /* so we skip this one. */ 01202 /* otherwise we like it (case 0) */ 01203 } else { /* still words in 'e' */ 01204 if (ast_strlen_zero(*src)) 01205 continue; /* cmds is shorter than 'e', not good */ 01206 /* Here we have leftover words in cmds and 'e', 01207 * but there is a mismatch. We only accept this one if match_type == -1 01208 * and this is the last word for both. 01209 */ 01210 if (match_type != -1 || !ast_strlen_zero(src[1]) || 01211 !ast_strlen_zero(dst[1])) /* not the one we look for */ 01212 continue; 01213 /* good, we are in case match_type == -1 and mismatch on last word */ 01214 } 01215 if (src - cmds > matchlen) { /* remember the candidate */ 01216 matchlen = src - cmds; 01217 cand = e; 01218 } 01219 } 01220 return e ? e : cand; 01221 } 01222 01223 static char *find_best(char *argv[]) 01224 { 01225 static char cmdline[80]; 01226 int x; 01227 /* See how close we get, then print the candidate */ 01228 char *myargv[AST_MAX_CMD_LEN]; 01229 for (x=0;x<AST_MAX_CMD_LEN;x++) 01230 myargv[x]=NULL; 01231 AST_LIST_LOCK(&helpers); 01232 for (x=0;argv[x];x++) { 01233 myargv[x] = argv[x]; 01234 if (!find_cli(myargv, -1)) 01235 break; 01236 } 01237 AST_LIST_UNLOCK(&helpers); 01238 ast_join(cmdline, sizeof(cmdline), myargv); 01239 return cmdline; 01240 } 01241 01242 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed) 01243 { 01244 if (e->deprecate_cmd) { 01245 __ast_cli_unregister(e->deprecate_cmd, e); 01246 } 01247 if (e->inuse) { 01248 ast_log(LOG_WARNING, "Can't remove command that is in use\n"); 01249 } else { 01250 AST_LIST_LOCK(&helpers); 01251 AST_LIST_REMOVE(&helpers, e, list); 01252 AST_LIST_UNLOCK(&helpers); 01253 free(e->_full_cmd); 01254 e->_full_cmd = NULL; 01255 if (e->new_handler) { 01256 /* this is a new-style entry. Reset fields and free memory. */ 01257 bzero((char **)(e->cmda), sizeof(e->cmda)); 01258 free(e->command); 01259 e->command = NULL; 01260 e->usage = NULL; 01261 } 01262 } 01263 return 0; 01264 } 01265 01266 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed) 01267 { 01268 struct ast_cli_entry *cur; 01269 int i, lf, ret = -1; 01270 01271 if (e->handler == NULL) { /* new style entry, run the handler to init fields */ 01272 struct ast_cli_args a; /* fake argument */ 01273 char **dst = (char **)e->cmda; /* need to cast as the entry is readonly */ 01274 char *s; 01275 01276 bzero (&a, sizeof(a)); 01277 e->new_handler(e, CLI_INIT, &a); 01278 /* XXX check that usage and command are filled up */ 01279 s = ast_skip_blanks(e->command); 01280 s = e->command = ast_strdup(s); 01281 for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) { 01282 *dst++ = s; /* store string */ 01283 s = ast_skip_nonblanks(s); 01284 if (*s == '\0') /* we are done */ 01285 break; 01286 *s++ = '\0'; 01287 s = ast_skip_blanks(s); 01288 } 01289 *dst++ = NULL; 01290 } 01291 if (set_full_cmd(e)) 01292 goto done; 01293 AST_LIST_LOCK(&helpers); 01294 01295 if (find_cli(e->cmda, 1)) { 01296 ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", e->_full_cmd); 01297 free(e->_full_cmd); 01298 e->_full_cmd = NULL; 01299 goto done; 01300 } 01301 if (!ed) { 01302 e->deprecated = 0; 01303 } else { 01304 e->deprecated = 1; 01305 e->summary = ed->summary; 01306 e->usage = ed->usage; 01307 /* XXX If command A deprecates command B, and command B deprecates command C... 01308 Do we want to show command A or command B when telling the user to use new syntax? 01309 This currently would show command A. 01310 To show command B, you just need to always use ed->_full_cmd. 01311 */ 01312 e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd); 01313 } 01314 01315 lf = e->cmdlen; 01316 AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) { 01317 int len = cur->cmdlen; 01318 if (lf < len) 01319 len = lf; 01320 if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) { 01321 AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list); 01322 break; 01323 } 01324 } 01325 AST_LIST_TRAVERSE_SAFE_END; 01326 01327 if (!cur) 01328 AST_LIST_INSERT_TAIL(&helpers, e, list); 01329 ret = 0; /* success */ 01330 01331 done: 01332 AST_LIST_UNLOCK(&helpers); 01333 01334 if (e->deprecate_cmd) { 01335 /* This command deprecates another command. Register that one also. */ 01336 __ast_cli_register(e->deprecate_cmd, e); 01337 } 01338 01339 return ret; 01340 } 01341 01342 /* wrapper function, so we can unregister deprecated commands recursively */ 01343 int ast_cli_unregister(struct ast_cli_entry *e) 01344 { 01345 return __ast_cli_unregister(e, NULL); 01346 } 01347 01348 /* wrapper function, so we can register deprecated commands recursively */ 01349 int ast_cli_register(struct ast_cli_entry *e) 01350 { 01351 return __ast_cli_register(e, NULL); 01352 } 01353 01354 /* 01355 * register/unregister an array of entries. 01356 */ 01357 void ast_cli_register_multiple(struct ast_cli_entry *e, int len) 01358 { 01359 int i; 01360 01361 for (i = 0; i < len; i++) 01362 ast_cli_register(e + i); 01363 } 01364 01365 void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len) 01366 { 01367 int i; 01368 01369 for (i = 0; i < len; i++) 01370 ast_cli_unregister(e + i); 01371 } 01372 01373 01374 /*! \brief helper for final part of 01375 * handle_help. if locked = 0 it's just "help_workhorse", 01376 * otherwise assume the list is already locked and print 01377 * an error message if not found. 01378 */ 01379 static char *help1(int fd, char *match[], int locked) 01380 { 01381 char matchstr[80] = ""; 01382 struct ast_cli_entry *e; 01383 int len = 0; 01384 int found = 0; 01385 struct cli_iterator i = { NULL, NULL}; 01386 01387 if (match) { 01388 ast_join(matchstr, sizeof(matchstr), match); 01389 len = strlen(matchstr); 01390 } 01391 if (!locked) 01392 AST_LIST_LOCK(&helpers); 01393 while ( (e = cli_next(&i)) ) { 01394 /* Hide commands that start with '_' */ 01395 if (e->_full_cmd[0] == '_') 01396 continue; 01397 /* Hide commands that are marked as deprecated. */ 01398 if (e->deprecated) 01399 continue; 01400 if (match && strncasecmp(matchstr, e->_full_cmd, len)) 01401 continue; 01402 ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>")); 01403 found++; 01404 } 01405 if (!locked) 01406 AST_LIST_UNLOCK(&helpers); 01407 if (!locked && !found && matchstr[0]) 01408 ast_cli(fd, "No such command '%s'.\n", matchstr); 01409 return CLI_SUCCESS; 01410 } 01411 01412 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 01413 { 01414 char fullcmd[80]; 01415 struct ast_cli_entry *my_e; 01416 01417 if (cmd == CLI_INIT) { 01418 e->command = "help"; 01419 e->usage = 01420 "Usage: help [topic]\n" 01421 " When called with a topic as an argument, displays usage\n" 01422 " information on the given command. If called without a\n" 01423 " topic, it provides a list of commands.\n"; 01424 return NULL; 01425 01426 } else if (cmd == CLI_GENERATE) { 01427 /* skip first 4 or 5 chars, "help " */ 01428 int l = strlen(a->line); 01429 01430 if (l > 5) 01431 l = 5; 01432 /* XXX watch out, should stop to the non-generator parts */ 01433 return __ast_cli_generator(a->line + l, a->word, a->n, 0); 01434 } 01435 if (a->argc == 1) 01436 return help1(a->fd, NULL, 0); 01437 01438 AST_LIST_LOCK(&helpers); 01439 my_e = find_cli(a->argv + 1, 1); /* try exact match first */ 01440 if (!my_e) 01441 return help1(a->fd, a->argv + 1, 1 /* locked */); 01442 if (my_e->usage) 01443 ast_cli(a->fd, "%s", my_e->usage); 01444 else { 01445 ast_join(fullcmd, sizeof(fullcmd), a->argv+1); 01446 ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd); 01447 } 01448 AST_LIST_UNLOCK(&helpers); 01449 return RESULT_SUCCESS; 01450 } 01451 01452 static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace) 01453 { 01454 char *dup, *cur; 01455 int x = 0; 01456 int quoted = 0; 01457 int escaped = 0; 01458 int whitespace = 1; 01459 int dummy = 0; 01460 01461 if (trailingwhitespace == NULL) 01462 trailingwhitespace = &dummy; 01463 *trailingwhitespace = 0; 01464 if (s == NULL) /* invalid, though! */ 01465 return NULL; 01466 /* make a copy to store the parsed string */ 01467 if (!(dup = ast_strdup(s))) 01468 return NULL; 01469 01470 cur = dup; 01471 /* scan the original string copying into cur when needed */ 01472 for (; *s ; s++) { 01473 if (x >= max - 1) { 01474 ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s); 01475 break; 01476 } 01477 if (*s == '"' && !escaped) { 01478 quoted = !quoted; 01479 if (quoted && whitespace) { 01480 /* start a quoted string from previous whitespace: new argument */ 01481 argv[x++] = cur; 01482 whitespace = 0; 01483 } 01484 } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) { 01485 /* If we are not already in whitespace, and not in a quoted string or 01486 processing an escape sequence, and just entered whitespace, then 01487 finalize the previous argument and remember that we are in whitespace 01488 */ 01489 if (!whitespace) { 01490 *cur++ = '\0'; 01491 whitespace = 1; 01492 } 01493 } else if (*s == '\\' && !escaped) { 01494 escaped = 1; 01495 } else { 01496 if (whitespace) { 01497 /* we leave whitespace, and are not quoted. So it's a new argument */ 01498 argv[x++] = cur; 01499 whitespace = 0; 01500 } 01501 *cur++ = *s; 01502 escaped = 0; 01503 } 01504 } 01505 /* Null terminate */ 01506 *cur++ = '\0'; 01507 /* XXX put a NULL in the last argument, because some functions that take 01508 * the array may want a null-terminated array. 01509 * argc still reflects the number of non-NULL entries. 01510 */ 01511 argv[x] = NULL; 01512 *argc = x; 01513 *trailingwhitespace = whitespace; 01514 return dup; 01515 } 01516 01517 /*! \brief Return the number of unique matches for the generator */ 01518 int ast_cli_generatornummatches(const char *text, const char *word) 01519 { 01520 int matches = 0, i = 0; 01521 char *buf = NULL, *oldbuf = NULL; 01522 01523 while ((buf = ast_cli_generator(text, word, i++))) { 01524 if (!oldbuf || strcmp(buf,oldbuf)) 01525 matches++; 01526 if (oldbuf) 01527 free(oldbuf); 01528 oldbuf = buf; 01529 } 01530 if (oldbuf) 01531 free(oldbuf); 01532 return matches; 01533 } 01534 01535 char **ast_cli_completion_matches(const char *text, const char *word) 01536 { 01537 char **match_list = NULL, *retstr, *prevstr; 01538 size_t match_list_len, max_equal, which, i; 01539 int matches = 0; 01540 01541 /* leave entry 0 free for the longest common substring */ 01542 match_list_len = 1; 01543 while ((retstr = ast_cli_generator(text, word, matches)) != NULL) { 01544 if (matches + 1 >= match_list_len) { 01545 match_list_len <<= 1; 01546 if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list)))) 01547 return NULL; 01548 } 01549 match_list[++matches] = retstr; 01550 } 01551 01552 if (!match_list) 01553 return match_list; /* NULL */ 01554 01555 /* Find the longest substring that is common to all results 01556 * (it is a candidate for completion), and store a copy in entry 0. 01557 */ 01558 prevstr = match_list[1]; 01559 max_equal = strlen(prevstr); 01560 for (which = 2; which <= matches; which++) { 01561 for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++) 01562 continue; 01563 max_equal = i; 01564 } 01565 01566 if (!(retstr = ast_malloc(max_equal + 1))) 01567 return NULL; 01568 01569 ast_copy_string(retstr, match_list[1], max_equal + 1); 01570 match_list[0] = retstr; 01571 01572 /* ensure that the array is NULL terminated */ 01573 if (matches + 1 >= match_list_len) { 01574 if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list)))) 01575 return NULL; 01576 } 01577 match_list[matches + 1] = NULL; 01578 01579 return match_list; 01580 } 01581 01582 /*! \brief returns true if there are more words to match */ 01583 static int more_words (char * const *dst) 01584 { 01585 int i; 01586 for (i = 0; dst[i]; i++) { 01587 if (dst[i][0] != '[') 01588 return -1; 01589 } 01590 return 0; 01591 } 01592 01593 /* 01594 * generate the entry at position 'state' 01595 */ 01596 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock) 01597 { 01598 char *argv[AST_MAX_ARGS]; 01599 struct ast_cli_entry *e; 01600 struct cli_iterator i = { NULL, NULL }; 01601 int x = 0, argindex, matchlen; 01602 int matchnum=0; 01603 char *ret = NULL; 01604 char matchstr[80] = ""; 01605 int tws = 0; 01606 /* Split the argument into an array of words */ 01607 char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws); 01608 01609 if (!dup) /* malloc error */ 01610 return NULL; 01611 01612 /* Compute the index of the last argument (could be an empty string) */ 01613 argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x; 01614 01615 /* rebuild the command, ignore terminating white space and flatten space */ 01616 ast_join(matchstr, sizeof(matchstr)-1, argv); 01617 matchlen = strlen(matchstr); 01618 if (tws) { 01619 strcat(matchstr, " "); /* XXX */ 01620 if (matchlen) 01621 matchlen++; 01622 } 01623 if (lock) 01624 AST_LIST_LOCK(&helpers); 01625 while ( (e = cli_next(&i)) ) { 01626 /* XXX repeated code */ 01627 int src = 0, dst = 0, n = 0; 01628 01629 /* 01630 * Try to match words, up to and excluding the last word, which 01631 * is either a blank or something that we want to extend. 01632 */ 01633 for (;src < argindex; dst++, src += n) { 01634 n = word_match(argv[src], e->cmda[dst]); 01635 if (n < 0) 01636 break; 01637 } 01638 01639 if (src != argindex && more_words(e->cmda + dst)) /* not a match */ 01640 continue; 01641 ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n); 01642 matchnum += n; /* this many matches here */ 01643 if (ret) { 01644 /* 01645 * argv[src] is a valid prefix of the next word in this 01646 * command. If this is also the correct entry, return it. 01647 */ 01648 if (matchnum > state) 01649 break; 01650 free(ret); 01651 ret = NULL; 01652 } else if (ast_strlen_zero(e->cmda[dst])) { 01653 /* 01654 * This entry is a prefix of the command string entered 01655 * (only one entry in the list should have this property). 01656 * Run the generator if one is available. In any case we are done. 01657 */ 01658 if (e->generator) 01659 ret = e->generator(matchstr, word, argindex, state - matchnum); 01660 else if (e->new_handler) { /* new style command */ 01661 struct ast_cli_args a = { 01662 .line = matchstr, .word = word, 01663 .pos = argindex, 01664 .n = state - matchnum }; 01665 ret = e->new_handler(e, CLI_GENERATE, &a); 01666 } 01667 if (ret) 01668 break; 01669 } 01670 } 01671 if (lock) 01672 AST_LIST_UNLOCK(&helpers); 01673 free(dup); 01674 return ret; 01675 } 01676 01677 char *ast_cli_generator(const char *text, const char *word, int state) 01678 { 01679 return __ast_cli_generator(text, word, state, 1); 01680 } 01681 01682 int ast_cli_command(int fd, const char *s) 01683 { 01684 char *args[AST_MAX_ARGS + 1]; 01685 struct ast_cli_entry *e; 01686 int x; 01687 int res; 01688 char *dup = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL); 01689 01690 if (dup == NULL) 01691 return -1; 01692 01693 if (x < 1) /* We need at least one entry, otherwise ignore */ 01694 goto done; 01695 01696 AST_LIST_LOCK(&helpers); 01697 e = find_cli(args + 1, 0); 01698 if (e) 01699 ast_atomic_fetchadd_int(&e->inuse, 1); 01700 AST_LIST_UNLOCK(&helpers); 01701 if (e == NULL) { 01702 ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(args + 1)); 01703 goto done; 01704 } 01705 /* 01706 * Within the handler, argv[-1] contains a pointer to the ast_cli_entry. 01707 * Remember that the array returned by parse_args is NULL-terminated. 01708 */ 01709 args[0] = (char *)e; 01710 01711 if (!e->new_handler) /* old style */ 01712 res = e->handler(fd, x, args + 1); 01713 else { 01714 struct ast_cli_args a = { 01715 .fd = fd, .argc = x, .argv = args+1 }; 01716 char *retval = e->new_handler(e, CLI_HANDLER, &a); 01717 01718 if (retval == CLI_SUCCESS) 01719 res = RESULT_SUCCESS; 01720 else if (retval == CLI_SHOWUSAGE) 01721 res = RESULT_SHOWUSAGE; 01722 else 01723 res = RESULT_FAILURE; 01724 } 01725 switch (res) { 01726 case RESULT_SHOWUSAGE: 01727 ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n")); 01728 break; 01729 01730 case RESULT_FAILURE: 01731 ast_cli(fd, "Command '%s' failed.\n", s); 01732 /* FALLTHROUGH */ 01733 default: 01734 AST_LIST_LOCK(&helpers); 01735 if (e->deprecated == 1) { 01736 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); 01737 e->deprecated = 2; 01738 } 01739 AST_LIST_UNLOCK(&helpers); 01740 break; 01741 } 01742 ast_atomic_fetchadd_int(&e->inuse, -1); 01743 done: 01744 free(dup); 01745 return 0; 01746 }