-/*-*- Mode: C; c-basic-offset: 8 -*-*/
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
#include "macro.h"
#define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC)
-#define LOG_BUFFER_MAX 1024
static LogTarget log_target = LOG_TARGET_CONSOLE;
-static int log_max_level = LOG_DEBUG;
+static int log_max_level = LOG_INFO;
static int console_fd = STDERR_FILENO;
static int syslog_fd = -1;
static int kmsg_fd = -1;
-/* Akin to glibc's __abort_msg; which is private and we hance cannot
+static bool syslog_is_stream = false;
+
+static bool show_color = false;
+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;
return console_fd;
}
- log_info("Succesfully opened /dev/console for logging.");
+ log_debug("Successfully opened /dev/console for logging.");
} else
console_fd = STDERR_FILENO;
return 0;
if ((kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
- log_info("Failed to open /dev/kmsg for logging: %s", strerror(errno));
+ log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno));
return -errno;
}
- log_info("Succesfully opened /dev/kmsg for logging.");
+ log_debug("Successfully opened /dev/kmsg for logging.");
return 0;
}
syslog_fd = -1;
}
+static int create_log_socket(int type) {
+ struct timeval tv;
+ int fd;
+
+ if (getpid() == 1)
+ /* systemd should not block on syslog */
+ type |= SOCK_NONBLOCK;
+ if ((fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0)) < 0)
+ return -errno;
+
+ /* Make sure we don't block for more than 5s when talking to
+ * syslog */
+ timeval_store(&tv, SYSLOG_TIMEOUT_USEC);
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ return fd;
+}
+
static int log_open_syslog(void) {
union {
struct sockaddr sa;
struct sockaddr_un un;
} sa;
- struct timeval tv;
int r;
if (syslog_fd >= 0)
return 0;
- if ((syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
- r = -errno;
- goto fail;
- }
-
- /* Make sure we don't block for more than 5s when talking to
- * syslog */
- timeval_store(&tv, SYSLOG_TIMEOUT_USEC);
- if (setsockopt(syslog_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
- r = -errno;
- goto fail;
- }
-
zero(sa);
sa.un.sun_family = AF_UNIX;
strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
- if (connect(syslog_fd, &sa.sa, sizeof(sa)) < 0) {
+ if ((syslog_fd = create_log_socket(SOCK_DGRAM)) < 0) {
r = -errno;
goto fail;
}
- log_info("Succesfully opened syslog for logging.");
+ if (connect(syslog_fd, &sa.sa, sizeof(sa)) < 0) {
+ close_nointr_nofail(syslog_fd);
+
+ /* Some legacy syslog systems still use stream
+ * sockets. They really shouldn't. But what can we
+ * do... */
+ if ((syslog_fd = create_log_socket(SOCK_STREAM)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (connect(syslog_fd, &sa.sa, sizeof(sa)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ syslog_is_stream = true;
+ } else
+ syslog_is_stream = false;
+
+ log_debug("Successfully opened syslog for logging.");
return 0;
fail:
log_close_syslog();
- log_info("Failed to open syslog for logging: %s", strerror(-r));
+ log_debug("Failed to open syslog for logging: %s", strerror(-r));
return r;
}
* the fs. If we don't use /dev/kmsg we still keep it open,
* because there is no reason to close it. */
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG)
- if ((r = log_open_syslog()) >= 0) {
- log_close_console();
- return r;
- }
+ if (log_target == LOG_TARGET_NULL) {
+ log_close_syslog();
+ log_close_console();
+ return 0;
+ }
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_KMSG)
- if ((r = log_open_kmsg()) >= 0) {
- log_close_syslog();
- log_close_console();
- return r;
- }
+ if (log_target != LOG_TARGET_AUTO ||
+ getpid() == 1 ||
+ isatty(STDERR_FILENO) <= 0) {
+
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG)
+ if ((r = log_open_syslog()) >= 0) {
+ log_close_console();
+ return r;
+ }
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_KMSG)
+ if ((r = log_open_kmsg()) >= 0) {
+ log_close_syslog();
+ log_close_console();
+ return r;
+ }
+ }
log_close_syslog();
+
+ /* Get the real /dev/console if we are PID=1, hence reopen */
+ log_close_console();
return log_open_console();
}
log_target = target;
}
+void log_close(void) {
+ log_close_console();
+ log_close_kmsg();
+ log_close_syslog();
+}
+
+void log_forget_fds(void) {
+ console_fd = kmsg_fd = syslog_fd = -1;
+}
+
void log_set_max_level(int level) {
assert((level & LOG_PRIMASK) == level);
}
static int write_to_console(
- int level,
- const char*file,
- int line,
- const char *func,
- const char *buffer) {
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *buffer) {
char location[64];
struct iovec iovec[5];
snprintf(location, sizeof(location), "(%s:%u) ", file, line);
char_array_0(location);
- highlight = LOG_PRI(level) <= LOG_ERR;
+ highlight = LOG_PRI(level) <= LOG_ERR && show_color;
zero(iovec);
- IOVEC_SET_STRING(iovec[n++], location);
+ if (show_location)
+ IOVEC_SET_STRING(iovec[n++], location);
if (highlight)
- IOVEC_SET_STRING(iovec[n++], "\x1B[1;31m");
+ IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_ON);
IOVEC_SET_STRING(iovec[n++], buffer);
if (highlight)
- IOVEC_SET_STRING(iovec[n++], "\x1B[0m");
+ IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
IOVEC_SET_STRING(iovec[n++], "\n");
if (writev(console_fd, iovec, n) < 0)
if (syslog_fd < 0)
return 0;
- snprintf(header_priority, sizeof(header_priority), "<%i>", LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(level)));
+ snprintf(header_priority, sizeof(header_priority), "<%i>", level);
char_array_0(header_priority);
t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
return -EINVAL;
- snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) getpid());
+ snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
char_array_0(header_pid);
zero(iovec);
IOVEC_SET_STRING(iovec[0], header_priority);
IOVEC_SET_STRING(iovec[1], header_time);
- IOVEC_SET_STRING(iovec[2], __progname);
+ IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
IOVEC_SET_STRING(iovec[3], header_pid);
IOVEC_SET_STRING(iovec[4], buffer);
+ /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
+ if (syslog_is_stream)
+ iovec[4].iov_len++;
+
zero(msghdr);
msghdr.msg_iov = iovec;
msghdr.msg_iovlen = ELEMENTSOF(iovec);
- if (sendmsg(syslog_fd, &msghdr, 0) < 0)
- return -errno;
+ for (;;) {
+ ssize_t n;
+
+ n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
+ if (n < 0)
+ return -errno;
+
+ if (!syslog_is_stream ||
+ (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
+ break;
+
+ IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
+ }
return 1;
}
if (kmsg_fd < 0)
return 0;
- snprintf(header_priority, sizeof(header_priority), "<%i>", LOG_PRI(level));
+ snprintf(header_priority, sizeof(header_priority), "<%i>", level);
char_array_0(header_priority);
- snprintf(header_pid, sizeof(header_pid), "[%llu]: ", (unsigned long long) getpid());
+ snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
char_array_0(header_pid);
zero(iovec);
IOVEC_SET_STRING(iovec[0], header_priority);
- IOVEC_SET_STRING(iovec[1], __progname);
+ IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
IOVEC_SET_STRING(iovec[2], header_pid);
IOVEC_SET_STRING(iovec[3], buffer);
IOVEC_SET_STRING(iovec[4], "\n");
const char*file,
int line,
const char *func,
- const char *buffer) {
+ char *buffer) {
- int r;
+ int r = 0;
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG) {
+ if (log_target == LOG_TARGET_NULL)
+ return 0;
- if ((r = write_to_syslog(level, file, line, func, buffer)) < 0) {
- log_close_syslog();
- log_open_kmsg();
- } else if (r > 0)
- return r;
- }
+ /* Patch in LOG_DAEMON facility if necessary */
+ if ((level & LOG_FACMASK) == 0)
+ level = LOG_DAEMON | LOG_PRI(level);
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_KMSG) {
+ do {
+ char *e;
+ int k = 0;
- if ((r = write_to_kmsg(level, file, line, func, buffer)) < 0) {
- log_close_kmsg();
- log_open_console();
- } else if (r > 0)
- return r;
- }
+ buffer += strspn(buffer, NEWLINE);
+
+ if (buffer[0] == 0)
+ break;
+
+ if ((e = strpbrk(buffer, NEWLINE)))
+ *(e++) = 0;
+
+ if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG) {
+
+ k = write_to_syslog(level, file, line, func, buffer);
+ if (k < 0) {
+ if (k != -EAGAIN)
+ log_close_syslog();
+ log_open_kmsg();
+ } else if (k > 0)
+ r++;
+ }
+
+ if (k <= 0 &&
+ (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_KMSG)) {
+
+ k = write_to_kmsg(level, file, line, func, buffer);
+ if (k < 0) {
+ log_close_kmsg();
+ log_open_console();
+ } else if (k > 0)
+ r++;
+ }
+
+ if (k <= 0) {
+ k = write_to_console(level, file, line, func, buffer);
+ if (k < 0)
+ return k;
+ }
+
+ buffer = e;
+ } while (buffer);
+
+ return r;
+}
+
+int log_dump_internal(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ char *buffer) {
+
+ int saved_errno, r;
+
+ /* This modifies the buffer... */
- return write_to_console(level, file, line, func, buffer);
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return 0;
+
+ saved_errno = errno;
+ r = log_dispatch(level, file, line, func, buffer);
+ errno = saved_errno;
+
+ return r;
}
int log_meta(
const char *func,
const char *format, ...) {
- char buffer[LOG_BUFFER_MAX];
+ char buffer[LINE_MAX];
int saved_errno, r;
va_list ap;
- if (_likely(LOG_PRI(level) > log_max_level))
+ if (_likely_(LOG_PRI(level) > log_max_level))
return 0;
saved_errno = errno;
const char *func,
const char *format, ...) {
- static char buffer[LOG_BUFFER_MAX];
+ static char buffer[LINE_MAX];
int saved_errno = errno;
va_list ap;
if ((e = getenv("SYSTEMD_LOG_LEVEL")))
if (log_set_max_level_from_string(e) < 0)
log_warning("Failed to parse log level %s. Ignoring.", e);
+
+ if ((e = getenv("SYSTEMD_LOG_COLOR")))
+ if (log_show_color_from_string(e) < 0)
+ log_warning("Failed to parse bool %s. Ignoring.", e);
+
+ if ((e = getenv("SYSTEMD_LOG_LOCATION")))
+ if (log_show_location_from_string(e) < 0)
+ log_warning("Failed to parse bool %s. Ignoring.", e);
}
LogTarget log_get_target(void) {
return log_max_level;
}
+void log_show_color(bool b) {
+ show_color = b;
+}
+
+void log_show_location(bool b) {
+ show_location = b;
+}
+
+int log_show_color_from_string(const char *e) {
+ int t;
+
+ if ((t = parse_boolean(e)) < 0)
+ return -EINVAL;
+
+ log_show_color(t);
+ return 0;
+}
+
+int log_show_location_from_string(const char *e) {
+ int t;
+
+ if ((t = parse_boolean(e)) < 0)
+ return -EINVAL;
+
+ log_show_location(t);
+ return 0;
+}
+
static const char *const log_target_table[] = {
[LOG_TARGET_CONSOLE] = "console",
[LOG_TARGET_SYSLOG] = "syslog",
[LOG_TARGET_KMSG] = "kmsg",
[LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
+ [LOG_TARGET_NULL] = "null",
+ [LOG_TARGET_AUTO] = "auto"
};
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);