diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/asterisk.c | 190 | ||||
-rw-r--r-- | main/cli.c | 359 | ||||
-rw-r--r-- | main/logger.c | 210 | ||||
-rw-r--r-- | main/manager.c | 8 | ||||
-rw-r--r-- | main/pbx.c | 2 |
5 files changed, 550 insertions, 219 deletions
diff --git a/main/asterisk.c b/main/asterisk.c index 86667e061..e2e01a51f 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -319,6 +319,9 @@ int daemon(int, int); /* defined in libresolv of all places */ struct ast_flags ast_options = { AST_DEFAULT_OPTIONS }; struct ast_flags ast_compat = { 0 }; +/*! Maximum active system verbosity level. */ +int ast_verb_sys_level; + int option_verbose; /*!< Verbosity level */ int option_debug; /*!< Debug level */ double ast_option_maxload; /*!< Max load avg on system */ @@ -347,6 +350,8 @@ struct console { int uid; /*!< Remote user ID. */ int gid; /*!< Remote group ID. */ int levels[NUMLOGLEVELS]; /*!< Which log levels are enabled for the console */ + /*! Verbosity level of this console. */ + int option_verbose; }; struct ast_atexit { @@ -609,7 +614,8 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c ast_cli(a->fd, " Maximum open file handles: %d\n", ast_option_maxfiles); else ast_cli(a->fd, " Maximum open file handles: Not set\n"); - ast_cli(a->fd, " Verbosity: %d\n", option_verbose); + ast_cli(a->fd, " Root console verbosity: %d\n", option_verbose); + ast_cli(a->fd, " Current console verbosity: %d\n", ast_verb_console_get()); ast_cli(a->fd, " Debug level: %d\n", option_debug); ast_cli(a->fd, " Maximum load average: %lf\n", ast_option_maxload); #if defined(HAVE_SYSINFO) @@ -1331,29 +1337,33 @@ void ast_console_toggle_mute(int fd, int silent) } /*! - * \brief log the string to all attached console clients + * \brief log the string to all attached network console clients */ static void ast_network_puts_mutable(const char *string, int level) { int x; - for (x = 0;x < AST_MAX_CONNECTS; x++) { - if (consoles[x].mute) + + for (x = 0; x < AST_MAX_CONNECTS; ++x) { + if (consoles[x].fd < 0 + || consoles[x].mute + || consoles[x].levels[level]) { continue; - if (consoles[x].fd > -1) { - if (!consoles[x].levels[level]) - fdprint(consoles[x].p[1], string); } + fdprint(consoles[x].p[1], string); } } /*! - * \brief log the string to the console, and all attached - * console clients + * \brief log the string to the root console, and all attached + * network console clients */ void ast_console_puts_mutable(const char *string, int level) { + /* Send to the root console */ fputs(string, stdout); fflush(stdout); + + /* Send to any network console clients */ ast_network_puts_mutable(string, level); } @@ -1363,26 +1373,45 @@ void ast_console_puts_mutable(const char *string, int level) static void ast_network_puts(const char *string) { int x; - for (x = 0; x < AST_MAX_CONNECTS; x++) { - if (consoles[x].fd > -1) - fdprint(consoles[x].p[1], string); + + for (x = 0; x < AST_MAX_CONNECTS; ++x) { + if (consoles[x].fd < 0) { + continue; + } + fdprint(consoles[x].p[1], string); } } /*! - * \brief write the string to the console, and all attached - * console clients + * \brief write the string to the root console, and all attached + * network console clients */ void ast_console_puts(const char *string) { + /* Send to the root console */ fputs(string, stdout); fflush(stdout); + + /* Send to any network console clients */ ast_network_puts(string); } -static void network_verboser(const char *s) +static void network_verboser(const char *string) { - ast_network_puts_mutable(s, __LOG_VERBOSE); + int x; + int verb_level; + + /* Send to any network console clients if client verbocity allows. */ + verb_level = VERBOSE_MAGIC2LEVEL(string); + for (x = 0; x < AST_MAX_CONNECTS; ++x) { + if (consoles[x].fd < 0 + || consoles[x].mute + || consoles[x].levels[__LOG_VERBOSE] + || consoles[x].option_verbose < verb_level) { + continue; + } + fdprint(consoles[x].p[1], string); + } } static pthread_t lthread; @@ -1446,6 +1475,7 @@ static int read_credentials(int fd, char *buffer, size_t size, struct console *c return result; } +/* This is the thread running the remote console on the main process. */ static void *netconsole(void *vconsole) { struct console *con = vconsole; @@ -1461,6 +1491,7 @@ static void *netconsole(void *vconsole) ast_copy_string(hostname, "<Unknown>", sizeof(hostname)); snprintf(outbuf, sizeof(outbuf), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ast_get_version()); fdprint(con->fd, outbuf); + ast_verb_console_register(&con->option_verbose); for (;;) { fds[0].fd = con->fd; fds[0].events = POLLIN; @@ -1523,6 +1554,7 @@ static void *netconsole(void *vconsole) break; } } + ast_verb_console_unregister(); if (!ast_opt_hide_connect) { ast_verb(3, "Remote UNIX connection disconnected\n"); } @@ -1575,24 +1607,25 @@ static void *listener(void *unused) } if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) { ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno)); - consoles[x].fd = -1; fdprint(s, "Server failed to create pipe\n"); close(s); break; } flags = fcntl(consoles[x].p[1], F_GETFL); fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK); - consoles[x].fd = s; consoles[x].mute = 1; /* Default is muted, we will un-mute if necessary */ /* Default uid and gid to -2, so then in cli.c/cli_has_permissions() we will be able to know if the user didn't send the credentials. */ consoles[x].uid = -2; consoles[x].gid = -2; + /* Server default of remote console verbosity level is OFF. */ + consoles[x].option_verbose = 0; + consoles[x].fd = s; if (ast_pthread_create_detached_background(&consoles[x].t, NULL, netconsole, &consoles[x])) { + consoles[x].fd = -1; ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno)); close(consoles[x].p[0]); close(consoles[x].p[1]); - consoles[x].fd = -1; fdprint(s, "Server failed to spawn thread\n"); close(s); } @@ -2074,12 +2107,6 @@ static int console_state_init(void *ptr) AST_THREADSTORAGE_CUSTOM(console_state, console_state_init, ast_free_ptr); -/* These gymnastics are due to platforms which designate char as unsigned by - * default. Level is the negative character -- offset by 1, because \0 is the - * EOS delimiter. */ -#define VERBOSE_MAGIC2LEVEL(x) (((char) -*(signed char *) (x)) - 1) -#define VERBOSE_HASMAGIC(x) (*(signed char *) (x) < 0) - static int console_print(const char *s, int local) { struct console_state_data *state = @@ -2174,6 +2201,7 @@ static int ast_all_zeros(char *s) return 1; } +/* This is the main console CLI command handler. Run by the main() thread. */ static void consolehandler(char *s) { printf("%s", term_end()); @@ -2211,38 +2239,56 @@ static int remoteconsolehandler(char *s) else ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh"); ret = 1; - } else if (strncasecmp(s, "core set verbose ", 17) == 0) { - int old_verbose = option_verbose; - if (strncasecmp(s + 17, "atleast ", 8) == 0) { - int tmp; - if (sscanf(s + 25, "%d", &tmp) != 1) { - fprintf(stderr, "Usage: core set verbose [atleast] <level>\n"); - } else { - if (tmp > option_verbose) { - option_verbose = tmp; - } - if (old_verbose != option_verbose) { - fprintf(stdout, "Set remote console verbosity from %d to %d\n", old_verbose, option_verbose); - } else { - fprintf(stdout, "Verbosity level unchanged.\n"); - } + } else if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) && + (s[4] == '\0' || isspace(s[4]))) { + quit_handler(0, SHUTDOWN_FAST, 0); + ret = 1; + } else if (s[0]) { + char *shrunk = ast_strdupa(s); + char *cur; + char *prev; + + /* + * Remove duplicate spaces from shrunk for matching purposes. + * + * shrunk has at least one character in it to start with or we + * couldn't get here. + */ + for (prev = shrunk, cur = shrunk + 1; *cur; ++cur) { + if (*prev == ' ' && *cur == ' ') { + /* Skip repeated space delimiter. */ + continue; } - } else { - if (sscanf(s + 17, "%d", &option_verbose) != 1) { - fprintf(stderr, "Usage: core set verbose [atleast] <level>\n"); + *++prev = *cur; + } + *++prev = '\0'; + + if (strncasecmp(shrunk, "core set verbose ", 17) == 0) { + /* + * We need to still set the rasterisk option_verbose in case we are + * talking to an earlier version which doesn't prefilter verbose + * levels. This is really a compromise as we should always take + * whatever the server sends. + */ + + if (!strncasecmp(shrunk + 17, "off", 3)) { + ast_verb_console_set(0); } else { - if (old_verbose != option_verbose) { - fprintf(stdout, "Set remote console verbosity to %d\n", option_verbose); - } else { - fprintf(stdout, "Verbosity level unchanged.\n"); + int verbose_new; + int atleast; + + atleast = 8; + if (strncasecmp(shrunk + 17, "atleast ", atleast)) { + atleast = 0; + } + + if (sscanf(shrunk + 17 + atleast, "%30d", &verbose_new) == 1) { + if (!atleast || ast_verb_console_get() < verbose_new) { + ast_verb_console_set(verbose_new); + } } } } - ret = 1; - } else if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) && - (s[4] == '\0' || isspace(s[4]))) { - quit_handler(0, SHUTDOWN_FAST, 0); - ret = 1; } return ret; @@ -2564,6 +2610,31 @@ static struct ast_cli_entry cli_asterisk[] = { #endif /* ! LOW_MEMORY */ }; +static void send_rasterisk_connect_commands(void) +{ + char buf[80]; + + /* + * Tell the server asterisk instance about the verbose level + * initially desired. + */ + if (option_verbose) { + snprintf(buf, sizeof(buf), "core set verbose atleast %d silent", option_verbose); + fdsend(ast_consock, buf); + } + + if (option_debug) { + snprintf(buf, sizeof(buf), "core set debug atleast %d", option_debug); + fdsend(ast_consock, buf); + } + + if (!ast_opt_mute) { + fdsend(ast_consock, "logger mute silent"); + } else { + printf("log and verbose output currently muted ('logger mute' to unmute)\n"); + } +} + static int ast_el_read_char(EditLine *editline, char *cp) { int num_read = 0; @@ -2617,10 +2688,7 @@ static int ast_el_read_char(EditLine *editline, char *cp) fprintf(stderr, "Reconnect succeeded after %.3f seconds\n", 1.0 / reconnects_per_second * tries); printf("%s", term_quit()); WELCOME_MESSAGE; - if (!ast_opt_mute) - fdsend(ast_consock, "logger mute silent"); - else - printf("log and verbose output currently muted ('logger mute' to unmute)\n"); + send_rasterisk_connect_commands(); break; } else usleep(1000000 / reconnects_per_second); @@ -3171,11 +3239,7 @@ static void ast_remotecontrol(char *data) else pid = -1; if (!data) { - if (!ast_opt_mute) { - fdsend(ast_consock, "logger mute silent"); - } else { - printf("log and verbose output currently muted ('logger mute' to unmute)\n"); - } + send_rasterisk_connect_commands(); } if (ast_opt_exec && data) { /* hack to print output then exit if asterisk -rx is used */ @@ -3652,6 +3716,7 @@ static void canary_exit(void) kill(canary_pid, SIGKILL); } +/* Execute CLI commands on startup. Run by main() thread. */ static void run_startup_commands(void) { int fd; @@ -4082,6 +4147,9 @@ int main(int argc, char *argv[]) } } + /* Initial value of the maximum active system verbosity level. */ + ast_verb_sys_level = option_verbose; + if (ast_tryconnect()) { /* One is already running */ if (ast_opt_remote) { diff --git a/main/cli.c b/main/cli.c index a9f466499..fa5cc987a 100644 --- a/main/cli.c +++ b/main/cli.c @@ -106,8 +106,6 @@ AST_RWLIST_HEAD(module_level_list, module_level); /*! list of module names and their debug levels */ static struct module_level_list debug_modules = AST_RWLIST_HEAD_INIT_VALUE; -/*! list of module names and their verbose levels */ -static struct module_level_list verbose_modules = AST_RWLIST_HEAD_INIT_VALUE; AST_THREADSTORAGE(ast_cli_buf); @@ -151,19 +149,7 @@ unsigned int ast_debug_get_by_module(const char *module) unsigned int ast_verbose_get_by_module(const char *module) { - struct module_level *ml; - unsigned int res = 0; - - AST_RWLIST_RDLOCK(&verbose_modules); - AST_LIST_TRAVERSE(&verbose_modules, ml, entry) { - if (!strcasecmp(ml->module, module)) { - res = ml->level; - break; - } - } - AST_RWLIST_UNLOCK(&verbose_modules); - - return res; + return 0; } /*! \internal @@ -357,14 +343,19 @@ static char *handle_core_reload(struct ast_cli_entry *e, int cmd, struct ast_cli return CLI_SUCCESS; } + /*! - * \brief Find the debug or verbose file setting - * \arg debug 1 for debug, 0 for verbose + * \brief Find the module level setting + * + * \param module Module name to look for. + * \param mll List to search. + * + * \retval level struct found on success. + * \retval NULL not found. */ -static struct module_level *find_module_level(const char *module, unsigned int debug) +static struct module_level *find_module_level(const char *module, struct module_level_list *mll) { struct module_level *ml; - struct module_level_list *mll = debug ? &debug_modules : &verbose_modules; AST_LIST_TRAVERSE(mll, ml, entry) { if (!strcasecmp(ml->module, module)) @@ -414,52 +405,77 @@ static char *complete_number(const char *partial, unsigned int min, unsigned int return NULL; } -static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +static void status_debug_verbose(struct ast_cli_args *a, const char *what, int old_val, int cur_val) +{ + char was_buf[30]; + const char *was; + + if (old_val) { + snprintf(was_buf, sizeof(was_buf), "%d", old_val); + was = was_buf; + } else { + was = "OFF"; + } + + if (old_val == cur_val) { + ast_cli(a->fd, "%s is still %s.\n", what, was); + } else { + char now_buf[30]; + const char *now; + + if (cur_val) { + snprintf(now_buf, sizeof(now_buf), "%d", cur_val); + now = now_buf; + } else { + now = "OFF"; + } + + ast_cli(a->fd, "%s was %s and is now %s.\n", what, was, now); + } +} + +static char *handle_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { int oldval; int newlevel; - unsigned int is_debug; int atleast = 0; - int fd = a->fd; - int argc = a->argc; - const char * const *argv = a->argv; const char *argv3 = a->argv ? S_OR(a->argv[3], "") : ""; - int *dst; - char *what; - struct module_level_list *mll; struct module_level *ml; switch (cmd) { case CLI_INIT: - e->command = "core set {debug|verbose}"; + e->command = "core set debug"; e->usage = #if !defined(LOW_MEMORY) - "Usage: core set {debug|verbose} [atleast] <level> [module]\n" + "Usage: core set debug [atleast] <level> [module]\n" #else - "Usage: core set {debug|verbose} [atleast] <level>\n" + "Usage: core set debug [atleast] <level>\n" #endif - " core set {debug|verbose} off\n" + " core set debug off\n" + "\n" #if !defined(LOW_MEMORY) - " Sets level of debug or verbose messages to be displayed or\n" + " Sets level of debug messages to be displayed or\n" " sets a module name to display debug messages from.\n" #else - " Sets level of debug or verbose messages to be displayed.\n" + " Sets level of debug messages to be displayed.\n" #endif - " 0 or off means no messages should be displayed.\n" - " Equivalent to -d[d[...]] or -v[v[v...]] on startup\n"; + " 0 or off means no messages should be displayed.\n" + " Equivalent to -d[d[...]] on startup\n"; return NULL; case CLI_GENERATE: - if (a->pos == 3 || (a->pos == 4 && !strcasecmp(a->argv[3], "atleast"))) { + if (!strcasecmp(argv3, "atleast")) { + atleast = 1; + } + if (a->pos == 3 || (a->pos == 4 && atleast)) { const char *pos = a->pos == 3 ? argv3 : S_OR(a->argv[4], ""); int numbermatch = (ast_strlen_zero(pos) || strchr("123456789", pos[0])) ? 0 : 21; + if (a->n < 21 && numbermatch == 0) { return complete_number(pos, 0, 0x7fffffff, a->n); } else if (pos[0] == '0') { if (a->n == 0) { return ast_strdup("0"); - } else { - return NULL; } } else if (a->n == (21 - numbermatch)) { if (a->pos == 3 && !strncasecmp(argv3, "off", strlen(argv3))) { @@ -471,8 +487,11 @@ static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_arg return ast_strdup("atleast"); } #if !defined(LOW_MEMORY) - } else if (a->pos == 4 || (a->pos == 5 && !strcasecmp(argv3, "atleast"))) { - return ast_complete_source_filename(a->pos == 4 ? S_OR(a->argv[4], "") : S_OR(a->argv[5], ""), a->n); + } else if ((a->pos == 4 && !atleast && strcasecmp(argv3, "off")) + || (a->pos == 5 && atleast)) { + const char *pos = S_OR(a->argv[a->pos], ""); + + return ast_complete_source_filename(pos, a->n); #endif } return NULL; @@ -481,117 +500,200 @@ static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_arg * we are guaranteed to be called with argc >= e->args; */ - if (argc <= e->args) + if (a->argc <= e->args) { return CLI_SHOWUSAGE; - if (!strcasecmp(argv[e->args - 1], "debug")) { - dst = &option_debug; - oldval = option_debug; - what = "Core debug"; - is_debug = 1; - } else { - dst = &option_verbose; - oldval = option_verbose; - what = "Verbosity"; - is_debug = 0; } - if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) { + + if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args], "off")) { newlevel = 0; + } else { + if (!strcasecmp(a->argv[e->args], "atleast")) { + atleast = 1; + } + if (a->argc != e->args + atleast + 1 && a->argc != e->args + atleast + 2) { + return CLI_SHOWUSAGE; + } + if (sscanf(a->argv[e->args + atleast], "%30d", &newlevel) != 1) { + return CLI_SHOWUSAGE; + } - mll = is_debug ? &debug_modules : &verbose_modules; + if (a->argc == e->args + atleast + 2) { + /* We have specified a module name. */ + char *mod = ast_strdupa(a->argv[e->args + atleast + 1]); + int mod_len = strlen(mod); - AST_RWLIST_WRLOCK(mll); - while ((ml = AST_RWLIST_REMOVE_HEAD(mll, entry))) { - ast_free(ml); - } - ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE); - AST_RWLIST_UNLOCK(mll); + if (3 < mod_len && !strcasecmp(mod + mod_len - 3, ".so")) { + mod[mod_len - 3] = '\0'; + } - goto done; + AST_RWLIST_WRLOCK(&debug_modules); + + ml = find_module_level(mod, &debug_modules); + if (!newlevel) { + if (!ml) { + /* Specified off for a nonexistent entry. */ + AST_RWLIST_UNLOCK(&debug_modules); + ast_cli(a->fd, "Core debug is still 0 for '%s'.\n", mod); + return CLI_SUCCESS; + } + AST_RWLIST_REMOVE(&debug_modules, ml, entry); + if (AST_RWLIST_EMPTY(&debug_modules)) { + ast_clear_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE); + } + AST_RWLIST_UNLOCK(&debug_modules); + ast_cli(a->fd, "Core debug was %d and has been set to 0 for '%s'.\n", + ml->level, mod); + ast_free(ml); + return CLI_SUCCESS; + } + + if (ml) { + if ((atleast && newlevel < ml->level) || ml->level == newlevel) { + ast_cli(a->fd, "Core debug is still %d for '%s'.\n", ml->level, mod); + AST_RWLIST_UNLOCK(&debug_modules); + return CLI_SUCCESS; + } + oldval = ml->level; + ml->level = newlevel; + } else { + ml = ast_calloc(1, sizeof(*ml) + strlen(mod) + 1); + if (!ml) { + AST_RWLIST_UNLOCK(&debug_modules); + return CLI_FAILURE; + } + oldval = ml->level; + ml->level = newlevel; + strcpy(ml->module, mod); + AST_RWLIST_INSERT_TAIL(&debug_modules, ml, entry); + } + ast_set_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE); + + ast_cli(a->fd, "Core debug was %d and has been set to %d for '%s'.\n", + oldval, ml->level, ml->module); + + AST_RWLIST_UNLOCK(&debug_modules); + + return CLI_SUCCESS; + } } - if (!strcasecmp(argv[e->args], "atleast")) - atleast = 1; - if (argc != e->args + atleast + 1 && argc != e->args + atleast + 2) - return CLI_SHOWUSAGE; - if (sscanf(argv[e->args + atleast], "%30d", &newlevel) != 1) - return CLI_SHOWUSAGE; - if (argc == e->args + atleast + 2) { - /* We have specified a module name. */ - char *mod = ast_strdupa(argv[e->args + atleast + 1]); - if ((strlen(mod) > 3) && !strcasecmp(mod + strlen(mod) - 3, ".so")) { - mod[strlen(mod) - 3] = '\0'; + /* Update global debug level */ + if (!newlevel) { + /* Specified level was 0 or off. */ + AST_RWLIST_WRLOCK(&debug_modules); + while ((ml = AST_RWLIST_REMOVE_HEAD(&debug_modules, entry))) { + ast_free(ml); } + ast_clear_flag(&ast_options, AST_OPT_FLAG_DEBUG_MODULE); + AST_RWLIST_UNLOCK(&debug_modules); + } + oldval = option_debug; + if (!atleast || newlevel > option_debug) { + option_debug = newlevel; + } - mll = is_debug ? &debug_modules : &verbose_modules; + /* Report debug level status */ + status_debug_verbose(a, "Core debug", oldval, option_debug); - AST_RWLIST_WRLOCK(mll); + return CLI_SUCCESS; +} - ml = find_module_level(mod, is_debug); - if (!newlevel) { - if (!ml) { - /* Specified off for a nonexistent entry. */ - AST_RWLIST_UNLOCK(mll); - return CLI_SUCCESS; - } - AST_RWLIST_REMOVE(mll, ml, entry); - if (AST_RWLIST_EMPTY(mll)) - ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE); - AST_RWLIST_UNLOCK(mll); - ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, ml->level, mod); - ast_free(ml); - return CLI_SUCCESS; +static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int oldval; + int newlevel; + int atleast = 0; + int silent = 0; + const char *argv3 = a->argv ? S_OR(a->argv[3], "") : ""; + + switch (cmd) { + case CLI_INIT: + e->command = "core set verbose"; + e->usage = + "Usage: core set verbose [atleast] <level> [silent]\n" + " core set verbose off\n" + "\n" + " Sets level of verbose messages to be displayed.\n" + " 0 or off means no verbose messages should be displayed.\n" + " The silent option means the command does not report what\n" + " happened to the verbose level.\n" + " Equivalent to -v[v[...]] on startup\n"; + return NULL; + + case CLI_GENERATE: + if (!strcasecmp(argv3, "atleast")) { + atleast = 1; } + if (a->pos == 3 || (a->pos == 4 && atleast)) { + const char *pos = a->pos == 3 ? argv3 : S_OR(a->argv[4], ""); + int numbermatch = (ast_strlen_zero(pos) || strchr("123456789", pos[0])) ? 0 : 21; - if (ml) { - if ((atleast && newlevel < ml->level) || ml->level == newlevel) { - ast_cli(fd, "%s is %d for '%s'\n", what, ml->level, mod); - AST_RWLIST_UNLOCK(mll); - return CLI_SUCCESS; + if (a->n < 21 && numbermatch == 0) { + return complete_number(pos, 0, 0x7fffffff, a->n); + } else if (pos[0] == '0') { + if (a->n == 0) { + return ast_strdup("0"); + } + } else if (a->n == (21 - numbermatch)) { + if (a->pos == 3 && !strncasecmp(argv3, "off", strlen(argv3))) { + return ast_strdup("off"); + } else if (a->pos == 3 && !strncasecmp(argv3, "atleast", strlen(argv3))) { + return ast_strdup("atleast"); + } + } else if (a->n == (22 - numbermatch) && a->pos == 3 && ast_strlen_zero(argv3)) { + return ast_strdup("atleast"); } - oldval = ml->level; - ml->level = newlevel; - } else { - ml = ast_calloc(1, sizeof(*ml) + strlen(mod) + 1); - if (!ml) { - AST_RWLIST_UNLOCK(mll); - return CLI_FAILURE; + } else if ((a->pos == 4 && !atleast && strcasecmp(argv3, "off")) + || (a->pos == 5 && atleast)) { + const char *pos = S_OR(a->argv[a->pos], ""); + + if (a->n == 0 && !strncasecmp(pos, "silent", strlen(pos))) { + return ast_strdup("silent"); } - oldval = ml->level; - ml->level = newlevel; - strcpy(ml->module, mod); - AST_RWLIST_INSERT_TAIL(mll, ml, entry); } + return NULL; + } + /* all the above return, so we proceed with the handler. + * we are guaranteed to be called with argc >= e->args; + */ - ast_set_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE); + if (a->argc <= e->args) { + return CLI_SHOWUSAGE; + } - AST_RWLIST_UNLOCK(mll); + if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args], "off")) { + newlevel = 0; + } else { + if (!strcasecmp(a->argv[e->args], "atleast")) { + atleast = 1; + } + if (a->argc == e->args + atleast + 2 + && !strcasecmp(a->argv[e->args + atleast + 1], "silent")) { + silent = 1; + } + if (a->argc != e->args + atleast + silent + 1) { + return CLI_SHOWUSAGE; + } + if (sscanf(a->argv[e->args + atleast], "%30d", &newlevel) != 1) { + return CLI_SHOWUSAGE; + } + } - ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, ml->level, ml->module); + /* Update verbose level */ + oldval = ast_verb_console_get(); + if (!atleast || newlevel > oldval) { + ast_verb_console_set(newlevel); + } else { + newlevel = oldval; + } + if (silent) { + /* Be silent after setting the level. */ return CLI_SUCCESS; - } else if (!newlevel) { - /* Specified level as 0 instead of off. */ - mll = is_debug ? &debug_modules : &verbose_modules; - - AST_RWLIST_WRLOCK(mll); - while ((ml = AST_RWLIST_REMOVE_HEAD(mll, entry))) { - ast_free(ml); - } - ast_clear_flag(&ast_options, is_debug ? AST_OPT_FLAG_DEBUG_MODULE : AST_OPT_FLAG_VERBOSE_MODULE); - AST_RWLIST_UNLOCK(mll); } -done: - if (!atleast || newlevel > *dst) - *dst = newlevel; - if (oldval > 0 && *dst == 0) - ast_cli(fd, "%s is now OFF\n", what); - else if (*dst > 0) { - if (oldval == *dst) - ast_cli(fd, "%s is at least %d\n", what, *dst); - else - ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst); - } + /* Report verbose level status */ + status_debug_verbose(a, "Console verbose", oldval, newlevel); return CLI_SUCCESS; } @@ -1684,7 +1786,8 @@ static struct ast_cli_entry cli_cli[] = { AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel"), - AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"), + AST_CLI_DEFINE(handle_debug, "Set level of debug chattiness"), + AST_CLI_DEFINE(handle_verbose, "Set level of verbose chattiness"), AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"), diff --git a/main/logger.c b/main/logger.c index 52bb5c021..3ecf2fcef 100644 --- a/main/logger.c +++ b/main/logger.c @@ -121,7 +121,7 @@ struct logchannel { int disabled; /*! syslog facility */ int facility; - /*! Verbosity level */ + /*! Verbosity level. (-1 if use option_verbose for the level.) */ int verbosity; /*! Type of log channel */ enum logtypes type; @@ -240,32 +240,48 @@ AST_THREADSTORAGE(log_buf); static void logger_queue_init(void); -static unsigned int make_components(const char *s, int lineno, int *verbosity) +static void make_components(struct logchannel *chan) { char *w; - unsigned int res = 0; - char *stringp = ast_strdupa(s); + unsigned int logmask = 0; + char *stringp = ast_strdupa(chan->components); unsigned int x; + int verb_level; - *verbosity = 3; + /* Default to using option_verbose as the verbosity level of the logging channel. */ + verb_level = -1; while ((w = strsep(&stringp, ","))) { - w = ast_skip_blanks(w); - + w = ast_strip(w); + if (ast_strlen_zero(w)) { + continue; + } if (!strcmp(w, "*")) { - res = 0xFFFFFFFF; - break; - } else if (!strncasecmp(w, "verbose(", 8) && sscanf(w + 8, "%d)", verbosity) == 1) { - res |= (1 << __LOG_VERBOSE); - } else for (x = 0; x < ARRAY_LEN(levels); x++) { - if (levels[x] && !strcasecmp(w, levels[x])) { - res |= (1 << x); - break; + logmask = 0xFFFFFFFF; + } else if (!strncasecmp(w, "verbose(", 8)) { + if (levels[__LOG_VERBOSE] && sscanf(w + 8, "%30u)", &verb_level) == 1) { + logmask |= (1 << __LOG_VERBOSE); + } + } else { + for (x = 0; x < ARRAY_LEN(levels); ++x) { + if (levels[x] && !strcasecmp(w, levels[x])) { + logmask |= (1 << x); + break; + } } } } - - return res; + if (chan->type == LOGTYPE_CONSOLE) { + /* + * Force to use the root console verbose level so if the + * user specified any verbose level then it does not interfere + * with calculating the ast_verb_sys_level value. + */ + chan->verbosity = -1; + } else { + chan->verbosity = verb_level; + } + chan->logmask = logmask; } static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno) @@ -335,7 +351,7 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo } chan->type = LOGTYPE_FILE; } - chan->logmask = make_components(chan->components, lineno, &chan->verbosity); + make_components(chan); return chan; } @@ -840,10 +856,12 @@ static int reload_logger(int rotate, const char *altconf) if (logfiles.queue_log) { res = logger_queue_restart(queue_rotate); AST_RWLIST_UNLOCK(&logchannels); + ast_verb_update(); ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", ""); ast_verb(1, "Asterisk Queue Logger restarted\n"); } else { AST_RWLIST_UNLOCK(&logchannels); + ast_verb_update(); } return res; @@ -1028,12 +1046,6 @@ static void ast_log_vsyslog(struct logmsg *msg) syslog(syslog_level, "%s", buf); } -/* These gymnastics are due to platforms which designate char as unsigned by - * default. Level is the negative character -- offset by 1, because \0 is the - * EOS delimiter. */ -#define VERBOSE_MAGIC2LEVEL(x) (((char) -*(signed char *) (x)) - 1) -#define VERBOSE_HASMAGIC(x) (*(signed char *) (x) < 0) - /*! \brief Print a normal log message to the channels */ static void logger_print_normal(struct logmsg *logmsg) { @@ -1044,7 +1056,9 @@ static void logger_print_normal(struct logmsg *logmsg) if (logmsg->level == __LOG_VERBOSE) { char *tmpmsg = ast_strdupa(logmsg->message + 1); + level = VERBOSE_MAGIC2LEVEL(logmsg->message); + /* Iterate through the list of verbosers and pass them the log message string */ AST_RWLIST_RDLOCK(&verbosers); AST_RWLIST_TRAVERSE(&verbosers, v, list) @@ -1070,7 +1084,8 @@ static void logger_print_normal(struct logmsg *logmsg) if (chan->disabled) { continue; } - if (logmsg->level == __LOG_VERBOSE && level > chan->verbosity) { + if (logmsg->level == __LOG_VERBOSE + && (((chan->verbosity < 0) ? option_verbose : chan->verbosity)) < level) { continue; } @@ -1246,6 +1261,7 @@ int init_logger(void) /* create log channels */ init_logger_chain(0 /* locked */, NULL); + ast_verb_update(); logger_initialized = 1; return 0; @@ -1707,6 +1723,148 @@ void ast_verbose(const char *fmt, ...) } } +/*! Console verbosity level node. */ +struct verb_console { + /*! List node link */ + AST_LIST_ENTRY(verb_console) node; + /*! Console verbosity level. */ + int *level; +}; + +/*! Registered console verbosity levels */ +static AST_RWLIST_HEAD_STATIC(verb_consoles, verb_console); + +/*! ast_verb_update() reentrancy protection lock. */ +AST_MUTEX_DEFINE_STATIC(verb_update_lock); + +void ast_verb_update(void) +{ + struct logchannel *log; + struct verb_console *console; + int verb_level; + + ast_mutex_lock(&verb_update_lock); + + AST_RWLIST_RDLOCK(&verb_consoles); + + /* Default to the root console verbosity. */ + verb_level = option_verbose; + + /* Determine max remote console level. */ + AST_LIST_TRAVERSE(&verb_consoles, console, node) { + if (verb_level < *console->level) { + verb_level = *console->level; + } + } + AST_RWLIST_UNLOCK(&verb_consoles); + + /* Determine max logger channel level. */ + AST_RWLIST_RDLOCK(&logchannels); + AST_RWLIST_TRAVERSE(&logchannels, log, list) { + if (verb_level < log->verbosity) { + verb_level = log->verbosity; + } + } + AST_RWLIST_UNLOCK(&logchannels); + + ast_verb_sys_level = verb_level; + + ast_mutex_unlock(&verb_update_lock); +} + +/*! + * \internal + * \brief Unregister a console verbose level. + * + * \param console Which console to unregister. + * + * \return Nothing + */ +static void verb_console_unregister(struct verb_console *console) +{ + AST_RWLIST_WRLOCK(&verb_consoles); + console = AST_RWLIST_REMOVE(&verb_consoles, console, node); + AST_RWLIST_UNLOCK(&verb_consoles); + if (console) { + ast_verb_update(); + } +} + +static void verb_console_free(void *v_console) +{ + struct verb_console *console = v_console; + + verb_console_unregister(console); + ast_free(console); +} + +/*! Thread specific console verbosity level node. */ +AST_THREADSTORAGE_CUSTOM(my_verb_console, NULL, verb_console_free); + +void ast_verb_console_register(int *level) +{ + struct verb_console *console; + + console = ast_threadstorage_get(&my_verb_console, sizeof(*console)); + if (!console || !level) { + return; + } + console->level = level; + + AST_RWLIST_WRLOCK(&verb_consoles); + AST_RWLIST_INSERT_HEAD(&verb_consoles, console, node); + AST_RWLIST_UNLOCK(&verb_consoles); + ast_verb_update(); +} + +void ast_verb_console_unregister(void) +{ + struct verb_console *console; + + console = ast_threadstorage_get(&my_verb_console, sizeof(*console)); + if (!console) { + return; + } + verb_console_unregister(console); +} + +int ast_verb_console_get(void) +{ + struct verb_console *console; + int verb_level; + + console = ast_threadstorage_get(&my_verb_console, sizeof(*console)); + AST_RWLIST_RDLOCK(&verb_consoles); + if (!console) { + verb_level = 0; + } else if (console->level) { + verb_level = *console->level; + } else { + verb_level = option_verbose; + } + AST_RWLIST_UNLOCK(&verb_consoles); + return verb_level; +} + +void ast_verb_console_set(int verb_level) +{ + struct verb_console *console; + + console = ast_threadstorage_get(&my_verb_console, sizeof(*console)); + if (!console) { + return; + } + + AST_RWLIST_WRLOCK(&verb_consoles); + if (console->level) { + *console->level = verb_level; + } else { + option_verbose = verb_level; + } + AST_RWLIST_UNLOCK(&verb_consoles); + ast_verb_update(); +} + int ast_register_verbose(void (*v)(const char *string)) { struct verb *verb; @@ -1750,7 +1908,7 @@ static void update_logchannels(void) global_logmask = 0; AST_RWLIST_TRAVERSE(&logchannels, cur, list) { - cur->logmask = make_components(cur->components, cur->lineno, &cur->verbosity); + make_components(cur); global_logmask |= cur->logmask; } diff --git a/main/manager.c b/main/manager.c index 15f10f7ed..0b6cffee5 100644 --- a/main/manager.c +++ b/main/manager.c @@ -1860,13 +1860,13 @@ static struct ast_manager_user *get_manager_by_name_locked(const char *name) * \param session manager session to get parameter from. * \return displayconnects config option value. */ -static int manager_displayconnects (struct mansession_session *session) +static int manager_displayconnects(struct mansession_session *session) { struct ast_manager_user *user = NULL; int ret = 0; AST_RWLIST_RDLOCK(&users); - if ((user = get_manager_by_name_locked (session->username))) { + if ((user = get_manager_by_name_locked(session->username))) { ret = user->displayconnects; } AST_RWLIST_UNLOCK(&users); @@ -5888,7 +5888,9 @@ static void purge_sessions(int n_max) while ((session = ao2_iterator_next(&i)) && n_max > 0) { ao2_lock(session); if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) { - if (session->authenticated && manager_displayconnects(session)) { + if (session->authenticated + && VERBOSITY_ATLEAST(2) + && manager_displayconnects(session)) { ast_verb(2, "HTTP Manager '%s' timed out from %s\n", session->username, ast_sockaddr_stringify_addr(&session->addr)); } diff --git a/main/pbx.c b/main/pbx.c index c5eb5f116..ae857e9b3 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -4886,7 +4886,7 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con, pbx_substitute_variables_helper(c, substitute, passdata, sizeof(passdata)-1); } ast_debug(1, "Launching '%s'\n", app->name); - { + if (VERBOSITY_ATLEAST(3)) { ast_verb(3, "Executing [%s@%s:%d] " COLORIZE_FMT "(\"" COLORIZE_FMT "\", \"" COLORIZE_FMT "\") %s\n", exten, context, priority, COLORIZE(COLOR_BRCYAN, 0, app->name), |