diff --git a/configs/samples/logger.conf.sample b/configs/samples/logger.conf.sample index 0fa7dcc..d6bc8e7 100644 --- a/configs/samples/logger.conf.sample +++ b/configs/samples/logger.conf.sample @@ -68,6 +68,19 @@ ; ; exec_after_rotate=gzip -9 ${filename}.2 ; +; The exec_after_rotate command allows use of the Asterisk +; Expression parser to generate the full command-line to run. +; Using this option causes Asterisk to delay logging to files +; until the system is fully booted, preventing startup logging. +; The post_rotate_exec option can be used instead to allow full +; initialization of the logger system very early in startup. +; post_rotate_exec allows shell variable substitution to be used +; for ${filename}. This is the preferred method of running an +; external command after rotation, if set any exec_after_rotate +; setting is ignored. +; +; post_rotate_exec=gzip -9 ${filename}.2 +; ; ; For each file, specify what to log. ; diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h index d49de17..b9b3b00 100644 --- a/include/asterisk/_private.h +++ b/include/asterisk/_private.h @@ -17,7 +17,7 @@ int load_modules(unsigned int); /*!< Provided by loader.c */ int load_pbx(void); /*!< Provided by pbx.c */ -int init_logger(void); /*!< Provided by logger.c */ +int init_logger(int preload); /*!< Provided by logger.c */ void close_logger(void); /*!< Provided by logger.c */ void logger_queue_start(void); /*!< Provided by logger.c */ void clean_time_zones(void); /*!< Provided by localtime.c */ diff --git a/include/asterisk/app.h b/include/asterisk/app.h index 3975fda..fccb29c 100644 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -869,12 +869,23 @@ int ast_vm_test_destroy_user(const char *context, const char *mailbox); int ast_vm_test_create_user(const char *context, const char *mailbox); #endif -/*! \brief Safely spawn an external program while closing file descriptors - \note This replaces the \b system call in all Asterisk modules +/*! + * \brief Safely spawn an external program while closing file descriptors + * \note This replaces the \b system call in all Asterisk modules */ int ast_safe_system(const char *s); /*! + * \brief Safely spawn an external program while closing file descriptors + * + * \param s Commmand to execute with /bin/sh -c + * \param envp Custom environment to use for the command + * + * \note execle is used if envp is not NULL, otherwise execl is used. +*/ +int ast_safe_systeme(const char *s, char * const envp[]); + +/*! * \brief Replace the SIGCHLD handler * * Normally, Asterisk has a SIGCHLD handler that is cleaning up all zombie diff --git a/main/asterisk.c b/main/asterisk.c index 53bcead..c247d04 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -1147,7 +1147,7 @@ void ast_unreplace_sigchld(void) ast_mutex_unlock(&safe_system_lock); } -int ast_safe_system(const char *s) +int ast_safe_systeme(const char *s, char * const envp[]) { pid_t pid; int res; @@ -1178,7 +1178,11 @@ int ast_safe_system(const char *s) /* Close file descriptors and launch system command */ ast_close_fds_above_n(STDERR_FILENO); #endif - execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL); + if (envp) { + execle("/bin/sh", "/bin/sh", "-c", s, (char *) NULL, envp); + } else { + execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL); + } _exit(1); } else if (pid > 0) { for (;;) { @@ -1202,6 +1206,11 @@ int ast_safe_system(const char *s) return res; } +int ast_safe_system(const char *s) +{ + return ast_safe_systeme(s, NULL); +} + /*! * \brief enable or disable a logging level to a specified console */ @@ -4293,6 +4302,40 @@ int main(int argc, char *argv[]) print_intro_message(runuser, rungroup); + /* GCC 4.9 gives a bogus "right-hand operand of comma expression has + * no effect" warning */ + (void) sigemptyset(&sigs); + (void) sigaddset(&sigs, SIGHUP); + (void) sigaddset(&sigs, SIGTERM); + (void) sigaddset(&sigs, SIGINT); + (void) sigaddset(&sigs, SIGPIPE); + (void) sigaddset(&sigs, SIGWINCH); + pthread_sigmask(SIG_BLOCK, &sigs, NULL); + sigaction(SIGURG, &urg_handler, NULL); + signal(SIGINT, __quit_handler); + signal(SIGTERM, __quit_handler); + sigaction(SIGHUP, &hup_handler, NULL); + sigaction(SIGPIPE, &ignore_sig_handler, NULL); + + /* ensure that the random number generators are seeded with a different value every time + Asterisk is started + */ + srand((unsigned int) getpid() + (unsigned int) time(NULL)); + initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool)); + + threadstorage_init(); + + if (ast_utils_init()) { + printf("Failed: ast_utils_init\n%s", term_quit()); + exit(1); + } + + /* Early logging subsystem startup */ + if (init_logger(1)) { + printf("Failed: init_logger(1)\n%s", term_quit()); + exit(1); + } + if (ast_opt_console) { ast_verb(0, "[ Initializing Custom Configuration Options ]\n"); } @@ -4320,11 +4363,6 @@ int main(int argc, char *argv[]) callerid_init(); ast_builtins_init(); - if (ast_utils_init()) { - printf("Failed: ast_utils_init\n%s", term_quit()); - exit(1); - } - if (ast_tps_init()) { printf("Failed: ast_tps_init\n%s", term_quit()); exit(1); @@ -4408,33 +4446,6 @@ int main(int argc, char *argv[]) } ast_makesocket(); - /* GCC 4.9 gives a bogus "right-hand operand of comma expression has - * no effect" warning */ - (void) sigemptyset(&sigs); - (void) sigaddset(&sigs, SIGHUP); - (void) sigaddset(&sigs, SIGTERM); - (void) sigaddset(&sigs, SIGINT); - (void) sigaddset(&sigs, SIGPIPE); - (void) sigaddset(&sigs, SIGWINCH); - pthread_sigmask(SIG_BLOCK, &sigs, NULL); - sigaction(SIGURG, &urg_handler, NULL); - signal(SIGINT, __quit_handler); - signal(SIGTERM, __quit_handler); - sigaction(SIGHUP, &hup_handler, NULL); - sigaction(SIGPIPE, &ignore_sig_handler, NULL); - - /* ensure that the random number generators are seeded with a different value every time - Asterisk is started - */ - srand((unsigned int) getpid() + (unsigned int) time(NULL)); - initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool)); - - if (init_logger()) { /* Start logging subsystem */ - printf("Failed: init_logger\n%s", term_quit()); - exit(1); - } - - threadstorage_init(); if (ast_rtp_engine_init()) { printf("Failed: ast_rtp_engine_init\n%s", term_quit()); @@ -4601,6 +4612,12 @@ int main(int argc, char *argv[]) exit(moduleresult == -2 ? 2 : 1); } + /* Finish logger initialization incase exec_after_rotate is used. */ + if (init_logger(0)) { + printf("Failed: init_logger(0)\n%s", term_quit()); + exit(1); + } + /* loads the cli_permissoins.conf file needed to implement cli restrictions. */ ast_cli_perms_init(0); diff --git a/main/logger.c b/main/logger.c index bdcd6c4..ad4cbce 100644 --- a/main/logger.c +++ b/main/logger.c @@ -77,11 +77,13 @@ static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */ static char queue_log_name[256] = QUEUELOG; static char exec_after_rotate[256] = ""; +static char post_rotate_exec[256] = ""; static int filesize_reload_needed; static unsigned int global_logmask = 0xFFFF; static int queuelog_init; static int logger_initialized; +static int final_load; static volatile int next_unique_callid = 1; /* Used to assign unique call_ids to calls */ static int display_callids; @@ -367,15 +369,16 @@ static struct logchannel *make_logchannel(const char *channel, const char *compo * \retval 0 Success * \retval -1 No config found or Failed */ -static int init_logger_chain(int locked, const char *altconf) +static int init_logger_chain(const char *altconf) { struct logchannel *chan; struct ast_config *cfg; struct ast_variable *var; - const char *s; + const char *s = NULL; struct ast_flags config_flags = { 0 }; - if (!(cfg = ast_config_load2(S_OR(altconf, "logger.conf"), "logger", config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) { + cfg = ast_config_load2(S_OR(altconf, "logger.conf"), "logger", config_flags); + if (cfg == CONFIG_STATUS_FILEINVALID) { cfg = NULL; } @@ -398,33 +401,51 @@ static int init_logger_chain(int locked, const char *altconf) ast_free(chan); } global_logmask = 0; - if (!locked) { - AST_RWLIST_UNLOCK(&logchannels); - } errno = 0; /* close syslog */ closelog(); + /* Defer full startup if exec_after_rotate is used. */ + if (cfg) { + /* Only one or the other is used, prefer post_rotate_exec. */ + if (ast_strlen_zero(ast_variable_retrieve(cfg, "general", "post_rotate_exec"))) { + s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"); + } + if (!ast_strlen_zero(s)) { + if (!final_load) { + /* exec_after_rotate can't be used before final load, + * use default console logging for now. */ + ast_config_destroy(cfg); + cfg = NULL; + } else { + ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate)); + } + } + } + /* If no config file, we're fine, set default options. */ if (!cfg) { - if (!(chan = ast_calloc(1, sizeof(*chan)))) { + chan = ast_calloc(1, sizeof(*chan)); + if (!chan) { fprintf(stderr, "Failed to initialize default logging\n"); - return -1; + goto noconfig_return; } chan->type = LOGTYPE_CONSOLE; chan->logmask = __LOG_WARNING | __LOG_NOTICE | __LOG_ERROR; - if (!locked) { - AST_RWLIST_WRLOCK(&logchannels); - } AST_RWLIST_INSERT_HEAD(&logchannels, chan, list); global_logmask |= chan->logmask; + logger_initialized = 1; + +noconfig_return: if (!locked) { AST_RWLIST_UNLOCK(&logchannels); } - return -1; + /* If s is assigned then we're here because exec_after_rotate was found during + * preload, so don't display an error unless we failed to allocate chan. */ + return chan && s ? 0 : -1; } if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) { @@ -453,8 +474,8 @@ static int init_logger_chain(int locked, const char *altconf) if ((s = ast_variable_retrieve(cfg, "general", "queue_log_realtime_use_gmt"))) { logfiles.queue_log_realtime_use_gmt = ast_true(s); } - if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate"))) { - ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate)); + if ((s = ast_variable_retrieve(cfg, "general", "post_rotate_exec"))) { + ast_copy_string(post_rotate_exec, s, sizeof(post_rotate_exec)); } if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) { if (strcasecmp(s, "timestamp") == 0) { @@ -475,9 +496,6 @@ static int init_logger_chain(int locked, const char *altconf) } } - if (!locked) { - AST_RWLIST_WRLOCK(&logchannels); - } var = ast_variable_browse(cfg, "logfiles"); for (; var; var = var->next) { if (!(chan = make_logchannel(var->name, var->value, var->lineno, 0))) { @@ -502,6 +520,7 @@ static int init_logger_chain(int locked, const char *altconf) } ast_config_destroy(cfg); + logger_initialized = 1; return 0; } @@ -712,7 +731,16 @@ static int rotate_file(const char *filename) } } - if (!ast_strlen_zero(exec_after_rotate)) { + if (!ast_strlen_zero(post_rotate_exec)) { + char buf[PATH_MAX]; + char *envp[] = { buf, (char *)0 }; + + if (snprintf(buf, sizeof(buf), "filename=%s", filename) > sizeof(buf)) { + ast_log(LOG_ERROR, "Failed to initialize environment for post_rotate_exec. Command not run.\n"); + } else if (ast_safe_systeme(post_rotate_exec, envp) == -1) { + ast_log(LOG_ERROR, "Error executing '%s'\n", buf); + } + } else if (!ast_strlen_zero(exec_after_rotate)) { struct ast_channel *c = ast_dummy_channel_alloc(); char buf[512]; @@ -722,9 +750,10 @@ static int rotate_file(const char *filename) c = ast_channel_unref(c); } if (ast_safe_system(buf) == -1) { - ast_log(LOG_WARNING, "error executing '%s'\n", buf); + ast_log(LOG_ERROR, "Error executing '%s'\n", buf); } } + return res; } @@ -855,7 +884,7 @@ static int reload_logger(int rotate, const char *altconf) filesize_reload_needed = 0; - init_logger_chain(1 /* locked */, altconf); + init_logger_chain(altconf); ast_unload_realtime("queue_log"); if (logfiles.queue_log) { @@ -1413,39 +1442,57 @@ void logger_queue_start(void) } } -int init_logger(void) +int init_logger(int preload) { - int res; - /* auto rotate if sig SIGXFSZ comes a-knockin */ - sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL); - - /* Re-initialize the logmsgs mutex. The recursive mutex can be accessed prior - * to Asterisk being forked into the background, which can cause the thread - * ID tracked by the underlying pthread mutex to be different than the ID of - * the thread that unlocks the mutex. Since init_logger is called after the - * fork, it is safe to initialize the mutex here for future accesses. - */ - ast_mutex_destroy(&logmsgs.lock); - ast_mutex_init(&logmsgs.lock); - ast_cond_init(&logcond, NULL); - - /* start logger thread */ - if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) { - ast_cond_destroy(&logcond); - return -1; + AST_RWLIST_WRLOCK(&logchannels); + + if (preload) { + /* auto rotate if sig SIGXFSZ comes a-knockin */ + sigaction(SIGXFSZ, &handle_SIGXFSZ, NULL); + + /* Re-initialize the logmsgs mutex. The recursive mutex can be accessed prior + * to Asterisk being forked into the background, which can cause the thread + * ID tracked by the underlying pthread mutex to be different than the ID of + * the thread that unlocks the mutex. Since init_logger is called after the + * fork, it is safe to initialize the mutex here for future accesses. + */ + ast_mutex_destroy(&logmsgs.lock); + ast_mutex_init(&logmsgs.lock); + ast_cond_init(&logcond, NULL); + + ast_mkdir(ast_config_AST_LOG_DIR, 0777); + + /* start logger thread */ + if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) { + ast_cond_destroy(&logcond); + AST_RWLIST_UNLOCK(&logchannels); + + return -1; + } + } else { + final_load = 1; } - /* register the logger cli commands */ - ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger)); + if (!logger_initialized) { + int res; - ast_mkdir(ast_config_AST_LOG_DIR, 0777); + /* create log channels */ + res = init_logger_chain(NULL); + AST_RWLIST_UNLOCK(&logchannels); - /* create log channels */ - res = init_logger_chain(0 /* locked */, NULL); - ast_verb_update(); - logger_initialized = 1; - if (res) { - ast_log(LOG_ERROR, "Errors detected in logger.conf. Default console logging is being used.\n"); + ast_verb_update(); + if (!res) { + /* register the logger cli commands */ + ast_cli_register_multiple(cli_logger, ARRAY_LEN(cli_logger)); + } else { + ast_log(LOG_ERROR, "Errors detected in logger.conf. Default console logging is being used.\n"); + } + if (!ast_strlen_zero(exec_after_rotate)) { + ast_log(LOG_WARNING, "The exec_after_rotate option is in use, delaying full logging startup\n"); + ast_log(LOG_WARNING, "until the system is fully booted. Use post_rotate_exec instead if possible.\n"); + } + } else { + AST_RWLIST_UNLOCK(&logchannels); } return 0;