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