/***
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 .
***/
#include
#include
#include
#include
#include
#include
#include
#include
#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 int n = 0;
// might be useful going ahead
UNUSED(level);
if (console_fd < 0)
return 0;
zero(iovec);
IOVEC_SET_STRING(iovec[n++], "dracut-install: ");
if (show_location) {
char location[LINE_MAX] = {0};
if (snprintf(location, sizeof(location), "(%s:%s:%u) ", file, func, 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, unsigned 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, unsigned int line, const char *func, const char *format, va_list ap)
{
char buffer[LINE_MAX] = {0};
int saved_errno, r;
if (_likely_(LOG_PRI(level) > log_max_level))
return 0;
saved_errno = errno;
r = vsnprintf(buffer, sizeof(buffer), format, ap);
if (r <= 0) {
goto end;
}
char_array_0(buffer);
r = log_dispatch(level, file, line, func, buffer);
end:
errno = saved_errno;
return r;
}
int log_meta(int level, const char *file, unsigned 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, unsigned int line, const char *func,
const char *format)
{
static char buffer[LINE_MAX];
if (snprintf(buffer, sizeof(buffer), format, text, file, line, func) > 0) {
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, unsigned 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, unsigned 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);