chiark / gitweb /
mmap: resize arrays dynamically
[elogind.git] / src / journal / journald.c
index 1d40fa843ef9092d8f9f7f113010b5862c31bd18..f74c46158ac63d189c2baea5506ba23c257923b6 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/ioctl.h>
 #include <linux/sockios.h>
 #include <sys/statvfs.h>
+#include <sys/mman.h>
 
 #include <systemd/sd-journal.h>
 #include <systemd/sd-messages.h>
@@ -46,6 +47,7 @@
 #include "list.h"
 #include "journal-rate-limit.h"
 #include "journal-internal.h"
+#include "journal-vacuum.h"
 #include "conf-parser.h"
 #include "journald.h"
 #include "virt.h"
@@ -70,6 +72,7 @@
 #define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC)
 
 #define N_IOVEC_META_FIELDS 17
+#define N_IOVEC_KERNEL_FIELDS 64
 
 #define ENTRY_SIZE_MAX (1024*1024*32)
 
@@ -279,7 +282,6 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
         char *p;
         int r;
         JournalFile *f;
-        char ids[33];
         sd_id128_t machine;
 
         assert(s);
@@ -303,7 +305,8 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
         if (f)
                 return f;
 
-        if (asprintf(&p, "/var/log/journal/%s/user-%lu.journal", sd_id128_to_string(machine, ids), (unsigned long) uid) < 0)
+        if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-%lu.journal",
+                     SD_ID128_FORMAT_VAL(machine), (unsigned long) uid) < 0)
                 return s->system_journal;
 
         while (hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
@@ -313,7 +316,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
                 journal_file_close(f);
         }
 
-        r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, &s->system_metrics, s->system_journal, &f);
+        r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, s->compress, false, &s->system_metrics, s->mmap, s->system_journal, &f);
         free(p);
 
         if (r < 0)
