chiark / gitweb /
journal: automatically deduce journal metrics from file system sizes
[elogind.git] / src / journal / journald.c
index 52cdc7058c998a5fafd776685569b64622bbf10f..f637d9471dfb5be4ba6fd5f6a8c880cbdcc0c216 100644 (file)
@@ -53,6 +53,8 @@
 
 #define RECHECK_VAR_AVAILABLE_USEC (30*USEC_PER_SEC)
 
+#define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC)
+
 typedef struct StdoutStream StdoutStream;
 
 typedef struct Server {
@@ -73,8 +75,9 @@ typedef struct Server {
 
         JournalRateLimit *rate_limit;
 
-        JournalMetrics metrics;
-        uint64_t max_use;
+        JournalMetrics runtime_metrics;
+        JournalMetrics system_metrics;
+
         bool compress;
 
         uint64_t cached_available_space;
@@ -116,15 +119,17 @@ struct StdoutStream {
 static int server_flush_to_var(Server *s);
 
 static uint64_t available_space(Server *s) {
-        char ids[33];
-        sd_id128_t machine;
-        char *p;
+        char ids[33], *p;
         const char *f;
+        sd_id128_t machine;
         struct statvfs ss;
         uint64_t sum = 0, avail = 0, ss_avail = 0;
         int r;
         DIR *d;
-        usec_t ts = now(CLOCK_MONOTONIC);
+        usec_t ts;
+        JournalMetrics *m;
+
+        ts = now(CLOCK_MONOTONIC);
 
         if (s->cached_available_space_timestamp + RECHECK_AVAILABLE_SPACE_USEC > ts)
                 return s->cached_available_space;
@@ -133,10 +138,15 @@ static uint64_t available_space(Server *s) {
         if (r < 0)
                 return 0;
 
-        if (s->system_journal)
+        if (s->system_journal) {
                 f = "/var/log/journal/";
-        else
+                m = &s->system_metrics;
+        } else {
                 f = "/run/log/journal/";
+                m = &s->runtime_metrics;
+        }
+
+        assert(m);
 
         p = strappend(f, sd_id128_to_string(machine, ids));
         if (!p)
@@ -174,11 +184,11 @@ static uint64_t available_space(Server *s) {
                 sum += (uint64_t) st.st_blocks * (uint64_t) st.st_blksize;
         }
 
-        avail = sum >= s->max_use ? 0 : s->max_use - sum;
+        avail = sum >= m->max_use ? 0 : m->max_use - sum;
 
         ss_avail = ss.f_bsize * ss.f_bavail;
 
-        ss_avail = ss_avail < s->metrics.keep_free ? 0 : ss_avail - s->metrics.keep_free;
+        ss_avail = ss_avail < m->keep_free ? 0 : ss_avail - m->keep_free;
 
         if (ss_avail < avail)
                 avail = ss_avail;
@@ -283,7 +293,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
                 return s->system_journal;
 
         fix_perms(f, uid);
-        f->metrics = s->metrics;
+        f->metrics = s->system_metrics;
         f->compress = s->compress;
 
         r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f);
@@ -334,25 +344,32 @@ static void server_vacuum(Server *s) {
                 return;
         }
 
-        if (asprintf(&p, "/var/log/journal/%s", sd_id128_to_string(machine, ids)) < 0) {
-                log_error("Out of memory.");
-                return;
-        }
+        sd_id128_to_string(machine, ids);
 
-        r = journal_directory_vacuum(p, s->max_use, s->metrics.keep_free);
-        if (r < 0 && r != -ENOENT)
-                log_error("Failed to vacuum %s: %s", p, strerror(-r));
-        free(p);
+        if (s->system_journal) {
+                if (asprintf(&p, "/var/log/journal/%s", ids) < 0) {
+                        log_error("Out of memory.");
+                        return;
+                }
 
-        if (asprintf(&p, "/run/log/journal/%s", ids) < 0) {
-                log_error("Out of memory.");
-                return;
+                r = journal_directory_vacuum(p, s->system_metrics.max_use, s->system_metrics.keep_free);
+                if (r < 0 && r != -ENOENT)
+                        log_error("Failed to vacuum %s: %s", p, strerror(-r));
+                free(p);
         }
 
-        r = journal_directory_vacuum(p, s->max_use, s->metrics.keep_free);
-        if (r < 0 && r != -ENOENT)
-                log_error("Failed to vacuum %s: %s", p, strerror(-r));
-        free(p);
+
+        if (s->runtime_journal) {
+                if (asprintf(&p, "/run/log/journal/%s", ids) < 0) {
+                        log_error("Out of memory.");
+                        return;
+                }
+
+                r = journal_directory_vacuum(p, s->runtime_metrics.max_use, s->runtime_metrics.keep_free);
+                if (r < 0 && r != -ENOENT)
+                        log_error("Failed to vacuum %s: %s", p, strerror(-r));
+                free(p);
+        }
 
         s->cached_available_space_timestamp = 0;
 }
@@ -376,11 +393,22 @@ static char *shortened_cgroup_path(pid_t pid) {
         if (streq(init_path, "/"))
                 init_path[0] = 0;
 
-        if (startswith(process_path, init_path))
-                path = process_path + strlen(init_path);
-        else
+        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;
@@ -542,7 +570,7 @@ static void dispatch_message(Server *s,
                              struct timeval *tv,
                              int priority) {
         int rl;
-        char *path, *c;
+        char *path = NULL, *c;
 
         assert(s);
         assert(iovec || n == 0);
@@ -1132,7 +1160,9 @@ static int system_journal_open(Server *s) {
                 free(fn);
 
                 if (r >= 0) {
-                        s->system_journal->metrics = s->metrics;
+                        journal_default_metrics(&s->system_metrics, s->system_journal->fd);
+
+                        s->system_journal->metrics = s->system_metrics;
                         s->system_journal->compress = s->compress;
 
                         fix_perms(s->system_journal, 0);
@@ -1188,7 +1218,9 @@ static int system_journal_open(Server *s) {
                 }
 
                 if (s->runtime_journal) {
-                        s->runtime_journal->metrics = s->metrics;
+                        journal_default_metrics(&s->runtime_metrics, s->runtime_journal->fd);
+
+                        s->runtime_journal->metrics = s->runtime_metrics;
                         s->runtime_journal->compress = s->compress;
 
                         fix_perms(s->runtime_journal, 0);
@@ -1276,6 +1308,69 @@ finish:
         return r;
 }
 
+static void forward_syslog(Server *s, const void *buffer, size_t length, struct ucred *ucred, struct timeval *tv) {
+        struct msghdr msghdr;
+        struct iovec iovec;
+        struct cmsghdr *cmsg;
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+                            CMSG_SPACE(sizeof(struct timeval))];
+        } control;
+        union sockaddr_union sa;
+
+        assert(s);
+
+        zero(msghdr);
+
+        zero(iovec);
+        iovec.iov_base = (void*) buffer;
+        iovec.iov_len = length;
+        msghdr.msg_iov = &iovec;
+        msghdr.msg_iovlen = 1;
+
+        zero(sa);
+        sa.un.sun_family = AF_UNIX;
+        strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path));
+        msghdr.msg_name = &sa;
+        msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
+
+        zero(control);
+        msghdr.msg_control = &control;
+        msghdr.msg_controllen = sizeof(control);
+
+        cmsg = CMSG_FIRSTHDR(&msghdr);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_CREDENTIALS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+        memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
+        msghdr.msg_controllen = cmsg->cmsg_len;
+
+        /* Forward the syslog message we received via /dev/log to
+         * /run/systemd/syslog. Unfortunately we currently can't set
+         * the SO_TIMESTAMP auxiliary data, and hence we don't. */
+
+        if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
+                return;
+
+        if (errno == ESRCH) {
+                struct ucred u;
+
+                /* Hmm, presumably the sender process vanished
+                 * by now, so let's fix it as good as we
+                 * can, and retry */
+
+                u = *ucred;
+                u.pid = getpid();
+                memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
+
+                if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
+                        return;
+        }
+
+        log_debug("Failed to forward syslog message: %m");
+}
+
 static int process_event(Server *s, struct epoll_event *ev) {
         assert(s);
 
@@ -1396,6 +1491,7 @@ static int process_event(Server *s, struct epoll_event *ev) {
                                 else
                                         s->buffer[n] = 0;
 
+                                forward_syslog(s, s->buffer, n, ucred, tv);
                                 process_syslog_message(s, strstrip(s->buffer), ucred, tv);
                         } else
                                 process_native_message(s, s->buffer, n, ucred, tv);
@@ -1444,6 +1540,7 @@ static int open_syslog_socket(Server *s) {
         union sockaddr_union sa;
         int one, r;
         struct epoll_event ev;
+        struct timeval tv;
 
         assert(s);
 
@@ -1457,7 +1554,7 @@ static int open_syslog_socket(Server *s) {
 
                 zero(sa);
                 sa.un.sun_family = AF_UNIX;
-                strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path));
+                strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
 
                 unlink(sa.un.sun_path);
 
@@ -1484,6 +1581,15 @@ static int open_syslog_socket(Server *s) {
                 return -errno;
         }
 
+        /* Since we use the same socket for forwarding this to some
+         * other syslog implementation, make sure we don't hang
+         * forever */
+        timeval_store(&tv, SYSLOG_TIMEOUT_USEC);
+        if (setsockopt(s->syslog_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
+                log_error("SO_SNDTIMEO failed: %m");
+                return -errno;
+        }
+
         zero(ev);
         ev.events = EPOLLIN;
         ev.data.fd = s->syslog_fd;
@@ -1631,12 +1737,11 @@ static int server_init(Server *s) {
 
         zero(*s);
         s->syslog_fd = s->native_fd = s->stdout_fd = s->signal_fd = s->epoll_fd = -1;
-        s->metrics.max_size = DEFAULT_MAX_SIZE;
-        s->metrics.min_size = DEFAULT_MIN_SIZE;
-        s->metrics.keep_free = DEFAULT_KEEP_FREE;
-        s->max_use = DEFAULT_MAX_USE;
         s->compress = true;
 
+        memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics));
+        memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics));
+
         s->user_journals = hashmap_new(trivial_hash_func, trivial_compare_func);
         if (!s->user_journals) {
                 log_error("Out of memory.");
@@ -1752,6 +1857,8 @@ static void server_done(Server *s) {
 
         if (s->rate_limit)
                 journal_rate_limit_free(s->rate_limit);
+
+        free(s->buffer);
 }
 
 int main(int argc, char *argv[]) {
@@ -1769,7 +1876,6 @@ int main(int argc, char *argv[]) {
         }
 
         log_set_target(LOG_TARGET_CONSOLE);
-        log_set_max_level(LOG_DEBUG);
         log_parse_environment();
         log_open();