Sat Aug 6 00:39:28 2011

Asterisk developer's documentation


cli.c

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

Generated on Sat Aug 6 00:39:28 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7