Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

cli.c

Go to the documentation of this file.
00001 /* 00002 * Asterisk -- A telephony toolkit for Linux. 00003 * 00004 * Standard Command Line Interface 00005 * 00006 * Copyright (C) 1999, Mark Spencer 00007 * 00008 * Mark Spencer <markster@linux-support.net> 00009 * 00010 * This program is free software, distributed under the terms of 00011 * the GNU General Public License 00012 */ 00013 00014 #include <unistd.h> 00015 #include <stdlib.h> 00016 #include <asterisk/logger.h> 00017 #include <asterisk/options.h> 00018 #include <asterisk/cli.h> 00019 #include <asterisk/module.h> 00020 #include <asterisk/channel.h> 00021 #include <asterisk/channel_pvt.h> 00022 #include <asterisk/manager.h> 00023 #include <asterisk/utils.h> 00024 #include <asterisk/lock.h> 00025 #include <sys/signal.h> 00026 #include <stdio.h> 00027 #include <signal.h> 00028 #include <string.h> 00029 /* For rl_filename_completion */ 00030 #include "readline/readline.h" 00031 /* For module directory */ 00032 #include "asterisk.h" 00033 #include "build.h" 00034 #include "astconf.h" 00035 00036 #define VERSION_INFO "Asterisk " ASTERISK_VERSION " built by " BUILD_USER "@" BUILD_HOSTNAME \ " on a " BUILD_MACHINE " running " BUILD_OS 00037 00038 void ast_cli(int fd, char *fmt, ...) 00039 { 00040 char *stuff; 00041 int res = 0; 00042 00043 va_list ap; 00044 va_start(ap, fmt); 00045 res = vasprintf(&stuff, fmt, ap); 00046 va_end(ap); 00047 if (res == -1) { 00048 ast_log(LOG_ERROR, "Out of memory\n"); 00049 } else { 00050 ast_carefulwrite(fd, stuff, strlen(stuff), 100); 00051 free(stuff); 00052 } 00053 } 00054 00055 AST_MUTEX_DEFINE_STATIC(clilock); 00056 00057 struct ast_cli_entry *helpers = NULL; 00058 00059 static char load_help[] = 00060 "Usage: load <module name>\n" 00061 " Loads the specified module into Asterisk.\n"; 00062 00063 static char unload_help[] = 00064 "Usage: unload [-f|-h] <module name>\n" 00065 " Unloads the specified module from Asterisk. The -f\n" 00066 " option causes the module to be unloaded even if it is\n" 00067 " in use (may cause a crash) and the -h module causes the\n" 00068 " module to be unloaded even if the module says it cannot, \n" 00069 " which almost always will cause a crash.\n"; 00070 00071 static char help_help[] = 00072 "Usage: help [topic]\n" 00073 " When called with a topic as an argument, displays usage\n" 00074 " information on the given command. If called without a\n" 00075 " topic, it provides a list of commands.\n"; 00076 00077 static char chanlist_help[] = 00078 "Usage: show channels [concise]\n" 00079 " Lists currently defined channels and some information about\n" 00080 " them. If 'concise' is specified, format is abridged and in\n" 00081 " a more easily machine parsable format\n"; 00082 00083 static char reload_help[] = 00084 "Usage: reload\n" 00085 " Reloads configuration files for all modules which support\n" 00086 " reloading.\n"; 00087 00088 static char set_verbose_help[] = 00089 "Usage: set verbose <level>\n" 00090 " Sets level of verbose messages to be displayed. 0 means\n" 00091 " no messages should be displayed.\n"; 00092 00093 static char softhangup_help[] = 00094 "Usage: soft hangup <channel>\n" 00095 " Request that a channel be hung up. The hangup takes effect\n" 00096 " the next time the driver reads or writes from the channel\n"; 00097 00098 static int handle_load(int fd, int argc, char *argv[]) 00099 { 00100 if (argc != 2) 00101 return RESULT_SHOWUSAGE; 00102 if (ast_load_resource(argv[1])) { 00103 ast_cli(fd, "Unable to load module %s\n", argv[1]); 00104 return RESULT_FAILURE; 00105 } 00106 return RESULT_SUCCESS; 00107 } 00108 00109 static int handle_reload(int fd, int argc, char *argv[]) 00110 { 00111 if (argc != 1) 00112 return RESULT_SHOWUSAGE; 00113 ast_module_reload(); 00114 return RESULT_SUCCESS; 00115 } 00116 00117 static int handle_set_verbose(int fd, int argc, char *argv[]) 00118 { 00119 int val; 00120 /* Has a hidden 'at least' argument */ 00121 if ((argc != 3) && (argc != 4)) 00122 return RESULT_SHOWUSAGE; 00123 if ((argc == 4) && strcasecmp(argv[2], "atleast")) 00124 return RESULT_SHOWUSAGE; 00125 if (argc == 3) 00126 option_verbose = atoi(argv[2]); 00127 else { 00128 val = atoi(argv[3]); 00129 if (val > option_verbose) 00130 option_verbose = val; 00131 } 00132 return RESULT_SUCCESS; 00133 } 00134 00135 static int handle_unload(int fd, int argc, char *argv[]) 00136 { 00137 int x; 00138 int force=AST_FORCE_SOFT; 00139 if (argc < 2) 00140 return RESULT_SHOWUSAGE; 00141 for (x=1;x<argc;x++) { 00142 if (argv[x][0] == '-') { 00143 switch(argv[x][1]) { 00144 case 'f': 00145 force = AST_FORCE_FIRM; 00146 break; 00147 case 'h': 00148 force = AST_FORCE_HARD; 00149 break; 00150 default: 00151 return RESULT_SHOWUSAGE; 00152 } 00153 } else if (x != argc - 1) 00154 return RESULT_SHOWUSAGE; 00155 else if (ast_unload_resource(argv[x], force)) { 00156 ast_cli(fd, "Unable to unload resource %s\n", argv[x]); 00157 return RESULT_FAILURE; 00158 } 00159 } 00160 return RESULT_SUCCESS; 00161 } 00162 00163 #define MODLIST_FORMAT "%-25s %-40.40s %-10d\n" 00164 #define MODLIST_FORMAT2 "%-25s %-40.40s %-10s\n" 00165 00166 AST_MUTEX_DEFINE_STATIC(climodentrylock); 00167 static int climodentryfd = -1; 00168 00169 static int modlist_modentry(char *module, char *description, int usecnt) 00170 { 00171 ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt); 00172 return 0; 00173 } 00174 00175 static char modlist_help[] = 00176 "Usage: show modules\n" 00177 " Shows Asterisk modules currently in use, and usage " 00178 "statistics.\n"; 00179 00180 static char version_help[] = 00181 "Usage: show version\n" 00182 " Shows Asterisk version information.\n "; 00183 00184 static char *format_uptimestr(time_t timeval) 00185 { 00186 int years = 0, weeks = 0, days = 0, hours = 0, mins = 0, secs = 0; 00187 char timestr[256]=""; 00188 int bytes = 0; 00189 int maxbytes = 0; 00190 int offset = 0; 00191 #define SECOND (1) 00192 #define MINUTE (SECOND*60) 00193 #define HOUR (MINUTE*60) 00194 #define DAY (HOUR*24) 00195 #define WEEK (DAY*7) 00196 #define YEAR (DAY*365) 00197 #define ESS(x) ((x == 1) ? "" : "s") 00198 00199 maxbytes = sizeof(timestr); 00200 if (timeval < 0) 00201 return NULL; 00202 if (timeval > YEAR) { 00203 years = (timeval / YEAR); 00204 timeval -= (years * YEAR); 00205 if (years > 0) { 00206 snprintf(timestr + offset, maxbytes, "%d year%s, ", years, ESS(years)); 00207 bytes = strlen(timestr + offset); 00208 offset += bytes; 00209 maxbytes -= bytes; 00210 } 00211 } 00212 if (timeval > WEEK) { 00213 weeks = (timeval / WEEK); 00214 timeval -= (weeks * WEEK); 00215 if (weeks > 0) { 00216 snprintf(timestr + offset, maxbytes, "%d week%s, ", weeks, ESS(weeks)); 00217 bytes = strlen(timestr + offset); 00218 offset += bytes; 00219 maxbytes -= bytes; 00220 } 00221 } 00222 if (timeval > DAY) { 00223 days = (timeval / DAY); 00224 timeval -= (days * DAY); 00225 if (days > 0) { 00226 snprintf(timestr + offset, maxbytes, "%d day%s, ", days, ESS(days)); 00227 bytes = strlen(timestr + offset); 00228 offset += bytes; 00229 maxbytes -= bytes; 00230 } 00231 } 00232 if (timeval > HOUR) { 00233 hours = (timeval / HOUR); 00234 timeval -= (hours * HOUR); 00235 if (hours > 0) { 00236 snprintf(timestr + offset, maxbytes, "%d hour%s, ", hours, ESS(hours)); 00237 bytes = strlen(timestr + offset); 00238 offset += bytes; 00239 maxbytes -= bytes; 00240 } 00241 } 00242 if (timeval > MINUTE) { 00243 mins = (timeval / MINUTE); 00244 timeval -= (mins * MINUTE); 00245 if (mins > 0) { 00246 snprintf(timestr + offset, maxbytes, "%d minute%s, ", mins, ESS(mins)); 00247 bytes = strlen(timestr + offset); 00248 offset += bytes; 00249 maxbytes -= bytes; 00250 } 00251 } 00252 secs = timeval; 00253 00254 if (secs > 0) { 00255 snprintf(timestr + offset, maxbytes, "%d second%s", secs, ESS(secs)); 00256 } 00257 00258 return timestr ? strdup(timestr) : NULL; 00259 } 00260 00261 static int handle_showuptime(int fd, int argc, char *argv[]) 00262 { 00263 time_t curtime, tmptime; 00264 char *timestr; 00265 00266 time(&curtime); 00267 if (ast_startuptime) { 00268 tmptime = curtime - ast_startuptime; 00269 timestr = format_uptimestr(tmptime); 00270 if (timestr) { 00271 ast_cli(fd, "System uptime: %s\n", timestr); 00272 free(timestr); 00273 } 00274 } 00275 if (ast_lastreloadtime) { 00276 tmptime = curtime - ast_lastreloadtime; 00277 timestr = format_uptimestr(tmptime); 00278 if (timestr) { 00279 ast_cli(fd, "Last reload: %s\n", timestr); 00280 free(timestr); 00281 } 00282 } 00283 return RESULT_SUCCESS; 00284 } 00285 00286 static int handle_modlist(int fd, int argc, char *argv[]) 00287 { 00288 if (argc != 2) 00289 return RESULT_SHOWUSAGE; 00290 ast_mutex_lock(&climodentrylock); 00291 climodentryfd = fd; 00292 ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count"); 00293 ast_update_module_list(modlist_modentry); 00294 climodentryfd = -1; 00295 ast_mutex_unlock(&climodentrylock); 00296 return RESULT_SUCCESS; 00297 } 00298 00299 static int handle_version(int fd, int argc, char *argv[]) 00300 { 00301 if (argc != 2) 00302 return RESULT_SHOWUSAGE; 00303 ast_cli(fd, "%s\n", VERSION_INFO); 00304 return RESULT_SUCCESS; 00305 } 00306 static int handle_chanlist(int fd, int argc, char *argv[]) 00307 { 00308 #define FORMAT_STRING "%15s (%-10s %-12s %-4d) %7s %-12s %-15s\n" 00309 #define FORMAT_STRING2 "%15s (%-10s %-12s %-4s) %7s %-12s %-15s\n" 00310 #define CONCISE_FORMAT_STRING "%s:%s:%s:%d:%s:%s:%s:%s:%s:%d\n" 00311 00312 struct ast_channel *c=NULL; 00313 int numchans = 0; 00314 int concise = 0; 00315 if (argc < 2 || argc > 3) 00316 return RESULT_SHOWUSAGE; 00317 00318 concise = (argc == 3 && (!strcasecmp(argv[2],"concise"))); 00319 c = ast_channel_walk_locked(NULL); 00320 if(!concise) 00321 ast_cli(fd, FORMAT_STRING2, "Channel", "Context", "Extension", "Pri", "State", "Appl.", "Data"); 00322 while(c) { 00323 if(concise) 00324 ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state), 00325 c->appl ? c->appl : "(None)", c->data ? ( !ast_strlen_zero(c->data) ? c->data : "" ): "", 00326 (c->callerid && !ast_strlen_zero(c->callerid)) ? c->callerid : "", 00327 (c->accountcode && !ast_strlen_zero(c->accountcode)) ? c->accountcode : "",c->amaflags); 00328 else 00329 ast_cli(fd, FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state), 00330 c->appl ? c->appl : "(None)", c->data ? ( !ast_strlen_zero(c->data) ? c->data : "(Empty)" ): "(None)"); 00331 00332 numchans++; 00333 ast_mutex_unlock(&c->lock); 00334 c = ast_channel_walk_locked(c); 00335 } 00336 if(!concise) 00337 ast_cli(fd, "%d active channel(s)\n", numchans); 00338 return RESULT_SUCCESS; 00339 } 00340 00341 static char showchan_help[] = 00342 "Usage: show channel <channel>\n" 00343 " Shows lots of information about the specified channel.\n"; 00344 00345 static char debugchan_help[] = 00346 "Usage: debug channel <channel>\n" 00347 " Enables debugging on a specific channel.\n"; 00348 00349 static char nodebugchan_help[] = 00350 "Usage: no debug channel <channel>\n" 00351 " Disables debugging on a specific channel.\n"; 00352 00353 static char commandcomplete_help[] = 00354 "Usage: _command complete \"<line>\" text state\n" 00355 " This function is used internally to help with command completion and should.\n" 00356 " never be called by the user directly.\n"; 00357 00358 static char commandnummatches_help[] = 00359 "Usage: _command nummatches \"<line>\" text \n" 00360 " This function is used internally to help with command completion and should.\n" 00361 " never be called by the user directly.\n"; 00362 00363 static char commandmatchesarray_help[] = 00364 "Usage: _command matchesarray \"<line>\" text \n" 00365 " This function is used internally to help with command completion and should.\n" 00366 " never be called by the user directly.\n"; 00367 00368 static int handle_softhangup(int fd, int argc, char *argv[]) 00369 { 00370 struct ast_channel *c=NULL; 00371 if (argc != 3) 00372 return RESULT_SHOWUSAGE; 00373 c = ast_channel_walk_locked(NULL); 00374 while(c) { 00375 if (!strcasecmp(c->name, argv[2])) { 00376 ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name); 00377 ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT); 00378 ast_mutex_unlock(&c->lock); 00379 break; 00380 } 00381 ast_mutex_unlock(&c->lock); 00382 c = ast_channel_walk_locked(c); 00383 } 00384 if (!c) 00385 ast_cli(fd, "%s is not a known channel\n", argv[2]); 00386 return RESULT_SUCCESS; 00387 } 00388 00389 static char *__ast_cli_generator(char *text, char *word, int state, int lock); 00390 00391 static int handle_commandmatchesarray(int fd, int argc, char *argv[]) 00392 { 00393 char *buf; 00394 int buflen = 2048; 00395 int len = 0; 00396 char **matches; 00397 int x; 00398 00399 if (argc != 4) 00400 return RESULT_SHOWUSAGE; 00401 buf = malloc(buflen); 00402 if (!buf) 00403 return RESULT_FAILURE; 00404 buf[len] = '\0'; 00405 matches = ast_cli_completion_matches(argv[2], argv[3]); 00406 if (matches) { 00407 for (x=0; matches[x]; x++) { 00408 #if 0 00409 printf("command matchesarray for '%s' %s got '%s'\n", argv[2], argv[3], matches[x]); 00410 #endif 00411 if (len + strlen(matches[x]) >= buflen) { 00412 buflen += strlen(matches[x]) * 3; 00413 buf = realloc(buf, buflen); 00414 } 00415 len += sprintf( buf + len, "%s ", matches[x]); 00416 free(matches[x]); 00417 matches[x] = NULL; 00418 } 00419 free(matches); 00420 } 00421 #if 0 00422 printf("array for '%s' %s got '%s'\n", argv[2], argv[3], buf); 00423 #endif 00424 00425 if (buf) { 00426 ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF); 00427 free(buf); 00428 } else 00429 ast_cli(fd, "NULL\n"); 00430 00431 return RESULT_SUCCESS; 00432 } 00433 00434 00435 00436 static int handle_commandnummatches(int fd, int argc, char *argv[]) 00437 { 00438 int matches = 0; 00439 00440 if (argc != 4) 00441 return RESULT_SHOWUSAGE; 00442 00443 matches = ast_cli_generatornummatches(argv[2], argv[3]); 00444 00445 #if 0 00446 printf("Search for '%s' %s got '%d'\n", argv[2], argv[3], matches); 00447 #endif 00448 ast_cli(fd, "%d", matches); 00449 00450 return RESULT_SUCCESS; 00451 } 00452 00453 static int handle_commandcomplete(int fd, int argc, char *argv[]) 00454 { 00455 char *buf; 00456 #if 0 00457 printf("Search for %d args: '%s', '%s', '%s', '%s'\n", argc, argv[0], argv[1], argv[2], argv[3]); 00458 #endif 00459 if (argc != 5) 00460 return RESULT_SHOWUSAGE; 00461 buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0); 00462 #if 0 00463 printf("Search for '%s' %s %d got '%s'\n", argv[2], argv[3], atoi(argv[4]), buf); 00464 #endif 00465 if (buf) { 00466 ast_cli(fd, buf); 00467 free(buf); 00468 } else 00469 ast_cli(fd, "NULL\n"); 00470 return RESULT_SUCCESS; 00471 } 00472 00473 static int handle_debugchan(int fd, int argc, char *argv[]) 00474 { 00475 struct ast_channel *c=NULL; 00476 if (argc != 3) 00477 return RESULT_SHOWUSAGE; 00478 c = ast_channel_walk_locked(NULL); 00479 while(c) { 00480 if (!strcasecmp(c->name, argv[2])) { 00481 c->fin |= 0x80000000; 00482 c->fout |= 0x80000000; 00483 break; 00484 } 00485 ast_mutex_unlock(&c->lock); 00486 c = ast_channel_walk_locked(c); 00487 } 00488 if (c) { 00489 ast_cli(fd, "Debugging enabled on channel %s\n", c->name); 00490 ast_mutex_unlock(&c->lock); 00491 } 00492 else 00493 ast_cli(fd, "No such channel %s\n", argv[2]); 00494 return RESULT_SUCCESS; 00495 } 00496 00497 static int handle_nodebugchan(int fd, int argc, char *argv[]) 00498 { 00499 struct ast_channel *c=NULL; 00500 if (argc != 4) 00501 return RESULT_SHOWUSAGE; 00502 c = ast_channel_walk_locked(NULL); 00503 while(c) { 00504 if (!strcasecmp(c->name, argv[3])) { 00505 c->fin &= 0x7fffffff; 00506 c->fout &= 0x7fffffff; 00507 break; 00508 } 00509 ast_mutex_unlock(&c->lock); 00510 c = ast_channel_walk_locked(c); 00511 } 00512 if (c) { 00513 ast_cli(fd, "Debugging disabled on channel %s\n", c->name); 00514 ast_mutex_unlock(&c->lock); 00515 } else 00516 ast_cli(fd, "No such channel %s\n", argv[2]); 00517 return RESULT_SUCCESS; 00518 } 00519 00520 00521 00522 static int handle_showchan(int fd, int argc, char *argv[]) 00523 { 00524 struct ast_channel *c=NULL; 00525 if (argc != 3) 00526 return RESULT_SHOWUSAGE; 00527 c = ast_channel_walk_locked(NULL); 00528 while(c) { 00529 if (!strcasecmp(c->name, argv[2])) { 00530 ast_cli(fd, 00531 " -- General --\n" 00532 " Name: %s\n" 00533 " Type: %s\n" 00534 " UniqueID: %s\n" 00535 " Caller ID: %s\n" 00536 " DNID Digits: %s\n" 00537 " State: %s (%d)\n" 00538 " Rings: %d\n" 00539 " NativeFormat: %d\n" 00540 " WriteFormat: %d\n" 00541 " ReadFormat: %d\n" 00542 "1st File Descriptor: %d\n" 00543 " Frames in: %d%s\n" 00544 " Frames out: %d%s\n" 00545 " Time to Hangup: %ld\n" 00546 " -- PBX --\n" 00547 " Context: %s\n" 00548 " Extension: %s\n" 00549 " Priority: %d\n" 00550 " Call Group: %d\n" 00551 " Pickup Group: %d\n" 00552 " Application: %s\n" 00553 " Data: %s\n" 00554 " Stack: %d\n" 00555 " Blocking in: %s\n", 00556 c->name, c->type, c->uniqueid, 00557 (c->callerid ? c->callerid : "(N/A)"), 00558 (c->dnid ? c->dnid : "(N/A)" ), ast_state2str(c->_state), c->_state, c->rings, c->nativeformats, c->writeformat, c->readformat, 00559 c->fds[0], c->fin & 0x7fffffff, (c->fin & 0x80000000) ? " (DEBUGGED)" : "", 00560 c->fout & 0x7fffffff, (c->fout & 0x80000000) ? " (DEBUGGED)" : "", (long)c->whentohangup, 00561 c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ), 00562 ( c-> data ? (!ast_strlen_zero(c->data) ? c->data : "(Empty)") : "(None)"), 00563 c->stack, (c->blocking ? c->blockproc : "(Not Blocking)")); 00564 ast_mutex_unlock(&c->lock); 00565 break; 00566 } 00567 ast_mutex_unlock(&c->lock); 00568 c = ast_channel_walk_locked(c); 00569 } 00570 if (!c) 00571 ast_cli(fd, "%s is not a known channel\n", argv[2]); 00572 return RESULT_SUCCESS; 00573 } 00574 00575 static char *complete_ch(char *line, char *word, int pos, int state) 00576 { 00577 struct ast_channel *c; 00578 int which=0; 00579 char *ret; 00580 c = ast_channel_walk_locked(NULL); 00581 while(c) { 00582 if (!strncasecmp(word, c->name, strlen(word))) { 00583 if (++which > state) 00584 break; 00585 } 00586 ast_mutex_unlock(&c->lock); 00587 c = ast_channel_walk_locked(c); 00588 } 00589 if (c) { 00590 ret = strdup(c->name); 00591 ast_mutex_unlock(&c->lock); 00592 } else 00593 ret = NULL; 00594 return ret; 00595 } 00596 00597 static char *complete_fn(char *line, char *word, int pos, int state) 00598 { 00599 char *c; 00600 char filename[256]; 00601 if (pos != 1) 00602 return NULL; 00603 if (word[0] == '/') 00604 strncpy(filename, word, sizeof(filename)-1); 00605 else 00606 snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_MODULE_DIR, word); 00607 c = (char*)filename_completion_function(filename, state); 00608 if (c && word[0] != '/') 00609 c += (strlen((char*)ast_config_AST_MODULE_DIR) + 1); 00610 return c ? strdup(c) : c; 00611 } 00612 00613 static int handle_help(int fd, int argc, char *argv[]); 00614 00615 static struct ast_cli_entry builtins[] = { 00616 /* Keep alphabetized, with longer matches first (example: abcd before abc) */ 00617 { { "_command", "complete", NULL }, handle_commandcomplete, "Command complete", commandcomplete_help }, 00618 { { "_command", "nummatches", NULL }, handle_commandnummatches, "Returns number of command matches", commandnummatches_help }, 00619 { { "_command", "matchesarray", NULL }, handle_commandmatchesarray, "Returns command matches array", commandmatchesarray_help }, 00620 { { "debug", "channel", NULL }, handle_debugchan, "Enable debugging on a channel", debugchan_help, complete_ch }, 00621 { { "help", NULL }, handle_help, "Display help list, or specific help on a command", help_help }, 00622 { { "load", NULL }, handle_load, "Load a dynamic module by name", load_help, complete_fn }, 00623 { { "no", "debug", "channel", NULL }, handle_nodebugchan, "Disable debugging on a channel", nodebugchan_help, complete_ch }, 00624 { { "reload", NULL }, handle_reload, "Reload configuration", reload_help }, 00625 { { "set", "verbose", NULL }, handle_set_verbose, "Set level of verboseness", set_verbose_help }, 00626 { { "show", "channels", NULL }, handle_chanlist, "Display information on channels", chanlist_help }, 00627 { { "show", "channel", NULL }, handle_showchan, "Display information on a specific channel", showchan_help, complete_ch }, 00628 { { "show", "modules", NULL }, handle_modlist, "List modules and info", modlist_help }, 00629 { { "show", "uptime", NULL }, handle_showuptime, "Show uptime information", modlist_help }, 00630 { { "show", "version", NULL }, handle_version, "Display version info", version_help }, 00631 { { "soft", "hangup", NULL }, handle_softhangup, "Request a hangup on a given channel", softhangup_help, complete_ch }, 00632 { { "unload", NULL }, handle_unload, "Unload a dynamic module by name", unload_help, complete_fn }, 00633 { { NULL }, NULL, NULL, NULL } 00634 }; 00635 00636 static struct ast_cli_entry *find_cli(char *cmds[], int exact) 00637 { 00638 int x; 00639 int y; 00640 int match; 00641 struct ast_cli_entry *e=NULL; 00642 for (x=0;builtins[x].cmda[0];x++) { 00643 /* start optimistic */ 00644 match = 1; 00645 for (y=0;match && cmds[y]; y++) { 00646 /* If there are no more words in the candidate command, then we're 00647 there. */ 00648 if (!builtins[x].cmda[y] && !exact) 00649 break; 00650 /* If there are no more words in the command (and we're looking for 00651 an exact match) or there is a difference between the two words, 00652 then this is not a match */ 00653 if (!builtins[x].cmda[y] || strcasecmp(builtins[x].cmda[y], cmds[y])) 00654 match = 0; 00655 } 00656 /* If more words are needed to complete the command then this is not 00657 a candidate (unless we're looking for a really inexact answer */ 00658 if ((exact > -1) && builtins[x].cmda[y]) 00659 match = 0; 00660 if (match) 00661 return &builtins[x]; 00662 } 00663 for (e=helpers;e;e=e->next) { 00664 match = 1; 00665 for (y=0;match && cmds[y]; y++) { 00666 if (!e->cmda[y] && !exact) 00667 break; 00668 if (!e->cmda[y] || strcasecmp(e->cmda[y], cmds[y])) 00669 match = 0; 00670 } 00671 if ((exact > -1) && e->cmda[y]) 00672 match = 0; 00673 if (match) 00674 break; 00675 } 00676 return e; 00677 } 00678 00679 static void join(char *dest, size_t destsize, char *w[]) 00680 { 00681 int x; 00682 /* Join words into a string */ 00683 if (!dest || destsize < 1) { 00684 return; 00685 } 00686 dest[0] = '\0'; 00687 for (x=0;w[x];x++) { 00688 if (x) 00689 strncat(dest, " ", destsize - strlen(dest) - 1); 00690 strncat(dest, w[x], destsize - strlen(dest) - 1); 00691 } 00692 } 00693 00694 static void join2(char *dest, size_t destsize, char *w[]) 00695 { 00696 int x; 00697 /* Join words into a string */ 00698 if (!dest || destsize < 1) { 00699 return; 00700 } 00701 dest[0] = '\0'; 00702 for (x=0;w[x];x++) { 00703 strncat(dest, w[x], destsize - strlen(dest) - 1); 00704 } 00705 } 00706 00707 static char *find_best(char *argv[]) 00708 { 00709 static char cmdline[80]; 00710 int x; 00711 /* See how close we get, then print the */ 00712 char *myargv[AST_MAX_CMD_LEN]; 00713 for (x=0;x<AST_MAX_CMD_LEN;x++) 00714 myargv[x]=NULL; 00715 for (x=0;argv[x];x++) { 00716 myargv[x] = argv[x]; 00717 if (!find_cli(myargv, -1)) 00718 break; 00719 } 00720 join(cmdline, sizeof(cmdline), myargv); 00721 return cmdline; 00722 } 00723 00724 int ast_cli_unregister(struct ast_cli_entry *e) 00725 { 00726 struct ast_cli_entry *cur, *l=NULL; 00727 ast_mutex_lock(&clilock); 00728 cur = helpers; 00729 while(cur) { 00730 if (e == cur) { 00731 if (e->inuse) { 00732 ast_log(LOG_WARNING, "Can't remove command that is in use\n"); 00733 } else { 00734 /* Rewrite */ 00735 if (l) 00736 l->next = e->next; 00737 else 00738 helpers = e->next; 00739 e->next = NULL; 00740 break; 00741 } 00742 } 00743 l = cur; 00744 cur = cur->next; 00745 } 00746 ast_mutex_unlock(&clilock); 00747 return 0; 00748 } 00749 00750 int ast_cli_register(struct ast_cli_entry *e) 00751 { 00752 struct ast_cli_entry *cur, *l=NULL; 00753 char fulle[80] ="", fulltst[80] =""; 00754 static int len; 00755 ast_mutex_lock(&clilock); 00756 join2(fulle, sizeof(fulle), e->cmda); 00757 if (find_cli(e->cmda, -1)) { 00758 ast_mutex_unlock(&clilock); 00759 ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle); 00760 return -1; 00761 } 00762 cur = helpers; 00763 while(cur) { 00764 join2(fulltst, sizeof(fulltst), cur->cmda); 00765 len = strlen(fulltst); 00766 if (strlen(fulle) < len) 00767 len = strlen(fulle); 00768 if (strncasecmp(fulle, fulltst, len) < 0) { 00769 if (l) { 00770 e->next = l->next; 00771 l->next = e; 00772 } else { 00773 e->next = helpers; 00774 helpers = e; 00775 } 00776 break; 00777 } 00778 l = cur; 00779 cur = cur->next; 00780 } 00781 if (!cur) { 00782 if (l) 00783 l->next = e; 00784 else 00785 helpers = e; 00786 e->next = NULL; 00787 } 00788 ast_mutex_unlock(&clilock); 00789 return 0; 00790 } 00791 00792 static int help_workhorse(int fd, char *match[]) 00793 { 00794 char fullcmd1[80]; 00795 char fullcmd2[80]; 00796 char matchstr[80]; 00797 char *fullcmd; 00798 struct ast_cli_entry *e, *e1, *e2; 00799 e1 = builtins; 00800 e2 = helpers; 00801 if (match) 00802 join(matchstr, sizeof(matchstr), match); 00803 while(e1->cmda[0] || e2) { 00804 if (e2) 00805 join(fullcmd2, sizeof(fullcmd2), e2->cmda); 00806 if (e1->cmda[0]) 00807 join(fullcmd1, sizeof(fullcmd1), e1->cmda); 00808 if (!e1->cmda[0] || 00809 (e2 && (strcmp(fullcmd2, fullcmd1) < 0))) { 00810 /* Use e2 */ 00811 e = e2; 00812 fullcmd = fullcmd2; 00813 /* Increment by going to next */ 00814 e2 = e2->next; 00815 } else { 00816 /* Use e1 */ 00817 e = e1; 00818 fullcmd = fullcmd1; 00819 e1++; 00820 } 00821 /* Hide commands that start with '_' */ 00822 if (fullcmd[0] == '_') 00823 continue; 00824 if (match) { 00825 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) { 00826 continue; 00827 } 00828 } 00829 ast_cli(fd, "%25.25s %s\n", fullcmd, e->summary); 00830 } 00831 return 0; 00832 } 00833 00834 static int handle_help(int fd, int argc, char *argv[]) { 00835 struct ast_cli_entry *e; 00836 char fullcmd[80]; 00837 if ((argc < 1)) 00838 return RESULT_SHOWUSAGE; 00839 if (argc > 1) { 00840 e = find_cli(argv + 1, 1); 00841 if (e) 00842 ast_cli(fd, e->usage); 00843 else { 00844 if (find_cli(argv + 1, -1)) { 00845 return help_workhorse(fd, argv + 1); 00846 } else { 00847 join(fullcmd, sizeof(fullcmd), argv+1); 00848 ast_cli(fd, "No such command '%s'.\n", fullcmd); 00849 } 00850 } 00851 } else { 00852 return help_workhorse(fd, NULL); 00853 } 00854 return RESULT_SUCCESS; 00855 } 00856 00857 static char *parse_args(char *s, int *max, char *argv[]) 00858 { 00859 char *dup, *cur; 00860 int x=0; 00861 int quoted=0; 00862 int escaped=0; 00863 int whitespace=1; 00864 00865 dup = strdup(s); 00866 if (dup) { 00867 cur = dup; 00868 while(*s) { 00869 switch(*s) { 00870 case '"': 00871 /* If it's escaped, put a literal quote */ 00872 if (escaped) 00873 goto normal; 00874 else 00875 quoted = !quoted; 00876 if (quoted && whitespace) { 00877 /* If we're starting a quote, coming off white space start a new word, too */ 00878 argv[x++] = cur; 00879 whitespace=0; 00880 } 00881 escaped = 0; 00882 break; 00883 case ' ': 00884 case '\t': 00885 if (!quoted && !escaped) { 00886 /* If we're not quoted, mark this as whitespace, and 00887 end the previous argument */ 00888 whitespace = 1; 00889 *(cur++) = '\0'; 00890 } else 00891 /* Otherwise, just treat it as anything else */ 00892 goto normal; 00893 break; 00894 case '\\': 00895 /* If we're escaped, print a literal, otherwise enable escaping */ 00896 if (escaped) { 00897 goto normal; 00898 } else { 00899 escaped=1; 00900 } 00901 break; 00902 default: 00903 normal: 00904 if (whitespace) { 00905 if (x >= AST_MAX_ARGS -1) { 00906 ast_log(LOG_WARNING, "Too many arguments, truncating\n"); 00907 break; 00908 } 00909 /* Coming off of whitespace, start the next argument */ 00910 argv[x++] = cur; 00911 whitespace=0; 00912 } 00913 *(cur++) = *s; 00914 escaped=0; 00915 } 00916 s++; 00917 } 00918 /* Null terminate */ 00919 *(cur++) = '\0'; 00920 argv[x] = NULL; 00921 *max = x; 00922 } 00923 return dup; 00924 } 00925 00926 /* This returns the number of unique matches for the generator */ 00927 int ast_cli_generatornummatches(char *text, char *word) 00928 { 00929 int matches = 0, i = 0; 00930 char *buf, *oldbuf = NULL; 00931 00932 00933 while ( (buf = ast_cli_generator(text, word, i)) ) { 00934 if (++i > 1 && strcmp(buf,oldbuf) == 0) { 00935 continue; 00936 } 00937 oldbuf = buf; 00938 matches++; 00939 } 00940 00941 return matches; 00942 } 00943 00944 char **ast_cli_completion_matches(char *text, char *word) 00945 { 00946 char **match_list = NULL, *retstr, *prevstr; 00947 size_t match_list_len, max_equal, which, i; 00948 int matches = 0; 00949 00950 match_list_len = 1; 00951 while ((retstr = ast_cli_generator(text, word, matches)) != NULL) { 00952 if (matches + 1 >= match_list_len) { 00953 match_list_len <<= 1; 00954 match_list = realloc(match_list, match_list_len * sizeof(char *)); 00955 } 00956 match_list[++matches] = retstr; 00957 } 00958 00959 if (!match_list) 00960 return (char **) NULL; 00961 00962 which = 2; 00963 prevstr = match_list[1]; 00964 max_equal = strlen(prevstr); 00965 for (; which <= matches; which++) { 00966 for (i = 0; i < max_equal && prevstr[i] == match_list[which][i]; i++) 00967 continue; 00968 max_equal = i; 00969 } 00970 00971 retstr = malloc(max_equal + 1); 00972 (void) strncpy(retstr, match_list[1], max_equal); 00973 retstr[max_equal] = '\0'; 00974 match_list[0] = retstr; 00975 00976 if (matches + 1 >= match_list_len) 00977 match_list = realloc(match_list, (match_list_len + 1) * sizeof(char *)); 00978 match_list[matches + 1] = (char *) NULL; 00979 00980 return (match_list); 00981 } 00982 00983 static char *__ast_cli_generator(char *text, char *word, int state, int lock) 00984 { 00985 char *argv[AST_MAX_ARGS]; 00986 struct ast_cli_entry *e, *e1, *e2; 00987 int x; 00988 int matchnum=0; 00989 char *dup, *res; 00990 char fullcmd1[80]; 00991 char fullcmd2[80]; 00992 char matchstr[80]; 00993 char *fullcmd; 00994 00995 if ((dup = parse_args(text, &x, argv))) { 00996 join(matchstr, sizeof(matchstr), argv); 00997 if (lock) 00998 ast_mutex_lock(&clilock); 00999 e1 = builtins; 01000 e2 = helpers; 01001 while(e1->cmda[0] || e2) { 01002 if (e2) 01003 join(fullcmd2, sizeof(fullcmd2), e2->cmda); 01004 if (e1->cmda[0]) 01005 join(fullcmd1, sizeof(fullcmd1), e1->cmda); 01006 if (!e1->cmda[0] || 01007 (e2 && (strcmp(fullcmd2, fullcmd1) < 0))) { 01008 /* Use e2 */ 01009 e = e2; 01010 fullcmd = fullcmd2; 01011 /* Increment by going to next */ 01012 e2 = e2->next; 01013 } else { 01014 /* Use e1 */ 01015 e = e1; 01016 fullcmd = fullcmd1; 01017 e1++; 01018 } 01019 if ((fullcmd[0] != '_') && !strncasecmp(text, fullcmd, strlen(text))) { 01020 /* We contain the first part of one or more commands */ 01021 matchnum++; 01022 if (matchnum > state) { 01023 /* Now, what we're supposed to return is the next word... */ 01024 if (!ast_strlen_zero(word) && x>0) { 01025 res = e->cmda[x-1]; 01026 } else { 01027 res = e->cmda[x]; 01028 } 01029 if (res) { 01030 if (lock) 01031 ast_mutex_unlock(&clilock); 01032 free(dup); 01033 return res ? strdup(res) : NULL; 01034 } 01035 } 01036 } 01037 if (e->generator && !strncasecmp(matchstr, fullcmd, strlen(fullcmd))) { 01038 /* We have a command in its entirity within us -- theoretically only one 01039 command can have this occur */ 01040 fullcmd = e->generator(text, word, (!ast_strlen_zero(word) ? (x - 1) : (x)), state); 01041 if (lock) 01042 ast_mutex_unlock(&clilock); 01043 free(dup); 01044 return fullcmd; 01045 } 01046 01047 } 01048 if (lock) 01049 ast_mutex_unlock(&clilock); 01050 free(dup); 01051 } 01052 return NULL; 01053 } 01054 01055 char *ast_cli_generator(char *text, char *word, int state) 01056 { 01057 return __ast_cli_generator(text, word, state, 1); 01058 } 01059 01060 int ast_cli_command(int fd, char *s) 01061 { 01062 char *argv[AST_MAX_ARGS]; 01063 struct ast_cli_entry *e; 01064 int x; 01065 char *dup; 01066 x = AST_MAX_ARGS; 01067 if ((dup = parse_args(s, &x, argv))) { 01068 /* We need at least one entry, or ignore */ 01069 if (x > 0) { 01070 ast_mutex_lock(&clilock); 01071 e = find_cli(argv, 0); 01072 if (e) 01073 e->inuse++; 01074 ast_mutex_unlock(&clilock); 01075 if (e) { 01076 switch(e->handler(fd, x, argv)) { 01077 case RESULT_SHOWUSAGE: 01078 ast_cli(fd, e->usage); 01079 break; 01080 } 01081 } else 01082 ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv)); 01083 if (e) { 01084 ast_mutex_lock(&clilock); 01085 e->inuse--; 01086 ast_mutex_unlock(&clilock); 01087 } 01088 } 01089 free(dup); 01090 } else { 01091 ast_log(LOG_WARNING, "Out of memory\n"); 01092 return -1; 01093 } 01094 return 0; 01095 } 01096

Generated on Tue Aug 17 16:13:52 2004 for Asterisk by doxygen 1.3.8