Fri Apr 15 20:38:07 2016

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

Generated on 15 Apr 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1