diff --git a/src/include/libradius.h b/src/include/libradius.h index 22d67f237993..8f858d0f1b5e 100644 --- a/src/include/libradius.h +++ b/src/include/libradius.h @@ -758,6 +758,17 @@ void fr_cbuff_rp_insert(fr_cbuff_t *cbuff, void *obj); void *fr_cbuff_rp_next(fr_cbuff_t *cbuff, TALLOC_CTX *ctx); /* debug.c */ + +/** Optional callback passed to fr_fault_setup + * + * Allows optional logic to be run before calling the main fault handler. + * + * If the callback returns < 0, the main fault handler will not be called. + * + * @param signum signal raised. + * @return 0 on success < 0 on failure. + */ +typedef int (*fr_fault_cb)(int signum); typedef struct fr_bt_marker fr_bt_marker_t; void fr_debug_break(void); @@ -765,6 +776,7 @@ void backtrace_print(fr_cbuff_t *cbuff, void *obj); fr_bt_marker_t *fr_backtrace_attach(fr_cbuff_t **cbuff, TALLOC_CTX *obj); void NEVER_RETURNS fr_fault(int sig); int fr_fault_setup(char const *cmd, char const *program); +void fr_fault_set_cb(fr_fault_cb cb); /* rbtree.c */ typedef struct rbtree_t rbtree_t; diff --git a/src/include/log.h b/src/include/log.h index 3cd01ffa4d2c..2c63dbde37d4 100644 --- a/src/include/log.h +++ b/src/include/log.h @@ -66,7 +66,7 @@ typedef struct fr_log_t { int colourise; //!< Prefix log messages with VT100 escape codes to change text //!< colour. int fd; //!< File descriptor to write messages to. - log_dst_t dest; //!< Log destination. + log_dst_t dst; //!< Log destination. char *file; //!< Path to log file. char *debug_file; //!< Path to debug log file. } fr_log_t; @@ -77,6 +77,10 @@ extern FR_NAME_NUMBER const syslog_str2fac[]; extern FR_NAME_NUMBER const log_str2dst[]; extern fr_log_t default_log; +bool radlog_std_restore(void); +bool radlog_std_to_log(fr_log_t *log); +int radlog_init(fr_log_t *log, bool close_std); + int vradlog(log_type_t lvl, char const *fmt, va_list ap) #ifdef __GNUC__ __attribute__ ((format (printf, 2, 0))) diff --git a/src/lib/debug.c b/src/lib/debug.c index 55d32523c64c..982bc47d4c8b 100644 --- a/src/lib/debug.c +++ b/src/lib/debug.c @@ -61,6 +61,7 @@ struct fr_bt_marker { #endif static char panic_action[512]; +static fr_fault_cb panic_cb; static int fr_debugger_present = -1; /** Stub callback to see if the SIGTRAP handler is overriden @@ -73,12 +74,12 @@ static void _sigtrap_handler(UNUSED int signum) signal(SIGTRAP, SIG_DFL); } -/** Break in GDB (if were running under GDB) +/** Break in debugger (if were running under a debugger) * - * If the server is running under GDB this will raise a SIGTRAP which - * will pause the running process. + * If the server is running under a debugger this will raise a + * SIGTRAP which will pause the running process. * - * If the server is not running under GDB then this will do nothing. + * If the server is not running under debugger then this will do nothing. */ void fr_debug_break(void) { @@ -237,6 +238,11 @@ void NEVER_RETURNS fr_fault(int sig) fprintf(stderr, "FATAL SIGNAL: %s\n", strsignal(sig)); + /* + * Run the callback if one was registered + */ + if (panic_cb && (panic_cb(sig) < 0)) fr_exit_now(1); + /* * Produce a simple backtrace - They've very basic but at least give us an * idea of the area of the code we hit the issue in. @@ -353,3 +359,12 @@ int fr_fault_setup(char const *cmd, char const *program) return 0; } +/** Set a callback to be called before fr_fault() + * + * @param cb to execute. If callback returns < 0 + * fr_fault will exit before running panic_action code. + */ +void fr_fault_set_cb(fr_fault_cb cb) +{ + panic_cb = cb; +}; diff --git a/src/main/command.c b/src/main/command.c index fb64b5588469..8363c79438d5 100644 --- a/src/main/command.c +++ b/src/main/command.c @@ -810,7 +810,7 @@ static char debug_log_file_buffer[1024]; static int command_debug_file(rad_listen_t *listener, int argc, char *argv[]) { - if (debug_flag && default_log.dest == L_DST_STDOUT) { + if (debug_flag && default_log.dst == L_DST_STDOUT) { cprintf(listener, "ERROR: Cannot redirect debug logs to a file when already in debugging mode.\n"); return -1; } diff --git a/src/main/log.c b/src/main/log.c index cd83e7f0f13b..853f8aa6d658 100644 --- a/src/main/log.c +++ b/src/main/log.c @@ -36,6 +36,8 @@ RCSID("$Id$") # include #endif +#include + /* * Logging facility names */ @@ -152,11 +154,138 @@ bool log_dates_utc = false; fr_log_t default_log = { .colourise = true, .fd = STDOUT_FILENO, - .dest = L_DST_STDOUT, + .dst = L_DST_STDOUT, .file = NULL, .debug_file = NULL, }; +static int stderr_fd = -1; //!< The original unmolested stderr file descriptor +static int stdout_fd = -1; //!< The original unmolested stdout file descriptor + +/** Restore the original stderr/stdout file descriptors + * + * Only effective if radlog_init was called with close_stdout = false. + * + * @return true if stdout/stderr have been restored, else false. + */ +bool radlog_std_restore(void) +{ + if ((stderr_fd > 0) && (stdout_fd > 0)) { + dup2(stderr_fd, STDOUT_FILENO); + dup2(stdout_fd, STDERR_FILENO); + return true; + } + return false; +} + +/** Set stderr/stdout to the current log descriptor. + * + * @param log Logger to manipulate. + * @return true if stdout/stderr have been altered, else false. + */ +bool radlog_std_to_log(fr_log_t *log) +{ + if (log->fd > 0) { + dup2(log->fd, STDOUT_FILENO); + dup2(log->fd, STDERR_FILENO); + return true; + } + return false; +} + +/** Initialise file descriptors based on logging destination + * + * @param log Logger to manipulate. + * @param close_std Whether we should close stderr and stdout completely, or just redirect them. + * @return 0 on success -1 on failure. + */ +int radlog_init(fr_log_t *log, bool close_std) +{ + int devnull; + + devnull = open("/dev/null", O_RDWR); + if (devnull < 0) { + fr_strerror_printf("Error opening /dev/null: %s", fr_syserror(errno)); + return -1; + } + + /* + * Dup the original stdout/stderr file descriptors so + * we can restore them later. + */ + if (!close_std) { + stderr_fd = dup(STDERR_FILENO); + stdout_fd = dup(STDOUT_FILENO); + } + + /* + * STDOUT & STDERR go to /dev/null, unless we have "-x", + * then STDOUT & STDERR go to the "-l log" destination. + * + * The complexity here is because "-l log" can go to + * STDOUT or STDERR, too. + */ + if (log->dst == L_DST_STDOUT) { + setlinebuf(stdout); + log->fd = STDOUT_FILENO; + + /* + * If we're debugging, allow STDERR to go to + * STDOUT too, for executed programs, + */ + if (debug_flag) { + dup2(STDOUT_FILENO, STDERR_FILENO); + } else { + dup2(devnull, STDERR_FILENO); + } + + } else if (log->dst == L_DST_STDERR) { + setlinebuf(stderr); + log->fd = STDERR_FILENO; + + /* + * If we're debugging, allow STDOUT to go to + * STDERR too, for executed programs, + */ + if (debug_flag) { + dup2(STDERR_FILENO, STDOUT_FILENO); + } else { + dup2(devnull, STDOUT_FILENO); + } + + } else if (log->dst == L_DST_SYSLOG) { + /* + * Discard STDOUT and STDERR no matter what the + * status of debugging. Syslog isn't a file + * descriptor, so we can't use it. + */ + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + + } else if (debug_flag) { + /* + * If we're debugging, allow STDOUT and STDERR to + * go to the log file. + */ + radlog_std_to_log(log); + + } else { + /* + * Not debugging, and the log isn't STDOUT or + * STDERR. Ensure that we move both of them to + * /dev/null, so that the calling terminal can + * exit, and the output from executed programs + * doesn't pollute STDOUT / STDERR. + */ + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + } + + close(devnull); + + return 0; +} + /* * Log the message to the logfile. Include the severity and * a time stamp. @@ -182,7 +311,7 @@ int vradlog(log_type_t type, char const *fmt, va_list ap) * If we don't want any messages, then * throw them away. */ - if (default_log.dest == L_DST_NULL) { + if (default_log.dst == L_DST_NULL) { return 0; } @@ -209,7 +338,7 @@ int vradlog(log_type_t type, char const *fmt, va_list ap) * Print timestamps for non-debugging, and for high levels * of debugging. */ - if (default_log.dest != L_DST_SYSLOG) { + if (default_log.dst != L_DST_SYSLOG) { if ((debug_flag != 1) && (debug_flag != 2)) { time_t timeval; @@ -262,7 +391,7 @@ int vradlog(log_type_t type, char const *fmt, va_list ap) buffer[sizeof(buffer) - 1] = '\0'; } - switch (default_log.dest) { + switch (default_log.dst) { #ifdef HAVE_SYSLOG_H case L_DST_SYSLOG: diff --git a/src/main/mainconfig.c b/src/main/mainconfig.c index a14431e1c0d8..ecd6c187d180 100644 --- a/src/main/mainconfig.c +++ b/src/main/mainconfig.c @@ -690,7 +690,7 @@ static int switch_users(CONF_SECTION *cs) * specified on the command-line. */ if (uid_name || gid_name) { - if ((default_log.dest == L_DST_FILES) && + if ((default_log.dst == L_DST_FILES) && (default_log.fd < 0)) { default_log.fd = open(mainconfig.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); @@ -860,7 +860,7 @@ int read_mainconfig(int reload) * If there was no log destination set on the command line, * set it now. */ - if (default_log.dest == L_DST_NULL) { + if (default_log.dst == L_DST_NULL) { if (cf_section_parse(cs, NULL, serverdest_config) < 0) { fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n"); cf_file_free(cs); @@ -873,16 +873,16 @@ int read_mainconfig(int reload) return -1; } - default_log.dest = fr_str2int(log_str2dst, radlog_dest, + default_log.dst = fr_str2int(log_str2dst, radlog_dest, L_DST_NUM_DEST); - if (default_log.dest == L_DST_NUM_DEST) { + if (default_log.dst == L_DST_NUM_DEST) { fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n", radlog_dest); cf_file_free(cs); return -1; } - if (default_log.dest == L_DST_SYSLOG) { + if (default_log.dst == L_DST_SYSLOG) { /* * Make sure syslog_facility isn't NULL * before using it @@ -908,7 +908,7 @@ int read_mainconfig(int reload) openlog(progname, LOG_PID, mainconfig.syslog_facility); #endif - } else if (default_log.dest == L_DST_FILES) { + } else if (default_log.dst == L_DST_FILES) { if (!mainconfig.log_file) { fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n"); cf_file_free(cs); @@ -929,7 +929,7 @@ int read_mainconfig(int reload) * did switch uid/gid, then the code in switch_users() * took care of setting the file permissions correctly. */ - if ((default_log.dest == L_DST_FILES) && + if ((default_log.dst == L_DST_FILES) && (default_log.fd < 0)) { default_log.fd = open(mainconfig.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); @@ -1068,7 +1068,7 @@ void hup_logfile(void) { int fd, old_fd; - if (default_log.dest != L_DST_FILES) return; + if (default_log.dst != L_DST_FILES) return; fd = open(mainconfig.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); diff --git a/src/main/radiusd.c b/src/main/radiusd.c index 3923c451aee0..aa7a885de95c 100644 --- a/src/main/radiusd.c +++ b/src/main/radiusd.c @@ -92,6 +92,12 @@ static void die_horribly(char const *reason) } #endif +static int _restore_std(UNUSED int signal) +{ + if (!radlog_std_restore()) radlog_std_to_log(&default_log); + return 0; +} + /* * The main guy. */ @@ -106,8 +112,6 @@ int main(int argc, char *argv[]) int flag = 0; int from_child[2] = {-1, -1}; - int devnull; - /* * We probably don't want to free the talloc autofree context * directly, so we'll allocate a new context beneath it, and @@ -157,7 +161,7 @@ int main(int argc, char *argv[]) * Don't put output anywhere until we get told a little * more. */ - default_log.dest = L_DST_NULL; + default_log.dst = L_DST_NULL; default_log.fd = -1; mainconfig.log_file = NULL; @@ -192,7 +196,7 @@ int main(int argc, char *argv[]) goto do_stdout; } mainconfig.log_file = strdup(optarg); - default_log.dest = L_DST_FILES; + default_log.dst = L_DST_FILES; default_log.fd = open(mainconfig.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); if (default_log.fd < 0) { @@ -251,7 +255,7 @@ int main(int argc, char *argv[]) /* Don't print timestamps */ debug_flag += 2; fr_log_fp = stdout; - default_log.dest = L_DST_STDOUT; + default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; version(); @@ -265,7 +269,7 @@ int main(int argc, char *argv[]) mainconfig.log_auth_goodpass = true; do_stdout: fr_log_fp = stdout; - default_log.dest = L_DST_STDOUT; + default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; break; @@ -348,11 +352,6 @@ int main(int argc, char *argv[]) #ifndef __MINGW32__ - devnull = open("/dev/null", O_RDWR); - if (devnull < 0) { - ERROR("Failed opening /dev/null: %s", fr_syserror(errno)); - exit(EXIT_FAILURE); - } /* * Disconnect from session @@ -365,7 +364,16 @@ int main(int argc, char *argv[]) * system() later. */ if (dont_fork == false) { + int devnull; + + devnull = open("/dev/null", O_RDWR); + if (devnull < 0) { + ERROR("Failed opening /dev/null: %s", fr_syserror(errno)); + exit(EXIT_FAILURE); + } dup2(devnull, STDIN_FILENO); + + close(devnull); } if (pipe(from_child) != 0) { @@ -424,77 +432,25 @@ int main(int argc, char *argv[]) #endif /* - * Ensure that we're using the CORRECT pid after forking, - * NOT the one we started with. + * Ensure that we're using the CORRECT pid after forking, + * NOT the one we started with. */ radius_pid = getpid(); /* - * STDOUT & STDERR go to /dev/null, unless we have "-x", - * then STDOUT & STDERR go to the "-l log" destination. - * - * The complexity here is because "-l log" can go to - * STDOUT or STDERR, too. + * Restore stderr and stdout before calling panic_action + * if were running in foreground mode. */ - if (default_log.dest == L_DST_STDOUT) { - setlinebuf(stdout); - default_log.fd = STDOUT_FILENO; + if (!dont_fork) fr_fault_set_cb(_restore_std); - /* - * If we're debugging, allow STDERR to go to - * STDOUT too, for executed programs, - */ - if (debug_flag) { - dup2(STDOUT_FILENO, STDERR_FILENO); - } else { - dup2(devnull, STDERR_FILENO); - } - - } else if (default_log.dest == L_DST_STDERR) { - setlinebuf(stderr); - default_log.fd = STDERR_FILENO; - - /* - * If we're debugging, allow STDOUT to go to - * STDERR too, for executed programs, - */ - if (debug_flag) { - dup2(STDERR_FILENO, STDOUT_FILENO); - } else { - dup2(devnull, STDOUT_FILENO); - } - - } else if (default_log.dest == L_DST_SYSLOG) { - /* - * Discard STDOUT and STDERR no matter what the - * status of debugging. Syslog isn't a file - * descriptor, so we can't use it. - */ - dup2(devnull, STDOUT_FILENO); - dup2(devnull, STDERR_FILENO); - - } else if (debug_flag) { - /* - * If we're debugging, allow STDOUT and STDERR to - * go to the log file. - */ - dup2(default_log.fd, STDOUT_FILENO); - dup2(default_log.fd, STDERR_FILENO); - - } else { - /* - * Not debugging, and the log isn't STDOUT or - * STDERR. Ensure that we move both of them to - * /dev/null, so that the calling terminal can - * exit, and the output from executed programs - * doesn't pollute STDOUT / STDERR. - */ - dup2(devnull, STDOUT_FILENO); - dup2(devnull, STDERR_FILENO); + /* + * Redirect stderr/stdout as appropriate. + */ + if (radlog_init(&default_log, !dont_fork) < 0) { + ERROR("%s", fr_strerror()); + exit(EXIT_FAILURE); } - close(devnull); - /* * Start the event loop(s) and threads. */ diff --git a/src/main/unittest.c b/src/main/unittest.c index 77918b8ab494..6e7b80010b35 100644 --- a/src/main/unittest.c +++ b/src/main/unittest.c @@ -426,7 +426,7 @@ int main(int argc, char *argv[]) * We always log to stdout. */ fr_log_fp = stdout; - default_log.dest = L_DST_STDOUT; + default_log.dst = L_DST_STDOUT; default_log.fd = STDOUT_FILENO; /* Process the options. */ diff --git a/src/modules/rlm_unbound/rlm_unbound.c b/src/modules/rlm_unbound/rlm_unbound.c index 01082c86c84a..102a6916298d 100644 --- a/src/modules/rlm_unbound/rlm_unbound.c +++ b/src/modules/rlm_unbound/rlm_unbound.c @@ -472,7 +472,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) res = ub_ctx_debuglevel(inst->ub, dlevel); if (res) goto error; - switch(default_log.dest) { + switch(default_log.dst) { case L_DST_STDOUT: if (!debug_flag) { debug_method = 3;