Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:47 2007

Asterisk developer's documentation :: Codename Pineapple


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

Asterisk is a trademark for Digium, inc.. | Edvina.net | Asterisk.org | This documentation was generated with Doxygen