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