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 \
   " 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    /* 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  "%-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    /* Keep alphabetized, with longer matches first (example: abcd before abc) */
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       /* start optimistic */
00592       match = 1;
00593       for (y=0;match && cmds[y]; y++) {
00594          /* If there are no more words in the candidate command, then we're
00595             there.  */
00596          if (!builtins[x].cmda[y] && !exact)
00597             break;
00598          /* If there are no more words in the command (and we're looking for
00599             an exact match) or there is a difference between the two words,
00600             then this is not a match */
00601          if (!builtins[x].cmda[y] || strcasecmp(builtins[x].cmda[y], cmds[y]))
00602             match = 0;
00603       }
00604       /* If more words are needed to complete the command then this is not
00605          a candidate (unless we're looking for a really inexact answer  */
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    /* Join words into a string */
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    /* Join words into a string */
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    /* See how close we get, then print the  */
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             /* Rewrite */
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          /* Use e2 */
00753          e = e2;
00754          fullcmd = fullcmd2;
00755          /* Increment by going to next */
00756          e2 = e2->next;
00757       } else {
00758          /* Use e1 */
00759          e = e1;
00760          fullcmd = fullcmd1;
00761          e1++;
00762       }
00763       /* Hide commands that start with '_' */
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             /* If it's escaped, put a literal quote */
00814             if (escaped) 
00815                goto normal;
00816             else 
00817                quoted = !quoted;
00818             if (quoted && whitespace) {
00819                /* If we're starting a quote, coming off white space start a new word, too */
00820                argv[x++] = cur;
00821                whitespace=0;
00822             }
00823             escaped = 0;
00824             break;
00825          case ' ':
00826          case '\t':
00827             if (!quoted && !escaped) {
00828                /* If we're not quoted, mark this as whitespace, and
00829                   end the previous argument */
00830                whitespace = 1;
00831                *(cur++) = '\0';
00832             } else
00833                /* Otherwise, just treat it as anything else */ 
00834                goto normal;
00835             break;
00836          case '\\':
00837             /* If we're escaped, print a literal, otherwise enable escaping */
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                /* Coming off of whitespace, start the next argument */
00852                argv[x++] = cur;
00853                whitespace=0;
00854             }
00855             *(cur++) = *s;
00856             escaped=0;
00857          }
00858          s++;
00859       }
00860       /* Null terminate */
00861       *(cur++) = '\0';
00862       argv[x] = NULL;
00863       *max = x;
00864    }
00865    return dup;
00866 }
00867 
00868 /* This returns the number of unique matches for the generator */
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             /* Use e2 */
00951             e = e2;
00952             fullcmd = fullcmd2;
00953             /* Increment by going to next */
00954             e2 = e2->next;
00955          } else {
00956             /* Use e1 */
00957             e = e1;
00958             fullcmd = fullcmd1;
00959             e1++;
00960          }
00961          if ((fullcmd[0] != '_') && !strncasecmp(text, fullcmd, strlen(text))) {
00962             /* We contain the first part of one or more commands */
00963             matchnum++;
00964             if (matchnum > state) {
00965                /* Now, what we're supposed to return is the next word... */
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             /* We have a command in its entirity within us -- theoretically only one
00981                command can have this occur */
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       /* We need at least one entry, or ignore */
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 

Generated on Sun Apr 18 23:33:50 2004 for Asterisk by doxygen 1.3.6-20040222