Thu Dec 17 17:39:58 2009

Asterisk developer's documentation


cli.c

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

Generated on Thu Dec 17 17:39:58 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7