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 <sys/signal.h>
00023 #include <stdio.h>
00024 #include <signal.h>
00025 #include <string.h>
00026 #include <pthread.h>
00027 /* For rl_filename_completion */
00028 #include "readline/readline.h"
00029 /* For module directory */
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    /* Has a hidden 'at least' argument */
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    /* Keep alphabetized */
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       /* start optimistic */
00581       match = 1;
00582       for (y=0;match && cmds[y]; y++) {
00583          /* If there are no more words in the candidate command, then we're
00584             there.  */
00585          if (!builtins[x].cmda[y] && !exact)
00586             break;
00587          /* If there are no more words in the command (and we're looking for
00588             an exact match) or there is a difference between the two words,
00589             then this is not a match */
00590          if (!builtins[x].cmda[y] || strcasecmp(builtins[x].cmda[y], cmds[y]))
00591             match = 0;
00592       }
00593       /* If more words are needed to complete the command then this is not
00594          a candidate (unless we're looking for a really inexact answer  */
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    /* Join words into a string */
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    /* Join words into a string */
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    /* See how close we get, then print the  */
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             /* Rewrite */
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          /* Use e2 */
00742          e = e2;
00743          fullcmd = fullcmd2;
00744          /* Increment by going to next */
00745          e2 = e2->next;
00746       } else {
00747          /* Use e1 */
00748          e = e1;
00749          fullcmd = fullcmd1;
00750          e1++;
00751       }
00752       /* Hide commands that start with '_' */
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             /* If it's escaped, put a literal quote */
00803             if (escaped) 
00804                goto normal;
00805             else 
00806                quoted = !quoted;
00807             if (quoted && whitespace) {
00808                /* If we're starting a quote, coming off white space start a new word, too */
00809                argv[x++] = cur;
00810                whitespace=0;
00811             }
00812             escaped = 0;
00813             break;
00814          case ' ':
00815          case '\t':
00816             if (!quoted && !escaped) {
00817                /* If we're not quoted, mark this as whitespace, and
00818                   end the previous argument */
00819                whitespace = 1;
00820                *(cur++) = '\0';
00821             } else
00822                /* Otherwise, just treat it as anything else */ 
00823                goto normal;
00824             break;
00825          case '\\':
00826             /* If we're escaped, print a literal, otherwise enable escaping */
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                /* Coming off of whitespace, start the next argument */
00841                argv[x++] = cur;
00842                whitespace=0;
00843             }
00844             *(cur++) = *s;
00845             escaped=0;
00846          }
00847          s++;
00848       }
00849       /* Null terminate */
00850       *(cur++) = '\0';
00851       argv[x] = NULL;
00852       *max = x;
00853    }
00854    return dup;
00855 }
00856 
00857 /* This returns the number of unique matches for the generator */
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             /* Use e2 */
00940             e = e2;
00941             fullcmd = fullcmd2;
00942             /* Increment by going to next */
00943             e2 = e2->next;
00944          } else {
00945             /* Use e1 */
00946             e = e1;
00947             fullcmd = fullcmd1;
00948             e1++;
00949          }
00950          if ((fullcmd[0] != '_') && !strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
00951             /* We contain the first part of one or more commands */
00952             matchnum++;
00953             if (matchnum > state) {
00954                /* Now, what we're supposed to return is the next word... */
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             /* We have a command in its entirity within us -- theoretically only one
00970                command can have this occur */
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       /* We need at least one entry, or ignore */
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 }

Generated on Fri Oct 31 07:05:05 2003 for Asterisk by doxygen 1.3.4