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