@@ -339,7 +342,7 @@ static void server_rotate(Server *s) {
         log_info("Rotating...");
 
         if (s->runtime_journal) {
-                r = journal_file_rotate(&s->runtime_journal);
+                r = journal_file_rotate(&s->runtime_journal, s->compress, false);
                 if (r < 0)
                         if (s->runtime_journal)
                                 log_error("Failed to rotate %s: %s", s->runtime_journal->path, strerror(-r));
@@ -350,7 +353,7 @@ static void server_rotate(Server *s) {
         }
 
         if (s->system_journal) {
-                r = journal_file_rotate(&s->system_journal);
+                r = journal_file_rotate(&s->system_journal, s->compress, true);
                 if (r < 0)
                         if (s->system_journal)
                                 log_error("Failed to rotate %s: %s", s->system_journal->path, strerror(-r));
@@ -362,7 +365,7 @@ static void server_rotate(Server *s) {
         }
 
         HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
-                r = journal_file_rotate(&f);
+                r = journal_file_rotate(&f, s->compress, false);
                 if (r < 0)
                         if (f->path)
                                 log_error("Failed to rotate %s: %s", f->path, strerror(-r));
@@ -1807,13 +1810,14 @@ static bool is_us(const char *pid) {
 }
 
 static void dev_kmsg_record(Server *s, char *p, size_t l) {
-        struct iovec iovec[N_IOVEC_META_FIELDS + 7];
+        struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_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;
+        unsigned n = 0, z = 0, j;
         usec_t usec;
-        char *identifier = NULL, *pid = NULL, *e, *f;
+        char *identifier = NULL, *pid = NULL, *e, *f, *k;
         uint64_t serial;
+        size_t pl;
 
         assert(s);
         assert(p);
@@ -1844,6 +1848,22 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
         if (r < 0)
                 return;
 
+        if (s->kernel_seqnum) {
+                /* We already read this one? */
+                if (serial < *s->kernel_seqnum)
+                        return;
+
+                /* Did we lose any? */
+                if (serial > *s->kernel_seqnum)
+                        driver_message(s, SD_MESSAGE_JOURNAL_MISSED, "Missed %llu kernel messages", (unsigned long long) serial - *s->kernel_seqnum - 1);
+
+                /* Make sure we never read this one again. Note that
+                 * we always store the next message serial we expect
+                 * here, simply because this makes handling the first
+                 * message with serial 0 easy. */
+                *s->kernel_seqnum = serial + 1;
+        }
+
         l -= (e - p) + 1;
         p = e + 1;
         f = memchr(p, ';', l);
@@ -1862,9 +1882,40 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
         l -= (f - p) + 1;
         p = f + 1;
         e = memchr(p, '\n', l);
-        if (e)
+        if (!e)
+                return;
+        *e = 0;
+
+        pl = e - p;
+        l -= (e - p) + 1;
+        k = e + 1;
+
+        for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) {
+                char *m;
+                /* Meta data fields attached */
+
+                if (*k != ' ')
+                        break;
+
+                k ++, l --;
+
+                e = memchr(k, '\n', l);
+                if (!e)
+                        return;
+
                 *e = 0;
 
+                m = cunescape_length_with_prefix(k, e - k, "_KERNEL_");
+                if (!m)
+                        break;
+
+                IOVEC_SET_STRING(iovec[n++], m);
+                z++;
+
+                l -= (e - k) + 1;
+                k = e + 1;
+        }
+
         if (asprintf(&source_time, "_SOURCE_MONOTONIC_TIMESTAMP=%llu",
                      (unsigned long long) usec) >= 0)
                 IOVEC_SET_STRING(iovec[n++], source_time);
@@ -1900,13 +1951,16 @@ static void dev_kmsg_record(Server *s, char *p, size_t l) {
                         IOVEC_SET_STRING(iovec[n++], syslog_facility);
         }
 
-        message = strappend("MESSAGE=", p);
+        message = cunescape_length_with_prefix(p, pl, "MESSAGE=");
         if (message)
                 IOVEC_SET_STRING(iovec[n++], message);
 
         dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority);
 
 finish:
+        for (j = 0; j < z; j++)
+                free(iovec[j].iov_base);
+
         free(message);
         free(syslog_priority);
         free(syslog_identifier);
@@ -1953,14 +2007,12 @@ static int system_journal_open(Server *s) {
                 if (!fn)
                         return -ENOMEM;
 
-                r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, &s->system_metrics, NULL, &s->system_journal);
+                r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, true, &s->system_metrics, s->mmap, NULL, &s->system_journal);
                 free(fn);
 
-                if (r >= 0) {
-                        s->system_journal->compress = s->compress;
-
+                if (r >= 0)
                         server_fix_perms(s, s->system_journal, 0);
-                else if (r < 0) {
+                else if (r < 0) {
 
                         if (r != -ENOENT && r != -EROFS)
                                 log_warning("Failed to open system journal: %s", strerror(-r));
@@ -1982,7 +2034,7 @@ static int system_journal_open(Server *s) {
                          * if it already exists, so that we can flush
                          * it into the system journal */
 
-                        r = journal_file_open(fn, O_RDWR, 0640, &s->runtime_metrics, NULL, &s->runtime_journal);
+                        r = journal_file_open(fn, O_RDWR, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
                         free(fn);
 
                         if (r < 0) {
@@ -1998,7 +2050,7 @@ static int system_journal_open(Server *s) {
                          * it if necessary. */
 
                         (void) mkdir_parents(fn, 0755);
-                        r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, &s->runtime_metrics, NULL, &s->runtime_journal);
+                        r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
                         free(fn);
 
                         if (r < 0) {
@@ -2007,11 +2059,8 @@ static int system_journal_open(Server *s) {
                         }
                 }
 
-                if (s->runtime_journal) {
-                        s->runtime_journal->compress = s->compress;
-
+                if (s->runtime_journal)
                         server_fix_perms(s, s->runtime_journal, 0);
-                }
         }
 
         return r;
@@ -2571,6 +2620,41 @@ static int open_dev_kmsg(Server *s) {
         return 0;
 }
 
+static int open_kernel_seqnum(Server *s) {
+        int fd;
+        uint64_t *p;
+
+        assert(s);
+
+        /* We store the seqnum we last read in an mmaped file. That
+         * way we can just use it like a variable, but it is
+         * persistant and automatically flushed at reboot. */
+
+        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");
+                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);
+                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);
+                return 0;
+        }
+
+        close_nointr_nofail(fd);
+        s->kernel_seqnum = p;
+
+        return 0;
+}
+
 static int open_signalfd(Server *s) {
         sigset_t mask;
         struct epoll_event ev;
@@ -2704,10 +2788,16 @@ static int server_init(Server *s) {
         server_parse_config_file(s);
         server_parse_proc_cmdline(s);
 
+        mkdir_p("/run/systemd/journal", 0755);
+
         s->user_journals = hashmap_new(trivial_hash_func, trivial_compare_func);
         if (!s->user_journals)
                 return log_oom();
 
+        s->mmap = mmap_cache_new();
+        if (!s->mmap)
+                return log_oom();
+
         s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
         if (s->epoll_fd < 0) {
                 log_error("Failed to create epoll object: %m");
@@ -2771,6 +2861,10 @@ static int server_init(Server *s) {
         if (r < 0)
                 return r;
 
+        r = open_kernel_seqnum(s);
+        if (r < 0)
+                return r;
+
         r = open_signalfd(s);
         if (r < 0)
                 return r;
@@ -2825,8 +2919,14 @@ static void server_done(Server *s) {
         if (s->rate_limit)
                 journal_rate_limit_free(s->rate_limit);
 
+        if (s->kernel_seqnum)
+                munmap(s->kernel_seqnum, sizeof(uint64_t));
+
         free(s->buffer);
         free(s->tty_path);
+
+        if (s->mmap)
+                mmap_cache_unref(s->mmap);
 }
 
 int main(int argc, char *argv[]) {
@@ -2845,6 +2945,7 @@ int main(int argc, char *argv[]) {
 
         log_set_target(LOG_TARGET_SAFE);
         log_set_facility(LOG_SYSLOG);
+        log_set_max_level(LOG_DEBUG);
         log_parse_environment();
         log_open();