diff --git a/cmdline/snapraid.c b/cmdline/snapraid.c index 29c54c4d..5d85eadc 100644 --- a/cmdline/snapraid.c +++ b/cmdline/snapraid.c @@ -178,16 +178,18 @@ void log_open(const char* file) void log_close(const char* file) { - if (stdlog != stdout && stdlog != stderr && stdlog != 0) { - if (fclose(stdlog) != 0) { + FILE* log = stdlog; + + /* fclose() invalidates the stream even on error, so fatal reporting must use stderr. */ + stdlog = 0; + if (log != stdout && log != stderr && log != 0) { + if (fclose(log) != 0) { /* LCOV_EXCL_START */ log_fatal(errno, "Error closing the log file '%s'. %s.\n", file, strerror(errno)); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } } - - stdlog = 0; } /****************************************************************************/ @@ -1065,6 +1067,8 @@ int snapraid_main(int argc, char* argv[]) } } + msg_fatal_stderr = !opt.gui; + os_init(OS_INIT_OPT_AVOID_SLEEP | (opt.force_scan_winfind ? OS_INIT_OPT_WINFIND : 0)); app_init(); raid_init(); diff --git a/cmdline/support.c b/cmdline/support.c index dd5fe9e4..bc7fe19d 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -293,6 +293,7 @@ void log_error_errno(int err, const char* name) /* print */ int msg_level = 0; +int msg_fatal_stderr = 1; FILE* stdlog = 0; int msg_line_prev = 0; /**< Previous line width on stdout */ int msg_line_curr = 0; /**< Current line width on stdout */ @@ -374,34 +375,41 @@ static void msg(FILE* out, const char* format, ...) void vlog_fatal(int err, const char* format, va_list ap, const char* post) { + int log_failed = 0; + lock_msg(); if (stdlog) { if (is_hw(err)) - fprintf(stdlog, "msg:fatal_hardware: "); + log_failed = fprintf(stdlog, "msg:fatal_hardware: ") < 0; else - fprintf(stdlog, "msg:fatal: "); + log_failed = fprintf(stdlog, "msg:fatal: ") < 0; va_list ap_copy; va_copy(ap_copy, ap); - vfprintf(stdlog, format, ap_copy); + if (vfprintf(stdlog, format, ap_copy) < 0) + log_failed = 1; va_end(ap_copy); - if (post) - fprintf(stdlog, "%s", post); + if (post && fprintf(stdlog, "%s", post) < 0) + log_failed = 1; - fflush(stdlog); + if (fflush(stdlog) != 0) + log_failed = 1; } - va_list ap_copy2; - va_copy(ap_copy2, ap); - vmsg(stderr, format, ap_copy2); - va_end(ap_copy2); + /* A failed tagged write must retain stderr as the only usable fatal diagnostic. */ + if (!stdlog || log_failed || msg_fatal_stderr) { + va_list ap_copy2; + va_copy(ap_copy2, ap); + vmsg(stderr, format, ap_copy2); + va_end(ap_copy2); - if (post) - msg(stderr, "%s", post); + if (post) + msg(stderr, "%s", post); - fflush(stderr); + fflush(stderr); + } unlock_msg(); } diff --git a/cmdline/support.h b/cmdline/support.h index e997b7e0..98af27cd 100644 --- a/cmdline/support.h +++ b/cmdline/support.h @@ -92,7 +92,8 @@ typedef void log_ptr(int err, const char* format, ...) __attribute__((format(att * * Messages printed before an early termination. * - * These messages go in the log file and in stderr unconditionally. + * These messages go in the log file and in stderr, except in GUI mode + * when the tagged log message is emitted successfully. */ void vlog_fatal(int err, const char* format, va_list ap, const char* post); void log_fatal(int err, const char* format, ...) __attribute__((format(attribute_printf, 2, 3))); @@ -176,6 +177,11 @@ typedef void msg_ptr(const char* format, ...) __attribute__((format(attribute_pr */ extern int msg_level; +/** + * Emit fatal messages on stderr also when a log is configured. + */ +extern int msg_fatal_stderr; + /** * State messages. * diff --git a/doc/snapraid_log.1 b/doc/snapraid_log.1 index 45f50b99..928e7a01 100644 --- a/doc/snapraid_log.1 +++ b/doc/snapraid_log.1 @@ -70,6 +70,11 @@ is escaped to \\\\. Some tags are output only when the \`\-\-gui\` or \`\-\-gui\-verbose\` option is used. They provide extra verbose state and runtime progress only useful for a GUI interface. +In GUI mode, the structured log is intended to be consumed by the GUI. +Fatal errors are therefore not duplicated to standard error when they +are successfully written to the log. If the log is unavailable or +writing it fails, the fatal error is printed to standard error as a +fallback. .PP Integer values are marked as \`uint\` when they are unsigned and as \`int\` when they are signed. However, for practical purposes you can treat diff --git a/doc/snapraid_log.d b/doc/snapraid_log.d index 34933866..03bebdb8 100644 --- a/doc/snapraid_log.d +++ b/doc/snapraid_log.d @@ -50,6 +50,11 @@ Description Some tags are output only when the `--gui` or `--gui-verbose` option is used. They provide extra verbose state and runtime progress only useful for a GUI interface. + In GUI mode, the structured log is intended to be consumed by the GUI. + Fatal errors are therefore not duplicated to standard error when they + are successfully written to the log. If the log is unavailable or + writing it fails, the fatal error is printed to standard error as a + fallback. Integer values are marked as `uint` when they are unsigned and as `int` when they are signed. However, for practical purposes you can treat diff --git a/doc/snapraid_log.txt b/doc/snapraid_log.txt index dd6b2152..d2af6444 100644 --- a/doc/snapraid_log.txt +++ b/doc/snapraid_log.txt @@ -54,6 +54,11 @@ The following characters are escaped into sequences: Some tags are output only when the `--gui` or `--gui-verbose` option is used. They provide extra verbose state and runtime progress only useful for a GUI interface. +In GUI mode, the structured log is intended to be consumed by the GUI. +Fatal errors are therefore not duplicated to standard error when they +are successfully written to the log. If the log is unavailable or +writing it fails, the fatal error is printed to standard error as a +fallback. Integer values are marked as `uint` when they are unsigned and as `int` when they are signed. However, for practical purposes you can treat