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