diff options
Diffstat (limited to 'lib/log/log.c')
-rw-r--r-- | lib/log/log.c | 111 |
1 files changed, 102 insertions, 9 deletions
diff --git a/lib/log/log.c b/lib/log/log.c index 83e5be42c..bd1136393 100644 --- a/lib/log/log.c +++ b/lib/log/log.c @@ -18,10 +18,13 @@ #include "memlock.h" #include "defaults.h" +#include <stdio.h> #include <stdarg.h> #include <syslog.h> +#include <ctype.h> static FILE *_log_file; +static char _log_file_path[PATH_MAX]; static struct device _log_dev; static struct dm_str_list _log_dev_alias; @@ -33,7 +36,7 @@ static int _indent = 1; static int _log_suppress = 0; static char _msg_prefix[30] = " "; static int _already_logging = 0; -static int _abort_on_internal_errors = 0; +static int _abort_on_internal_errors_config = 0; static lvm2_log_fn_t _lvm2_log_fn = NULL; @@ -52,11 +55,59 @@ void init_log_fn(lvm2_log_fn_t log_fn) _lvm2_log_fn = NULL; } +/* + * Support envvar LVM_LOG_FILE_EPOCH and allow to attach + * extra keyword (consist of upto 32 alpha chars) to + * opened log file. After this 'epoch' word pid and starttime + * (in kernel units, read from /proc/self/stat) + * is automatically attached. + * If command/daemon forks multiple times, it could create multiple + * log files ensuring, there are no overwrites. + */ void init_log_file(const char *log_file, int append) { - const char *open_mode = append ? "a" : "w"; + static const char statfile[] = "/proc/self/stat"; + const char *env; + int pid; + unsigned long long starttime; + FILE *st; + int i = 0; + + _log_file_path[0] = '\0'; + if ((env = getenv("LVM_LOG_FILE_EPOCH"))) { + while (isalpha(env[i]) && i < 32) /* Up to 32 alphas */ + i++; + if (env[i]) { + if (i) + log_warn("WARNING: Ignoring invalid LVM_LOG_FILE_EPOCH envvar \"%s\".", env); + goto no_epoch; + } - if (!(_log_file = fopen(log_file, open_mode))) { + if (!(st = fopen(statfile, "r"))) + log_sys_error("fopen", statfile); + else if (fscanf(st, "%d %*s %*c %*d %*d %*d %*d " /* tty_nr */ + "%*d %*u %*u %*u %*u " /* mjflt */ + "%*u %*u %*u %*d %*d " /* cstim */ + "%*d %*d %*d %*d " /* itrealvalue */ + "%llu", &pid, &starttime) != 2) { + log_warn("WARNING: Cannot parse content of %s.", statfile); + } else { + if (dm_snprintf(_log_file_path, sizeof(_log_file_path), + "%s_%s_%d_%lld", log_file, env, pid, starttime) < 0) { + log_warn("WARNING: Debug log file path is too long for epoch."); + _log_file_path[0] = '\0'; + } else { + log_file = _log_file_path; + append = 1; /* force */ + } + } + + if (st && fclose(st)) + log_sys_debug("fclose", statfile); + } + +no_epoch: + if (!(_log_file = fopen(log_file, append ? "a" : "w"))) { log_sys_error("fopen", log_file); return; } @@ -64,6 +115,31 @@ void init_log_file(const char *log_file, int append) _log_to_file = 1; } +/* + * Unlink the log file depeding on command's return value + * + * When envvar LVM_EXPECTED_EXIT_STATUS is set, compare + * resulting status with this string. + * + * It's possible to specify 2 variants - having it equal to + * a single number or having it different from a single number. + * + * i.e. LVM_EXPECTED_EXIT_STATUS=">1" # delete when ret > 1. + */ +void unlink_log_file(int ret) +{ + const char *env; + + if (_log_file_path[0] && + (env = getenv("LVM_EXPECTED_EXIT_STATUS")) && + ((env[0] == '>' && ret > atoi(env + 1)) || + (atoi(env) == ret))) { + if (unlink(_log_file_path)) + log_sys_error("unlink", _log_file_path); + _log_file_path[0] = '\0'; + } +} + void init_log_direct(const char *log_file, int append) { int open_flags = append ? 0 : O_TRUNC; @@ -142,9 +218,10 @@ void init_indent(int indent) _indent = indent; } +/* If present, environment setting will override this. */ void init_abort_on_internal_errors(int fatal) { - _abort_on_internal_errors = fatal; + _abort_on_internal_errors_config = fatal; } void reset_lvm_errno(int store_errmsg) @@ -201,10 +278,24 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class, size_t msglen; const char *indent_spaces = ""; FILE *stream; + static int _abort_on_internal_errors_env_present = -1; + static int _abort_on_internal_errors_env = 0; + char *env_str; level &= ~(_LOG_STDERR|_LOG_ONCE); - if (_abort_on_internal_errors && + if (_abort_on_internal_errors_env_present < 0) { + if ((env_str = getenv("DM_ABORT_ON_INTERNAL_ERRORS"))) { + _abort_on_internal_errors_env_present = 1; + /* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */ + _abort_on_internal_errors_env = strcmp(env_str, "0"); + } else + _abort_on_internal_errors_env_present = 0; + } + + /* Use value from environment if present, otherwise use value from config. */ + if (((_abort_on_internal_errors_env_present && _abort_on_internal_errors_env) || + (!_abort_on_internal_errors_env_present && _abort_on_internal_errors_config)) && !strncmp(format, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1)) { fatal_internal_error = 1; /* Internal errors triggering abort cannot be suppressed. */ @@ -299,17 +390,19 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class, va_start(ap, format); switch (level) { case _LOG_DEBUG: - if ((verbose_level() == level) && - (strcmp("<backtrace>", format) == 0)) - break; if (verbose_level() < _LOG_DEBUG) break; if (!debug_class_is_logged(dm_errno_or_class)) break; + if ((verbose_level() == level) && + (strcmp("<backtrace>", format) == 0)) + break; /* fall through */ default: /* Typically only log_warn goes to stdout */ stream = (use_stderr || (level != _LOG_WARN)) ? stderr : stdout; + if (stream == stderr) + fflush(stdout); fprintf(stream, "%s%s%s%s", buf, log_command_name(), _msg_prefix, indent_spaces); vfprintf(stream, trformat, ap); @@ -333,7 +426,7 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class, vfprintf(_log_file, trformat, ap); va_end(ap); - fprintf(_log_file, "\n"); + fputc('\n', _log_file); fflush(_log_file); } |