5808e7
From 538bd9b42dabf1145cf7ab77f32347d516b01e88 Mon Sep 17 00:00:00 2001
5808e7
From: Lennart Poettering <lennart@poettering.net>
5808e7
Date: Tue, 12 May 2020 18:56:34 +0200
5808e7
Subject: [PATCH] journald: rework pid change handling
5808e7
5808e7
Let's introduce an explicit line ending marker for line endings due to
5808e7
pid change.
5808e7
5808e7
Let's also make sure we don't get confused with buffer management.
5808e7
5808e7
Fixes: #15654
5808e7
(cherry picked from commit 45ba1ea5e9264d385fa565328fe957ef1d78caa1)
5808e7
5808e7
Resolves: #2029426
5808e7
---
5808e7
 src/journal/journald-stream.c | 99 +++++++++++++++++++++++------------
5808e7
 1 file changed, 66 insertions(+), 33 deletions(-)
5808e7
5808e7
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
5808e7
index ab1a855943..5be5b0939c 100644
5808e7
--- a/src/journal/journald-stream.c
5808e7
+++ b/src/journal/journald-stream.c
5808e7
@@ -54,6 +54,7 @@ typedef enum LineBreak {
5808e7
         LINE_BREAK_NUL,
5808e7
         LINE_BREAK_LINE_MAX,
5808e7
         LINE_BREAK_EOF,
5808e7
+        LINE_BREAK_PID_CHANGE,
5808e7
         _LINE_BREAK_MAX,
5808e7
         _LINE_BREAK_INVALID = -1,
5808e7
 } LineBreak;
5808e7
@@ -310,6 +311,7 @@ static int stdout_stream_log(
5808e7
                 [LINE_BREAK_NUL]        = "_LINE_BREAK=nul",
5808e7
                 [LINE_BREAK_LINE_MAX]   = "_LINE_BREAK=line-max",
5808e7
                 [LINE_BREAK_EOF]        = "_LINE_BREAK=eof",
5808e7
+                [LINE_BREAK_PID_CHANGE] = "_LINE_BREAK=pid-change",
5808e7
         };
5808e7
 
5808e7
         const char *c = line_break_field_table[line_break];
5808e7
@@ -431,21 +433,43 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
5808e7
         assert_not_reached("Unknown stream state");
5808e7
 }
5808e7
 
