chiark / gitweb /
journald: enforce some syntax restrictions on field names sent from the client side
[elogind.git] / src / journal / journald.c
index c457d2786b266ee4e48006bccd4b78dbd800be73..d35e1c119ab5984604471f1ed08b240ecadc93c7 100644 (file)
@@ -54,6 +54,10 @@ typedef struct Server {
 
         char *buffer;
         size_t buffer_size;
+
+        JournalMetrics metrics;
+        uint64_t max_use;
+        bool compress;
 } Server;
 
 static void fix_perms(JournalFile *f, uid_t uid) {
@@ -143,6 +147,8 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
                 return s->system_journal;
 
         fix_perms(f, uid);
+        f->metrics = s->metrics;
+        f->compress = s->compress;
 
         r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f);
         if (r < 0) {
@@ -153,6 +159,66 @@ static JournalFile* find_journal(Server *s, uid_t uid) {
         return f;
 }
 
+static void server_vacuum(Server *s) {
+        Iterator i;
+        void *k;
+        char *p;
+        char ids[33];
+        sd_id128_t machine;
+        int r;
+        JournalFile *f;
+
+        log_info("Rotating...");
+
+        if (s->runtime_journal) {
+                r = journal_file_rotate(&s->runtime_journal);
+                if (r < 0)
+                        log_error("Failed to rotate %s: %s", s->runtime_journal->path, strerror(-r));
+        }
+
+        if (s->system_journal) {
+                r = journal_file_rotate(&s->system_journal);
+                if (r < 0)
+                        log_error("Failed to rotate %s: %s", s->system_journal->path, strerror(-r));
+        }
+
+        HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
+                r = journal_file_rotate(&f);
+                if (r < 0)
+                        log_error("Failed to rotate %s: %s", f->path, strerror(-r));
+                else
+                        hashmap_replace(s->user_journals, k, f);
+        }
+
+        log_info("Vacuuming...");
+
+        r = sd_id128_get_machine(&machine);
+        if (r < 0) {
+                log_error("Failed to get machine ID: %s", strerror(-r));
+                return;
+        }
+
+        if (asprintf(&p, "/var/log/journal/%s", sd_id128_to_string(machine, ids)) < 0) {
+                log_error("Out of memory.");
+                return;
+        }
+
+        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 (asprintf(&p, "/run/log/journal/%s", ids) < 0) {
+                log_error("Out of memory.");
+                return;
+        }
+
+        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);
+}
+
 static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv) {
         char *pid = NULL, *uid = NULL, *gid = NULL,
                 *source_time = NULL, *boot_id = NULL, *machine_id = NULL,
@@ -166,6 +232,7 @@ static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne
         char *t;
         uid_t loginuid = 0, realuid = 0;
         JournalFile *f;
+        bool vacuumed = false;
 
         assert(s);
         assert(iovec || n == 0);
@@ -262,12 +329,23 @@ static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigne
 
         assert(n <= m);
 
+retry:
         f = find_journal(s, realuid == 0 ? 0 : loginuid);
         if (!f)
                 log_warning("Dropping message, as we can't find a place to store the data.");
         else {
                 r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL);
 
+                if (r == -E2BIG && !vacuumed) {
+                        log_info("Allocation limit reached.");
+
+                        server_vacuum(s);
+                        vacuumed = true;
+
+                        log_info("Retrying write.");
+                        goto retry;
+                }
+
                 if (r < 0)
                         log_error("Failed to write entry, ignoring: %s", strerror(-r));
         }
@@ -317,6 +395,41 @@ static void process_syslog_message(Server *s, const char *buf, struct ucred *ucr
         free(syslog_priority);
 }
 
+static bool valid_user_field(const char *p, size_t l) {
+        const char *a;
+
+        /* We kinda enforce POSIX syntax recommendations for
+           environment variables here, but make a couple of additional
+           requirements.
+
+           http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
+
+        /* No empty field names */
+        if (l <= 0)
+                return false;
+
+        /* Don't allow names longer than 64 chars */
+        if (l > 64)
+                return false;
+
+        /* Variables starting with an underscore are protected */
+        if (p[0] == '_')
+                return false;
+
+        /* Don't allow digits as first character */
+        if (p[0] >= '0' && p[0] <= '9')
+                return false;
+
+        /* Only allow A-Z0-9 and '_' */
+        for (a = p; a < p + l; a++)
+                if (!((*a >= 'A' && *a <= 'Z') ||
+                      (*a >= '0' && *a <= '9') ||
+                      *a == '_'))
+                        return false;
+
+        return true;
+}
+
 static void process_native_message(Server *s, const void *buffer, size_t buffer_size, struct ucred *ucred, struct timeval *tv) {
         struct iovec *iovec = NULL;
         unsigned n = 0, m = 0, j;
@@ -350,8 +463,9 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_
                         continue;
                 }
 
-                if (*p == '.') {
-                        /* Control command, ignore for now */
+                if (*p == '.' || *p == '#') {
+                        /* Ignore control commands for now, and
+                         * comments too. */
                         remaining -= (e - p) + 1;
                         p = e + 1;
                         continue;
@@ -376,7 +490,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_
 
                 q = memchr(p, '=', e - p);
                 if (q) {
-                        if (p[0] != '_') {
+                        if (valid_user_field(p, q - p)) {
                                 /* If the field name starts with an
                                  * underscore, skip the variable,
                                  * since that indidates a trusted
@@ -417,7 +531,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_
                         k[e - p] = '=';
                         memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
 
-                        if (k[0] != '_') {
+                        if (valid_user_field(p, e - p)) {
                                 iovec[n].iov_base = k;
                                 iovec[n].iov_len = (e - p) + 1 + l;
                                 n++;
@@ -586,6 +700,9 @@ static int system_journal_open(Server *s) {
         free(fn);
 
         if (r >= 0) {
+                s->system_journal->metrics = s->metrics;
+                s->system_journal->compress = s->compress;
+
                 fix_perms(s->system_journal, 0);
                 return r;
         }
@@ -610,6 +727,9 @@ static int system_journal_open(Server *s) {
                 return r;
         }
 
+        s->runtime_journal->metrics = s->metrics;
+        s->runtime_journal->compress = s->compress;
+
         fix_perms(s->runtime_journal, 0);
         return r;
 }
@@ -715,6 +835,11 @@ static int server_init(Server *s) {
 
         zero(*s);
         s->syslog_fd = s->native_fd = s->signal_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;
 
         s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
         if (s->epoll_fd < 0) {
@@ -852,6 +977,7 @@ int main(int argc, char *argv[]) {
         }
 
         log_set_target(LOG_TARGET_CONSOLE);
+        log_set_max_level(LOG_DEBUG);
         log_parse_environment();
         log_open();