chiark / gitweb /
journald: fix some xsprrintf() buffer size fallout
[elogind.git] / src / journal / journald-kmsg.c
index 4f5e7a0c52f2ad3c5df5e48a07f25b4ef03ffe26..c4216c4043370bb8047c99a07faf9336cc9bfe16 100644 (file)
 #include <sys/mman.h>
 #include <sys/socket.h>
 
-#include <systemd/sd-messages.h>
+#include "systemd/sd-messages.h"
 #include <libudev.h>
 
-#include "journald.h"
+#include "journald-server.h"
 #include "journald-kmsg.h"
 #include "journald-syslog.h"
 
@@ -37,10 +37,11 @@ void server_forward_kmsg(
         int priority,
         const char *identifier,
         const char *message,
-        struct ucred *ucred) {
+        const struct ucred *ucred) {
 
         struct iovec iovec[5];
-        char header_priority[6], header_pid[16];
+        char header_priority[DECIMAL_STR_MAX(priority) + 3],
+             header_pid[sizeof("[]: ")-1 + DECIMAL_STR_MAX(pid_t) + 1];
         int n = 0;
         char *ident_buf = NULL;
 
@@ -60,8 +61,7 @@ void server_forward_kmsg(
         priority = syslog_fixup_facility(priority);
 
         /* First: priority field */
-        snprintf(header_priority, sizeof(header_priority), "<%i>", priority);
-        char_array_0(header_priority);
+        xsprintf(header_priority, "<%i>", priority);
         IOVEC_SET_STRING(iovec[n++], header_priority);
 
         /* Second: identifier and PID */
@@ -71,8 +71,7 @@ void server_forward_kmsg(
                         identifier = ident_buf;
                 }
 
-                snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) ucred->pid);
-                char_array_0(header_pid);
+                xsprintf(header_pid, "["PID_FMT"]: ", ucred->pid);
 
                 if (identifier)
                         IOVEC_SET_STRING(iovec[n++], identifier);
@@ -88,7 +87,7 @@ void server_forward_kmsg(
         IOVEC_SET_STRING(iovec[n++], "\n");
 
         if (writev(s->dev_kmsg_fd, iovec, n) < 0)
-                log_debug("Failed to write to /dev/kmsg for logging: %s", strerror(errno));
+                log_debug_errno(errno, "Failed to write to /dev/kmsg for logging: %m");
 
         free(ident_buf);
 }
@@ -104,12 +103,12 @@ static bool is_us(const char *pid) {
         return t == getpid();
 }
 
-static void dev_kmsg_record(Server *s, char *p, size_t l) {
+static void dev_kmsg_record(Server *s, const char *p, size_t l) {
         struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS];
         char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL;
         int priority, r;
         unsigned n = 0, z = 0, j;
-        usec_t usec;
+        unsigned long long usec;
         char *identifier = NULL, *pid = NULL, *e, *f, *k;
         uint64_t serial;
         size_t pl;
@@ -151,7 +150,8 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
 
                 /* Did we lose any? */
                 if (serial > *s->kernel_seqnum)
-                        server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, "Missed %llu kernel messages", (unsigned long long) serial - *s->kernel_seqnum - 1);
+                        server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, "Missed %"PRIu64" kernel messages",
+                                              serial - *s->kernel_seqnum);
 
                 /* Make sure we never read this one again. Note that
                  * we always store the next message serial we expect
@@ -171,7 +171,7 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
                 e = f;
         *e = 0;
 
-        r = parse_usec(p, &usec);
+        r = safe_atollu(p, &usec);
         if (r < 0)
                 return;
 
@@ -250,10 +250,12 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
                                         break;
 
                                 g = udev_list_entry_get_name(ll);
-                                b = strappend("_UDEV_DEVLINK=", g);
                                 if (g) {
-                                        IOVEC_SET_STRING(iovec[n++], b);
-                                        z++;
+                                        b = strappend("_UDEV_DEVLINK=", g);
+                                        if (b) {
+                                                IOVEC_SET_STRING(iovec[n++], b);
+                                                z++;
+                                        }
                                 }
 
                                 j++;
@@ -263,8 +265,7 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
                 }
         }
 
-        if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu",
-                     (unsigned long long) usec) >= 0)
+        if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu", usec) >= 0)
                 IOVEC_SET_STRING(iovec[n++], source_time);
 
         IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=kernel");
@@ -272,10 +273,13 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
         if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0)
                 IOVEC_SET_STRING(iovec[n++], syslog_priority);
 
+        if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
+                IOVEC_SET_STRING(iovec[n++], syslog_facility);
+
         if ((priority & LOG_FACMASK) == LOG_KERN)
                 IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=kernel");
         else {
-                syslog_parse_identifier((const char**) &p, &identifier, &pid);
+                pl -= syslog_parse_identifier((const char**) &p, &identifier, &pid);
 
                 /* Avoid any messages we generated ourselves via
                  * log_info() and friends. */
@@ -293,16 +297,13 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
                         if (syslog_pid)
                                 IOVEC_SET_STRING(iovec[n++], syslog_pid);
                 }