5808e7
-static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
5808e7
-        char *p;
5808e7
-        size_t remaining;
5808e7
+static int stdout_stream_found(
5808e7
+                StdoutStream *s,
5808e7
+                char *p,
5808e7
+                size_t l,
5808e7
+                LineBreak line_break) {
5808e7
+
5808e7
+        char saved;
5808e7
         int r;
5808e7
 
5808e7
         assert(s);
5808e7
+        assert(p);
5808e7
+
5808e7
+        /* Let's NUL terminate the specified buffer for this call, and revert back afterwards */
5808e7
+        saved = p[l];
5808e7
+        p[l] = 0;
5808e7
+        r = stdout_stream_line(s, p, line_break);
5808e7
+        p[l] = saved;
5808e7
 
5808e7
-        p = s->buffer;
5808e7
-        remaining = s->length;
5808e7
+        return r;
5808e7
+}
5808e7
+
5808e7
+static int stdout_stream_scan(
5808e7
+                StdoutStream *s,
5808e7
+                char *p,
5808e7
+                size_t remaining,
5808e7
+                LineBreak force_flush,
5808e7
+                size_t *ret_consumed) {
5808e7
 
5808e7
-        /* XXX: This function does nothing if (s->length == 0) */
5808e7
+        size_t consumed = 0;
5808e7
+        int r;
5808e7
+
5808e7
+        assert(s);
5808e7
+        assert(p);
5808e7
 
5808e7
         for (;;) {
5808e7
                 LineBreak line_break;
5808e7
-                size_t skip;
5808e7
+                size_t skip, found;
5808e7
                 char *end1, *end2;
5808e7
 
5808e7
                 end1 = memchr(p, '\n', remaining);
5808e7
@@ -453,43 +477,40 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
5808e7
 
5808e7
                 if (end2) {
5808e7
                         /* We found a NUL terminator */
5808e7
-                        skip = end2 - p + 1;
5808e7
+                        found = end2 - p;
5808e7
+                        skip = found + 1;
5808e7
                         line_break = LINE_BREAK_NUL;
5808e7
                 } else if (end1) {
5808e7
                         /* We found a \n terminator */
5808e7
-                        *end1 = 0;
5808e7
-                        skip = end1 - p + 1;
5808e7
+                        found = end1 - p;
5808e7
+                        skip = found + 1;
5808e7
                         line_break = LINE_BREAK_NEWLINE;
5808e7
                 } else if (remaining >= s->server->line_max) {
5808e7
                         /* Force a line break after the maximum line length */
5808e7
-                        *(p + s->server->line_max) = 0;
5808e7
-                        skip = remaining;
5808e7
+                        found = skip = s->server->line_max;
5808e7
                         line_break = LINE_BREAK_LINE_MAX;
5808e7
                 } else
5808e7
                         break;
5808e7
 
5808e7
-                r = stdout_stream_line(s, p, line_break);
5808e7
+                r = stdout_stream_found(s, p, found, line_break);
5808e7
                 if (r < 0)
5808e7
                         return r;
5808e7
 
5808e7
-                remaining -= skip;
5808e7
                 p += skip;
5808e7
+                consumed += skip;
5808e7
+                remaining -= skip;
5808e7
         }
5808e7
 
5808e7
-        if (force_flush && remaining > 0) {
5808e7
-                p[remaining] = 0;
5808e7
-                r = stdout_stream_line(s, p, LINE_BREAK_EOF);
5808e7
+        if (force_flush >= 0 && remaining > 0) {
5808e7
+                r = stdout_stream_found(s, p, remaining, force_flush);
5808e7
                 if (r < 0)
5808e7
                         return r;
5808e7
 
5808e7
-                p += remaining;
5808e7
-                remaining = 0;
5808e7
+                consumed += remaining;
5808e7
         }
5808e7
 
5808e7
-        if (p > s->buffer) {
5808e7
-                memmove(s->buffer, p, remaining);
5808e7
-                s->length = remaining;
5808e7
-        }
5808e7
+        if (ret_consumed)
5808e7
+                *ret_consumed = consumed;
5808e7
 
5808e7
         return 0;
5808e7
 }
5808e7
@@ -497,11 +518,12 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
5808e7
 static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
5808e7
         uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
5808e7
         StdoutStream *s = userdata;
5808e7
+        size_t limit, consumed;
5808e7
         struct ucred *ucred = NULL;
5808e7
         struct cmsghdr *cmsg;
5808e7
         struct iovec iovec;
5808e7
-        size_t limit;
5808e7
         ssize_t l;
5808e7
+        char *p;
5808e7
         int r;
5808e7
 
5808e7
         struct msghdr msghdr = {
5808e7
@@ -529,7 +551,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
5808e7
         /* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also,
5808e7
          * always leave room for a terminating NUL we might need to add. */
5808e7
         limit = MIN(s->allocated - 1, s->server->line_max);
5808e7
-
5808e7
+        assert(s->length <= limit);
5808e7
         iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length);
5808e7
 
5808e7
         l = recvmsg(s->fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
5808e7
@@ -543,7 +565,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
5808e7
         cmsg_close_all(&msghdr);
5808e7
 
5808e7
         if (l == 0) {
5808e7
-                stdout_stream_scan(s, true);
5808e7
+                (void) stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_EOF, NULL);
5808e7
                 goto terminate;
5808e7
         }
5808e7
 
5808e7
@@ -562,22 +584,33 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
5808e7
          * in the meantime.
5808e7
          */
5808e7
         if (ucred && ucred->pid != s->ucred.pid) {
5808e7
-                /* force out any previously half-written lines from a
5808e7
-                 * different process, before we switch to the new ucred
5808e7
-                 * structure for everything we just added */
5808e7
-                r = stdout_stream_scan(s, true);
5808e7
+                /* Force out any previously half-written lines from a different process, before we switch to
5808e7
+                 * the new ucred structure for everything we just added */
5808e7
+                r = stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_PID_CHANGE, NULL);
5808e7
                 if (r < 0)
5808e7
                         goto terminate;
5808e7
 
5808e7
-                s->ucred = *ucred;
5808e7
                 s->context = client_context_release(s->server, s->context);
5808e7
+
5808e7
+                p = s->buffer + s->length;
5808e7
+        } else {
5808e7
+                p = s->buffer;
5808e7
+                l += s->length;
5808e7
         }
5808e7
 
5808e7
-        s->length += l;
5808e7
-        r = stdout_stream_scan(s, false);
5808e7
+        /* Always copy in the new credentials */
5808e7
+        if (ucred)
5808e7
+                s->ucred = *ucred;
5808e7
+
5808e7
+        r = stdout_stream_scan(s, p, l, _LINE_BREAK_INVALID, &consumed);
5808e7
         if (r < 0)
5808e7
                 goto terminate;
5808e7
 
5808e7
+        /* Move what wasn't consumed to the front of the buffer */
5808e7
+        assert(consumed <= (size_t) l);
5808e7
+        s->length = l - consumed;
5808e7
+        memmove(s->buffer, p + consumed, s->length);
5808e7
+
5808e7
         return 1;
5808e7
 
5808e7
 terminate: