Wed Oct 28 11:45:36 2009

Asterisk developer's documentation


cli.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Standard Command Line Interface
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211551 $")
00029 
00030 #include "asterisk/_private.h"
00031 #include "asterisk/paths.h"   /* use ast_config_AST_MODULE_DIR */
00032 #include <sys/signal.h>
00033 #include <signal.h>
00034 #include <ctype.h>
00035 #include <regex.h>
00036 
00037 #include "asterisk/cli.h"
00038 #include "asterisk/linkedlists.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/lock.h"
00045 #include "editline/readline/readline.h"
00046 #include "asterisk/threadstorage.h"
00047 
00048 /*!
00049  * \brief map a debug or verbose value to a filename
00050  */
00051 struct ast_debug_file {
00052    unsigned int level;
00053    AST_RWLIST_ENTRY(ast_debug_file) entry;
00054    char filename[0];
00055 };
00056 
00057 AST_RWLIST_HEAD(debug_file_list, ast_debug_file);
00058 
00059 /*! list of filenames and their debug settings */
00060 static struct debug_file_list debug_files;
00061 /*! list of filenames and their verbose settings */
00062 static struct debug_file_list verbose_files;
00063 
00064 AST_THREADSTORAGE(ast_cli_buf);
00065 
00066 /*! \brief Initial buffer size for resulting strings in ast_cli() */
00067 #define AST_CLI_INITLEN   256
00068 
00069 void ast_cli(int fd, const char *fmt, ...)
00070 {
00071    int res;
00072    struct ast_str *buf;
00073    va_list ap;
00074 
00075    if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
00076       return;
00077 
00078    va_start(ap, fmt);
00079    res = ast_str_set_va(&buf, 0, fmt, ap);
00080    va_end(ap);
00081 
00082    if (res != AST_DYNSTR_BUILD_FAILED)
00083       ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
00084 }
00085 
00086 unsigned int ast_debug_get_by_file(const char *file) 
00087 {
00088    struct ast_debug_file *adf;
00089    unsigned int res = 0;
00090 
00091    AST_RWLIST_RDLOCK(&debug_files);
00092    AST_LIST_TRAVERSE(&debug_files, adf, entry) {
00093       if (!strncasecmp(adf->filename, file, strlen(adf->filename))) {
00094          res = adf->level;
00095          break;
00096       }
00097    }
00098    AST_RWLIST_UNLOCK(&debug_files);
00099 
00100    return res;
00101 }
00102 
00103 unsigned int ast_verbose_get_by_file(const char *file) 
00104 {
00105    struct ast_debug_file *adf;
00106    unsigned int res = 0;
00107 
00108    AST_RWLIST_RDLOCK(&verbose_files);
00109    AST_LIST_TRAVERSE(&verbose_files, adf, entry) {
00110       if (!strncasecmp(adf->filename, file, strlen(file))) {
00111          res = adf->level;
00112          break;
00113       }
00114    }
00115    AST_RWLIST_UNLOCK(&verbose_files);
00116 
00117    return res;
00118 }
00119 
00120 static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
00121 
00122 static char *complete_fn(const char *word, int state)
00123 {
00124    char *c, *d;
00125    char filename[PATH_MAX];
00126 
00127    if (word[0] == '/')
00128       ast_copy_string(filename, word, sizeof(filename));
00129    else
00130       snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
00131 
00132    c = d = filename_completion_function(filename, state);
00133    
00134    if (c && word[0] != '/')
00135       c += (strlen(ast_config_AST_MODULE_DIR) + 1);
00136    if (c)
00137       c = ast_strdup(c);
00138 
00139    free(d);
00140    
00141    return c;
00142 }
00143 
00144 static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00145 {
00146    /* "module load <mod>" */
00147    switch (cmd) {
00148    case CLI_INIT:
00149       e->command = "module load";
00150       e->usage =
00151          "Usage: module load <module name>\n"
00152          "       Loads the specified module into Asterisk.\n";
00153       return NULL;
00154 
00155    case CLI_GENERATE:
00156       if (a->pos != e->args)
00157          return NULL;
00158       return complete_fn(a->word, a->n);
00159    }
00160    if (a->argc != e->args + 1)
00161       return CLI_SHOWUSAGE;
00162    if (ast_load_resource(a->argv[e->args])) {
00163       ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]);
00164       return CLI_FAILURE;
00165    }
00166    return CLI_SUCCESS;
00167 }
00168 
00169 static char *handle_load_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00170 {
00171    char *res = handle_load(e, cmd, a);
00172    if (cmd == CLI_INIT)
00173       e->command = "load";
00174    return res;
00175 }
00176 
00177 static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00178 {
00179    int x;
00180 
00181    switch (cmd) {
00182    case CLI_INIT:
00183       e->command = "module reload";
00184       e->usage =
00185          "Usage: module reload [module ...]\n"
00186          "       Reloads configuration files for all listed modules which support\n"
00187          "       reloading, or for all supported modules if none are listed.\n";
00188       return NULL;
00189 
00190    case CLI_GENERATE:
00191       return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1);
00192    }
00193    if (a->argc == e->args) {
00194       ast_module_reload(NULL);
00195       return CLI_SUCCESS;
00196    }
00197    for (x = e->args; x < a->argc; x++) {
00198       int res = ast_module_reload(a->argv[x]);
00199       /* XXX reload has multiple error returns, including -1 on error and 2 on success */
00200       switch (res) {
00201       case 0:
00202          ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
00203          break;
00204       case 1:
00205          ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
00206          break;
00207       }
00208    }
00209    return CLI_SUCCESS;
00210 }
00211 
00212 static char *handle_reload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00213 {
00214    char *s = handle_reload(e, cmd, a);
00215    if (cmd == CLI_INIT)    /* override command name */
00216       e->command = "reload";
00217    return s;
00218 }
00219 
00220 /*! 
00221  * \brief Find the debug or verbose file setting 
00222  * \arg debug 1 for debug, 0 for verbose
00223  */
00224 static struct ast_debug_file *find_debug_file(const char *fn, unsigned int debug)
00225 {
00226    struct ast_debug_file *df = NULL;
00227    struct debug_file_list *dfl = debug ? &debug_files : &verbose_files;
00228 
00229    AST_LIST_TRAVERSE(dfl, df, entry) {
00230       if (!strcasecmp(df->filename, fn))
00231          break;
00232    }
00233 
00234    return df;
00235 }
00236 
00237 static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00238 {
00239    int oldval;
00240    int newlevel;
00241    int atleast = 0;
00242    int fd = a->fd;
00243    int argc = a->argc;
00244    char **argv = a->argv;
00245    int *dst;
00246    char *what;
00247    struct debug_file_list *dfl;
00248    struct ast_debug_file *adf;
00249    char *fn;
00250 
00251    switch (cmd) {
00252    case CLI_INIT:
00253       e->command = "core set {debug|verbose} [off|atleast]";
00254       e->usage =
00255          "Usage: core set {debug|verbose} [atleast] <level> [filename]\n"
00256          "       core set {debug|verbose} off\n"
00257          "       Sets level of debug or verbose messages to be displayed or \n"
00258          "       sets a filename to display debug messages from.\n"
00259          "  0 or off means no messages should be displayed.\n"
00260          "  Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
00261       return NULL;
00262 
00263    case CLI_GENERATE:
00264       return NULL;
00265    }
00266    /* all the above return, so we proceed with the handler.
00267     * we are guaranteed to be called with argc >= e->args;
00268     */
00269 
00270    if (argc < e->args)
00271       return CLI_SHOWUSAGE;
00272    if (!strcasecmp(argv[e->args - 2], "debug")) {
00273       dst = &option_debug;
00274       oldval = option_debug;
00275       what = "Core debug";
00276    } else {
00277       dst = &option_verbose;
00278       oldval = option_verbose;
00279       what = "Verbosity";
00280    }
00281    if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) {
00282       unsigned int debug = (*what == 'C');
00283       newlevel = 0;
00284 
00285       dfl = debug ? &debug_files : &verbose_files;
00286 
00287       AST_RWLIST_WRLOCK(dfl);
00288       while ((adf = AST_RWLIST_REMOVE_HEAD(dfl, entry)))
00289          ast_free(adf);
00290       ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00291       AST_RWLIST_UNLOCK(dfl);
00292 
00293       goto done;
00294    }
00295    if (!strcasecmp(argv[e->args-1], "atleast"))
00296       atleast = 1;
00297    if (argc != e->args + atleast && argc != e->args + atleast + 1)
00298       return CLI_SHOWUSAGE;
00299    if (sscanf(argv[e->args + atleast - 1], "%30d", &newlevel) != 1)
00300       return CLI_SHOWUSAGE;
00301    if (argc == e->args + atleast + 1) {
00302       unsigned int debug = (*what == 'C');
00303       dfl = debug ? &debug_files : &verbose_files;
00304 
00305       fn = argv[e->args + atleast];
00306 
00307       AST_RWLIST_WRLOCK(dfl);
00308 
00309       if ((adf = find_debug_file(fn, debug)) && !newlevel) {
00310          AST_RWLIST_REMOVE(dfl, adf, entry);
00311          if (AST_RWLIST_EMPTY(dfl))
00312             ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00313          AST_RWLIST_UNLOCK(dfl);
00314          ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, adf->level, fn);
00315          ast_free(adf);
00316          return CLI_SUCCESS;
00317       }
00318 
00319       if (adf) {
00320          if ((atleast && newlevel < adf->level) || adf->level == newlevel) {
00321             ast_cli(fd, "%s is %d for '%s'\n", what, adf->level, fn);
00322             AST_RWLIST_UNLOCK(dfl);
00323             return CLI_SUCCESS;
00324          }
00325       } else if (!(adf = ast_calloc(1, sizeof(*adf) + strlen(fn) + 1))) {
00326          AST_RWLIST_UNLOCK(dfl);
00327          return CLI_FAILURE;
00328       }
00329 
00330       oldval = adf->level;
00331       adf->level = newlevel;
00332       strcpy(adf->filename, fn);
00333 
00334       ast_set_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
00335 
00336       AST_RWLIST_INSERT_TAIL(dfl, adf, entry);
00337       AST_RWLIST_UNLOCK(dfl);
00338 
00339       ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, adf->level, adf->filename);
00340 
00341       return CLI_SUCCESS;
00342    }
00343 
00344 done:
00345    if (!atleast || newlevel > *dst)
00346       *dst = newlevel;
00347    if (oldval > 0 && *dst == 0)
00348       ast_cli(fd, "%s is now OFF\n", what);
00349    else if (*dst > 0) {
00350       if (oldval == *dst)
00351          ast_cli(fd, "%s is at least %d\n", what, *dst);
00352       else
00353          ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst);
00354    }
00355 
00356    return CLI_SUCCESS;
00357 }
00358 
00359 static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00360 {
00361    switch (cmd) {
00362    case CLI_INIT:
00363       e->command = "logger mute";
00364       e->usage = 
00365          "Usage: logger mute\n"
00366          "       Disables logging output to the current console, making it possible to\n"
00367          "       gather information without being disturbed by scrolling lines.\n";
00368       return NULL;
00369    case CLI_GENERATE:
00370       return NULL;
00371    }
00372 
00373    if (a->argc < 2 || a->argc > 3)
00374       return CLI_SHOWUSAGE;
00375 
00376    if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
00377       ast_console_toggle_mute(a->fd, 1);
00378    else
00379       ast_console_toggle_mute(a->fd, 0);
00380 
00381    return CLI_SUCCESS;
00382 }
00383 
00384 static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00385 {
00386    /* "module unload mod_1 [mod_2 .. mod_N]" */
00387    int x;
00388    int force = AST_FORCE_SOFT;
00389    char *s;
00390 
00391    switch (cmd) {
00392    case CLI_INIT:
00393       e->command = "module unload";
00394       e->usage =
00395          "Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
00396          "       Unloads the specified module from Asterisk. The -f\n"
00397          "       option causes the module to be unloaded even if it is\n"
00398          "       in use (may cause a crash) and the -h module causes the\n"
00399          "       module to be unloaded even if the module says it cannot, \n"
00400          "       which almost always will cause a crash.\n";
00401       return NULL;
00402 
00403    case CLI_GENERATE:
00404       return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
00405    }
00406    if (a->argc < e->args + 1)
00407       return CLI_SHOWUSAGE;
00408    x = e->args;   /* first argument */
00409    s = a->argv[x];
00410    if (s[0] == '-') {
00411       if (s[1] == 'f')
00412          force = AST_FORCE_FIRM;
00413       else if (s[1] == 'h')
00414          force = AST_FORCE_HARD;
00415       else
00416          return CLI_SHOWUSAGE;
00417       if (a->argc < e->args + 2) /* need at least one module name */
00418          return CLI_SHOWUSAGE;
00419       x++;  /* skip this argument */
00420    }
00421 
00422    for (; x < a->argc; x++) {
00423       if (ast_unload_resource(a->argv[x], force)) {
00424          ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
00425          return CLI_FAILURE;
00426       }
00427    }
00428    return CLI_SUCCESS;
00429 }
00430 
00431 static char *handle_unload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00432 {
00433    char *res = handle_unload(e, cmd, a);
00434    if (cmd == CLI_INIT)
00435       e->command = "unload";  /* XXX override */
00436    return res;
00437 }
00438 
00439 #define MODLIST_FORMAT  "%-30s %-40.40s %-10d\n"
00440 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
00441 
00442 AST_MUTEX_DEFINE_STATIC(climodentrylock);
00443 static int climodentryfd = -1;
00444 
00445 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
00446 {
00447    /* Comparing the like with the module */
00448    if (strcasestr(module, like) ) {
00449       ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
00450       return 1;
00451    } 
00452    return 0;
00453 }
00454 
00455 static void print_uptimestr(int fd, struct timeval timeval, const char *prefix, int printsec)
00456 {
00457    int x; /* the main part - years, weeks, etc. */
00458    struct ast_str *out;
00459 
00460 #define SECOND (1)
00461 #define MINUTE (SECOND*60)
00462 #define HOUR (MINUTE*60)
00463 #define DAY (HOUR*24)
00464 #define WEEK (DAY*7)
00465 #define YEAR (DAY*365)
00466 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
00467    if (timeval.tv_sec < 0) /* invalid, nothing to show */
00468       return;
00469 
00470    if (printsec)  {  /* plain seconds output */
00471       ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval.tv_sec);
00472       return;
00473    }
00474    out = ast_str_alloca(256);
00475    if (timeval.tv_sec > YEAR) {
00476       x = (timeval.tv_sec / YEAR);
00477       timeval.tv_sec -= (x * YEAR);
00478       ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00479    }
00480    if (timeval.tv_sec > WEEK) {
00481       x = (timeval.tv_sec / WEEK);
00482       timeval.tv_sec -= (x * WEEK);
00483       ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00484    }
00485    if (timeval.tv_sec > DAY) {
00486       x = (timeval.tv_sec / DAY);
00487       timeval.tv_sec -= (x * DAY);
00488       ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00489    }
00490    if (timeval.tv_sec > HOUR) {
00491       x = (timeval.tv_sec / HOUR);
00492       timeval.tv_sec -= (x * HOUR);
00493       ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00494    }
00495    if (timeval.tv_sec > MINUTE) {
00496       x = (timeval.tv_sec / MINUTE);
00497       timeval.tv_sec -= (x * MINUTE);
00498       ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00499    }
00500    x = timeval.tv_sec;
00501    if (x > 0 || out->used == 0)  /* if there is nothing, print 0 seconds */
00502       ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
00503    ast_cli(fd, "%s: %s\n", prefix, out->str);
00504 }
00505 
00506 static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00507 {
00508    struct timeval curtime = ast_tvnow();
00509    int printsec;
00510 
00511    switch (cmd) {
00512    case CLI_INIT:
00513       e->command = "core show uptime [seconds]";
00514       e->usage =
00515          "Usage: core show uptime [seconds]\n"
00516          "       Shows Asterisk uptime information.\n"
00517          "       The seconds word returns the uptime in seconds only.\n";
00518       return NULL;
00519 
00520    case CLI_GENERATE:
00521       return NULL;
00522    }
00523    /* regular handler */
00524    if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
00525       printsec = 1;
00526    else if (a->argc == e->args-1)
00527       printsec = 0;
00528    else
00529       return CLI_SHOWUSAGE;
00530    if (ast_startuptime.tv_sec)
00531       print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
00532    if (ast_lastreloadtime.tv_sec)
00533       print_uptimestr(a->fd, ast_tvsub(curtime, ast_lastreloadtime), "Last reload", printsec);
00534    return CLI_SUCCESS;
00535 }
00536 
00537 static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00538 {
00539    char *like;
00540 
00541    switch (cmd) {
00542    case CLI_INIT:
00543       e->command = "module show [like]";
00544       e->usage =
00545          "Usage: module show [like keyword]\n"
00546          "       Shows Asterisk modules currently in use, and usage statistics.\n";
00547       return NULL;
00548 
00549    case CLI_GENERATE:
00550       if (a->pos == e->args)
00551          return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
00552       else
00553          return NULL;
00554    }
00555    /* all the above return, so we proceed with the handler.
00556     * we are guaranteed to have argc >= e->args
00557     */
00558    if (a->argc == e->args - 1)
00559       like = "";
00560    else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
00561       like = a->argv[e->args];
00562    else
00563       return CLI_SHOWUSAGE;
00564       
00565    ast_mutex_lock(&climodentrylock);
00566    climodentryfd = a->fd; /* global, protected by climodentrylock */
00567    ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
00568    ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
00569    climodentryfd = -1;
00570    ast_mutex_unlock(&climodentrylock);
00571    return CLI_SUCCESS;
00572 }
00573 #undef MODLIST_FORMAT
00574 #undef MODLIST_FORMAT2
00575 
00576 static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00577 {
00578    struct timeval curtime = ast_tvnow();
00579    int showuptime, printsec;
00580 
00581    switch (cmd) {
00582    case CLI_INIT:
00583       e->command = "core show calls [uptime]";
00584       e->usage =
00585          "Usage: core show calls [uptime] [seconds]\n"
00586          "       Lists number of currently active calls and total number of calls\n"
00587          "       processed through PBX since last restart. If 'uptime' is specified\n"
00588          "       the system uptime is also displayed. If 'seconds' is specified in\n"
00589          "       addition to 'uptime', the system uptime is displayed in seconds.\n";
00590       return NULL;
00591 
00592    case CLI_GENERATE:
00593       if (a->pos != e->args)
00594          return NULL;
00595       return a->n == 0  ? ast_strdup("seconds") : NULL;
00596    }
00597 
00598    /* regular handler */
00599    if (a->argc >= e->args && !strcasecmp(a->argv[e->args-1],"uptime")) {
00600       showuptime = 1;
00601 
00602       if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
00603          printsec = 1;
00604       else if (a->argc == e->args)
00605          printsec = 0;
00606       else
00607          return CLI_SHOWUSAGE;
00608    } else if (a->argc == e->args-1) {
00609       showuptime = 0;
00610       printsec = 0;
00611    } else
00612       return CLI_SHOWUSAGE;
00613 
00614    if (option_maxcalls) {
00615       ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00616          ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00617          ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00618    } else {
00619       ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00620    }
00621    
00622    ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
00623 
00624    if (ast_startuptime.tv_sec && showuptime) {
00625       print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
00626    }
00627 
00628    return RESULT_SUCCESS;
00629 }
00630 
00631 static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00632 {
00633 #define FORMAT_STRING  "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00634 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00635 #define CONCISE_FORMAT_STRING  "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
00636 #define VERBOSE_FORMAT_STRING  "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
00637 #define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
00638 
00639    struct ast_channel *c = NULL;
00640    int numchans = 0, concise = 0, verbose = 0, count = 0;
00641    int fd, argc;
00642    char **argv;
00643 
00644    switch (cmd) {
00645    case CLI_INIT:
00646       e->command = "core show channels [concise|verbose|count]";
00647       e->usage =
00648          "Usage: core show channels [concise|verbose|count]\n"
00649          "       Lists currently defined channels and some information about them. If\n"
00650          "       'concise' is specified, the format is abridged and in a more easily\n"
00651          "       machine parsable format. If 'verbose' is specified, the output includes\n"
00652          "       more and longer fields. If 'count' is specified only the channel and call\n"
00653          "       count is output.\n"
00654          "  The 'concise' option is deprecated and will be removed from future versions\n"
00655          "  of Asterisk.\n";
00656       return NULL;
00657 
00658    case CLI_GENERATE:
00659       return NULL;
00660    }
00661    fd = a->fd;
00662    argc = a->argc;
00663    argv = a->argv;
00664 
00665    if (a->argc == e->args) {
00666       if (!strcasecmp(argv[e->args-1],"concise"))
00667          concise = 1;
00668       else if (!strcasecmp(argv[e->args-1],"verbose"))
00669          verbose = 1;
00670       else if (!strcasecmp(argv[e->args-1],"count"))
00671          count = 1;
00672       else
00673          return CLI_SHOWUSAGE;
00674    } else if (a->argc != e->args - 1)
00675       return CLI_SHOWUSAGE;
00676 
00677    if (!count) {
00678       if (!concise && !verbose)
00679          ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
00680       else if (verbose)
00681          ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
00682             "CallerID", "Duration", "Accountcode", "BridgedTo");
00683    }
00684 
00685    while ((c = ast_channel_walk_locked(c)) != NULL) {
00686       struct ast_channel *bc = ast_bridged_channel(c);
00687       char durbuf[10] = "-";
00688 
00689       if (!count) {
00690          if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
00691             int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
00692             if (verbose) {
00693                int durh = duration / 3600;
00694                int durm = (duration % 3600) / 60;
00695                int durs = duration % 60;
00696                snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
00697             } else {
00698                snprintf(durbuf, sizeof(durbuf), "%d", duration);
00699             }           
00700          }
00701          if (concise) {
00702             ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00703                c->appl ? c->appl : "(None)",
00704                S_OR(c->data, ""),   /* XXX different from verbose ? */
00705                S_OR(c->cid.cid_num, ""),
00706                S_OR(c->accountcode, ""),
00707                c->amaflags, 
00708                durbuf,
00709                bc ? bc->name : "(None)",
00710                c->uniqueid);
00711          } else if (verbose) {
00712             ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00713                c->appl ? c->appl : "(None)",
00714                c->data ? S_OR(c->data, "(Empty)" ): "(None)",
00715                S_OR(c->cid.cid_num, ""),
00716                durbuf,
00717                S_OR(c->accountcode, ""),
00718                bc ? bc->name : "(None)");
00719          } else {
00720             char locbuf[40] = "(None)";
00721             char appdata[40] = "(None)";
00722             
00723             if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
00724                snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
00725             if (c->appl)
00726                snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, S_OR(c->data, ""));
00727             ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
00728          }
00729       }
00730       numchans++;
00731       ast_channel_unlock(c);
00732    }
00733    if (!concise) {
00734       ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
00735       if (option_maxcalls)
00736          ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00737             ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00738             ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00739       else
00740          ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00741 
00742       ast_cli(fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
00743    }
00744    return CLI_SUCCESS;
00745    
00746 #undef FORMAT_STRING
00747 #undef FORMAT_STRING2
00748 #undef CONCISE_FORMAT_STRING
00749 #undef VERBOSE_FORMAT_STRING
00750 #undef VERBOSE_FORMAT_STRING2
00751 }
00752 
00753 static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00754 {
00755    struct ast_channel *c=NULL;
00756 
00757    switch (cmd) {
00758    case CLI_INIT:
00759       e->command = "soft hangup";
00760       e->usage =
00761          "Usage: soft hangup <channel>\n"
00762          "       Request that a channel be hung up. The hangup takes effect\n"
00763          "       the next time the driver reads or writes from the channel\n";
00764       return NULL;
00765    case CLI_GENERATE:
00766       return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
00767    }
00768    if (a->argc != 3)
00769       return CLI_SHOWUSAGE;
00770    c = ast_get_channel_by_name_locked(a->argv[2]);
00771    if (c) {
00772       ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
00773       ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
00774       ast_channel_unlock(c);
00775    } else
00776       ast_cli(a->fd, "%s is not a known channel\n", a->argv[2]);
00777    return CLI_SUCCESS;
00778 }
00779 
00780 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
00781 
00782 static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00783 {
00784    char *buf, *obuf;
00785    int buflen = 2048;
00786    int len = 0;
00787    char **matches;
00788    int x, matchlen;
00789    
00790    switch (cmd) {
00791    case CLI_INIT:
00792       e->command = "_command matchesarray";
00793       e->usage = 
00794          "Usage: _command matchesarray \"<line>\" text \n"
00795          "       This function is used internally to help with command completion and should.\n"
00796          "       never be called by the user directly.\n";
00797       return NULL;
00798    case CLI_GENERATE:
00799       return NULL;
00800    }
00801 
00802    if (a->argc != 4)
00803       return CLI_SHOWUSAGE;
00804    if (!(buf = ast_malloc(buflen)))
00805       return CLI_FAILURE;
00806    buf[len] = '\0';
00807    matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
00808    if (matches) {
00809       for (x=0; matches[x]; x++) {
00810          matchlen = strlen(matches[x]) + 1;
00811          if (len + matchlen >= buflen) {
00812             buflen += matchlen * 3;
00813             obuf = buf;
00814             if (!(buf = ast_realloc(obuf, buflen))) 
00815                /* Memory allocation failure...  Just free old buffer and be done */
00816                ast_free(obuf);
00817          }
00818          if (buf)
00819             len += sprintf( buf + len, "%s ", matches[x]);
00820          ast_free(matches[x]);
00821          matches[x] = NULL;
00822       }
00823       ast_free(matches);
00824    }
00825 
00826    if (buf) {
00827       ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
00828       ast_free(buf);
00829    } else
00830       ast_cli(a->fd, "NULL\n");
00831 
00832    return CLI_SUCCESS;
00833 }
00834 
00835 
00836 
00837 static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00838 {
00839    int matches = 0;
00840 
00841    switch (cmd) {
00842    case CLI_INIT:
00843       e->command = "_command nummatches";
00844       e->usage = 
00845          "Usage: _command nummatches \"<line>\" text \n"
00846          "       This function is used internally to help with command completion and should.\n"
00847          "       never be called by the user directly.\n";
00848       return NULL;
00849    case CLI_GENERATE:
00850       return NULL;
00851    }
00852 
00853    if (a->argc != 4)
00854       return CLI_SHOWUSAGE;
00855 
00856    matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
00857 
00858    ast_cli(a->fd, "%d", matches);
00859 
00860    return CLI_SUCCESS;
00861 }
00862 
00863 static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00864 {
00865    char *buf;
00866    switch (cmd) {
00867    case CLI_INIT:
00868       e->command = "_command complete";
00869       e->usage = 
00870          "Usage: _command complete \"<line>\" text state\n"
00871          "       This function is used internally to help with command completion and should.\n"
00872          "       never be called by the user directly.\n";
00873       return NULL;
00874    case CLI_GENERATE:
00875       return NULL;
00876    }
00877    if (a->argc != 5)
00878       return CLI_SHOWUSAGE;
00879    buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
00880    if (buf) {
00881       ast_cli(a->fd, "%s", buf);
00882       ast_free(buf);
00883    } else
00884       ast_cli(a->fd, "NULL\n");
00885    return CLI_SUCCESS;
00886 }
00887 
00888 static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00889 {
00890    struct ast_channel *c = NULL;
00891    int is_all, is_off = 0;
00892 
00893    switch (cmd) {
00894    case CLI_INIT:
00895       e->command = "core set debug channel";
00896       e->usage =
00897          "Usage: core set debug channel <all|channel> [off]\n"
00898          "       Enables/disables debugging on all or on a specific channel.\n";
00899       return NULL;
00900 
00901    case CLI_GENERATE:
00902       /* XXX remember to handle the optional "off" */
00903       if (a->pos != e->args)
00904          return NULL;
00905       return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
00906    }
00907    /* 'core set debug channel {all|chan_id}' */
00908    if (a->argc == e->args + 2) {
00909       if (!strcasecmp(a->argv[e->args + 1], "off"))
00910          is_off = 1;
00911       else
00912          return CLI_SHOWUSAGE;
00913    } else if (a->argc != e->args + 1)
00914       return CLI_SHOWUSAGE;
00915 
00916    is_all = !strcasecmp("all", a->argv[e->args]);
00917    if (is_all) {
00918       if (is_off) {
00919          global_fin &= ~DEBUGCHAN_FLAG;
00920          global_fout &= ~DEBUGCHAN_FLAG;
00921       } else {
00922          global_fin |= DEBUGCHAN_FLAG;
00923          global_fout |= DEBUGCHAN_FLAG;
00924       }
00925       c = ast_channel_walk_locked(NULL);
00926    } else {
00927       c = ast_get_channel_by_name_locked(a->argv[e->args]);
00928       if (c == NULL)
00929          ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
00930    }
00931    while (c) {
00932       if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
00933          if (is_off) {
00934             c->fin &= ~DEBUGCHAN_FLAG;
00935             c->fout &= ~DEBUGCHAN_FLAG;
00936          } else {
00937             c->fin |= DEBUGCHAN_FLAG;
00938             c->fout |= DEBUGCHAN_FLAG;
00939          }
00940          ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
00941       }
00942       ast_channel_unlock(c);
00943       if (!is_all)
00944          break;
00945       c = ast_channel_walk_locked(c);
00946    }
00947    ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
00948    return CLI_SUCCESS;
00949 }
00950 
00951 static char *handle_debugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00952 {
00953    char *res;
00954 
00955    if (cmd == CLI_HANDLER && a->argc != e->args + 1)
00956       return CLI_SHOWUSAGE;
00957    res = handle_core_set_debug_channel(e, cmd, a);
00958    if (cmd == CLI_INIT)
00959       e->command = "debug channel";
00960    return res;
00961 }
00962 
00963 static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00964 {
00965    char *res;
00966    if (cmd == CLI_HANDLER) {
00967       if (a->argc != e->args + 1)
00968          return CLI_SHOWUSAGE;
00969       /* pretend we have an extra "off" at the end. We can do this as the array
00970        * is NULL terminated so we overwrite that entry.
00971        */
00972       a->argv[e->args+1] = "off";
00973       a->argc++;
00974    }
00975    res = handle_core_set_debug_channel(e, cmd, a);
00976    if (cmd == CLI_INIT)
00977       e->command = "no debug channel";
00978    return res;
00979 }
00980       
00981 static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00982 {
00983    struct ast_channel *c=NULL;
00984    struct timeval now;
00985    struct ast_str *out = ast_str_alloca(2048);
00986    char cdrtime[256];
00987    char nf[256], wf[256], rf[256];
00988    long elapsed_seconds=0;
00989    int hour=0, min=0, sec=0;
00990 #ifdef CHANNEL_TRACE
00991    int trace_enabled;
00992 #endif
00993 
00994    switch (cmd) {
00995    case CLI_INIT:
00996       e->command = "core show channel";
00997       e->usage = 
00998          "Usage: core show channel <channel>\n"
00999          "       Shows lots of information about the specified channel.\n";
01000       return NULL;
01001    case CLI_GENERATE:
01002       return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
01003    }
01004    
01005    if (a->argc != 4)
01006       return CLI_SHOWUSAGE;
01007    now = ast_tvnow();
01008    c = ast_get_channel_by_name_locked(a->argv[3]);
01009    if (!c) {
01010       ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
01011       return CLI_SUCCESS;
01012    }
01013    if (c->cdr) {
01014       elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01015       hour = elapsed_seconds / 3600;
01016       min = (elapsed_seconds % 3600) / 60;
01017       sec = elapsed_seconds % 60;
01018       snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
01019    } else
01020       strcpy(cdrtime, "N/A");
01021    ast_cli(a->fd, 
01022       " -- General --\n"
01023       "           Name: %s\n"
01024       "           Type: %s\n"
01025       "       UniqueID: %s\n"
01026       "      Caller ID: %s\n"
01027       " Caller ID Name: %s\n"
01028       "    DNID Digits: %s\n"
01029       "       Language: %s\n"
01030       "          State: %s (%d)\n"
01031       "          Rings: %d\n"
01032       "  NativeFormats: %s\n"
01033       "    WriteFormat: %s\n"
01034       "     ReadFormat: %s\n"
01035       " WriteTranscode: %s\n"
01036       "  ReadTranscode: %s\n"
01037       "1st File Descriptor: %d\n"
01038       "      Frames in: %d%s\n"
01039       "     Frames out: %d%s\n"
01040       " Time to Hangup: %ld\n"
01041       "   Elapsed Time: %s\n"
01042       "  Direct Bridge: %s\n"
01043       "Indirect Bridge: %s\n"
01044       " --   PBX   --\n"
01045       "        Context: %s\n"
01046       "      Extension: %s\n"
01047       "       Priority: %d\n"
01048       "     Call Group: %llu\n"
01049       "   Pickup Group: %llu\n"
01050       "    Application: %s\n"
01051       "           Data: %s\n"
01052       "    Blocking in: %s\n",
01053       c->name, c->tech->type, c->uniqueid,
01054       S_OR(c->cid.cid_num, "(N/A)"),
01055       S_OR(c->cid.cid_name, "(N/A)"),
01056       S_OR(c->cid.cid_dnid, "(N/A)"), 
01057       c->language,   
01058       ast_state2str(c->_state), c->_state, c->rings, 
01059       ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
01060       ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
01061       ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
01062       c->writetrans ? "Yes" : "No",
01063       c->readtrans ? "Yes" : "No",
01064       c->fds[0],
01065       c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01066       c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01067       (long)c->whentohangup,
01068       cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
01069       c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
01070       ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
01071       (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
01072    
01073    if (pbx_builtin_serialize_variables(c, &out))
01074       ast_cli(a->fd,"      Variables:\n%s\n", out->str);
01075    if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
01076       ast_cli(a->fd,"  CDR Variables:\n%s\n", out->str);
01077 #ifdef CHANNEL_TRACE
01078    trace_enabled = ast_channel_trace_is_enabled(c);
01079    ast_cli(a->fd, "  Context Trace: %s\n", trace_enabled ? "Enabled" : "Disabled");
01080    if (trace_enabled && ast_channel_trace_serialize(c, &out))
01081       ast_cli(a->fd, "          Trace:\n%s\n", out->str);
01082 #endif
01083    ast_channel_unlock(c);
01084    return CLI_SUCCESS;
01085 }
01086 
01087 /*
01088  * helper function to generate CLI matches from a fixed set of values.
01089  * A NULL word is acceptable.
01090  */
01091 char *ast_cli_complete(const char *word, char *const choices[], int state)
01092 {
01093    int i, which = 0, len;
01094    len = ast_strlen_zero(word) ? 0 : strlen(word);
01095 
01096    for (i = 0; choices[i]; i++) {
01097       if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
01098          return ast_strdup(choices[i]);
01099    }
01100    return NULL;
01101 }
01102 
01103 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
01104 {
01105    struct ast_channel *c = NULL;
01106    int which = 0;
01107    int wordlen;
01108    char notfound = '\0';
01109    char *ret = &notfound; /* so NULL can break the loop */
01110 
01111    if (pos != rpos)
01112       return NULL;
01113 
01114    wordlen = strlen(word); 
01115 
01116    while (ret == &notfound && (c = ast_channel_walk_locked(c))) {
01117       if (!strncasecmp(word, c->name, wordlen) && ++which > state)
01118          ret = ast_strdup(c->name);
01119       ast_channel_unlock(c);
01120    }
01121    return ret == &notfound ? NULL : ret;
01122 }
01123 
01124 static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01125 {
01126 #define FORMAT_STRING  "%-25s  %-20s  %-20s\n"
01127 
01128    struct ast_group_info *gi = NULL;
01129    int numchans = 0;
01130    regex_t regexbuf;
01131    int havepattern = 0;
01132 
01133    switch (cmd) {
01134    case CLI_INIT:
01135       e->command = "group show channels";
01136       e->usage = 
01137          "Usage: group show channels [pattern]\n"
01138          "       Lists all currently active channels with channel group(s) specified.\n"
01139          "       Optional regular expression pattern is matched to group names for each\n"
01140          "       channel.\n";
01141       return NULL;
01142    case CLI_GENERATE:
01143       return NULL;
01144    }
01145 
01146    if (a->argc < 3 || a->argc > 4)
01147       return CLI_SHOWUSAGE;
01148    
01149    if (a->argc == 4) {
01150       if (regcomp(&regexbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
01151          return CLI_SHOWUSAGE;
01152       havepattern = 1;
01153    }
01154 
01155    ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
01156 
01157    ast_app_group_list_rdlock();
01158    
01159    gi = ast_app_group_list_head();
01160    while (gi) {
01161       if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
01162          ast_cli(a->fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
01163          numchans++;
01164       }
01165       gi = AST_LIST_NEXT(gi, group_list);
01166    }
01167    
01168    ast_app_group_list_unlock();
01169    
01170    if (havepattern)
01171       regfree(&regexbuf);
01172 
01173    ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
01174    return CLI_SUCCESS;
01175 #undef FORMAT_STRING
01176 }
01177 
01178 static struct ast_cli_entry cli_debug_channel_deprecated = AST_CLI_DEFINE(handle_debugchan_deprecated, "Enable debugging on channel");
01179 static struct ast_cli_entry cli_module_load_deprecated = AST_CLI_DEFINE(handle_load_deprecated, "Load a module");
01180 static struct ast_cli_entry cli_module_reload_deprecated = AST_CLI_DEFINE(handle_reload_deprecated, "reload modules by name");
01181 static struct ast_cli_entry cli_module_unload_deprecated = AST_CLI_DEFINE(handle_unload_deprecated, "unload modules by name");
01182 
01183 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
01184 
01185 static struct ast_cli_entry cli_cli[] = {
01186    /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
01187    AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
01188    AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
01189    AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
01190 
01191    AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
01192 
01193    AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
01194 
01195    AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
01196 
01197    AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
01198 
01199    AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel",
01200       .deprecate_cmd = &cli_debug_channel_deprecated),
01201 
01202    AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
01203 
01204    AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
01205 
01206    AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
01207 
01208    AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
01209 
01210    AST_CLI_DEFINE(handle_modlist, "List modules and info"),
01211 
01212    AST_CLI_DEFINE(handle_load, "Load a module by name", .deprecate_cmd = &cli_module_load_deprecated),
01213 
01214    AST_CLI_DEFINE(handle_reload, "Reload configuration", .deprecate_cmd = &cli_module_reload_deprecated),
01215 
01216    AST_CLI_DEFINE(handle_unload, "Unload a module by name", .deprecate_cmd = &cli_module_unload_deprecated ),
01217 
01218    AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
01219 
01220    AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
01221 };
01222 
01223 /*!
01224  * Some regexp characters in cli arguments are reserved and used as separators.
01225  */
01226 static const char cli_rsvd[] = "[]{}|*%";
01227 
01228 /*!
01229  * initialize the _full_cmd string and related parameters,
01230  * return 0 on success, -1 on error.
01231  */
01232 static int set_full_cmd(struct ast_cli_entry *e)
01233 {
01234    int i;
01235    char buf[80];
01236 
01237    ast_join(buf, sizeof(buf), e->cmda);
01238    e->_full_cmd = ast_strdup(buf);
01239    if (!e->_full_cmd) {
01240       ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
01241       return -1;
01242    }
01243    e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
01244    for (i = 0; e->cmda[i]; i++)
01245       ;
01246    e->args = i;
01247    return 0;
01248 }
01249 
01250 /*! \brief initialize the _full_cmd string in * each of the builtins. */
01251 void ast_builtins_init(void)
01252 {
01253    ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
01254 }
01255 
01256 static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
01257 {
01258    if (e) {
01259       return AST_LIST_NEXT(e, list);
01260    } else {
01261       return AST_LIST_FIRST(&helpers);
01262    }
01263 }
01264 
01265 /*!
01266  * match a word in the CLI entry.
01267  * returns -1 on mismatch, 0 on match of an optional word,
01268  * 1 on match of a full word.
01269  *
01270  * The pattern can be
01271  *   any_word     match for equal
01272  *   [foo|bar|baz]   optionally, one of these words
01273  *   {foo|bar|baz}   exactly, one of these words
01274  *   %         any word
01275  */
01276 static int word_match(const char *cmd, const char *cli_word)
01277 {
01278    int l;
01279    char *pos;
01280 
01281    if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
01282       return -1;
01283    if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
01284       return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
01285    /* regexp match, takes [foo|bar] or {foo|bar} */
01286    l = strlen(cmd);
01287    /* wildcard match - will extend in the future */
01288    if (l > 0 && cli_word[0] == '%') {
01289       return 1;   /* wildcard */
01290    }
01291    pos = strcasestr(cli_word, cmd);
01292    if (pos == NULL) /* not found, say ok if optional */
01293       return cli_word[0] == '[' ? 0 : -1;
01294    if (pos == cli_word) /* no valid match at the beginning */
01295       return -1;
01296    if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
01297       return 1;   /* valid match */
01298    return -1;  /* not found */
01299 }
01300 
01301 /*! \brief if word is a valid prefix for token, returns the pos-th
01302  * match as a malloced string, or NULL otherwise.
01303  * Always tell in *actual how many matches we got.
01304  */
01305 static char *is_prefix(const char *word, const char *token,
01306    int pos, int *actual)
01307 {
01308    int lw;
01309    char *s, *t1;
01310 
01311    *actual = 0;
01312    if (ast_strlen_zero(token))
01313       return NULL;
01314    if (ast_strlen_zero(word))
01315       word = "";  /* dummy */
01316    lw = strlen(word);
01317    if (strcspn(word, cli_rsvd) != lw)
01318       return NULL;   /* no match if word has reserved chars */
01319    if (strchr(cli_rsvd, token[0]) == NULL) { /* regular match */
01320       if (strncasecmp(token, word, lw))   /* no match */
01321          return NULL;
01322       *actual = 1;
01323       return (pos != 0) ? NULL : ast_strdup(token);
01324    }
01325    /* now handle regexp match */
01326 
01327    /* Wildcard always matches, so we never do is_prefix on them */
01328 
01329    t1 = ast_strdupa(token + 1);  /* copy, skipping first char */
01330    while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
01331       if (*s == '%') /* wildcard */
01332          continue;
01333       if (strncasecmp(s, word, lw)) /* no match */
01334          continue;
01335       (*actual)++;
01336       if (pos-- == 0)
01337          return ast_strdup(s);
01338    }
01339    return NULL;
01340 }
01341 
01342 /*!
01343  * \brief locate a cli command in the 'helpers' list (which must be locked).
01344  * exact has 3 values:
01345  *      0       returns if the search key is equal or longer than the entry.
01346  *    note that trailing optional arguments are skipped.
01347  *      -1      true if the mismatch is on the last word XXX not true!
01348  *      1       true only on complete, exact match.
01349  *
01350  * The search compares word by word taking care of regexps in e->cmda
01351  */
01352 static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
01353 {
01354    int matchlen = -1;   /* length of longest match so far */
01355    struct ast_cli_entry *cand = NULL, *e=NULL;
01356 
01357    while ( (e = cli_next(e)) ) {
01358       /* word-by word regexp comparison */
01359       char * const *src = cmds;
01360       char * const *dst = e->cmda;
01361       int n = 0;
01362       for (;; dst++, src += n) {
01363          n = word_match(*src, *dst);
01364          if (n < 0)
01365             break;
01366       }
01367       if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
01368          /* no more words in 'e' */
01369          if (ast_strlen_zero(*src)) /* exact match, cannot do better */
01370             break;
01371          /* Here, cmds has more words than the entry 'e' */
01372          if (match_type != 0) /* but we look for almost exact match... */
01373             continue;   /* so we skip this one. */
01374          /* otherwise we like it (case 0) */
01375       } else { /* still words in 'e' */
01376          if (ast_strlen_zero(*src))
01377             continue; /* cmds is shorter than 'e', not good */
01378          /* Here we have leftover words in cmds and 'e',
01379           * but there is a mismatch. We only accept this one if match_type == -1
01380           * and this is the last word for both.
01381           */
01382          if (match_type != -1 || !ast_strlen_zero(src[1]) ||
01383              !ast_strlen_zero(dst[1])) /* not the one we look for */
01384             continue;
01385          /* good, we are in case match_type == -1 and mismatch on last word */
01386       }
01387       if (src - cmds > matchlen) {  /* remember the candidate */
01388          matchlen = src - cmds;
01389          cand = e;
01390       }
01391    }
01392    return e ? e : cand;
01393 }
01394 
01395 static char *find_best(char *argv[])
01396 {
01397    static char cmdline[80];
01398    int x;
01399    /* See how close we get, then print the candidate */
01400    char *myargv[AST_MAX_CMD_LEN];
01401    for (x=0;x<AST_MAX_CMD_LEN;x++)
01402       myargv[x]=NULL;
01403    AST_RWLIST_RDLOCK(&helpers);
01404    for (x=0;argv[x];x++) {
01405       myargv[x] = argv[x];
01406       if (!find_cli(myargv, -1))
01407          break;
01408    }
01409    AST_RWLIST_UNLOCK(&helpers);
01410    ast_join(cmdline, sizeof(cmdline), myargv);
01411    return cmdline;
01412 }
01413 
01414 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01415 {
01416    if (e->deprecate_cmd) {
01417       __ast_cli_unregister(e->deprecate_cmd, e);
01418    }
01419    if (e->inuse) {
01420       ast_log(LOG_WARNING, "Can't remove command that is in use\n");
01421    } else {
01422       AST_RWLIST_WRLOCK(&helpers);
01423       AST_RWLIST_REMOVE(&helpers, e, list);
01424       AST_RWLIST_UNLOCK(&helpers);
01425       ast_free(e->_full_cmd);
01426       e->_full_cmd = NULL;
01427       if (e->handler) {
01428          /* this is a new-style entry. Reset fields and free memory. */
01429          char *cmda = (char *) e->cmda;
01430          memset(cmda, '\0', sizeof(e->cmda));
01431          ast_free(e->command);
01432          e->command = NULL;
01433          e->usage = NULL;
01434       }
01435    }
01436    return 0;
01437 }
01438 
01439 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
01440 {
01441    struct ast_cli_entry *cur;
01442    int i, lf, ret = -1;
01443 
01444    struct ast_cli_args a;  /* fake argument */
01445    char **dst = (char **)e->cmda;   /* need to cast as the entry is readonly */
01446    char *s;
01447 
01448    memset(&a, '\0', sizeof(a));
01449    e->handler(e, CLI_INIT, &a);
01450    /* XXX check that usage and command are filled up */
01451    s = ast_skip_blanks(e->command);
01452    s = e->command = ast_strdup(s);
01453    for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
01454       *dst++ = s; /* store string */
01455       s = ast_skip_nonblanks(s);
01456       if (*s == '\0')   /* we are done */
01457          break;
01458       *s++ = '\0';
01459       s = ast_skip_blanks(s);
01460    }
01461    *dst++ = NULL;
01462    
01463    AST_RWLIST_WRLOCK(&helpers);
01464    
01465    if (find_cli(e->cmda, 1)) {
01466       ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", e->_full_cmd);
01467       goto done;
01468    }
01469    if (set_full_cmd(e))
01470       goto done;
01471    if (!ed) {
01472       e->deprecated = 0;
01473    } else {
01474       e->deprecated = 1;
01475       e->summary = ed->summary;
01476       e->usage = ed->usage;
01477       /* XXX If command A deprecates command B, and command B deprecates command C...
01478          Do we want to show command A or command B when telling the user to use new syntax?
01479          This currently would show command A.
01480          To show command B, you just need to always use ed->_full_cmd.
01481        */
01482       e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
01483    }
01484 
01485    lf = e->cmdlen;
01486    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
01487       int len = cur->cmdlen;
01488       if (lf < len)
01489          len = lf;
01490       if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
01491          AST_RWLIST_INSERT_BEFORE_CURRENT(e, list); 
01492          break;
01493       }
01494    }
01495    AST_RWLIST_TRAVERSE_SAFE_END;
01496 
01497    if (!cur)
01498       AST_RWLIST_INSERT_TAIL(&helpers, e, list); 
01499    ret = 0; /* success */
01500 
01501 done:
01502    AST_RWLIST_UNLOCK(&helpers);
01503 
01504    if (e->deprecate_cmd) {
01505       /* This command deprecates another command.  Register that one also. */
01506       __ast_cli_register(e->deprecate_cmd, e);
01507    }
01508    
01509    return ret;
01510 }
01511 
01512 /* wrapper function, so we can unregister deprecated commands recursively */
01513 int ast_cli_unregister(struct ast_cli_entry *e)
01514 {
01515    return __ast_cli_unregister(e, NULL);
01516 }
01517 
01518 /* wrapper function, so we can register deprecated commands recursively */
01519 int ast_cli_register(struct ast_cli_entry *e)
01520 {
01521    return __ast_cli_register(e, NULL);
01522 }
01523 
01524 /*
01525  * register/unregister an array of entries.
01526  */
01527 int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
01528 {
01529    int i, res = 0;
01530 
01531    for (i = 0; i < len; i++)
01532       res |= ast_cli_register(e + i);
01533 
01534    return res;
01535 }
01536 
01537 int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
01538 {
01539    int i, res = 0;
01540 
01541    for (i = 0; i < len; i++)
01542       res |= ast_cli_unregister(e + i);
01543 
01544    return res;
01545 }
01546 
01547 
01548 /*! \brief helper for final part of handle_help
01549  *  if locked = 1, assume the list is already locked
01550  */
01551 static char *help1(int fd, char *match[], int locked)
01552 {
01553    char matchstr[80] = "";
01554    struct ast_cli_entry *e = NULL;
01555    int len = 0;
01556    int found = 0;
01557 
01558    if (match) {
01559       ast_join(matchstr, sizeof(matchstr), match);
01560       len = strlen(matchstr);
01561    }
01562    if (!locked)
01563       AST_RWLIST_RDLOCK(&helpers);
01564    while ( (e = cli_next(e)) ) {
01565       /* Hide commands that start with '_' */
01566       if (e->_full_cmd[0] == '_')
01567          continue;
01568       /* Hide commands that are marked as deprecated. */
01569       if (e->deprecated)
01570          continue;
01571       if (match && strncasecmp(matchstr, e->_full_cmd, len))
01572          continue;
01573       ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
01574       found++;
01575    }
01576    if (!locked)
01577       AST_RWLIST_UNLOCK(&helpers);
01578    if (!found && matchstr[0])
01579       ast_cli(fd, "No such command '%s'.\n", matchstr);
01580    return CLI_SUCCESS;
01581 }
01582 
01583 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01584 {
01585    char fullcmd[80];
01586    struct ast_cli_entry *my_e;
01587    char *res = CLI_SUCCESS;
01588 
01589    if (cmd == CLI_INIT) {
01590       e->command = "help";
01591       e->usage =
01592          "Usage: help [topic]\n"
01593          "       When called with a topic as an argument, displays usage\n"
01594          "       information on the given command. If called without a\n"
01595          "       topic, it provides a list of commands.\n";
01596       return NULL;
01597 
01598    } else if (cmd == CLI_GENERATE) {
01599       /* skip first 4 or 5 chars, "help " */
01600       int l = strlen(a->line);
01601 
01602       if (l > 5)
01603          l = 5;
01604       /* XXX watch out, should stop to the non-generator parts */
01605       return __ast_cli_generator(a->line + l, a->word, a->n, 0);
01606    }
01607    if (a->argc == 1)
01608       return help1(a->fd, NULL, 0);
01609 
01610    AST_RWLIST_RDLOCK(&helpers);
01611    my_e = find_cli(a->argv + 1, 1); /* try exact match first */
01612    if (!my_e) {
01613       res = help1(a->fd, a->argv + 1, 1 /* locked */);
01614       AST_RWLIST_UNLOCK(&helpers);
01615       return res;
01616    }
01617    if (my_e->usage)
01618       ast_cli(a->fd, "%s", my_e->usage);
01619    else {
01620       ast_join(fullcmd, sizeof(fullcmd), a->argv+1);
01621       ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd);
01622    }
01623    AST_RWLIST_UNLOCK(&helpers);
01624    return res;
01625 }
01626 
01627 static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
01628 {
01629    char *dup, *cur;
01630    int x = 0;
01631    int quoted = 0;
01632    int escaped = 0;
01633    int whitespace = 1;
01634    int dummy = 0;
01635 
01636    if (trailingwhitespace == NULL)
01637       trailingwhitespace = &dummy;
01638    *trailingwhitespace = 0;
01639    if (s == NULL) /* invalid, though! */
01640       return NULL;
01641    /* make a copy to store the parsed string */
01642    if (!(dup = ast_strdup(s)))
01643       return NULL;
01644 
01645    cur = dup;
01646    /* scan the original string copying into cur when needed */
01647    for (; *s ; s++) {
01648       if (x >= max - 1) {
01649          ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
01650          break;
01651       }
01652       if (*s == '"' && !escaped) {
01653          quoted = !quoted;
01654          if (quoted && whitespace) {
01655             /* start a quoted string from previous whitespace: new argument */
01656             argv[x++] = cur;
01657             whitespace = 0;
01658          }
01659       } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
01660          /* If we are not already in whitespace, and not in a quoted string or
01661             processing an escape sequence, and just entered whitespace, then
01662             finalize the previous argument and remember that we are in whitespace
01663          */
01664          if (!whitespace) {
01665             *cur++ = '\0';
01666             whitespace = 1;
01667          }
01668       } else if (*s == '\\' && !escaped) {
01669          escaped = 1;
01670       } else {
01671          if (whitespace) {
01672             /* we leave whitespace, and are not quoted. So it's a new argument */
01673             argv[x++] = cur;
01674             whitespace = 0;
01675          }
01676          *cur++ = *s;
01677          escaped = 0;
01678       }
01679    }
01680    /* Null terminate */
01681    *cur++ = '\0';
01682    /* XXX put a NULL in the last argument, because some functions that take
01683     * the array may want a null-terminated array.
01684     * argc still reflects the number of non-NULL entries.
01685     */
01686    argv[x] = NULL;
01687    *argc = x;
01688    *trailingwhitespace = whitespace;
01689    return dup;
01690 }
01691 
01692 /*! \brief Return the number of unique matches for the generator */
01693 int ast_cli_generatornummatches(const char *text, const char *word)
01694 {
01695    int matches = 0, i = 0;
01696    char *buf = NULL, *oldbuf = NULL;
01697 
01698    while ((buf = ast_cli_generator(text, word, i++))) {
01699       if (!oldbuf || strcmp(buf,oldbuf))
01700          matches++;
01701       if (oldbuf)
01702          ast_free(oldbuf);
01703       oldbuf = buf;
01704    }
01705    if (oldbuf)
01706       ast_free(oldbuf);
01707    return matches;
01708 }
01709 
01710 char **ast_cli_completion_matches(const char *text, const char *word)
01711 {
01712    char **match_list = NULL, *retstr, *prevstr;
01713    size_t match_list_len, max_equal, which, i;
01714    int matches = 0;
01715 
01716    /* leave entry 0 free for the longest common substring */
01717    match_list_len = 1;
01718    while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
01719       if (matches + 1 >= match_list_len) {
01720          match_list_len <<= 1;
01721          if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
01722             return NULL;
01723       }
01724       match_list[++matches] = retstr;
01725    }
01726 
01727    if (!match_list)
01728       return match_list; /* NULL */
01729 
01730    /* Find the longest substring that is common to all results
01731     * (it is a candidate for completion), and store a copy in entry 0.
01732     */
01733    prevstr = match_list[1];
01734    max_equal = strlen(prevstr);
01735    for (which = 2; which <= matches; which++) {
01736       for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
01737          continue;
01738       max_equal = i;
01739    }
01740 
01741    if (!(retstr = ast_malloc(max_equal + 1)))
01742       return NULL;
01743    
01744    ast_copy_string(retstr, match_list[1], max_equal + 1);
01745    match_list[0] = retstr;
01746 
01747    /* ensure that the array is NULL terminated */
01748    if (matches + 1 >= match_list_len) {
01749       if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
01750          return NULL;
01751    }
01752    match_list[matches + 1] = NULL;
01753 
01754    return match_list;
01755 }
01756 
01757 /*! \brief returns true if there are more words to match */
01758 static int more_words (char * const *dst)
01759 {
01760    int i;
01761    for (i = 0; dst[i]; i++) {
01762       if (dst[i][0] != '[')
01763          return -1;
01764    }
01765    return 0;
01766 }
01767    
01768 /*
01769  * generate the entry at position 'state'
01770  */
01771 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
01772 {
01773    char *argv[AST_MAX_ARGS];
01774    struct ast_cli_entry *e = NULL;
01775    int x = 0, argindex, matchlen;
01776    int matchnum=0;
01777    char *ret = NULL;
01778    char matchstr[80] = "";
01779    int tws = 0;
01780    /* Split the argument into an array of words */
01781    char *dup = parse_args(text, &x, argv, ARRAY_LEN(argv), &tws);
01782 
01783    if (!dup)   /* malloc error */
01784       return NULL;
01785 
01786    /* Compute the index of the last argument (could be an empty string) */
01787    argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
01788 
01789    /* rebuild the command, ignore terminating white space and flatten space */
01790    ast_join(matchstr, sizeof(matchstr)-1, argv);
01791    matchlen = strlen(matchstr);
01792    if (tws) {
01793       strcat(matchstr, " "); /* XXX */
01794       if (matchlen)
01795          matchlen++;
01796    }
01797    if (lock)
01798       AST_RWLIST_RDLOCK(&helpers);
01799    while ( (e = cli_next(e)) ) {
01800       /* XXX repeated code */
01801       int src = 0, dst = 0, n = 0;
01802 
01803       if (e->command[0] == '_')
01804          continue;
01805 
01806       /*
01807        * Try to match words, up to and excluding the last word, which
01808        * is either a blank or something that we want to extend.
01809        */
01810       for (;src < argindex; dst++, src += n) {
01811          n = word_match(argv[src], e->cmda[dst]);
01812          if (n < 0)
01813             break;
01814       }
01815 
01816       if (src != argindex && more_words(e->cmda + dst))  /* not a match */
01817          continue;
01818       ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n);
01819       matchnum += n; /* this many matches here */
01820       if (ret) {
01821          /*
01822           * argv[src] is a valid prefix of the next word in this
01823           * command. If this is also the correct entry, return it.
01824           */
01825          if (matchnum > state)
01826             break;
01827          ast_free(ret);
01828          ret = NULL;
01829       } else if (ast_strlen_zero(e->cmda[dst])) {
01830          /*
01831           * This entry is a prefix of the command string entered
01832           * (only one entry in the list should have this property).
01833           * Run the generator if one is available. In any case we are done.
01834           */
01835          if (e->handler) { /* new style command */
01836             struct ast_cli_args a = {
01837                .line = matchstr, .word = word,
01838                .pos = argindex,
01839                .n = state - matchnum };
01840             ret = e->handler(e, CLI_GENERATE, &a);
01841          }
01842          if (ret)
01843             break;
01844       }
01845    }
01846    if (lock)
01847       AST_RWLIST_UNLOCK(&helpers);
01848    ast_free(dup);
01849    return ret;
01850 }
01851 
01852 char *ast_cli_generator(const char *text, const char *word, int state)
01853 {
01854    return __ast_cli_generator(text, word, state, 1);
01855 }
01856 
01857 int ast_cli_command(int fd, const char *s)
01858 {
01859    char *args[AST_MAX_ARGS + 1];
01860    struct ast_cli_entry *e;
01861    int x;
01862    char *dup = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
01863    char *retval = NULL;
01864    struct ast_cli_args a = {
01865       .fd = fd, .argc = x, .argv = args+1 };
01866 
01867    if (dup == NULL)
01868       return -1;
01869 
01870    if (x < 1)  /* We need at least one entry, otherwise ignore */
01871       goto done;
01872 
01873    AST_RWLIST_RDLOCK(&helpers);
01874    e = find_cli(args + 1, 0);
01875    if (e)
01876       ast_atomic_fetchadd_int(&e->inuse, 1);
01877    AST_RWLIST_UNLOCK(&helpers);
01878    if (e == NULL) {
01879       ast_cli(fd, "No such command '%s' (type 'help %s' for other possible commands)\n", s, find_best(args + 1));
01880       goto done;
01881    }
01882    /*
01883     * Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
01884     * Remember that the array returned by parse_args is NULL-terminated.
01885     */
01886    args[0] = (char *)e;
01887 
01888    retval = e->handler(e, CLI_HANDLER, &a);
01889 
01890    if (retval == CLI_SHOWUSAGE) {
01891       ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
01892       AST_RWLIST_RDLOCK(&helpers);
01893       if (e->deprecated)
01894          ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
01895       AST_RWLIST_UNLOCK(&helpers);
01896    } else {
01897       if (retval == CLI_FAILURE)
01898          ast_cli(fd, "Command '%s' failed.\n", s);
01899       AST_RWLIST_RDLOCK(&helpers);
01900       if (e->deprecated == 1) {
01901          ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
01902          e->deprecated = 2;
01903       }
01904       AST_RWLIST_UNLOCK(&helpers);
01905    }
01906    ast_atomic_fetchadd_int(&e->inuse, -1);
01907 done:
01908    ast_free(dup);
01909    return 0;
01910 }
01911 
01912 int ast_cli_command_multiple(int fd, size_t size, const char *s)
01913 {
01914    char cmd[512];
01915    int x, y = 0, count = 0;
01916 
01917    for (x = 0; x < size; x++) {
01918       cmd[y] = s[x];
01919       y++;
01920       if (s[x] == '\0') {
01921          ast_cli_command(fd, cmd);
01922          y = 0;
01923          count++;
01924       }
01925    }
01926    return count;
01927 }

Generated on Wed Oct 28 11:45:36 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6