-
-                if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0)
-                        IOVEC_SET_STRING(iovec[n++], syslog_facility);
         }
 
         message = cunescape_length_with_prefix(p, pl, "MESSAGE=");
         if (message)
                 IOVEC_SET_STRING(iovec[n++], message);
 
-        server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority);
+        server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority, 0);
 
 finish:
         for (j = 0; j < z; j++)
@@ -318,7 +319,7 @@ finish:
         free(pid);
 }
 
-int server_read_dev_kmsg(Server *s) {
+static int server_read_dev_kmsg(Server *s) {
         char buffer[8192+1]; /* the kernel-side limit per record is 8K currently */
         ssize_t l;
 
@@ -333,14 +334,14 @@ int server_read_dev_kmsg(Server *s) {
                  * return EINVAL when we try. So handle this cleanly,
                  * but don' try to ever read from it again. */
                 if (errno == EINVAL) {
-                        epoll_ctl(s->epoll_fd, EPOLL_CTL_DEL, s->dev_kmsg_fd, NULL);
+                        s->dev_kmsg_event_source = sd_event_source_unref(s->dev_kmsg_event_source);
                         return 0;
                 }
 
                 if (errno == EAGAIN || errno == EINTR || errno == EPIPE)
                         return 0;
 
-                log_error("Failed to read from kernel: %m");
+                log_error_errno(errno, "Failed to read from kernel: %m");
                 return -errno;
         }
 
@@ -373,38 +374,67 @@ int server_flush_dev_kmsg(Server *s) {
         return 0;
 }
 
+static int dispatch_dev_kmsg(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
+        Server *s = userdata;
+
+        assert(es);
+        assert(fd == s->dev_kmsg_fd);
+        assert(s);
+
+        if (revents & EPOLLERR)
+                log_warning("/dev/kmsg buffer overrun, some messages lost.");
+
+        if (!(revents & EPOLLIN))
+                log_error("Got invalid event from epoll for /dev/kmsg: %"PRIx32, revents);
+
+        return server_read_dev_kmsg(s);
+}
+
 int server_open_dev_kmsg(Server *s) {
-        struct epoll_event ev;
+        int r;
 
         assert(s);
 
         s->dev_kmsg_fd = open("/dev/kmsg", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
         if (s->dev_kmsg_fd < 0) {
-                log_warning("Failed to open /dev/kmsg, ignoring: %m");
+                log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
+                         "Failed to open /dev/kmsg, ignoring: %m");
                 return 0;
         }
 
-        zero(ev);
-        ev.events = EPOLLIN;
-        ev.data.fd = s->dev_kmsg_fd;
-        if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->dev_kmsg_fd, &ev) < 0) {
+        r = sd_event_add_io(s->event, &s->dev_kmsg_event_source, s->dev_kmsg_fd, EPOLLIN, dispatch_dev_kmsg, s);
+        if (r < 0) {
 
                 /* This will fail with EPERM on older kernels where
                  * /dev/kmsg is not readable. */
-                if (errno == EPERM)
-                        return 0;
+                if (r == -EPERM) {
+                        r = 0;
+                        goto fail;
+                }
 
-                log_error("Failed to add /dev/kmsg fd to epoll object: %m");
-                return -errno;
+                log_error_errno(r, "Failed to add /dev/kmsg fd to event loop: %m");
+                goto fail;
+        }
+
+        r = sd_event_source_set_priority(s->dev_kmsg_event_source, SD_EVENT_PRIORITY_IMPORTANT+10);
+        if (r < 0) {
+                log_error_errno(r, "Failed to adjust priority of kmsg event source: %m");
+                goto fail;
         }
 
         s->dev_kmsg_readable = true;
 
         return 0;
+
+fail:
+        s->dev_kmsg_event_source = sd_event_source_unref(s->dev_kmsg_event_source);
+        s->dev_kmsg_fd = safe_close(s->dev_kmsg_fd);
+
+        return r;
 }
 
 int server_open_kernel_seqnum(Server *s) {
-        int fd;
+        _cleanup_close_ int fd;
         uint64_t *p;
 
         assert(s);
@@ -415,24 +445,21 @@ int server_open_kernel_seqnum(Server *s) {
 
         fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644);
         if (fd < 0) {
-                log_error("Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m");
+                log_error_errno(errno, "Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m");
                 return 0;
         }
 
         if (posix_fallocate(fd, 0, sizeof(uint64_t)) < 0) {
-                log_error("Failed to allocate sequential number file, ignoring: %m");
-                close_nointr_nofail(fd);
+                log_error_errno(errno, "Failed to allocate sequential number file, ignoring: %m");
                 return 0;
         }
 
         p = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
         if (p == MAP_FAILED) {
-                log_error("Failed to map sequential number file, ignoring: %m");
-                close_nointr_nofail(fd);
+                log_error_errno(errno, "Failed to map sequential number file, ignoring: %m");
                 return 0;
         }
 
-        close_nointr_nofail(fd);
         s->kernel_seqnum = p;
 
         return 0;