You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
295 lines
7.3 KiB
295 lines
7.3 KiB
/*** |
|
This file is part of systemd. |
|
|
|
Copyright 2010 Lennart Poettering |
|
|
|
systemd is free software; you can redistribute it and/or modify it |
|
under the terms of the GNU Lesser General Public License as published by |
|
the Free Software Foundation; either version 2.1 of the License, or |
|
(at your option) any later version. |
|
|
|
systemd is distributed in the hope that it will be useful, but |
|
WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
Lesser General Public License for more details. |
|
|
|
You should have received a copy of the GNU Lesser General Public License |
|
along with systemd; If not, see <http://www.gnu.org/licenses/>. |
|
***/ |
|
|
|
#include <stdarg.h> |
|
#include <stdio.h> |
|
#include <errno.h> |
|
#include <unistd.h> |
|
#include <fcntl.h> |
|
#include <sys/socket.h> |
|
#include <sys/un.h> |
|
#include <stddef.h> |
|
|
|
#include "log.h" |
|
#include "util.h" |
|
#include "macro.h" |
|
|
|
#define SNDBUF_SIZE (8*1024*1024) |
|
|
|
static LogTarget log_target = LOG_TARGET_CONSOLE; |
|
static int log_max_level = LOG_WARNING; |
|
static int log_facility = LOG_DAEMON; |
|
|
|
static int console_fd = STDERR_FILENO; |
|
|
|
static bool show_location = false; |
|
|
|
/* Akin to glibc's __abort_msg; which is private and we hence cannot |
|
* use here. */ |
|
static char *log_abort_msg = NULL; |
|
|
|
void log_close_console(void) |
|
{ |
|
|
|
if (console_fd < 0) |
|
return; |
|
|
|
if (getpid() == 1) { |
|
if (console_fd >= 3) |
|
close_nointr_nofail(console_fd); |
|
|
|
console_fd = -1; |
|
} |
|
} |
|
|
|
static int log_open_console(void) |
|
{ |
|
|
|
if (console_fd >= 0) |
|
return 0; |
|
|
|
if (getpid() == 1) { |
|
|
|
console_fd = open_terminal("/dev/console", O_WRONLY | O_NOCTTY | O_CLOEXEC); |
|
if (console_fd < 0) { |
|
log_error("Failed to open /dev/console for logging: %s", strerror(-console_fd)); |
|
return console_fd; |
|
} |
|
|
|
log_debug("Successfully opened /dev/console for logging."); |
|
} else |
|
console_fd = STDERR_FILENO; |
|
|
|
return 0; |
|
} |
|
|
|
int log_open(void) |
|
{ |
|
return log_open_console(); |
|
} |
|
|
|
void log_close(void) |
|
{ |
|
log_close_console(); |
|
} |
|
|
|
void log_set_max_level(int level) |
|
{ |
|
assert((level & LOG_PRIMASK) == level); |
|
|
|
log_max_level = level; |
|
} |
|
|
|
void log_set_facility(int facility) |
|
{ |
|
log_facility = facility; |
|
} |
|
|
|
static int write_to_console(int level, const char *file, unsigned int line, const char *func, const char *buffer) |
|
{ |
|
|
|
struct iovec iovec[5]; |
|
unsigned n = 0; |
|
|
|
if (console_fd < 0) |
|
return 0; |
|
|
|
zero(iovec); |
|
|
|
IOVEC_SET_STRING(iovec[n++], "dracut-install: "); |
|
|
|
if (show_location) { |
|
char location[64]; |
|
if (snprintf(location, sizeof(location), "(%s:%u) ", file, line) <= 0) |
|
return -errno; |
|
IOVEC_SET_STRING(iovec[n++], location); |
|
} |
|
|
|
IOVEC_SET_STRING(iovec[n++], buffer); |
|
IOVEC_SET_STRING(iovec[n++], "\n"); |
|
|
|
if (writev(console_fd, iovec, n) < 0) |
|
return -errno; |
|
|
|
return 1; |
|
} |
|
|
|
static int log_dispatch(int level, const char *file, int line, const char *func, char *buffer) |
|
{ |
|
|
|
int r = 0; |
|
|
|
if (log_target == LOG_TARGET_NULL) |
|
return 0; |
|
|
|
/* Patch in LOG_DAEMON facility if necessary */ |
|
if ((level & LOG_FACMASK) == 0) |
|
level = log_facility | LOG_PRI(level); |
|
|
|
do { |
|
char *e; |
|
int k = 0; |
|
|
|
buffer += strspn(buffer, NEWLINE); |
|
|
|
if (buffer[0] == 0) |
|
break; |
|
|
|
if ((e = strpbrk(buffer, NEWLINE))) |
|
*(e++) = 0; |
|
|
|
k = write_to_console(level, file, line, func, buffer); |
|
if (k < 0) |
|
return k; |
|
buffer = e; |
|
} while (buffer); |
|
|
|
return r; |
|
} |
|
|
|
int log_metav(int level, const char *file, int line, const char *func, const char *format, va_list ap) |
|
{ |
|
|
|
char buffer[LINE_MAX]; |
|
int saved_errno, r; |
|
|
|
if (_likely_(LOG_PRI(level) > log_max_level)) |
|
return 0; |
|
|
|
saved_errno = errno; |
|
vsnprintf(buffer, sizeof(buffer), format, ap); |
|
char_array_0(buffer); |
|
|
|
r = log_dispatch(level, file, line, func, buffer); |
|
errno = saved_errno; |
|
|
|
return r; |
|
} |
|
|
|
int log_meta(int level, const char *file, int line, const char *func, const char *format, ...) |
|
{ |
|
|
|
int r; |
|
va_list ap; |
|
|
|
va_start(ap, format); |
|
r = log_metav(level, file, line, func, format, ap); |
|
va_end(ap); |
|
|
|
return r; |
|
} |
|
|
|
#pragma GCC diagnostic push |
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral" |
|
_noreturn_ static void log_assert(const char *text, const char *file, int line, const char *func, const char *format) |
|
{ |
|
static char buffer[LINE_MAX]; |
|
|
|
snprintf(buffer, sizeof(buffer), format, text, file, line, func); |
|
|
|
char_array_0(buffer); |
|
log_abort_msg = buffer; |
|
|
|
log_dispatch(LOG_CRIT, file, line, func, buffer); |
|
abort(); |
|
} |
|
|
|
#pragma GCC diagnostic pop |
|
|
|
_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func) |
|
{ |
|
log_assert(text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); |
|
} |
|
|
|
_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) |
|
{ |
|
log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); |
|
} |
|
|
|
void log_set_target(LogTarget target) |
|
{ |
|
assert(target >= 0); |
|
assert(target < _LOG_TARGET_MAX); |
|
|
|
log_target = target; |
|
} |
|
|
|
int log_set_target_from_string(const char *e) |
|
{ |
|
LogTarget t; |
|
|
|
t = log_target_from_string(e); |
|
if (t < 0) |
|
return -EINVAL; |
|
|
|
log_set_target(t); |
|
return 0; |
|
} |
|
|
|
int log_set_max_level_from_string(const char *e) |
|
{ |
|
int t; |
|
|
|
t = log_level_from_string(e); |
|
if (t < 0) |
|
return t; |
|
|
|
log_set_max_level(t); |
|
return 0; |
|
} |
|
|
|
void log_parse_environment(void) |
|
{ |
|
const char *e; |
|
|
|
if ((e = getenv("DRACUT_INSTALL_LOG_TARGET"))) { |
|
if (log_set_target_from_string(e) < 0) |
|
log_warning("Failed to parse log target %s. Ignoring.", e); |
|
} else if ((e = getenv("DRACUT_LOG_TARGET"))) { |
|
if (log_set_target_from_string(e) < 0) |
|
log_warning("Failed to parse log target %s. Ignoring.", e); |
|
} |
|
|
|
if ((e = getenv("DRACUT_INSTALL_LOG_LEVEL"))) { |
|
if (log_set_max_level_from_string(e) < 0) |
|
log_warning("Failed to parse log level %s. Ignoring.", e); |
|
} else if ((e = getenv("DRACUT_LOG_LEVEL"))) { |
|
if (log_set_max_level_from_string(e) < 0) |
|
log_warning("Failed to parse log level %s. Ignoring.", e); |
|
} |
|
} |
|
|
|
LogTarget log_get_target(void) |
|
{ |
|
return log_target; |
|
} |
|
|
|
int log_get_max_level(void) |
|
{ |
|
return log_max_level; |
|
} |
|
|
|
static const char *const log_target_table[] = { |
|
[LOG_TARGET_CONSOLE] = "console", |
|
[LOG_TARGET_AUTO] = "auto", |
|
[LOG_TARGET_SAFE] = "safe", |
|
[LOG_TARGET_NULL] = "null" |
|
}; |
|
|
|
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
|
|
|