Wed Apr 6 11:29:44 2011

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: 291075 $")
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 #include <pwd.h>
00037 #include <grp.h>
00038 
00039 #include "asterisk/cli.h"
00040 #include "asterisk/linkedlists.h"
00041 #include "asterisk/module.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/channel.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/lock.h"
00047 #include "editline/readline/readline.h"
00048 #include "asterisk/threadstorage.h"
00049 #include "asterisk/translate.h"
00050 
00051 /*!
00052  * \brief List of restrictions per user.
00053  */
00054 struct cli_perm {
00055    unsigned int permit:1;           /*!< 1=Permit 0=Deny */
00056    char *command;          /*!< Command name (to apply restrictions) */
00057    AST_LIST_ENTRY(cli_perm) list;
00058 };
00059 
00060 AST_LIST_HEAD_NOLOCK(cli_perm_head, cli_perm);
00061 
00062 /*! \brief list of users to apply restrictions. */
00063 struct usergroup_cli_perm {
00064    int uid;          /*!< User ID (-1 disabled) */
00065    int gid;          /*!< Group ID (-1 disabled) */
00066    struct cli_perm_head *perms;     /*!< List of permissions. */
00067    AST_LIST_ENTRY(usergroup_cli_perm) list;/*!< List mechanics */
00068 };
00069 /*! \brief CLI permissions config file. */
00070 static const char perms_config[] = "cli_permissions.conf";
00071 /*! \brief Default permissions value 1=Permit 0=Deny */
00072 static int cli_default_perm = 1;
00073 
00074 /*! \brief mutex used to prevent a user from running the 'cli reload permissions' command while
00075  * it is already running. */
00076 AST_MUTEX_DEFINE_STATIC(permsconfiglock);
00077 /*! \brief  List of users and permissions. */
00078 static AST_RWLIST_HEAD_STATIC(cli_perms, usergroup_cli_perm);
00079 
00080 /*!
00081  * \brief map a debug or verbose level to a module name
00082  */
00083 struct module_level {
00084    unsigned int level;
00085    AST_RWLIST_ENTRY(module_level) entry;
00086    char module[0];
00087 };
00088 
00089 AST_RWLIST_HEAD(module_level_list, module_level);
00090 
00091 /*! list of module names and their debug levels */
00092 static struct module_level_list debug_modules;
00093 /*! list of module names and their verbose levels */
00094 static struct module_level_list verbose_modules;
00095 
00096 AST_THREADSTORAGE(ast_cli_buf);
00097 
00098 /*! \brief Initial buffer size for resulting strings in ast_cli() */
00099 #define AST_CLI_INITLEN   256
00100 
00101 void ast_cli(int fd, const char *fmt, ...)
00102 {
00103    int res;
00104    struct ast_str *buf;
00105    va_list ap;
00106 
00107    if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
00108       return;
00109 
00110    va_start(ap, fmt);
00111    res = ast_str_set_va(&buf, 0, fmt, ap);
00112    va_end(ap);
00113 
00114    if (res != AST_DYNSTR_BUILD_FAILED) {
00115       ast_carefulwrite(fd, ast_str_buffer(buf), ast_str_strlen(buf), 100);
00116    }
00117 }
00118 
00119 unsigned int ast_debug_get_by_module(const char *module) 
00120 {
00121    struct module_level *ml;
00122    unsigned int res = 0;
00123 
00124    AST_RWLIST_RDLOCK(&debug_modules);
00125    AST_LIST_TRAVERSE(&debug_modules, ml, entry) {
00126       if (!strcasecmp(ml->module, module)) {
00127          res = ml->level;
00128          break;
00129       }
00130    }
00131    AST_RWLIST_UNLOCK(&debug_modules);
00132 
00133    return res;
00134 }
00135 
00136 unsigned int ast_verbose_get_by_module(const char *module) 
00137 {
00138    struct module_level *ml;
00139    unsigned int res = 0;
00140 
00141    AST_RWLIST_RDLOCK(&verbose_modules);
00142    AST_LIST_TRAVERSE(&verbose_modules, ml, entry) {
00143       if (!strcasecmp(ml->module, module)) {
00144          res = ml->level;
00145          break;
00146       }
00147    }
00148    AST_RWLIST_UNLOCK(&verbose_modules);
00149 
00150    return res;
00151 }
00152 
00153 /*! \internal
00154  *  \brief Check if the user with 'uid' and 'gid' is allow to execute 'command',
00155  *    if command starts with '_' then not check permissions, just permit
00156  *    to run the 'command'.
00157  *    if uid == -1 or gid == -1 do not check permissions.
00158  *    if uid == -2 and gid == -2 is because rasterisk client didn't send
00159  *    the credentials, so the cli_default_perm will be applied.
00160  *  \param uid User ID.
00161  *  \param gid Group ID.
00162  *  \param command Command name to check permissions.
00163  *  \retval 1 if has permission
00164  *  \retval 0 if it is not allowed.
00165  */
00166 static int cli_has_permissions(int uid, int gid, const char *command)
00167 {
00168    struct usergroup_cli_perm *user_perm;
00169    struct cli_perm *perm;
00170    /* set to the default permissions general option. */
00171    int isallowg = cli_default_perm, isallowu = -1, ispattern;
00172    regex_t regexbuf;
00173 
00174    /* if uid == -1 or gid == -1 do not check permissions.
00175       if uid == -2 and gid == -2 is because rasterisk client didn't send
00176       the credentials, so the cli_default_perm will be applied. */
00177    if ((uid == CLI_NO_PERMS && gid == CLI_NO_PERMS) || command[0] == '_') {
00178       return 1;
00179    }
00180 
00181    if (gid < 0 && uid < 0) {
00182       return cli_default_perm;
00183    }
00184 
00185    AST_RWLIST_RDLOCK(&cli_perms);
00186    AST_LIST_TRAVERSE(&cli_perms, user_perm, list) {
00187       if (user_perm->gid != gid && user_perm->uid != uid) {
00188          continue;
00189       }
00190       AST_LIST_TRAVERSE(user_perm->perms, perm, list) {
00191          if (strcasecmp(perm->command, "all") && strncasecmp(perm->command, command, strlen(perm->command))) {
00192             /* if the perm->command is a pattern, check it against command. */
00193             ispattern = !regcomp(&regexbuf, perm->command, REG_EXTENDED | REG_NOSUB | REG_ICASE);
00194             if (ispattern && regexec(&regexbuf, command, 0, NULL, 0)) {
00195                regfree(&regexbuf);
00196                continue;
00197             }
00198             if (!ispattern) {
00199                continue;
00200             }
00201             regfree(&regexbuf);
00202          }
00203          if (user_perm->uid == uid) {
00204             /* this is a user definition. */
00205             isallowu = perm->permit;
00206          } else {
00207             /* otherwise is a group definition. */
00208             isallowg = perm->permit;
00209          }
00210       }
00211    }
00212    AST_RWLIST_UNLOCK(&cli_perms);
00213    if (isallowu > -1) {
00214       /* user definition override group definition. */
00215       isallowg = isallowu;
00216    }
00217 
00218    return isallowg;
00219 }
00220 
00221 static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
00222 
00223 static char *complete_fn(const char *word, int state)
00224 {
00225    char *c, *d;
00226    char filename[PATH_MAX];
00227 
00228    if (word[0] == '/')
00229       ast_copy_string(filename, word, sizeof(filename));
00230    else
00231       snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
00232 
00233    c = d = filename_completion_function(filename, state);
00234    
00235    if (c && word[0] != '/')
00236       c += (strlen(ast_config_AST_MODULE_DIR) + 1);
00237    if (c)
00238       c = ast_strdup(c);
00239 
00240    free(d);
00241    
00242    return c;
00243 }
00244 
00245 static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00246 {
00247    /* "module load <mod>" */
00248    switch (cmd) {
00249    case CLI_INIT:
00250       e->command = "module load";
00251       e->usage =
00252          "Usage: module load <module name>\n"
00253          "       Loads the specified module into Asterisk.\n";
00254       return NULL;
00255 
00256    case CLI_GENERATE:
00257       if (a->pos != e->args)
00258          return NULL;
00259       return complete_fn(a->word, a->n);
00260    }
00261    if (a->argc != e->args + 1)
00262       return CLI_SHOWUSAGE;
00263    if (ast_load_resource(a->argv[e->args])) {
00264       ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]);
00265       return CLI_FAILURE;
00266    }
00267    ast_cli(a->fd, "Loaded %s\n", a->argv[e->args]);
00268    return CLI_SUCCESS;
00269 }
00270 
00271 static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00272 {
00273    int x;
00274 
00275    switch (cmd) {
00276    case CLI_INIT:
00277       e->command = "module reload";
00278       e->usage =
00279          "Usage: module reload [module ...]\n"
00280          "       Reloads configuration files for all listed modules which support\n"
00281          "       reloading, or for all supported modules if none are listed.\n";
00282       return NULL;
00283 
00284    case CLI_GENERATE:
00285       return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1);
00286    }
00287    if (a->argc == e->args) {
00288       ast_module_reload(NULL);
00289       return CLI_SUCCESS;
00290    }
00291    for (x = e->args; x < a->argc; x++) {
00292       int res = ast_module_reload(a->argv[x]);
00293       /* XXX reload has multiple error returns, including -1 on error and 2 on success */
00294       switch (res) {
00295       case 0:
00296          ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
00297          break;
00298       case 1:
00299          ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
00300          break;
00301       }
00302    }
00303    return CLI_SUCCESS;
00304 }
00305 
00306 static char *handle_core_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00307 {
00308    switch (cmd) {
00309    case CLI_INIT:
00310       e->command = "core reload";
00311       e->usage =
00312          "Usage: core reload\n"
00313          "       Execute a global reload.\n";
00314       return NULL;
00315 
00316    case CLI_GENERATE:
00317       return NULL;
00318    }
00319 
00320    if (a->argc != e->args) {
00321       return CLI_SHOWUSAGE;
00322    }
00323 
00324    ast_module_reload(NULL);
00325 
00326    return CLI_SUCCESS;
00327 }
00328 /*! 
00329  * \brief Find the debug or verbose file setting 
00330  * \arg debug 1 for debug, 0 for verbose
00331  */
00332 static struct module_level *find_module_level(const char *module, unsigned int debug)
00333 {
00334    struct module_level *ml;
00335    struct module_level_list *mll = debug ? &debug_modules : &verbose_modules;
00336 
00337    AST_LIST_TRAVERSE(mll, ml, entry) {
00338       if (!strcasecmp(ml->module, module))
00339          return ml;
00340    }
00341 
00342    return NULL;
00343 }
00344 
00345 static char *complete_number(const char *partial, unsigned int min, unsigned int max, int n)
00346 {
00347    int i, count = 0;
00348    unsigned int prospective[2];
00349    unsigned int part = strtoul(partial, NULL, 10);
00350    char next[12];
00351 
00352    if (part < min || part > max) {
00353       return NULL;
00354    }
00355 
00356    for (i = 0; i < 21; i++) {
00357       if (i == 0) {
00358          prospective[0] = prospective[1] = part;
00359       } else if (part == 0 && !ast_strlen_zero(partial)) {
00360          break;
00361       } else if (i < 11) {
00362          prospective[0] = prospective[1] = part * 10 + (i - 1);
00363       } else {
00364          prospective[0] = (part * 10 + (i - 11)) * 10;
00365          prospective[1] = prospective[0] + 9;
00366       }
00367       if (i < 11 && (prospective[0] < min || prospective[0] > max)) {
00368          continue;
00369       } else if (prospective[1] < min || prospective[0] > max) {
00370          continue;
00371       }
00372 
00373       if (++count > n) {
00374          if (i < 11) {
00375             snprintf(next, sizeof(next), "%u", prospective[0]);
00376          } else {
00377             snprintf(next, sizeof(next), "%u...", prospective[0] / 10);
00378          }
00379          return ast_strdup(next);
00380       }
00381    }
00382    return NULL;
00383 }
00384 
00385 static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00386 {
00387    int oldval;
00388    int newlevel;
00389    unsigned int is_debug;
00390    int atleast = 0;
00391    int fd = a->fd;
00392    int argc = a->argc;
00393    const char * const *argv = a->argv;
00394    const char *argv3 = a->argv ? S_OR(a->argv[3], "") : "";
00395    int *dst;
00396    char *what;
00397    struct module_level_list *mll;
00398    struct module_level *ml;
00399 
00400    switch (cmd) {
00401    case CLI_INIT:
00402       e->command = "core set {debug|verbose}";
00403       e->usage =
00404 #if !defined(LOW_MEMORY)
00405          "Usage: core set {debug|verbose} [atleast] <level> [module]\n"
00406 #else
00407          "Usage: core set {debug|verbose} [atleast] <level>\n"
00408 #endif
00409          "       core set {debug|verbose} off\n"
00410 #if !defined(LOW_MEMORY)
00411          "       Sets level of debug or verbose messages to be displayed or\n"
00412          "       sets a module name to display debug messages from.\n"
00413 #else
00414          "       Sets level of debug or verbose messages to be displayed.\n"
00415 #endif
00416          "  0 or off means no messages should be displayed.\n"
00417          "  Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
00418       return NULL;
00419 
00420    case CLI_GENERATE:
00421       if (a->pos == 3 || (a->pos == 4 && !strcasecmp(a->argv[3], "atleast"))) {
00422          const char *pos = a->pos == 3 ? argv3 : S_OR(a->argv[4], "");
00423          int numbermatch = (ast_strlen_zero(pos) || strchr("123456789", pos[0])) ? 0 : 21;
00424          if (a->n < 21 && numbermatch == 0) {
00425             return complete_number(pos, 0, 0x7fffffff, a->n);
00426          } else if (pos[0] == '0') {
00427             if (a->n == 0) {
00428                return ast_strdup("0");
00429             } else {
00430                return NULL;
00431             }
00432          } else if (a->n == (21 - numbermatch)) {
00433             if (a->pos == 3 && !strncasecmp(argv3, "off", strlen(argv3))) {
00434                return ast_strdup("off");
00435             } else if (a->pos == 3 && !strncasecmp(argv3, "atleast", strlen(argv3))) {
00436                return ast_strdup("atleast");
00437             }
00438          } else if (a->n == (22 - numbermatch) && a->pos == 3 && ast_strlen_zero(argv3)) {
00439             return ast_strdup("atleast");
00440          }
00441 #if !defined(LOW_MEMORY)
00442       } else if (a->pos == 4 || (a->pos == 5 && !strcasecmp(argv3, "atleast"))) {
00443          return ast_complete_source_filename(a->pos == 4 ? S_OR(a->argv[4], "") : S_OR(a->argv[5], ""), a->n);
00444 #endif
00445       }
00446       return NULL;
00447    }
00448    /* all the above return, so we proceed with the handler.
00449     * we are guaranteed to be called with argc >= e->args;
00450     */
00451 
00452    if (argc <= e->args)
00453       return CLI_SHOWUSAGE;
00454    if (!strcasecmp(argv[e->args - 1], "debug")) {
00455       dst = &option_debug;
00456       oldval = option_debug;
00457       what = "Core debug";
00458       is_debug = 1;
00459    } else {
00460       dst = &option_verbose;
00461       oldval = option_verbose;
00462       what = "Verbosity";
00463       is_debug = 0;
00464    }
00465    if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) {
00466       newlevel = 0;
00467 
00468       mll = is_debug ? &debug_modules : &verbose_modules;
00469 
00470       AST_RWLIST_WRLOCK(mll);
00471       while ((ml = AST_RWLIST_REMOVE_HEAD(mll, entry))) {
00472          ast_free(ml);
00473       }
00474       ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
00475       AST_RWLIST_UNLOCK(mll);
00476 
00477       goto done;
00478    }
00479    if (!strcasecmp(argv[e->args], "atleast"))
00480       atleast = 1;
00481    if (argc != e->args + atleast + 1 && argc != e->args + atleast + 2)
00482       return CLI_SHOWUSAGE;
00483    if (sscanf(argv[e->args + atleast], "%30d", &newlevel) != 1)
00484       return CLI_SHOWUSAGE;
00485    if (argc == e->args + atleast + 2) {
00486       /* We have specified a module name. */
00487       char *mod = ast_strdupa(argv[e->args + atleast + 1]);
00488 
00489       if ((strlen(mod) > 3) && !strcasecmp(mod + strlen(mod) - 3, ".so")) {
00490          mod[strlen(mod) - 3] = '\0';
00491       }
00492 
00493       mll = is_debug ? &debug_modules : &verbose_modules;
00494 
00495       AST_RWLIST_WRLOCK(mll);
00496 
00497       ml = find_module_level(mod, is_debug);
00498       if (!newlevel) {
00499          if (!ml) {
00500             /* Specified off for a nonexistent entry. */
00501             AST_RWLIST_UNLOCK(mll);
00502             return CLI_SUCCESS;
00503          }
00504          AST_RWLIST_REMOVE(mll, ml, entry);
00505          if (AST_RWLIST_EMPTY(mll))
00506             ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
00507          AST_RWLIST_UNLOCK(mll);
00508          ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, ml->level, mod);
00509          ast_free(ml);
00510          return CLI_SUCCESS;
00511       }
00512 
00513       if (ml) {
00514          if ((atleast && newlevel < ml->level) || ml->level == newlevel) {
00515             ast_cli(fd, "%s is %d for '%s'\n", what, ml->level, mod);
00516             AST_RWLIST_UNLOCK(mll);
00517             return CLI_SUCCESS;
00518          }
00519          oldval = ml->level;
00520          ml->level = newlevel;
00521       } else {
00522          ml = ast_calloc(1, sizeof(*ml) + strlen(mod) + 1);
00523          if (!ml) {
00524             AST_RWLIST_UNLOCK(mll);
00525             return CLI_FAILURE;
00526          }
00527          oldval = ml->level;
00528          ml->level = newlevel;
00529          strcpy(ml->module, mod);
00530          AST_RWLIST_INSERT_TAIL(mll, ml, entry);
00531       }
00532 
00533       ast_set_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
00534 
00535       AST_RWLIST_UNLOCK(mll);
00536 
00537       ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, ml->level, ml->module);
00538 
00539       return CLI_SUCCESS;
00540    } else if (!newlevel) {
00541       /* Specified level as 0 instead of off. */
00542       mll = is_debug ? &debug_modules : &verbose_modules;
00543 
00544       AST_RWLIST_WRLOCK(mll);
00545       while ((ml = AST_RWLIST_REMOVE_HEAD(mll, entry))) {
00546          ast_free(ml);
00547       }
00548       ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE);
00549       AST_RWLIST_UNLOCK(mll);
00550    }
00551 
00552 done:
00553    if (!atleast || newlevel > *dst)
00554       *dst = newlevel;
00555    if (oldval > 0 && *dst == 0)
00556       ast_cli(fd, "%s is now OFF\n", what);
00557    else if (*dst > 0) {
00558       if (oldval == *dst)
00559          ast_cli(fd, "%s is at least %d\n", what, *dst);
00560       else
00561          ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst);
00562    }
00563 
00564    return CLI_SUCCESS;
00565 }
00566 
00567 static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00568 {
00569    switch (cmd) {
00570    case CLI_INIT:
00571       e->command = "logger mute";
00572       e->usage = 
00573          "Usage: logger mute\n"
00574          "       Disables logging output to the current console, making it possible to\n"
00575          "       gather information without being disturbed by scrolling lines.\n";
00576       return NULL;
00577    case CLI_GENERATE:
00578       return NULL;
00579    }
00580 
00581    if (a->argc < 2 || a->argc > 3)
00582       return CLI_SHOWUSAGE;
00583 
00584    if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
00585       ast_console_toggle_mute(a->fd, 1);
00586    else
00587       ast_console_toggle_mute(a->fd, 0);
00588 
00589    return CLI_SUCCESS;
00590 }
00591 
00592 static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00593 {
00594    /* "module unload mod_1 [mod_2 .. mod_N]" */
00595    int x;
00596    int force = AST_FORCE_SOFT;
00597    const char *s;
00598 
00599    switch (cmd) {
00600    case CLI_INIT:
00601       e->command = "module unload";
00602       e->usage =
00603          "Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
00604          "       Unloads the specified module from Asterisk. The -f\n"
00605          "       option causes the module to be unloaded even if it is\n"
00606          "       in use (may cause a crash) and the -h module causes the\n"
00607          "       module to be unloaded even if the module says it cannot, \n"
00608          "       which almost always will cause a crash.\n";
00609       return NULL;
00610 
00611    case CLI_GENERATE:
00612       return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
00613    }
00614    if (a->argc < e->args + 1)
00615       return CLI_SHOWUSAGE;
00616    x = e->args;   /* first argument */
00617    s = a->argv[x];
00618    if (s[0] == '-') {
00619       if (s[1] == 'f')
00620          force = AST_FORCE_FIRM;
00621       else if (s[1] == 'h')
00622          force = AST_FORCE_HARD;
00623       else
00624          return CLI_SHOWUSAGE;
00625       if (a->argc < e->args + 2) /* need at least one module name */
00626          return CLI_SHOWUSAGE;
00627       x++;  /* skip this argument */
00628    }
00629 
00630    for (; x < a->argc; x++) {
00631       if (ast_unload_resource(a->argv[x], force)) {
00632          ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
00633          return CLI_FAILURE;
00634       }
00635       ast_cli(a->fd, "Unloaded %s\n", a->argv[x]);
00636    }
00637 
00638    return CLI_SUCCESS;
00639 }
00640 
00641 #define MODLIST_FORMAT  "%-30s %-40.40s %-10d\n"
00642 #define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
00643 
00644 AST_MUTEX_DEFINE_STATIC(climodentrylock);
00645 static int climodentryfd = -1;
00646 
00647 static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
00648 {
00649    /* Comparing the like with the module */
00650    if (strcasestr(module, like) ) {
00651       ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
00652       return 1;
00653    } 
00654    return 0;
00655 }
00656 
00657 static void print_uptimestr(int fd, struct timeval timeval, const char *prefix, int printsec)
00658 {
00659    int x; /* the main part - years, weeks, etc. */
00660    struct ast_str *out;
00661 
00662 #define SECOND (1)
00663 #define MINUTE (SECOND*60)
00664 #define HOUR (MINUTE*60)
00665 #define DAY (HOUR*24)
00666 #define WEEK (DAY*7)
00667 #define YEAR (DAY*365)
00668 #define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
00669    if (timeval.tv_sec < 0) /* invalid, nothing to show */
00670       return;
00671 
00672    if (printsec)  {  /* plain seconds output */
00673       ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval.tv_sec);
00674       return;
00675    }
00676    out = ast_str_alloca(256);
00677    if (timeval.tv_sec > YEAR) {
00678       x = (timeval.tv_sec / YEAR);
00679       timeval.tv_sec -= (x * YEAR);
00680       ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00681    }
00682    if (timeval.tv_sec > WEEK) {
00683       x = (timeval.tv_sec / WEEK);
00684       timeval.tv_sec -= (x * WEEK);
00685       ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00686    }
00687    if (timeval.tv_sec > DAY) {
00688       x = (timeval.tv_sec / DAY);
00689       timeval.tv_sec -= (x * DAY);
00690       ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00691    }
00692    if (timeval.tv_sec > HOUR) {
00693       x = (timeval.tv_sec / HOUR);
00694       timeval.tv_sec -= (x * HOUR);
00695       ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00696    }
00697    if (timeval.tv_sec > MINUTE) {
00698       x = (timeval.tv_sec / MINUTE);
00699       timeval.tv_sec -= (x * MINUTE);
00700       ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
00701    }
00702    x = timeval.tv_sec;
00703    if (x > 0 || ast_str_strlen(out) == 0) /* if there is nothing, print 0 seconds */
00704       ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
00705    ast_cli(fd, "%s: %s\n", prefix, ast_str_buffer(out));
00706 }
00707 
00708 static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
00709 {
00710    if (e) {
00711       return AST_LIST_NEXT(e, list);
00712    } else {
00713       return AST_LIST_FIRST(&helpers);
00714    }
00715 }
00716 
00717 static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00718 {
00719    struct timeval curtime = ast_tvnow();
00720    int printsec;
00721 
00722    switch (cmd) {
00723    case CLI_INIT:
00724       e->command = "core show uptime [seconds]";
00725       e->usage =
00726          "Usage: core show uptime [seconds]\n"
00727          "       Shows Asterisk uptime information.\n"
00728          "       The seconds word returns the uptime in seconds only.\n";
00729       return NULL;
00730 
00731    case CLI_GENERATE:
00732       return NULL;
00733    }
00734    /* regular handler */
00735    if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
00736       printsec = 1;
00737    else if (a->argc == e->args-1)
00738       printsec = 0;
00739    else
00740       return CLI_SHOWUSAGE;
00741    if (ast_startuptime.tv_sec)
00742       print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
00743    if (ast_lastreloadtime.tv_sec)
00744       print_uptimestr(a->fd, ast_tvsub(curtime, ast_lastreloadtime), "Last reload", printsec);
00745    return CLI_SUCCESS;
00746 }
00747 
00748 static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00749 {
00750    const char *like;
00751 
00752    switch (cmd) {
00753    case CLI_INIT:
00754       e->command = "module show [like]";
00755       e->usage =
00756          "Usage: module show [like keyword]\n"
00757          "       Shows Asterisk modules currently in use, and usage statistics.\n";
00758       return NULL;
00759 
00760    case CLI_GENERATE:
00761       if (a->pos == e->args)
00762          return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
00763       else
00764          return NULL;
00765    }
00766    /* all the above return, so we proceed with the handler.
00767     * we are guaranteed to have argc >= e->args
00768     */
00769    if (a->argc == e->args - 1)
00770       like = "";
00771    else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
00772       like = a->argv[e->args];
00773    else
00774       return CLI_SHOWUSAGE;
00775       
00776    ast_mutex_lock(&climodentrylock);
00777    climodentryfd = a->fd; /* global, protected by climodentrylock */
00778    ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
00779    ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
00780    climodentryfd = -1;
00781    ast_mutex_unlock(&climodentrylock);
00782    return CLI_SUCCESS;
00783 }
00784 #undef MODLIST_FORMAT
00785 #undef MODLIST_FORMAT2
00786 
00787 static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00788 {
00789    struct timeval curtime = ast_tvnow();
00790    int showuptime, printsec;
00791 
00792    switch (cmd) {
00793    case CLI_INIT:
00794       e->command = "core show calls [uptime]";
00795       e->usage =
00796          "Usage: core show calls [uptime] [seconds]\n"
00797          "       Lists number of currently active calls and total number of calls\n"
00798          "       processed through PBX since last restart. If 'uptime' is specified\n"
00799          "       the system uptime is also displayed. If 'seconds' is specified in\n"
00800          "       addition to 'uptime', the system uptime is displayed in seconds.\n";
00801       return NULL;
00802 
00803    case CLI_GENERATE:
00804       if (a->pos != e->args)
00805          return NULL;
00806       return a->n == 0  ? ast_strdup("seconds") : NULL;
00807    }
00808 
00809    /* regular handler */
00810    if (a->argc >= e->args && !strcasecmp(a->argv[e->args-1],"uptime")) {
00811       showuptime = 1;
00812 
00813       if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
00814          printsec = 1;
00815       else if (a->argc == e->args)
00816          printsec = 0;
00817       else
00818          return CLI_SHOWUSAGE;
00819    } else if (a->argc == e->args-1) {
00820       showuptime = 0;
00821       printsec = 0;
00822    } else
00823       return CLI_SHOWUSAGE;
00824 
00825    if (option_maxcalls) {
00826       ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00827          ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00828          ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00829    } else {
00830       ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00831    }
00832    
00833    ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
00834 
00835    if (ast_startuptime.tv_sec && showuptime) {
00836       print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
00837    }
00838 
00839    return RESULT_SUCCESS;
00840 }
00841 
00842 static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00843 {
00844 #define FORMAT_STRING  "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00845 #define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
00846 #define CONCISE_FORMAT_STRING  "%s!%s!%s!%d!%s!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
00847 #define VERBOSE_FORMAT_STRING  "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n"
00848 #define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-11.11s %-20.20s\n"
00849 
00850    struct ast_channel *c = NULL;
00851    int numchans = 0, concise = 0, verbose = 0, count = 0;
00852    struct ast_channel_iterator *iter = NULL;
00853 
00854    switch (cmd) {
00855    case CLI_INIT:
00856       e->command = "core show channels [concise|verbose|count]";
00857       e->usage =
00858          "Usage: core show channels [concise|verbose|count]\n"
00859          "       Lists currently defined channels and some information about them. If\n"
00860          "       'concise' is specified, the format is abridged and in a more easily\n"
00861          "       machine parsable format. If 'verbose' is specified, the output includes\n"
00862          "       more and longer fields. If 'count' is specified only the channel and call\n"
00863          "       count is output.\n"
00864          "  The 'concise' option is deprecated and will be removed from future versions\n"
00865          "  of Asterisk.\n";
00866       return NULL;
00867 
00868    case CLI_GENERATE:
00869       return NULL;
00870    }
00871 
00872    if (a->argc == e->args) {
00873       if (!strcasecmp(a->argv[e->args-1],"concise"))
00874          concise = 1;
00875       else if (!strcasecmp(a->argv[e->args-1],"verbose"))
00876          verbose = 1;
00877       else if (!strcasecmp(a->argv[e->args-1],"count"))
00878          count = 1;
00879       else
00880          return CLI_SHOWUSAGE;
00881    } else if (a->argc != e->args - 1)
00882       return CLI_SHOWUSAGE;
00883 
00884    if (!count) {
00885       if (!concise && !verbose)
00886          ast_cli(a->fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
00887       else if (verbose)
00888          ast_cli(a->fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data", 
00889             "CallerID", "Duration", "Accountcode", "PeerAccount", "BridgedTo");
00890    }
00891 
00892    if (!count && !(iter = ast_channel_iterator_all_new())) {
00893       return CLI_FAILURE;
00894    }
00895 
00896    for (; iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
00897       struct ast_channel *bc;
00898       char durbuf[10] = "-";
00899 
00900       ast_channel_lock(c);
00901 
00902       bc = ast_bridged_channel(c);
00903 
00904       if (!count) {
00905          if ((concise || verbose)  && c->cdr && !ast_tvzero(c->cdr->start)) {
00906             int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
00907             if (verbose) {
00908                int durh = duration / 3600;
00909                int durm = (duration % 3600) / 60;
00910                int durs = duration % 60;
00911                snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
00912             } else {
00913                snprintf(durbuf, sizeof(durbuf), "%d", duration);
00914             }           
00915          }
00916          if (concise) {
00917             ast_cli(a->fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00918                c->appl ? c->appl : "(None)",
00919                S_OR(c->data, ""),   /* XXX different from verbose ? */
00920                S_COR(c->caller.id.number.valid, c->caller.id.number.str, ""),
00921                S_OR(c->accountcode, ""),
00922                S_OR(c->peeraccount, ""),
00923                c->amaflags, 
00924                durbuf,
00925                bc ? bc->name : "(None)",
00926                c->uniqueid);
00927          } else if (verbose) {
00928             ast_cli(a->fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
00929                c->appl ? c->appl : "(None)",
00930                c->data ? S_OR(c->data, "(Empty)" ): "(None)",
00931                S_COR(c->caller.id.number.valid, c->caller.id.number.str, ""),
00932                durbuf,
00933                S_OR(c->accountcode, ""),
00934                S_OR(c->peeraccount, ""),
00935                bc ? bc->name : "(None)");
00936          } else {
00937             char locbuf[40] = "(None)";
00938             char appdata[40] = "(None)";
00939             
00940             if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten)) 
00941                snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
00942             if (c->appl)
00943                snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, S_OR(c->data, ""));
00944             ast_cli(a->fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
00945          }
00946       }
00947       ast_channel_unlock(c);
00948    }
00949 
00950    if (iter) {
00951       ast_channel_iterator_destroy(iter);
00952    }
00953 
00954    if (!concise) {
00955       numchans = ast_active_channels();
00956       ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
00957       if (option_maxcalls)
00958          ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
00959             ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
00960             ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
00961       else
00962          ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
00963 
00964       ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
00965    }
00966 
00967    return CLI_SUCCESS;
00968    
00969 #undef FORMAT_STRING
00970 #undef FORMAT_STRING2
00971 #undef CONCISE_FORMAT_STRING
00972 #undef VERBOSE_FORMAT_STRING
00973 #undef VERBOSE_FORMAT_STRING2
00974 }
00975 
00976 static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00977 {
00978    struct ast_channel *c=NULL;
00979 
00980    switch (cmd) {
00981    case CLI_INIT:
00982       e->command = "channel request hangup";
00983       e->usage =
00984          "Usage: channel request hangup <channel>|<all>\n"
00985          "       Request that a channel be hung up. The hangup takes effect\n"
00986          "       the next time the driver reads or writes from the channel.\n"
00987          "       If 'all' is specified instead of a channel name, all channels\n"
00988          "       will see the hangup request.\n";
00989       return NULL;
00990    case CLI_GENERATE:
00991       return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
00992    }
00993 
00994    if (a->argc != 4) {
00995       return CLI_SHOWUSAGE;
00996    }
00997 
00998    if (!strcasecmp(a->argv[3], "all")) {
00999       struct ast_channel_iterator *iter = NULL;
01000       if (!(iter = ast_channel_iterator_all_new())) {
01001          return CLI_FAILURE;
01002       }
01003       for (; iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
01004          ast_channel_lock(c);
01005          ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
01006          ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
01007          ast_channel_unlock(c);
01008       }
01009       ast_channel_iterator_destroy(iter);
01010    } else if ((c = ast_channel_get_by_name(a->argv[3]))) {
01011       ast_channel_lock(c);
01012       ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
01013       ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
01014       ast_channel_unlock(c);
01015       c = ast_channel_unref(c);
01016    } else {
01017       ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
01018    }
01019 
01020    return CLI_SUCCESS;
01021 }
01022 
01023 /*! \brief handles CLI command 'cli show permissions' */
01024 static char *handle_cli_show_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01025 {
01026    struct usergroup_cli_perm *cp;
01027    struct cli_perm *perm;
01028    struct passwd *pw = NULL;
01029    struct group *gr = NULL;
01030 
01031    switch (cmd) {
01032    case CLI_INIT:
01033       e->command = "cli show permissions";
01034       e->usage =
01035          "Usage: cli show permissions\n"
01036          "       Shows CLI configured permissions.\n";
01037       return NULL;
01038    case CLI_GENERATE:
01039       return NULL;
01040    }
01041 
01042    AST_RWLIST_RDLOCK(&cli_perms);
01043    AST_LIST_TRAVERSE(&cli_perms, cp, list) {
01044       if (cp->uid >= 0) {
01045          pw = getpwuid(cp->uid);
01046          if (pw) {
01047             ast_cli(a->fd, "user: %s [uid=%d]\n", pw->pw_name, cp->uid);
01048          }
01049       } else {
01050          gr = getgrgid(cp->gid);
01051          if (gr) {
01052             ast_cli(a->fd, "group: %s [gid=%d]\n", gr->gr_name, cp->gid);
01053          }
01054       }
01055       ast_cli(a->fd, "Permissions:\n");
01056       if (cp->perms) {
01057          AST_LIST_TRAVERSE(cp->perms, perm, list) {
01058             ast_cli(a->fd, "\t%s -> %s\n", perm->permit ? "permit" : "deny", perm->command);
01059          }
01060       }
01061       ast_cli(a->fd, "\n");
01062    }
01063    AST_RWLIST_UNLOCK(&cli_perms);
01064 
01065    return CLI_SUCCESS;
01066 }
01067 
01068 /*! \brief handles CLI command 'cli reload permissions' */
01069 static char *handle_cli_reload_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01070 {
01071    switch (cmd) {
01072    case CLI_INIT:
01073       e->command = "cli reload permissions";
01074       e->usage =
01075          "Usage: cli reload permissions\n"
01076          "       Reload the 'cli_permissions.conf' file.\n";
01077       return NULL;
01078    case CLI_GENERATE:
01079       return NULL;
01080    }
01081 
01082    ast_cli_perms_init(1);
01083 
01084    return CLI_SUCCESS;
01085 }
01086 
01087 /*! \brief handles CLI command 'cli check permissions' */
01088 static char *handle_cli_check_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01089 {
01090    struct passwd *pw = NULL;
01091    struct group *gr;
01092    int gid = -1, uid = -1;
01093    char command[AST_MAX_ARGS] = "";
01094    struct ast_cli_entry *ce = NULL;
01095    int found = 0;
01096    char *group, *tmp;
01097 
01098    switch (cmd) {
01099    case CLI_INIT:
01100       e->command = "cli check permissions";
01101       e->usage =
01102          "Usage: cli check permissions {<username>|@<groupname>|<username>@<groupname>} [<command>]\n"
01103          "       Check permissions config for a user@group or list the allowed commands for the specified user.\n"
01104          "       The username or the groupname may be omitted.\n";
01105       return NULL;
01106    case CLI_GENERATE:
01107       if (a->pos >= 4) {
01108          return ast_cli_generator(a->line + strlen("cli check permissions") + strlen(a->argv[3]) + 1, a->word, a->n);
01109       }
01110       return NULL;
01111    }
01112 
01113    if (a->argc < 4) {
01114       return CLI_SHOWUSAGE;
01115    }
01116 
01117    tmp = ast_strdupa(a->argv[3]);
01118    group = strchr(tmp, '@');
01119    if (group) {
01120       gr = getgrnam(&group[1]);
01121       if (!gr) {
01122          ast_cli(a->fd, "Unknown group '%s'\n", &group[1]);
01123          return CLI_FAILURE;
01124       }
01125       group[0] = '\0';
01126       gid = gr->gr_gid;
01127    }
01128 
01129    if (!group && ast_strlen_zero(tmp)) {
01130       ast_cli(a->fd, "You didn't supply a username\n");
01131    } else if (!ast_strlen_zero(tmp) && !(pw = getpwnam(tmp))) {
01132       ast_cli(a->fd, "Unknown user '%s'\n", tmp);
01133       return CLI_FAILURE;
01134    } else if (pw) {
01135       uid = pw->pw_uid;
01136    }
01137 
01138    if (a->argc == 4) {
01139       while ((ce = cli_next(ce))) {
01140          /* Hide commands that start with '_' */
01141          if (ce->_full_cmd[0] == '_') {
01142             continue;
01143          }
01144          if (cli_has_permissions(uid, gid, ce->_full_cmd)) {
01145             ast_cli(a->fd, "%30.30s %s\n", ce->_full_cmd, S_OR(ce->summary, "<no description available>"));
01146             found++;
01147          }
01148       }
01149       if (!found) {
01150          ast_cli(a->fd, "You are not allowed to run any command on Asterisk\n");
01151       }
01152    } else {
01153       ast_join(command, sizeof(command), a->argv + 4);
01154       ast_cli(a->fd, "%s '%s%s%s' is %s to run command: '%s'\n", uid >= 0 ? "User" : "Group", tmp,
01155          group && uid >= 0 ? "@" : "",
01156          group ? &group[1] : "",
01157          cli_has_permissions(uid, gid, command) ? "allowed" : "not allowed", command);
01158    }
01159 
01160    return CLI_SUCCESS;
01161 }
01162 
01163 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
01164 
01165 static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01166 {
01167    char *buf, *obuf;
01168    int buflen = 2048;
01169    int len = 0;
01170    char **matches;
01171    int x, matchlen;
01172    
01173    switch (cmd) {
01174    case CLI_INIT:
01175       e->command = "_command matchesarray";
01176       e->usage = 
01177          "Usage: _command matchesarray \"<line>\" text \n"
01178          "       This function is used internally to help with command completion and should.\n"
01179          "       never be called by the user directly.\n";
01180       return NULL;
01181    case CLI_GENERATE:
01182       return NULL;
01183    }
01184 
01185    if (a->argc != 4)
01186       return CLI_SHOWUSAGE;
01187    if (!(buf = ast_malloc(buflen)))
01188       return CLI_FAILURE;
01189    buf[len] = '\0';
01190    matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
01191    if (matches) {
01192       for (x=0; matches[x]; x++) {
01193          matchlen = strlen(matches[x]) + 1;
01194          if (len + matchlen >= buflen) {
01195             buflen += matchlen * 3;
01196             obuf = buf;
01197             if (!(buf = ast_realloc(obuf, buflen))) 
01198                /* Memory allocation failure...  Just free old buffer and be done */
01199                ast_free(obuf);
01200          }
01201          if (buf)
01202             len += sprintf( buf + len, "%s ", matches[x]);
01203          ast_free(matches[x]);
01204          matches[x] = NULL;
01205       }
01206       ast_free(matches);
01207    }
01208 
01209    if (buf) {
01210       ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
01211       ast_free(buf);
01212    } else
01213       ast_cli(a->fd, "NULL\n");
01214 
01215    return CLI_SUCCESS;
01216 }
01217 
01218 
01219 
01220 static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01221 {
01222    int matches = 0;
01223 
01224    switch (cmd) {
01225    case CLI_INIT:
01226       e->command = "_command nummatches";
01227       e->usage = 
01228          "Usage: _command nummatches \"<line>\" text \n"
01229          "       This function is used internally to help with command completion and should.\n"
01230          "       never be called by the user directly.\n";
01231       return NULL;
01232    case CLI_GENERATE:
01233       return NULL;
01234    }
01235 
01236    if (a->argc != 4)
01237       return CLI_SHOWUSAGE;
01238 
01239    matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
01240 
01241    ast_cli(a->fd, "%d", matches);
01242 
01243    return CLI_SUCCESS;
01244 }
01245 
01246 static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01247 {
01248    char *buf;
01249    switch (cmd) {
01250    case CLI_INIT:
01251       e->command = "_command complete";
01252       e->usage = 
01253          "Usage: _command complete \"<line>\" text state\n"
01254          "       This function is used internally to help with command completion and should.\n"
01255          "       never be called by the user directly.\n";
01256       return NULL;
01257    case CLI_GENERATE:
01258       return NULL;
01259    }
01260    if (a->argc != 5)
01261       return CLI_SHOWUSAGE;
01262    buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
01263    if (buf) {
01264       ast_cli(a->fd, "%s", buf);
01265       ast_free(buf);
01266    } else
01267       ast_cli(a->fd, "NULL\n");
01268    return CLI_SUCCESS;
01269 }
01270 
01271 struct channel_set_debug_args {
01272    int fd;
01273    int is_off;
01274 };
01275 
01276 static int channel_set_debug(void *obj, void *arg, void *data, int flags)
01277 {
01278    struct ast_channel *chan = obj;
01279    struct channel_set_debug_args *args = data;
01280 
01281    ast_channel_lock(chan);
01282 
01283    if (!(chan->fin & DEBUGCHAN_FLAG) || !(chan->fout & DEBUGCHAN_FLAG)) {
01284       if (args->is_off) {
01285          chan->fin &= ~DEBUGCHAN_FLAG;
01286          chan->fout &= ~DEBUGCHAN_FLAG;
01287       } else {
01288          chan->fin |= DEBUGCHAN_FLAG;
01289          chan->fout |= DEBUGCHAN_FLAG;
01290       }
01291       ast_cli(args->fd, "Debugging %s on channel %s\n", args->is_off ? "disabled" : "enabled",
01292             chan->name);
01293    }
01294 
01295    ast_channel_unlock(chan);
01296 
01297    return 0;
01298 }
01299 
01300 static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01301 {
01302    struct ast_channel *c = NULL;
01303    struct channel_set_debug_args args = {
01304       .fd = a->fd,
01305    };
01306 
01307    switch (cmd) {
01308    case CLI_INIT:
01309       e->command = "core set debug channel";
01310       e->usage =
01311          "Usage: core set debug channel <all|channel> [off]\n"
01312          "       Enables/disables debugging on all or on a specific channel.\n";
01313       return NULL;
01314    case CLI_GENERATE:
01315       /* XXX remember to handle the optional "off" */
01316       if (a->pos != e->args)
01317          return NULL;
01318       return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
01319    }
01320 
01321    if (cmd == (CLI_HANDLER + 1000)) {
01322       /* called from handle_nodebugchan_deprecated */
01323       args.is_off = 1;
01324    } else if (a->argc == e->args + 2) {
01325       /* 'core set debug channel {all|chan_id}' */
01326       if (!strcasecmp(a->argv[e->args + 1], "off"))
01327          args.is_off = 1;
01328       else
01329          return CLI_SHOWUSAGE;
01330    } else if (a->argc != e->args + 1) {
01331       return CLI_SHOWUSAGE;
01332    }
01333 
01334    if (!strcasecmp("all", a->argv[e->args])) {
01335       if (args.is_off) {
01336          global_fin &= ~DEBUGCHAN_FLAG;
01337          global_fout &= ~DEBUGCHAN_FLAG;
01338       } else {
01339          global_fin |= DEBUGCHAN_FLAG;
01340          global_fout |= DEBUGCHAN_FLAG;
01341       }
01342       ast_channel_callback(channel_set_debug, NULL, &args, OBJ_NODATA | OBJ_MULTIPLE);
01343    } else {
01344       if ((c = ast_channel_get_by_name(a->argv[e->args]))) {
01345          channel_set_debug(c, NULL, &args, 0);
01346          ast_channel_unref(c);
01347       } else {
01348          ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
01349       }
01350    }
01351 
01352    ast_cli(a->fd, "Debugging on new channels is %s\n", args.is_off ? "disabled" : "enabled");
01353 
01354    return CLI_SUCCESS;
01355 }
01356 
01357 static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01358 {
01359    char *res;
01360 
01361    switch (cmd) {
01362    case CLI_INIT:
01363       e->command = "no debug channel";
01364       return NULL;
01365    case CLI_HANDLER:
01366       /* exit out of switch statement */
01367       break;
01368    default:
01369       return NULL;
01370    }
01371 
01372    if (a->argc != e->args + 1)
01373       return CLI_SHOWUSAGE;
01374 
01375    /* add a 'magic' value to the CLI_HANDLER command so that
01376     * handle_core_set_debug_channel() will act as if 'off'
01377     * had been specified as part of the command
01378     */
01379    res = handle_core_set_debug_channel(e, CLI_HANDLER + 1000, a);
01380 
01381    return res;
01382 }
01383       
01384 static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01385 {
01386    struct ast_channel *c=NULL;
01387    struct timeval now;
01388    struct ast_str *out = ast_str_thread_get(&ast_str_thread_global_buf, 16);
01389    char cdrtime[256];
01390    char nf[256], wf[256], rf[256];
01391    struct ast_str *write_transpath = ast_str_alloca(256);
01392    struct ast_str *read_transpath = ast_str_alloca(256);
01393    long elapsed_seconds=0;
01394    int hour=0, min=0, sec=0;
01395 #ifdef CHANNEL_TRACE
01396    int trace_enabled;
01397 #endif
01398 
01399    switch (cmd) {
01400    case CLI_INIT:
01401       e->command = "core show channel";
01402       e->usage = 
01403          "Usage: core show channel <channel>\n"
01404          "       Shows lots of information about the specified channel.\n";
01405       return NULL;
01406    case CLI_GENERATE:
01407       return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
01408    }
01409    
01410    if (a->argc != 4) {
01411       return CLI_SHOWUSAGE;
01412    }
01413 
01414    now = ast_tvnow();
01415 
01416    if (!(c = ast_channel_get_by_name(a->argv[3]))) {
01417       ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
01418       return CLI_SUCCESS;
01419    }
01420 
01421    ast_channel_lock(c);
01422 
01423    if (c->cdr) {
01424       elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
01425       hour = elapsed_seconds / 3600;
01426       min = (elapsed_seconds % 3600) / 60;
01427       sec = elapsed_seconds % 60;
01428       snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
01429    } else {
01430       strcpy(cdrtime, "N/A");
01431    }
01432 
01433    ast_cli(a->fd, 
01434       " -- General --\n"
01435       "           Name: %s\n"
01436       "           Type: %s\n"
01437       "       UniqueID: %s\n"
01438       "       LinkedID: %s\n"
01439       "      Caller ID: %s\n"
01440       " Caller ID Name: %s\n"
01441       "    DNID Digits: %s\n"
01442       "       Language: %s\n"
01443       "          State: %s (%d)\n"
01444       "          Rings: %d\n"
01445       "  NativeFormats: %s\n"
01446       "    WriteFormat: %s\n"
01447       "     ReadFormat: %s\n"
01448       " WriteTranscode: %s %s\n"
01449       "  ReadTranscode: %s %s\n"
01450       "1st File Descriptor: %d\n"
01451       "      Frames in: %d%s\n"
01452       "     Frames out: %d%s\n"
01453       " Time to Hangup: %ld\n"
01454       "   Elapsed Time: %s\n"
01455       "  Direct Bridge: %s\n"
01456       "Indirect Bridge: %s\n"
01457       " --   PBX   --\n"
01458       "        Context: %s\n"
01459       "      Extension: %s\n"
01460       "       Priority: %d\n"
01461       "     Call Group: %llu\n"
01462       "   Pickup Group: %llu\n"
01463       "    Application: %s\n"
01464       "           Data: %s\n"
01465       "    Blocking in: %s\n",
01466       c->name, c->tech->type, c->uniqueid, c->linkedid,
01467       S_COR(c->caller.id.number.valid, c->caller.id.number.str, "(N/A)"),
01468       S_COR(c->caller.id.name.valid, c->caller.id.name.str, "(N/A)"),
01469       S_OR(c->dialed.number.str, "(N/A)"),
01470       c->language,   
01471       ast_state2str(c->_state), c->_state, c->rings, 
01472       ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats), 
01473       ast_getformatname_multiple(wf, sizeof(wf), c->writeformat), 
01474       ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
01475       c->writetrans ? "Yes" : "No",
01476       ast_translate_path_to_str(c->writetrans, &write_transpath),
01477       c->readtrans ? "Yes" : "No",
01478       ast_translate_path_to_str(c->readtrans, &read_transpath),
01479       c->fds[0],
01480       c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01481       c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
01482       (long)c->whentohangup.tv_sec,
01483       cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>", 
01484       c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
01485       ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
01486       (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
01487    
01488    if (pbx_builtin_serialize_variables(c, &out)) {
01489       ast_cli(a->fd,"      Variables:\n%s\n", ast_str_buffer(out));
01490    }
01491 
01492    if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1)) {
01493       ast_cli(a->fd,"  CDR Variables:\n%s\n", ast_str_buffer(out));
01494    }
01495 
01496 #ifdef CHANNEL_TRACE
01497    trace_enabled = ast_channel_trace_is_enabled(c);
01498    ast_cli(a->fd, "  Context Trace: %s\n", trace_enabled ? "Enabled" : "Disabled");
01499    if (trace_enabled && ast_channel_trace_serialize(c, &out))
01500       ast_cli(a->fd, "          Trace:\n%s\n", ast_str_buffer(out));
01501 #endif
01502 
01503    ast_channel_unlock(c);
01504    c = ast_channel_unref(c);
01505 
01506    return CLI_SUCCESS;
01507 }
01508 
01509 /*
01510  * helper function to generate CLI matches from a fixed set of values.
01511  * A NULL word is acceptable.
01512  */
01513 char *ast_cli_complete(const char *word, const char * const choices[], int state)
01514 {
01515    int i, which = 0, len;
01516    len = ast_strlen_zero(word) ? 0 : strlen(word);
01517 
01518    for (i = 0; choices[i]; i++) {
01519       if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
01520          return ast_strdup(choices[i]);
01521    }
01522    return NULL;
01523 }
01524 
01525 char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
01526 {
01527    struct ast_channel *c = NULL;
01528    int which = 0;
01529    char notfound = '\0';
01530    char *ret = &notfound; /* so NULL can break the loop */
01531    struct ast_channel_iterator *iter;
01532 
01533    if (pos != rpos) {
01534       return NULL;
01535    }
01536 
01537    if (ast_strlen_zero(word)) {
01538       iter = ast_channel_iterator_all_new();
01539    } else {
01540       iter = ast_channel_iterator_by_name_new(word, strlen(word));
01541    }
01542 
01543    if (!iter) {
01544       return NULL;
01545    }
01546 
01547    while (ret == &notfound && (c = ast_channel_iterator_next(iter))) {
01548       if (++which > state) {
01549          ast_channel_lock(c);
01550          ret = ast_strdup(c->name);
01551          ast_channel_unlock(c);
01552       }
01553       ast_channel_unref(c);
01554    }
01555 
01556    ast_channel_iterator_destroy(iter);
01557 
01558    return ret == &notfound ? NULL : ret;
01559 }
01560 
01561 static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01562 {
01563 #define FORMAT_STRING  "%-25s  %-20s  %-20s\n"
01564 
01565    struct ast_group_info *gi = NULL;
01566    int numchans = 0;
01567    regex_t regexbuf;
01568    int havepattern = 0;
01569 
01570    switch (cmd) {
01571    case CLI_INIT:
01572       e->command = "group show channels";
01573       e->usage = 
01574          "Usage: group show channels [pattern]\n"
01575          "       Lists all currently active channels with channel group(s) specified.\n"
01576          "       Optional regular expression pattern is matched to group names for each\n"
01577          "       channel.\n";
01578       return NULL;
01579    case CLI_GENERATE:
01580       return NULL;
01581    }
01582 
01583    if (a->argc < 3 || a->argc > 4)
01584       return CLI_SHOWUSAGE;
01585    
01586    if (a->argc == 4) {
01587       if (regcomp(&regexbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
01588          return CLI_SHOWUSAGE;
01589       havepattern = 1;
01590    }
01591 
01592    ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
01593 
01594    ast_app_group_list_rdlock();
01595    
01596    gi = ast_app_group_list_head();
01597    while (gi) {
01598       if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
01599          ast_cli(a->fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
01600          numchans++;
01601       }
01602       gi = AST_LIST_NEXT(gi, group_list);
01603    }
01604    
01605    ast_app_group_list_unlock();
01606    
01607    if (havepattern)
01608       regfree(&regexbuf);
01609 
01610    ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
01611    return CLI_SUCCESS;
01612 #undef FORMAT_STRING
01613 }
01614 
01615 static char *handle_cli_wait_fullybooted(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01616 {
01617    switch (cmd) {
01618    case CLI_INIT:
01619       e->command = "core waitfullybooted";
01620       e->usage =
01621          "Usage: core waitfullybooted\n"
01622          "  Wait until Asterisk has fully booted.\n";
01623       return NULL;
01624    case CLI_GENERATE:
01625       return NULL;
01626    }
01627 
01628    while (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
01629       usleep(100);
01630    }
01631 
01632    ast_cli(a->fd, "Asterisk has fully booted.\n");
01633 
01634    return CLI_SUCCESS;
01635 }
01636 
01637 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
01638 
01639 static struct ast_cli_entry cli_cli[] = {
01640    /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
01641    AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
01642    AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
01643    AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
01644 
01645    AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
01646 
01647    AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
01648 
01649    AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
01650 
01651    AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
01652 
01653    AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel"),
01654 
01655    AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
01656 
01657    AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
01658 
01659    AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
01660 
01661    AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
01662 
01663    AST_CLI_DEFINE(handle_modlist, "List modules and info"),
01664 
01665    AST_CLI_DEFINE(handle_load, "Load a module by name"),
01666 
01667    AST_CLI_DEFINE(handle_reload, "Reload configuration for a module"),
01668 
01669    AST_CLI_DEFINE(handle_core_reload, "Global reload"),
01670 
01671    AST_CLI_DEFINE(handle_unload, "Unload a module by name"),
01672 
01673    AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
01674 
01675    AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
01676 
01677    AST_CLI_DEFINE(handle_cli_reload_permissions, "Reload CLI permissions config"),
01678 
01679    AST_CLI_DEFINE(handle_cli_show_permissions, "Show CLI permissions"),
01680 
01681    AST_CLI_DEFINE(handle_cli_check_permissions, "Try a permissions config for a user"),
01682 
01683    AST_CLI_DEFINE(handle_cli_wait_fullybooted, "Wait for Asterisk to be fully booted"),
01684 };
01685 
01686 /*!
01687  * Some regexp characters in cli arguments are reserved and used as separators.
01688  */
01689 static const char cli_rsvd[] = "[]{}|*%";
01690 
01691 /*!
01692  * initialize the _full_cmd string and related parameters,
01693  * return 0 on success, -1 on error.
01694  */
01695 static int set_full_cmd(struct ast_cli_entry *e)
01696 {
01697    int i;
01698    char buf[80];
01699 
01700    ast_join(buf, sizeof(buf), e->cmda);
01701    e->_full_cmd = ast_strdup(buf);
01702    if (!e->_full_cmd) {
01703       ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
01704       return -1;
01705    }
01706    e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
01707    for (i = 0; e->cmda[i]; i++)
01708       ;
01709    e->args = i;
01710    return 0;
01711 }
01712 
01713 /*! \brief cleanup (free) cli_perms linkedlist. */
01714 static void destroy_user_perms(void)
01715 {
01716    struct cli_perm *perm;
01717    struct usergroup_cli_perm *user_perm;
01718 
01719    AST_RWLIST_WRLOCK(&cli_perms);
01720    while ((user_perm = AST_LIST_REMOVE_HEAD(&cli_perms, list))) {
01721       while ((perm = AST_LIST_REMOVE_HEAD(user_perm->perms, list))) {
01722          ast_free(perm->command);
01723          ast_free(perm);
01724       }
01725       ast_free(user_perm);
01726    }
01727    AST_RWLIST_UNLOCK(&cli_perms);
01728 }
01729 
01730 int ast_cli_perms_init(int reload)
01731 {
01732    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01733    struct ast_config *cfg;
01734    char *cat = NULL;
01735    struct ast_variable *v;
01736    struct usergroup_cli_perm *user_group, *cp_entry;
01737    struct cli_perm *perm = NULL;
01738    struct passwd *pw;
01739    struct group *gr;
01740 
01741    if (ast_mutex_trylock(&permsconfiglock)) {
01742       ast_log(LOG_NOTICE, "You must wait until last 'cli reload permissions' command finish\n");
01743       return 1;
01744    }
01745 
01746    cfg = ast_config_load2(perms_config, "" /* core, can't reload */, config_flags);
01747    if (!cfg) {
01748       ast_mutex_unlock(&permsconfiglock);
01749       return 1;
01750    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01751       ast_mutex_unlock(&permsconfiglock);
01752       return 0;
01753    }
01754 
01755    /* free current structures. */
01756    destroy_user_perms();
01757 
01758    while ((cat = ast_category_browse(cfg, cat))) {
01759       if (!strcasecmp(cat, "general")) {
01760          /* General options */
01761          for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
01762             if (!strcasecmp(v->name, "default_perm")) {
01763                cli_default_perm = (!strcasecmp(v->value, "permit")) ? 1: 0;
01764             }
01765          }
01766          continue;
01767       }
01768 
01769       /* users or groups */
01770       gr = NULL, pw = NULL;
01771       if (cat[0] == '@') {
01772          /* This is a group */
01773          gr = getgrnam(&cat[1]);
01774          if (!gr) {
01775             ast_log (LOG_WARNING, "Unknown group '%s'\n", &cat[1]);
01776             continue;
01777          }
01778       } else {
01779          /* This is a user */
01780          pw = getpwnam(cat);
01781          if (!pw) {
01782             ast_log (LOG_WARNING, "Unknown user '%s'\n", cat);
01783             continue;
01784          }
01785       }
01786       user_group = NULL;
01787       /* Check for duplicates */
01788       AST_RWLIST_WRLOCK(&cli_perms);
01789       AST_LIST_TRAVERSE(&cli_perms, cp_entry, list) {
01790          if ((pw && cp_entry->uid == pw->pw_uid) || (gr && cp_entry->gid == gr->gr_gid)) {
01791             /* if it is duplicated, just added this new settings, to 
01792             the current list. */
01793             user_group = cp_entry;
01794             break;
01795          }
01796       }
01797       AST_RWLIST_UNLOCK(&cli_perms);
01798 
01799       if (!user_group) {
01800          /* alloc space for the new user config. */
01801          user_group = ast_calloc(1, sizeof(*user_group));
01802          if (!user_group) {
01803             continue;
01804          }
01805          user_group->uid = (pw ? pw->pw_uid : -1);
01806          user_group->gid = (gr ? gr->gr_gid : -1);
01807          user_group->perms = ast_calloc(1, sizeof(*user_group->perms));
01808          if (!user_group->perms) {
01809             ast_free(user_group);
01810             continue;
01811          }
01812       }
01813       for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
01814          if (ast_strlen_zero(v->value)) {
01815             /* we need to check this condition cause it could break security. */
01816             ast_log(LOG_WARNING, "Empty permit/deny option in user '%s'\n", cat);
01817             continue;
01818          }
01819          if (!strcasecmp(v->name, "permit")) {
01820             perm = ast_calloc(1, sizeof(*perm));
01821             if (perm) {
01822                perm->permit = 1;
01823                perm->command = ast_strdup(v->value);
01824             }
01825          } else if (!strcasecmp(v->name, "deny")) {
01826             perm = ast_calloc(1, sizeof(*perm));
01827             if (perm) {
01828                perm->permit = 0;
01829                perm->command = ast_strdup(v->value);
01830             }
01831          } else {
01832             /* up to now, only 'permit' and 'deny' are possible values. */
01833             ast_log(LOG_WARNING, "Unknown '%s' option\n", v->name);
01834             continue;
01835          }
01836          if (perm) {
01837             /* Added the permission to the user's list. */
01838             AST_LIST_INSERT_TAIL(user_group->perms, perm, list);
01839             perm = NULL;
01840          }
01841       }
01842       AST_RWLIST_WRLOCK(&cli_perms);
01843       AST_RWLIST_INSERT_TAIL(&cli_perms, user_group, list);
01844       AST_RWLIST_UNLOCK(&cli_perms);
01845    }
01846 
01847    ast_config_destroy(cfg);
01848    ast_mutex_unlock(&permsconfiglock);
01849    return 0;
01850 }
01851 
01852 /*! \brief initialize the _full_cmd string in * each of the builtins. */
01853 void ast_builtins_init(void)
01854 {
01855    ast_cli_register_multiple(cli_cli, ARRAY_LEN(cli_cli));
01856 }
01857 
01858 /*!
01859  * match a word in the CLI entry.
01860  * returns -1 on mismatch, 0 on match of an optional word,
01861  * 1 on match of a full word.
01862  *
01863  * The pattern can be
01864  *   any_word           match for equal
01865  *   [foo|bar|baz]      optionally, one of these words
01866  *   {foo|bar|baz}      exactly, one of these words
01867  *   %                  any word
01868  */
01869 static int word_match(const char *cmd, const char *cli_word)
01870 {
01871    int l;
01872    char *pos;
01873 
01874    if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
01875       return -1;
01876    if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
01877       return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
01878    /* regexp match, takes [foo|bar] or {foo|bar} */
01879    l = strlen(cmd);
01880    /* wildcard match - will extend in the future */
01881    if (l > 0 && cli_word[0] == '%') {
01882       return 1;   /* wildcard */
01883    }
01884    pos = strcasestr(cli_word, cmd);
01885    if (pos == NULL) /* not found, say ok if optional */
01886       return cli_word[0] == '[' ? 0 : -1;
01887    if (pos == cli_word) /* no valid match at the beginning */
01888       return -1;
01889    if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
01890       return 1;   /* valid match */
01891    return -1;  /* not found */
01892 }
01893 
01894 /*! \brief if word is a valid prefix for token, returns the pos-th
01895  * match as a malloced string, or NULL otherwise.
01896  * Always tell in *actual how many matches we got.
01897  */
01898 static char *is_prefix(const char *word, const char *token,
01899    int pos, int *actual)
01900 {
01901    int lw;
01902    char *s, *t1;
01903 
01904    *actual = 0;
01905    if (ast_strlen_zero(token))
01906       return NULL;
01907    if (ast_strlen_zero(word))
01908       word = "";  /* dummy */
01909    lw = strlen(word);
01910    if (strcspn(word, cli_rsvd) != lw)
01911       return NULL;   /* no match if word has reserved chars */
01912    if (strchr(cli_rsvd, token[0]) == NULL) { /* regular match */
01913       if (strncasecmp(token, word, lw))   /* no match */
01914          return NULL;
01915       *actual = 1;
01916       return (pos != 0) ? NULL : ast_strdup(token);
01917    }
01918    /* now handle regexp match */
01919 
01920    /* Wildcard always matches, so we never do is_prefix on them */
01921 
01922    t1 = ast_strdupa(token + 1);  /* copy, skipping first char */
01923    while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
01924       if (*s == '%') /* wildcard */
01925          continue;
01926       if (strncasecmp(s, word, lw)) /* no match */
01927          continue;
01928       (*actual)++;
01929       if (pos-- == 0)
01930          return ast_strdup(s);
01931    }
01932    return NULL;
01933 }
01934 
01935 /*!
01936  * \internal
01937  * \brief locate a cli command in the 'helpers' list (which must be locked).
01938  *     The search compares word by word taking care of regexps in e->cmda
01939  *     This function will return NULL when nothing is matched, or the ast_cli_entry that matched.
01940  * \param cmds
01941  * \param match_type has 3 possible values:
01942  *      0       returns if the search key is equal or longer than the entry.
01943  *                note that trailing optional arguments are skipped.
01944  *      -1      true if the mismatch is on the last word XXX not true!
01945  *      1       true only on complete, exact match.
01946  *
01947  */
01948 static struct ast_cli_entry *find_cli(const char * const cmds[], int match_type)
01949 {
01950    int matchlen = -1;   /* length of longest match so far */
01951    struct ast_cli_entry *cand = NULL, *e=NULL;
01952 
01953    while ( (e = cli_next(e)) ) {
01954       /* word-by word regexp comparison */
01955       const char * const *src = cmds;
01956       const char * const *dst = e->cmda;
01957       int n = 0;
01958       for (;; dst++, src += n) {
01959          n = word_match(*src, *dst);
01960          if (n < 0)
01961             break;
01962       }
01963       if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
01964          /* no more words in 'e' */
01965          if (ast_strlen_zero(*src)) /* exact match, cannot do better */
01966             break;
01967          /* Here, cmds has more words than the entry 'e' */
01968          if (match_type != 0) /* but we look for almost exact match... */
01969             continue;   /* so we skip this one. */
01970          /* otherwise we like it (case 0) */
01971       } else { /* still words in 'e' */
01972          if (ast_strlen_zero(*src))
01973             continue; /* cmds is shorter than 'e', not good */
01974          /* Here we have leftover words in cmds and 'e',
01975           * but there is a mismatch. We only accept this one if match_type == -1
01976           * and this is the last word for both.
01977           */
01978          if (match_type != -1 || !ast_strlen_zero(src[1]) ||
01979              !ast_strlen_zero(dst[1])) /* not the one we look for */
01980             continue;
01981          /* good, we are in case match_type == -1 and mismatch on last word */
01982       }
01983       if (src - cmds > matchlen) {  /* remember the candidate */
01984          matchlen = src - cmds;
01985          cand = e;
01986       }
01987    }
01988 
01989    return e ? e : cand;
01990 }
01991 
01992 static char *find_best(const char *argv[])
01993 {
01994    static char cmdline[80];
01995    int x;
01996    /* See how close we get, then print the candidate */
01997    const char *myargv[AST_MAX_CMD_LEN] = { NULL, };
01998 
01999    AST_RWLIST_RDLOCK(&helpers);
02000    for (x = 0; argv[x]; x++) {
02001       myargv[x] = argv[x];
02002       if (!find_cli(myargv, -1))
02003          break;
02004    }
02005    AST_RWLIST_UNLOCK(&helpers);
02006    ast_join(cmdline, sizeof(cmdline), myargv);
02007    return cmdline;
02008 }
02009 
02010 static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
02011 {
02012    if (e->inuse) {
02013       ast_log(LOG_WARNING, "Can't remove command that is in use\n");
02014    } else {
02015       AST_RWLIST_WRLOCK(&helpers);
02016       AST_RWLIST_REMOVE(&helpers, e, list);
02017       AST_RWLIST_UNLOCK(&helpers);
02018       ast_free(e->_full_cmd);
02019       e->_full_cmd = NULL;
02020       if (e->handler) {
02021          /* this is a new-style entry. Reset fields and free memory. */
02022          char *cmda = (char *) e->cmda;
02023          memset(cmda, '\0', sizeof(e->cmda));
02024          ast_free(e->command);
02025          e->command = NULL;
02026          e->usage = NULL;
02027       }
02028    }
02029    return 0;
02030 }
02031 
02032 static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
02033 {
02034    struct ast_cli_entry *cur;
02035    int i, lf, ret = -1;
02036 
02037    struct ast_cli_args a;  /* fake argument */
02038    char **dst = (char **)e->cmda;   /* need to cast as the entry is readonly */
02039    char *s;
02040 
02041    memset(&a, '\0', sizeof(a));
02042    e->handler(e, CLI_INIT, &a);
02043    /* XXX check that usage and command are filled up */
02044    s = ast_skip_blanks(e->command);
02045    s = e->command = ast_strdup(s);
02046    for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
02047       *dst++ = s; /* store string */
02048       s = ast_skip_nonblanks(s);
02049       if (*s == '\0')   /* we are done */
02050          break;
02051       *s++ = '\0';
02052       s = ast_skip_blanks(s);
02053    }
02054    *dst++ = NULL;
02055    
02056    AST_RWLIST_WRLOCK(&helpers);
02057    
02058    if (find_cli(e->cmda, 1)) {
02059       ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", S_OR(e->_full_cmd, e->command));
02060       goto done;
02061    }
02062    if (set_full_cmd(e))
02063       goto done;
02064 
02065    lf = e->cmdlen;
02066    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
02067       int len = cur->cmdlen;
02068       if (lf < len)
02069          len = lf;
02070       if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
02071          AST_RWLIST_INSERT_BEFORE_CURRENT(e, list); 
02072          break;
02073       }
02074    }
02075    AST_RWLIST_TRAVERSE_SAFE_END;
02076 
02077    if (!cur)
02078       AST_RWLIST_INSERT_TAIL(&helpers, e, list); 
02079    ret = 0; /* success */
02080 
02081 done:
02082    AST_RWLIST_UNLOCK(&helpers);
02083 
02084    return ret;
02085 }
02086 
02087 /* wrapper function, so we can unregister deprecated commands recursively */
02088 int ast_cli_unregister(struct ast_cli_entry *e)
02089 {
02090    return __ast_cli_unregister(e, NULL);
02091 }
02092 
02093 /* wrapper function, so we can register deprecated commands recursively */
02094 int ast_cli_register(struct ast_cli_entry *e)
02095 {
02096    return __ast_cli_register(e, NULL);
02097 }
02098 
02099 /*
02100  * register/unregister an array of entries.
02101  */
02102 int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
02103 {
02104    int i, res = 0;
02105 
02106    for (i = 0; i < len; i++)
02107       res |= ast_cli_register(e + i);
02108 
02109    return res;
02110 }
02111 
02112 int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
02113 {
02114    int i, res = 0;
02115 
02116    for (i = 0; i < len; i++)
02117       res |= ast_cli_unregister(e + i);
02118 
02119    return res;
02120 }
02121 
02122 
02123 /*! \brief helper for final part of handle_help
02124  *  if locked = 1, assume the list is already locked
02125  */
02126 static char *help1(int fd, const char * const match[], int locked)
02127 {
02128    char matchstr[80] = "";
02129    struct ast_cli_entry *e = NULL;
02130    int len = 0;
02131    int found = 0;
02132 
02133    if (match) {
02134       ast_join(matchstr, sizeof(matchstr), match);
02135       len = strlen(matchstr);
02136    }
02137    if (!locked)
02138       AST_RWLIST_RDLOCK(&helpers);
02139    while ( (e = cli_next(e)) ) {
02140       /* Hide commands that start with '_' */
02141       if (e->_full_cmd[0] == '_')
02142          continue;
02143       if (match && strncasecmp(matchstr, e->_full_cmd, len))
02144          continue;
02145       ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
02146       found++;
02147    }
02148    if (!locked)
02149       AST_RWLIST_UNLOCK(&helpers);
02150    if (!found && matchstr[0])
02151       ast_cli(fd, "No such command '%s'.\n", matchstr);
02152    return CLI_SUCCESS;
02153 }
02154 
02155 static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02156 {
02157    char fullcmd[80];
02158    struct ast_cli_entry *my_e;
02159    char *res = CLI_SUCCESS;
02160 
02161    if (cmd == CLI_INIT) {
02162       e->command = "core show help";
02163       e->usage =
02164          "Usage: core show help [topic]\n"
02165          "       When called with a topic as an argument, displays usage\n"
02166          "       information on the given command. If called without a\n"
02167          "       topic, it provides a list of commands.\n";
02168       return NULL;
02169 
02170    } else if (cmd == CLI_GENERATE) {
02171       /* skip first 14 or 15 chars, "core show help " */
02172       int l = strlen(a->line);
02173 
02174       if (l > 15) {
02175          l = 15;
02176       }
02177       /* XXX watch out, should stop to the non-generator parts */
02178       return __ast_cli_generator(a->line + l, a->word, a->n, 0);
02179    }
02180    if (a->argc == e->args) {
02181       return help1(a->fd, NULL, 0);
02182    }
02183 
02184    AST_RWLIST_RDLOCK(&helpers);
02185    my_e = find_cli(a->argv + 3, 1); /* try exact match first */
02186    if (!my_e) {
02187       res = help1(a->fd, a->argv + 3, 1 /* locked */);
02188       AST_RWLIST_UNLOCK(&helpers);
02189       return res;
02190    }
02191    if (my_e->usage)
02192       ast_cli(a->fd, "%s", my_e->usage);
02193    else {
02194       ast_join(fullcmd, sizeof(fullcmd), a->argv + 3);
02195       ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd);
02196    }
02197    AST_RWLIST_UNLOCK(&helpers);
02198    return res;
02199 }
02200 
02201 static char *parse_args(const char *s, int *argc, const char *argv[], int max, int *trailingwhitespace)
02202 {
02203    char *duplicate, *cur;
02204    int x = 0;
02205    int quoted = 0;
02206    int escaped = 0;
02207    int whitespace = 1;
02208    int dummy = 0;
02209 
02210    if (trailingwhitespace == NULL)
02211       trailingwhitespace = &dummy;
02212    *trailingwhitespace = 0;
02213    if (s == NULL) /* invalid, though! */
02214       return NULL;
02215    /* make a copy to store the parsed string */
02216    if (!(duplicate = ast_strdup(s)))
02217       return NULL;
02218 
02219    cur = duplicate;
02220    /* scan the original string copying into cur when needed */
02221    for (; *s ; s++) {
02222       if (x >= max - 1) {
02223          ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
02224          break;
02225       }
02226       if (*s == '"' && !escaped) {
02227          quoted = !quoted;
02228          if (quoted && whitespace) {
02229             /* start a quoted string from previous whitespace: new argument */
02230             argv[x++] = cur;
02231             whitespace = 0;
02232          }
02233       } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
02234          /* If we are not already in whitespace, and not in a quoted string or
02235             processing an escape sequence, and just entered whitespace, then
02236             finalize the previous argument and remember that we are in whitespace
02237          */
02238          if (!whitespace) {
02239             *cur++ = '\0';
02240             whitespace = 1;
02241          }
02242       } else if (*s == '\\' && !escaped) {
02243          escaped = 1;
02244       } else {
02245          if (whitespace) {
02246             /* we leave whitespace, and are not quoted. So it's a new argument */
02247             argv[x++] = cur;
02248             whitespace = 0;
02249          }
02250          *cur++ = *s;
02251          escaped = 0;
02252       }
02253    }
02254    /* Null terminate */
02255    *cur++ = '\0';
02256    /* XXX put a NULL in the last argument, because some functions that take
02257     * the array may want a null-terminated array.
02258     * argc still reflects the number of non-NULL entries.
02259     */
02260    argv[x] = NULL;
02261    *argc = x;
02262    *trailingwhitespace = whitespace;
02263    return duplicate;
02264 }
02265 
02266 /*! \brief Return the number of unique matches for the generator */
02267 int ast_cli_generatornummatches(const char *text, const char *word)
02268 {
02269    int matches = 0, i = 0;
02270    char *buf = NULL, *oldbuf = NULL;
02271 
02272    while ((buf = ast_cli_generator(text, word, i++))) {
02273       if (!oldbuf || strcmp(buf,oldbuf))
02274          matches++;
02275       if (oldbuf)
02276          ast_free(oldbuf);
02277       oldbuf = buf;
02278    }
02279    if (oldbuf)
02280       ast_free(oldbuf);
02281    return matches;
02282 }
02283 
02284 char **ast_cli_completion_matches(const char *text, const char *word)
02285 {
02286    char **match_list = NULL, *retstr, *prevstr;
02287    size_t match_list_len, max_equal, which, i;
02288    int matches = 0;
02289 
02290    /* leave entry 0 free for the longest common substring */
02291    match_list_len = 1;
02292    while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
02293       if (matches + 1 >= match_list_len) {
02294          match_list_len <<= 1;
02295          if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
02296             return NULL;
02297       }
02298       match_list[++matches] = retstr;
02299    }
02300 
02301    if (!match_list)
02302       return match_list; /* NULL */
02303 
02304    /* Find the longest substring that is common to all results
02305     * (it is a candidate for completion), and store a copy in entry 0.
02306     */
02307    prevstr = match_list[1];
02308    max_equal = strlen(prevstr);
02309    for (which = 2; which <= matches; which++) {
02310       for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
02311          continue;
02312       max_equal = i;
02313    }
02314 
02315    if (!(retstr = ast_malloc(max_equal + 1)))
02316       return NULL;
02317    
02318    ast_copy_string(retstr, match_list[1], max_equal + 1);
02319    match_list[0] = retstr;
02320 
02321    /* ensure that the array is NULL terminated */
02322    if (matches + 1 >= match_list_len) {
02323       if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
02324          return NULL;
02325    }
02326    match_list[matches + 1] = NULL;
02327 
02328    return match_list;
02329 }
02330 
02331 /*! \brief returns true if there are more words to match */
02332 static int more_words (const char * const *dst)
02333 {
02334    int i;
02335    for (i = 0; dst[i]; i++) {
02336       if (dst[i][0] != '[')
02337          return -1;
02338    }
02339    return 0;
02340 }
02341    
02342 /*
02343  * generate the entry at position 'state'
02344  */
02345 static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
02346 {
02347    const char *argv[AST_MAX_ARGS];
02348    struct ast_cli_entry *e = NULL;
02349    int x = 0, argindex, matchlen;
02350    int matchnum=0;
02351    char *ret = NULL;
02352    char matchstr[80] = "";
02353    int tws = 0;
02354    /* Split the argument into an array of words */
02355    char *duplicate = parse_args(text, &x, argv, ARRAY_LEN(argv), &tws);
02356 
02357    if (!duplicate)   /* malloc error */
02358       return NULL;
02359 
02360    /* Compute the index of the last argument (could be an empty string) */
02361    argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
02362 
02363    /* rebuild the command, ignore terminating white space and flatten space */
02364    ast_join(matchstr, sizeof(matchstr)-1, argv);
02365    matchlen = strlen(matchstr);
02366    if (tws) {
02367       strcat(matchstr, " "); /* XXX */
02368       if (matchlen)
02369          matchlen++;
02370    }
02371    if (lock)
02372       AST_RWLIST_RDLOCK(&helpers);
02373    while ( (e = cli_next(e)) ) {
02374       /* XXX repeated code */
02375       int src = 0, dst = 0, n = 0;
02376 
02377       if (e->command[0] == '_')
02378          continue;
02379 
02380       /*
02381        * Try to match words, up to and excluding the last word, which
02382        * is either a blank or something that we want to extend.
02383        */
02384       for (;src < argindex; dst++, src += n) {
02385          n = word_match(argv[src], e->cmda[dst]);
02386          if (n < 0)
02387             break;
02388       }
02389 
02390       if (src != argindex && more_words(e->cmda + dst))  /* not a match */
02391          continue;
02392       ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n);
02393       matchnum += n; /* this many matches here */
02394       if (ret) {
02395          /*
02396           * argv[src] is a valid prefix of the next word in this
02397           * command. If this is also the correct entry, return it.
02398           */
02399          if (matchnum > state)
02400             break;
02401          ast_free(ret);
02402          ret = NULL;
02403       } else if (ast_strlen_zero(e->cmda[dst])) {
02404          /*
02405           * This entry is a prefix of the command string entered
02406           * (only one entry in the list should have this property).
02407           * Run the generator if one is available. In any case we are done.
02408           */
02409          if (e->handler) { /* new style command */
02410             struct ast_cli_args a = {
02411                .line = matchstr, .word = word,
02412                .pos = argindex,
02413                .n = state - matchnum,
02414                .argv = argv,
02415                .argc = x};
02416             ret = e->handler(e, CLI_GENERATE, &a);
02417          }
02418          if (ret)
02419             break;
02420       }
02421    }
02422    if (lock)
02423       AST_RWLIST_UNLOCK(&helpers);
02424    ast_free(duplicate);
02425    return ret;
02426 }
02427 
02428 char *ast_cli_generator(const char *text, const char *word, int state)
02429 {
02430    return __ast_cli_generator(text, word, state, 1);
02431 }
02432 
02433 int ast_cli_command_full(int uid, int gid, int fd, const char *s)
02434 {
02435    const char *args[AST_MAX_ARGS + 1];
02436    struct ast_cli_entry *e;
02437    int x;
02438    char *duplicate = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
02439    char tmp[AST_MAX_ARGS + 1];
02440    char *retval = NULL;
02441    struct ast_cli_args a = {
02442       .fd = fd, .argc = x, .argv = args+1 };
02443 
02444    if (duplicate == NULL)
02445       return -1;
02446 
02447    if (x < 1)  /* We need at least one entry, otherwise ignore */
02448       goto done;
02449 
02450    AST_RWLIST_RDLOCK(&helpers);
02451    e = find_cli(args + 1, 0);
02452    if (e)
02453       ast_atomic_fetchadd_int(&e->inuse, 1);
02454    AST_RWLIST_UNLOCK(&helpers);
02455    if (e == NULL) {
02456       ast_cli(fd, "No such command '%s' (type 'core show help %s' for other possible commands)\n", s, find_best(args + 1));
02457       goto done;
02458    }
02459 
02460    ast_join(tmp, sizeof(tmp), args + 1);
02461    /* Check if the user has rights to run this command. */
02462    if (!cli_has_permissions(uid, gid, tmp)) {
02463       ast_cli(fd, "You don't have permissions to run '%s' command\n", tmp);
02464       ast_free(duplicate);
02465       return 0;
02466    }
02467 
02468    /*
02469     * Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
02470     * Remember that the array returned by parse_args is NULL-terminated.
02471     */
02472    args[0] = (char *)e;
02473 
02474    retval = e->handler(e, CLI_HANDLER, &a);
02475 
02476    if (retval == CLI_SHOWUSAGE) {
02477       ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
02478    } else {
02479       if (retval == CLI_FAILURE)
02480          ast_cli(fd, "Command '%s' failed.\n", s);
02481    }
02482    ast_atomic_fetchadd_int(&e->inuse, -1);
02483 done:
02484    ast_free(duplicate);
02485    return 0;
02486 }
02487 
02488 int ast_cli_command_multiple_full(int uid, int gid, int fd, size_t size, const char *s)
02489 {
02490    char cmd[512];
02491    int x, y = 0, count = 0;
02492 
02493    for (x = 0; x < size; x++) {
02494       cmd[y] = s[x];
02495       y++;
02496       if (s[x] == '\0') {
02497          ast_cli_command_full(uid, gid, fd, cmd);
02498          y = 0;
02499          count++;
02500       }
02501    }
02502    return count;
02503 }

Generated on Wed Apr 6 11:29:44 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7