#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <stddef.h>
#include "log.h"
#include "util.h"
#include "macro.h"
+#include "socket-util.h"
-#define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC)
+#define SNDBUF_SIZE (8*1024*1024)
static LogTarget log_target = LOG_TARGET_CONSOLE;
static int log_max_level = LOG_INFO;
+static int log_facility = LOG_DAEMON;
static int console_fd = STDERR_FILENO;
static int syslog_fd = -1;
static int kmsg_fd = -1;
+static int journal_fd = -1;
static bool syslog_is_stream = false;
if (getpid() == 1) {
- if ((console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
+ 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;
}
if (kmsg_fd >= 0)
return 0;
- if ((kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0) {
+ kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (kmsg_fd < 0) {
log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno));
return -errno;
}
}
static int create_log_socket(int type) {
- struct timeval tv;
int fd;
- if ((fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0)) < 0)
- return -errno;
+ /* All output to the syslog/journal fds we do asynchronously,
+ * and if the buffers are full we just drop the messages */
- /* 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);
+ fd = socket(AF_UNIX, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
return -errno;
- }
+
+ fd_inc_sndbuf(fd, SNDBUF_SIZE);
return fd;
}
static int log_open_syslog(void) {
- union {
- struct sockaddr sa;
- struct sockaddr_un un;
- } sa;
+ union sockaddr_union sa;
int r;
if (syslog_fd >= 0)
sa.un.sun_family = AF_UNIX;
strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
- if ((syslog_fd = create_log_socket(SOCK_DGRAM)) < 0) {
- r = -errno;
+ syslog_fd = create_log_socket(SOCK_DGRAM);
+ if (syslog_fd < 0) {
+ r = syslog_fd;
goto fail;
}
- if (connect(syslog_fd, &sa.sa, sizeof(sa)) < 0) {
+ if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 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;
+ syslog_fd = create_log_socket(SOCK_STREAM);
+ if (syslog_fd < 0) {
+ r = syslog_fd;
goto fail;
}
- if (connect(syslog_fd, &sa.sa, sizeof(sa)) < 0) {
+ if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
r = -errno;
goto fail;
}
return r;
}
+void log_close_journal(void) {
+
+ if (journal_fd < 0)
+ return;
+
+ close_nointr_nofail(journal_fd);
+ journal_fd = -1;
+}
+
+static int log_open_journal(void) {
+ union sockaddr_union sa;
+ int r;
+
+ if (journal_fd >= 0)
+ return 0;
+
+ journal_fd = create_log_socket(SOCK_DGRAM);
+ if (journal_fd < 0) {
+ r = journal_fd;
+ goto fail;
+ }
+
+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
+
+ if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ log_debug("Successfully opened journal for logging.");
+
+ return 0;
+
+fail:
+ log_close_journal();
+ log_debug("Failed to open journal for logging: %s", strerror(-r));
+ return r;
+}
+
int log_open(void) {
int r;
* because there is no reason to close it. */
if (log_target == LOG_TARGET_NULL) {
+ log_close_journal();
log_close_syslog();
log_close_console();
return 0;
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_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL) {
+ r = log_open_journal();
+ if (r >= 0) {
+ log_close_syslog();
+ log_close_console();
+ return r;
+ }
+ }
+
+ if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_SYSLOG) {
+ r = log_open_syslog();
+ if (r >= 0) {
+ log_close_journal();
log_close_console();
return r;
}
+ }
+
if (log_target == LOG_TARGET_AUTO ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_KMSG)
- if ((r = log_open_kmsg()) >= 0) {
+ log_target == LOG_TARGET_KMSG) {
+ r = log_open_kmsg();
+ if (r >= 0) {
+ log_close_journal();
log_close_syslog();
log_close_console();
return r;
}
+ }
}
+ log_close_journal();
log_close_syslog();
/* Get the real /dev/console if we are PID=1, hence reopen */
}
void log_close(void) {
- log_close_console();
- log_close_kmsg();
+ log_close_journal();
log_close_syslog();
+ log_close_kmsg();
+ log_close_console();
+}
+
+void log_forget_fds(void) {
+ console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
}
void log_set_max_level(int level) {
log_max_level = level;
}
+void log_set_facility(int facility) {
+ log_facility = facility;
+}
+
static int write_to_console(
int level,
const char*file,
if (console_fd < 0)
return 0;
- snprintf(location, sizeof(location), "(%s:%u) ", file, line);
- char_array_0(location);
-
highlight = LOG_PRI(level) <= LOG_ERR && show_color;
zero(iovec);
- if (show_location)
+
+ if (show_location) {
+ snprintf(location, sizeof(location), "(%s:%u) ", file, line);
+ char_array_0(location);
IOVEC_SET_STRING(iovec[n++], location);
+ }
+
if (highlight)
- IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_ON);
+ IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
IOVEC_SET_STRING(iovec[n++], buffer);
if (highlight)
IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
for (;;) {
ssize_t n;
- if ((n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL)) < 0)
+ n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
+ if (n < 0)
return -errno;
if (!syslog_is_stream ||
return 1;
}
+static int write_to_journal(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *buffer) {
+
+ char header[LINE_MAX];
+ struct iovec iovec[3];
+ struct msghdr mh;
+
+ if (journal_fd < 0)
+ return 0;
+
+ snprintf(header, sizeof(header),
+ "PRIORITY=%i\n"
+ "SYSLOG_FACILITY=%i\n"
+ "CODE_FILE=%s\n"
+ "CODE_LINE=%i\n"
+ "CODE_FUNCTION=%s\n"
+ "MESSAGE=",
+ LOG_PRI(level),
+ LOG_FAC(level),
+ file,
+ line,
+ func);
+
+ char_array_0(header);
+
+ zero(iovec);
+ IOVEC_SET_STRING(iovec[0], header);
+ IOVEC_SET_STRING(iovec[1], buffer);
+ IOVEC_SET_STRING(iovec[2], "\n");
+
+ zero(mh);
+ mh.msg_iov = iovec;
+ mh.msg_iovlen = ELEMENTSOF(iovec);
+
+ if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0)
+ return -errno;
+
+ return 1;
+}
+
static int log_dispatch(
int level,
const char*file,
/* Patch in LOG_DAEMON facility if necessary */
if ((level & LOG_FACMASK) == 0)
- level = LOG_DAEMON | LOG_PRI(level);
+ level = log_facility | LOG_PRI(level);
do {
char *e;
*(e++) = 0;
if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+ log_target == LOG_TARGET_JOURNAL) {
+
+ k = write_to_journal(level, file, line, func, buffer);
+ if (k < 0) {
+ if (k != -EAGAIN)
+ log_close_journal();
+ log_open_kmsg();
+ } else if (k > 0)
+ r++;
+ }
+
+ if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_SYSLOG) {
- if ((k = write_to_syslog(level, file, line, func, buffer)) < 0) {
- log_close_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++;
log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_KMSG)) {
- if ((k = write_to_kmsg(level, file, line, func, buffer)) < 0) {
+ 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)) < 0)
- return k;
+ 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_meta(
+int log_metav(
int level,
const char*file,
int line,
const char *func,
- const char *format, ...) {
+ const char *format,
+ va_list ap) {
char buffer[LINE_MAX];
int saved_errno, r;
- va_list ap;
if (_likely_(LOG_PRI(level) > log_max_level))
return 0;
saved_errno = errno;
-
- va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
- va_end(ap);
-
char_array_0(buffer);
r = log_dispatch(level, file, line, func, buffer);
return r;
}
-void log_assert(
+int log_meta(
+ int level,
const char*file,
int line,
const char *func,
const char *format, ...) {
- static char buffer[LINE_MAX];
- int saved_errno = errno;
+ int r;
va_list ap;
va_start(ap, format);
- vsnprintf(buffer, sizeof(buffer), format, ap);
+ 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
- /* If the user chose to ignore this SIGABRT, we are happy to go on, as if nothing happened. */
- errno = saved_errno;
+_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.");
}
int log_set_target_from_string(const char *e) {
LogTarget t;
- if ((t = log_target_from_string(e)) < 0)
+ t = log_target_from_string(e);
+ if (t < 0)
return -EINVAL;
log_set_target(t);
int log_set_max_level_from_string(const char *e) {
int t;
- if ((t = log_level_from_string(e)) < 0)
- return -EINVAL;
+ t = log_level_from_string(e);
+ if (t < 0)
+ return t;
log_set_max_level(t);
return 0;
int log_show_color_from_string(const char *e) {
int t;
- if ((t = parse_boolean(e)) < 0)
- return -EINVAL;
+ t = parse_boolean(e);
+ if (t < 0)
+ return t;
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;
+ t = parse_boolean(e);
+ if (t < 0)
+ return t;
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_JOURNAL] = "journal",
+ [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
+ [LOG_TARGET_SYSLOG] = "syslog",
[LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
- [LOG_TARGET_NULL] = "null",
- [LOG_TARGET_AUTO] = "auto"
+ [LOG_TARGET_AUTO] = "auto",
+ [LOG_TARGET_NULL] = "null"
};
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);