chiark / gitweb /
journald: be more careful when we try to flush the runtime journal to disk and the...
[elogind.git] / src / journal / journald-server.c
index 3eaf0d3ee240b9dc775aff62359aeee00bdd4d5f..88163c01167a19407bb38098ec28b768b3fb6773 100644 (file)
 #include <linux/sockios.h>
 #include <sys/statvfs.h>
 #include <sys/mman.h>
+#include <sys/timerfd.h>
 
 #include <libudev.h>
 #include <systemd/sd-journal.h>
 #include <systemd/sd-messages.h>
 #include <systemd/sd-daemon.h>
 
-#ifdef HAVE_LOGIND
-#include <systemd/sd-login.h>
-#endif
-
+#include "fileio.h"
 #include "mkdir.h"
 #include "hashmap.h"
 #include "journal-file.h"
@@ -66,6 +64,7 @@
 
 #define USER_JOURNALS_MAX 1024
 
+#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE)
 #define DEFAULT_RATE_LIMIT_INTERVAL (10*USEC_PER_SEC)
 #define DEFAULT_RATE_LIMIT_BURST 200
 
@@ -91,13 +90,14 @@ DEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
 
 static uint64_t available_space(Server *s) {
-        char ids[33], *p;
+        char ids[33];
+        _cleanup_free_ char *p = NULL;
         const char *f;
         sd_id128_t machine;
         struct statvfs ss;
         uint64_t sum = 0, avail = 0, ss_avail = 0;
         int r;
-        DIR *d;
+        _cleanup_closedir_ DIR *d = NULL;
         usec_t ts;
         JournalMetrics *m;
 
@@ -125,13 +125,11 @@ static uint64_t available_space(Server *s) {
                 return 0;
 
         d = opendir(p);
-        free(p);
-
         if (!d)
                 return 0;
 
         if (fstatvfs(dirfd(d), &ss) < 0)
-                goto finish;
+                return 0;
 
         for (;;) {
                 struct stat st;
@@ -170,14 +168,11 @@ static uint64_t available_space(Server *s) {
         s->cached_available_space = avail;
         s->cached_available_space_timestamp = ts;
 
-finish:
-        closedir(d);
-
         return avail;
 }
 
 static void server_read_file_gid(Server *s) {
-        const char *adm = "adm";
+        const char *g = "systemd-journal";
         int r;
 
         assert(s);
@@ -185,9 +180,9 @@ static void server_read_file_gid(Server *s) {
         if (s->file_gid_valid)
                 return;
 
-        r = get_group_creds(&adm, &s->file_gid);
+        r = get_group_creds(&g, &s->file_gid);
         if (r < 0)
-                log_warning("Failed to resolve 'adm' group: %s", strerror(-r));
+                log_warning("Failed to resolve '%s' group: %s", g, strerror(-r));
 
         /* if we couldn't read the gid, then it will be 0, but that's
          * fine and we shouldn't try to resolve the group again, so
@@ -347,6 +342,33 @@ void server_rotate(Server *s) {
         }
 }
 
+void server_sync(Server *s) {
+        JournalFile *f;
+        void *k;
+        Iterator i;
+        int r;
+
+        static const struct itimerspec sync_timer_disable = {};
+
+        if (s->system_journal) {
+                r = journal_file_set_offline(s->system_journal);
+                if (r < 0)
+                        log_error("Failed to sync system journal: %s", strerror(-r));
+        }
+
+        HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
+                r = journal_file_set_offline(f);
+                if (r < 0)
+                        log_error("Failed to sync user journal: %s", strerror(-r));
+        }
+
+        r = timerfd_settime(s->sync_timer_fd, 0, &sync_timer_disable, NULL);
+        if (r < 0)
+                log_error("Failed to disable max timer: %m");
+
+        s->sync_scheduled = false;
+}
+
 void server_vacuum(Server *s) {
         char *p;
         char ids[33];
@@ -394,48 +416,6 @@ void server_vacuum(Server *s) {
         s->cached_available_space_timestamp = 0;
 }
 
-static char *shortened_cgroup_path(pid_t pid) {
-        int r;
-        char *process_path, *init_path, *path;
-
-        assert(pid > 0);
-
-        r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &process_path);
-        if (r < 0)
-                return NULL;
-
-        r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &init_path);
-        if (r < 0) {
-                free(process_path);
-                return NULL;
-        }
-
-        if (endswith(init_path, "/system"))
-                init_path[strlen(init_path) - 7] = 0;
-        else if (streq(init_path, "/"))
-                init_path[0] = 0;
-
-        if (startswith(process_path, init_path)) {
-                char *p;
-
-                p = strdup(process_path + strlen(init_path));
-                if (!p) {
-                        free(process_path);
-                        free(init_path);
-                        return NULL;
-                }
-                path = p;
-        } else {
-                path = process_path;
-                process_path = NULL;
-        }
-
-        free(process_path);
-        free(init_path);
-
-        return path;
-}
-
 bool shall_try_append_again(JournalFile *f, int r) {
 
         /* -E2BIG            Hit configured limit
@@ -490,8 +470,10 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
         }
 
         r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL);
-        if (r >= 0)
+        if (r >= 0) {
+                server_schedule_sync(s);
                 return;
+        }
 
         if (vacuumed || !shall_try_append_again(f, r)) {
                 log_error("Failed to write entry, ignoring: %s", strerror(-r));
@@ -519,18 +501,26 @@ static void dispatch_message_real(
                 const char *label, size_t label_len,
                 const char *unit_id) {
 
-        char *pid = NULL, *uid = NULL, *gid = NULL,
-                *source_time = NULL, *boot_id = NULL, *machine_id = NULL,
-                *comm = NULL, *cmdline = NULL, *hostname = NULL,
-                *audit_session = NULL, *audit_loginuid = NULL,
-                *exe = NULL, *cgroup = NULL, *session = NULL,
-                *owner_uid = NULL, *unit = NULL, *selinux_context = NULL;
-
-        char idbuf[33];
+        char pid[sizeof("_PID=") + DECIMAL_STR_MAX(pid_t)],
+                uid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)],
+                gid[sizeof("_GID=") + DECIMAL_STR_MAX(gid_t)],
+                owner_uid[sizeof("_SYSTEMD_OWNER_UID=") + DECIMAL_STR_MAX(uid_t)],
+                source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)],
+                boot_id[sizeof("_BOOT_ID=") + 32] = "_BOOT_ID=",
+                machine_id[sizeof("_MACHINE_ID=") + 32] = "_MACHINE_ID=";
+        char *comm, *exe, *cmdline, *cgroup, *session, *unit, *hostname;
         sd_id128_t id;
         int r;
-        char *t;
-        uid_t loginuid = 0, realuid = 0;
+        char *t, *c;
+        uid_t realuid = 0, owner = 0, journal_uid;
+        bool owner_valid = false;
+#ifdef HAVE_AUDIT
+        char audit_session[sizeof("_AUDIT_SESSION=") + DECIMAL_STR_MAX(uint32_t)],
+                audit_loginuid[sizeof("_AUDIT_LOGINUID=") + DECIMAL_STR_MAX(uid_t)];
+
+        uint32_t audit;
+        uid_t loginuid;
+#endif
 
         assert(s);
         assert(iovec);
@@ -538,165 +528,154 @@ static void dispatch_message_real(
         assert(n + N_IOVEC_META_FIELDS <= m);
 
         if (ucred) {
-                uint32_t audit;
-#ifdef HAVE_LOGIND
-                uid_t owner;
-#endif
-
                 realuid = ucred->uid;
 
-                if (asprintf(&pid, "_PID=%lu", (unsigned long) ucred->pid) >= 0)
-                        IOVEC_SET_STRING(iovec[n++], pid);
+                sprintf(pid, "_PID=%lu", (unsigned long) ucred->pid);
+                IOVEC_SET_STRING(iovec[n++], pid);
 
-                if (asprintf(&uid, "_UID=%lu", (unsigned long) ucred->uid) >= 0)
-                        IOVEC_SET_STRING(iovec[n++], uid);
+                sprintf(uid, "_UID=%lu", (unsigned long) ucred->uid);
+                IOVEC_SET_STRING(iovec[n++], uid);
 
-                if (asprintf(&gid, "_GID=%lu", (unsigned long) ucred->gid) >= 0)
-                        IOVEC_SET_STRING(iovec[n++], gid);
+                sprintf(gid, "_GID=%lu", (unsigned long) ucred->gid);
+                IOVEC_SET_STRING(iovec[n++], gid);
 
                 r = get_process_comm(ucred->pid, &t);
                 if (r >= 0) {
-                        comm = strappend("_COMM=", t);
+                        comm = strappenda("_COMM=", t);
                         free(t);
-
-                        if (comm)
-                                IOVEC_SET_STRING(iovec[n++], comm);
+                        IOVEC_SET_STRING(iovec[n++], comm);
                 }
 
                 r = get_process_exe(ucred->pid, &t);
                 if (r >= 0) {
-                        exe = strappend("_EXE=", t);
+                        exe = strappenda("_EXE=", t);
                         free(t);
-
-                        if (exe)
-                                IOVEC_SET_STRING(iovec[n++], exe);
+                        IOVEC_SET_STRING(iovec[n++], exe);
                 }
 
-                r = get_process_cmdline(ucred->pid, LINE_MAX, false, &t);
+                r = get_process_cmdline(ucred->pid, 0, false, &t);
                 if (r >= 0) {
-                        cmdline = strappend("_CMDLINE=", t);
+                        cmdline = strappenda("_CMDLINE=", t);
                         free(t);
-
-                        if (cmdline)
-                                IOVEC_SET_STRING(iovec[n++], cmdline);
+                        IOVEC_SET_STRING(iovec[n++], cmdline);
                 }
 
+#ifdef HAVE_AUDIT
                 r = audit_session_from_pid(ucred->pid, &audit);
-                if (r >= 0)
-                        if (asprintf(&audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit) >= 0)
-                                IOVEC_SET_STRING(iovec[n++], audit_session);
+                if (r >= 0) {
+                        sprintf(audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit);
+                        IOVEC_SET_STRING(iovec[n++], audit_session);
+                }
 
                 r = audit_loginuid_from_pid(ucred->pid, &loginuid);
-                if (r >= 0)
-                        if (asprintf(&audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0)
-                                IOVEC_SET_STRING(iovec[n++], audit_loginuid);
-
-                t = shortened_cgroup_path(ucred->pid);
-                if (t) {
-                        cgroup = strappend("_SYSTEMD_CGROUP=", t);
-                        free(t);
-
-                        if (cgroup)
-                                IOVEC_SET_STRING(iovec[n++], cgroup);
+                if (r >= 0) {
+                        sprintf(audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid);
+                        IOVEC_SET_STRING(iovec[n++], audit_loginuid);
                 }
+#endif
 
-#ifdef HAVE_LOGIND
-                if (sd_pid_get_session(ucred->pid, &t) >= 0) {
-                        session = strappend("_SYSTEMD_SESSION=", t);
-                        free(t);
+                r = cg_pid_get_path_shifted(ucred->pid, NULL, &c);
+                if (r >= 0) {
+                        cgroup = strappenda("_SYSTEMD_CGROUP=", c);
+                        IOVEC_SET_STRING(iovec[n++], cgroup);
 
-                        if (session)
+                        r = cg_path_get_session(c, &t);
+                        if (r >= 0) {
+                                session = strappenda("_SYSTEMD_SESSION=", t);
+                                free(t);
                                 IOVEC_SET_STRING(iovec[n++], session);
-                }
+                        }
 
-                if (sd_pid_get_owner_uid(ucred->uid, &owner) >= 0)
-                        if (asprintf(&owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner) >= 0)
-                                IOVEC_SET_STRING(iovec[n++], owner_uid);
-#endif
+                        if (cg_path_get_owner_uid(c, &owner) >= 0) {
+                                owner_valid = true;
 
-                if (cg_pid_get_unit(ucred->pid, &t) >= 0) {
-                        unit = strappend("_SYSTEMD_UNIT=", t);
-                        free(t);
-                } else if (unit_id)
-                        unit = strappend("_SYSTEMD_UNIT=", unit_id);
+                                sprintf(owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner);
+                                IOVEC_SET_STRING(iovec[n++], owner_uid);
+                        }
 
-                if (unit)
-                        IOVEC_SET_STRING(iovec[n++], unit);
+                        if (cg_path_get_unit(c, &t) >= 0) {
+                                unit = strappenda("_SYSTEMD_UNIT=", t);
+                                free(t);
+                        } else if (cg_path_get_user_unit(c, &t) >= 0) {
+                                unit = strappenda("_SYSTEMD_USER_UNIT=", t);
+                                free(t);
+                        } else if (unit_id) {
+                                if (session)
+                                        unit = strappenda("_SYSTEMD_USER_UNIT=", unit_id);
+                                else
+                                        unit = strappenda("_SYSTEMD_UNIT=", unit_id);
+                        } else
+                                unit = NULL;
+
+                        if (unit)
+                                IOVEC_SET_STRING(iovec[n++], unit);
+
+                        free(c);
+                }
 
 #ifdef HAVE_SELINUX
                 if (label) {
-                        selinux_context = malloc(sizeof("_SELINUX_CONTEXT=") + label_len);
-                        if (selinux_context) {
-                                memcpy(selinux_context, "_SELINUX_CONTEXT=", sizeof("_SELINUX_CONTEXT=")-1);
-                                memcpy(selinux_context+sizeof("_SELINUX_CONTEXT=")-1, label, label_len);
-                                selinux_context[sizeof("_SELINUX_CONTEXT=")-1+label_len] = 0;
-                                IOVEC_SET_STRING(iovec[n++], selinux_context);
-                        }
+                        char *selinux_context = alloca(sizeof("_SELINUX_CONTEXT=") + label_len);
+
+                        *((char*) mempcpy(stpcpy(selinux_context, "_SELINUX_CONTEXT="), label, label_len)) = 0;
+                        IOVEC_SET_STRING(iovec[n++], selinux_context);
                 } else {
                         security_context_t con;
 
                         if (getpidcon(ucred->pid, &con) >= 0) {
-                                selinux_context = strappend("_SELINUX_CONTEXT=", con);
-                                if (selinux_context)
-                                        IOVEC_SET_STRING(iovec[n++], selinux_context);
+                                char *selinux_context = strappenda("_SELINUX_CONTEXT=", con);
 
                                 freecon(con);
+                                IOVEC_SET_STRING(iovec[n++], selinux_context);
                         }
                 }
 #endif
         }
 
         if (tv) {
-                if (asprintf(&source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu",
-                             (unsigned long long) timeval_load(tv)) >= 0)
-                        IOVEC_SET_STRING(iovec[n++], source_time);
+                sprintf(source_time, "_SOURCE_REALTIME_TIMESTAMP=%llu", (unsigned long long) timeval_load(tv));
+                IOVEC_SET_STRING(iovec[n++], source_time);
         }
 
         /* Note that strictly speaking storing the boot id here is
          * redundant since the entry includes this in-line
          * anyway. However, we need this indexed, too. */
         r = sd_id128_get_boot(&id);
-        if (r >= 0)
-                if (asprintf(&boot_id, "_BOOT_ID=%s", sd_id128_to_string(id, idbuf)) >= 0)
-                        IOVEC_SET_STRING(iovec[n++], boot_id);
+        if (r >= 0) {
+                sd_id128_to_string(id, boot_id + sizeof("_BOOT_ID=") - 1);
+                IOVEC_SET_STRING(iovec[n++], boot_id);
+        }
 
         r = sd_id128_get_machine(&id);
-        if (r >= 0)
-                if (asprintf(&machine_id, "_MACHINE_ID=%s", sd_id128_to_string(id, idbuf)) >= 0)
-                        IOVEC_SET_STRING(iovec[n++], machine_id);
+        if (r >= 0) {
+                sd_id128_to_string(id, machine_id + sizeof("_MACHINE_ID=") - 1);
+                IOVEC_SET_STRING(iovec[n++], machine_id);
+        }
 
         t = gethostname_malloc();
         if (t) {
-                hostname = strappend("_HOSTNAME=", t);
+                hostname = strappenda("_HOSTNAME=", t);
                 free(t);
-                if (hostname)
-                        IOVEC_SET_STRING(iovec[n++], hostname);
+                IOVEC_SET_STRING(iovec[n++], hostname);
         }
 
         assert(n <= m);
 
-        write_to_journal(s,
-                         s->split_mode == SPLIT_NONE ? 0 :
-                         (s->split_mode == SPLIT_UID ? realuid :
-                          (realuid == 0 ? 0 : loginuid)), iovec, n);
-
-        free(pid);
-        free(uid);
-        free(gid);
-        free(comm);
-        free(exe);
-        free(cmdline);
-        free(source_time);
-        free(boot_id);
-        free(machine_id);
-        free(hostname);
-        free(audit_session);
-        free(audit_loginuid);
-        free(cgroup);
-        free(session);
-        free(owner_uid);
-        free(unit);
-        free(selinux_context);
+        if (s->split_mode == SPLIT_UID && realuid > 0)
+                /* Split up strictly by any UID */
+                journal_uid = realuid;
+        else if (s->split_mode == SPLIT_LOGIN && realuid > 0 && owner_valid && owner > 0)
+                /* Split up by login UIDs, this avoids creation of
+                 * individual journals for system UIDs.  We do this
+                 * only if the realuid is not root, in order not to
+                 * accidentally leak privileged information to the
+                 * user that is logged by a privileged process that is
+                 * part of an unprivileged session.*/
+                journal_uid = owner;
+        else
+                journal_uid = 0;
+
+        write_to_journal(s, journal_uid, iovec, n);
 }
 
 void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) {
@@ -705,7 +684,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
         struct iovec iovec[N_IOVEC_META_FIELDS + 4];
         int n = 0;
         va_list ap;
-        struct ucred ucred;
+        struct ucred ucred = {};
 
         assert(s);
         assert(format);
@@ -726,7 +705,6 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
                 IOVEC_SET_STRING(iovec[n++], mid);
         }
 
-        zero(ucred);
         ucred.pid = getpid();
         ucred.uid = getuid();
         ucred.gid = getgid();
@@ -743,8 +721,9 @@ void server_dispatch_message(
                 const char *unit_id,
                 int priority) {
 
-        int rl;
-        char *path = NULL, *c;
+        int rl, r;
+        _cleanup_free_ char *path = NULL;
+        char *c;
 
         assert(s);
         assert(iovec || n == 0);
@@ -758,8 +737,8 @@ void server_dispatch_message(
         if (!ucred)
                 goto finish;
 
-        path = shortened_cgroup_path(ucred->pid);
-        if (!path)
+        r = cg_pid_get_path_shifted(ucred->pid, NULL, &path);
+        if (r < 0)
                 goto finish;
 
         /* example: /user/lennart/3/foobar
@@ -778,18 +757,16 @@ void server_dispatch_message(
                 }
         }
 
-        rl = journal_rate_limit_test(s->rate_limit, path, priority & LOG_PRIMASK, available_space(s));
+        rl = journal_rate_limit_test(s->rate_limit, path,
+                                     priority & LOG_PRIMASK, available_space(s));
 
-        if (rl == 0) {
-                free(path);
+        if (rl == 0)
                 return;
-        }
 
         /* Write a suppression message if we suppressed something */
         if (rl > 1)
-                server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED, "Suppressed %u messages from %s", rl - 1, path);
-
-        free(path);
+                server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED,
+                                      "Suppressed %u messages from %s", rl - 1, path);
 
 finish:
         dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id);
@@ -934,6 +911,8 @@ int server_flush_to_var(Server *s) {
                 return r;
         }
 
+        sd_journal_set_data_threshold(j, 0);
+
         SD_JOURNAL_FOREACH(j) {
                 Object *o = NULL;
                 JournalFile *f;
@@ -959,6 +938,12 @@ int server_flush_to_var(Server *s) {
                 server_rotate(s);
                 server_vacuum(s);
 
+                if (!s->system_journal) {
+                        log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful.");
+                        r = -EIO;
+                        goto finish;
+                }
+
                 log_debug("Retrying write.");
                 r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL);
                 if (r < 0) {
@@ -976,8 +961,7 @@ finish:
         if (r >= 0)
                 rm_rf("/run/log/journal", false, true, false);
 
-        if (j)
-                sd_journal_close(j);
+        sd_journal_close(j);
 
         return r;
 }
@@ -1007,11 +991,10 @@ int process_event(Server *s, struct epoll_event *ev) {
                         return -errno;
                 }
 
-                log_info("Received SIG%s", signal_to_string(sfsi.ssi_signo));
-
                 if (sfsi.ssi_signo == SIGUSR1) {
                         touch("/run/systemd/journal/flushed");
                         server_flush_to_var(s);
+                        server_sync(s);
                         return 1;
                 }
 
@@ -1021,8 +1004,23 @@ int process_event(Server *s, struct epoll_event *ev) {
                         return 1;
                 }
 
+                log_info("Received SIG%s", signal_to_string(sfsi.ssi_signo));
+
                 return 0;
 
+        } else if (ev->data.fd == s->sync_timer_fd) {
+                int r;
+                uint64_t t;
+
+                log_debug("Got sync request from epoll.");
+
+                r = read(ev->data.fd, (void *)&t, sizeof(t));
+                if (r < 0)
+                        return 0;
+
+                server_sync(s);
+                return 1;
+
         } else if (ev->data.fd == s->dev_kmsg_fd) {
                 int r;
 
@@ -1232,7 +1230,8 @@ static int open_signalfd(Server *s) {
 }
 
 static int server_parse_proc_cmdline(Server *s) {
-        char *line, *w, *state;
+        _cleanup_free_ char *line = NULL;
+        char *w, *state;
         int r;
         size_t l;
 
@@ -1246,13 +1245,11 @@ static int server_parse_proc_cmdline(Server *s) {
         }
 
         FOREACH_WORD_QUOTED(w, l, line, state) {
-                char *word;
+                _cleanup_free_ char *word;
 
                 word = strndup(w, l);
-                if (!word) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                if (!word)
+                        return -ENOMEM;
 
                 if (startswith(word, "systemd.journald.forward_to_syslog=")) {
                         r = parse_boolean(word + 35);
@@ -1274,25 +1271,18 @@ static int server_parse_proc_cmdline(Server *s) {
                                 s->forward_to_console = r;
                 } else if (startswith(word, "systemd.journald"))
                         log_warning("Invalid systemd.journald parameter. Ignoring.");
-
-                free(word);
         }
 
-        r = 0;
-
-finish:
-        free(line);
-        return r;
+        return 0;
 }
 
 static int server_parse_config_file(Server *s) {
-        FILE *f;
-        const char *fn;
+        static const char fn[] = "/etc/systemd/journald.conf";
+        _cleanup_fclose_ FILE *f = NULL;
         int r;
 
         assert(s);
 
-        fn = "/etc/systemd/journald.conf";
         f = fopen(fn, "re");
         if (!f) {
                 if (errno == ENOENT)
@@ -1302,25 +1292,75 @@ static int server_parse_config_file(Server *s) {
                 return -errno;
         }
 
-        r = config_parse(fn, f, "Journal\0", config_item_perf_lookup, (void*) journald_gperf_lookup, false, s);
+        r = config_parse(NULL, fn, f, "Journal\0", config_item_perf_lookup,
+                         (void*) journald_gperf_lookup, false, false, s);
         if (r < 0)
                 log_warning("Failed to parse configuration file: %s", strerror(-r));
 
-        fclose(f);
-
         return r;
 }
 
+static int server_open_sync_timer(Server *s) {
+        int r;
+        struct epoll_event ev;
+
+        assert(s);
+
+        s->sync_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+        if (s->sync_timer_fd < 0)
+                return -errno;
+
+        zero(ev);
+        ev.events = EPOLLIN;
+        ev.data.fd = s->sync_timer_fd;
+
+        r = epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->sync_timer_fd, &ev);
+        if (r < 0) {
+                log_error("Failed to add idle timer fd to epoll object: %m");
+                return -errno;
+        }
+
+        return 0;
+}
+
+int server_schedule_sync(Server *s) {
+        int r;
+
+        assert(s);
+
+        if (s->sync_scheduled)
+                return 0;
+
+        if (s->sync_interval_usec) {
+                struct itimerspec sync_timer_enable = {
+                        .it_value.tv_sec = s->sync_interval_usec / USEC_PER_SEC,
+                        .it_value.tv_nsec = s->sync_interval_usec % MSEC_PER_SEC,
+                };
+
+                r = timerfd_settime(s->sync_timer_fd, 0, &sync_timer_enable, NULL);
+                if (r < 0)
+                        return -errno;
+        }
+
+        s->sync_scheduled = true;
+
+        return 0;
+}
+
 int server_init(Server *s) {
         int n, r, fd;
 
         assert(s);
 
         zero(*s);
-        s->syslog_fd = s->native_fd = s->stdout_fd = s->signal_fd = s->epoll_fd = s->dev_kmsg_fd = -1;
+        s->sync_timer_fd = s->syslog_fd = s->native_fd = s->stdout_fd =
+            s->signal_fd = s->epoll_fd = s->dev_kmsg_fd = -1;
         s->compress = true;
         s->seal = true;
 
+        s->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC;
+        s->sync_scheduled = false;
+
         s->rate_limit_interval = DEFAULT_RATE_LIMIT_INTERVAL;
         s->rate_limit_burst = DEFAULT_RATE_LIMIT_BURST;
 
@@ -1336,6 +1376,12 @@ int server_init(Server *s) {
 
         server_parse_config_file(s);
         server_parse_proc_cmdline(s);
+        if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
+                log_debug("Setting both rate limit interval and burst from %llu,%u to 0,0",
+                          (long long unsigned) s->rate_limit_interval,
+                          s->rate_limit_burst);
+                s->rate_limit_interval = s->rate_limit_burst = 0;
+        }
 
         mkdir_p("/run/systemd/journal", 0755);
 
@@ -1414,6 +1460,10 @@ int server_init(Server *s) {
         if (r < 0)
                 return r;
 
+        r = server_open_sync_timer(s);
+        if (r < 0)
+                return r;
+
         r = open_signalfd(s);
         if (r < 0)
                 return r;
@@ -1422,7 +1472,8 @@ int server_init(Server *s) {
         if (!s->udev)
                 return -ENOMEM;
 
-        s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst);
+        s->rate_limit = journal_rate_limit_new(s->rate_limit_interval,
+                                               s->rate_limit_burst);
         if (!s->rate_limit)
                 return -ENOMEM;
 
@@ -1485,6 +1536,9 @@ void server_done(Server *s) {
         if (s->dev_kmsg_fd >= 0)
                 close_nointr_nofail(s->dev_kmsg_fd);
 
+        if (s->sync_timer_fd >= 0)
+                close_nointr_nofail(s->sync_timer_fd);
+
         if (s->rate_limit)
                 journal_rate_limit_free(s->rate_limit);