]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
journald: add minimal client metadata caching 6392/head
authorLennart Poettering <lennart@poettering.net>
Mon, 17 Jul 2017 21:36:35 +0000 (23:36 +0200)
committerLennart Poettering <lennart@poettering.net>
Mon, 31 Jul 2017 16:21:21 +0000 (18:21 +0200)
Cache client metadata, in order to be improve runtime behaviour under
pressure.

This is inspired by @vcaputo's work, specifically:

https://github.com/systemd/systemd/pull/2280

That code implements related but different semantics.

For a longer explanation what this change implements please have a look
at the long source comment this patch adds to journald-context.c.

After this commit:

        # time bash -c 'dd bs=$((1024*1024)) count=$((1*1024)) if=/dev/urandom | systemd-cat'
        1024+0 records in
        1024+0 records out
        1073741824 bytes (1.1 GB, 1.0 GiB) copied, 11.2783 s, 95.2 MB/s

        real 0m11.283s
        user 0m0.007s
        sys 0m6.216s

Before this commit:

        # time bash -c 'dd bs=$((1024*1024)) count=$((1*1024)) if=/dev/urandom | systemd-cat'
        1024+0 records in
        1024+0 records out
        1073741824 bytes (1.1 GB, 1.0 GiB) copied, 52.0788 s, 20.6 MB/s

        real 0m52.099s
        user 0m0.014s
        sys 0m7.170s

As side effect, this corrects the journal's rate limiter feature: we now
always use the unit name as key for the ratelimiter.

src/journal/journald-audit.c
src/journal/journald-context.c [new file with mode: 0644]
src/journal/journald-context.h [new file with mode: 0644]
src/journal/journald-kmsg.c
src/journal/journald-native.c
src/journal/journald-server.c
src/journal/journald-server.h
src/journal/journald-stream.c
src/journal/journald-syslog.c
src/journal/meson.build

index a433c91c54a1d960bd8905ca016de252b5dd28b8..38ac3befddc8ca76a2eec531bd7ef908e03fd4db 100644 (file)
@@ -413,7 +413,7 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
                 goto finish;
         }
 
-        server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, NULL, 0, NULL, LOG_NOTICE, 0);
+        server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, LOG_NOTICE, 0);
 
 finish:
         /* free() all entries that map_all_fields() added. All others
diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c
new file mode 100644 (file)
index 0000000..10e9615
--- /dev/null
@@ -0,0 +1,588 @@
+/***
+  This file is part of systemd.
+
+  Copyright 2017 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#include "alloc-util.h"
+#include "audit-util.h"
+#include "cgroup-util.h"
+#include "journald-context.h"
+#include "process-util.h"
+#include "string-util.h"
+#include "user-util.h"
+
+/* This implements a metadata cache for clients, which are identified by their PID. Requesting metadata through /proc
+ * is expensive, hence let's cache the data if we can. Note that this means the metadata might be out-of-date when we
+ * store it, but it might already be anyway, as we request the data asynchronously from /proc at a different time the
+ * log entry was originally created. We hence just increase the "window of inaccuracy" a bit.
+ *
+ * The cache is indexed by the PID. Entries may be "pinned" in the cache, in which case the entries are not removed
+ * until they are unpinned. Unpinned entries are kept around until cache pressure is seen. Cache entries older than 5s
+ * are never used (a sad attempt to deal with the UNIX weakness of PIDs reuse), cache entries older than 1s are
+ * refreshed in an incremental way (meaning: data is reread from /proc, but any old data we can't refresh is not
+ * flushed out). Data newer than 1s is used immediately without refresh.
+ *
+ * Log stream clients (i.e. all clients using the AF_UNIX/SOCK_STREAM stdout/stderr transport) will pin a cache entry
+ * as long as their socket is connected. Note that cache entries are shared between different transports. That means a
+ * cache entry pinned for the stream connection logic may be reused for the syslog or native protocols.
+ *
+ * Caching metadata like this has two major benefits:
+ *
+ * 1. Reading metadata is expensive, and we can thus substantially speed up log processing under flood.
+ *
+ * 2. Because metadata caching is shared between stream and datagram transports and stream connections pin a cache
+ *    entry there's a good chance we can properly map a substantial set of datagram log messages to their originating
+ *    service, as all services (unless explicitly configured otherwise) will have their stdout/stderr connected to a
+ *    stream connection. This should improve cases where a service process logs immediately before exiting and we
+ *    previously had trouble associating the log message with the service.
+ *
+ * NB: With and without the metadata cache: the implicitly added entry metadata in the journal (with the exception of
+ *     UID/PID/GID and SELinux label) must be understood as possibly slightly out of sync (i.e. sometimes slighly older
+ *     and sometimes slightly newer than what was current at the log event).
+ */
+
+/* We refresh every 1s */
+#define REFRESH_USEC (1*USEC_PER_SEC)
+
+/* Data older than 5s we flush out */
+#define MAX_USEC (5*USEC_PER_SEC)
+
+/* Keep at most 16K entries in the cache. (Note though that this limit may be violated if enough streams pin entries in
+ * the cache, in which case we *do* permit this limit to be breached. That's safe however, as the number of stream
+ * clients itself is limited.) */
+#define CACHE_MAX (16*1024)
+
+static int client_context_compare(const void *a, const void *b) {
+        const ClientContext *x = a, *y = b;
+
+        if (x->timestamp < y->timestamp)
+                return -1;
+        if (x->timestamp > y->timestamp)
+                return 1;
+
+        if (x->pid < y->pid)
+                return -1;
+        if (x->pid > y->pid)
+                return 1;
+
+        return 0;
+}
+
+static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
+        ClientContext *c;
+        int r;
+
+        assert(s);
+        assert(pid_is_valid(pid));
+        assert(ret);
+
+        r = hashmap_ensure_allocated(&s->client_contexts, NULL);
+        if (r < 0)
+                return r;
+
+        r = prioq_ensure_allocated(&s->client_contexts_lru, client_context_compare);
+        if (r < 0)
+                return r;
+
+        c = new0(ClientContext, 1);
+        if (!c)
+                return -ENOMEM;
+
+        c->pid = pid;
+
+        c->uid = UID_INVALID;
+        c->gid = GID_INVALID;
+        c->auditid = AUDIT_SESSION_INVALID;
+        c->loginuid = UID_INVALID;
+        c->owner_uid = UID_INVALID;
+        c->lru_index = PRIOQ_IDX_NULL;
+        c->timestamp = USEC_INFINITY;
+
+        r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
+        if (r < 0) {
+                free(c);
+                return r;
+        }
+
+        *ret = c;
+        return 0;
+}
+
+static void client_context_reset(ClientContext *c) {
+        assert(c);
+
+        c->timestamp = USEC_INFINITY;
+
+        c->uid = UID_INVALID;
+        c->gid = GID_INVALID;
+
+        c->comm = mfree(c->comm);
+        c->exe = mfree(c->exe);
+        c->cmdline = mfree(c->cmdline);
+        c->capeff = mfree(c->capeff);
+
+        c->auditid = AUDIT_SESSION_INVALID;
+        c->loginuid = UID_INVALID;
+
+        c->cgroup = mfree(c->cgroup);
+        c->session = mfree(c->session);
+        c->owner_uid = UID_INVALID;
+        c->unit = mfree(c->unit);
+        c->user_unit = mfree(c->user_unit);
+        c->slice = mfree(c->slice);
+        c->user_slice = mfree(c->user_slice);
+
+        c->invocation_id = SD_ID128_NULL;
+
+        c->label = mfree(c->label);
+        c->label_size = 0;
+}
+
+static ClientContext* client_context_free(Server *s, ClientContext *c) {
+        assert(s);
+
+        if (!c)
+                return NULL;
+
+        assert_se(hashmap_remove(s->client_contexts, PID_TO_PTR(c->pid)) == c);
+
+        if (c->in_lru)
+                assert_se(prioq_remove(s->client_contexts_lru, c, &c->lru_index) >= 0);
+
+        client_context_reset(c);
+
+        return mfree(c);
+}
+
+static void client_context_read_uid_gid(ClientContext *c, const struct ucred *ucred) {
+        assert(c);
+        assert(pid_is_valid(c->pid));
+
+        /* The ucred data passed in is always the most current and accurate, if we have any. Use it. */
+        if (ucred && uid_is_valid(ucred->uid))
+                c->uid = ucred->uid;
+        else
+                (void) get_process_uid(c->pid, &c->uid);
+
+        if (ucred && gid_is_valid(ucred->gid))
+                c->gid = ucred->gid;
+        else
+                (void) get_process_gid(c->pid, &c->gid);
+}
+
+static void client_context_read_basic(ClientContext *c) {
+        char *t;
+
+        assert(c);
+        assert(pid_is_valid(c->pid));
+
+        if (get_process_comm(c->pid, &t) >= 0)
+                free_and_replace(c->comm, t);
+
+        if (get_process_exe(c->pid, &t) >= 0)
+                free_and_replace(c->exe, t);
+
+        if (get_process_cmdline(c->pid, 0, false, &t) >= 0)
+                free_and_replace(c->cmdline, t);
+
+        if (get_process_capeff(c->pid, &t) >= 0)
+                free_and_replace(c->capeff, t);
+}
+
+static int client_context_read_label(
+                ClientContext *c,
+                const char *label, size_t label_size) {
+
+        assert(c);
+        assert(pid_is_valid(c->pid));
+        assert(label_size == 0 || label);
+
+        if (label_size > 0) {
+                char *l;
+
+                /* If we got an SELinux label passed in it counts. */
+
+                l = newdup_suffix0(char, label, label_size);
+                if (!l)
+                        return -ENOMEM;
+
+                free_and_replace(c->label, l);
+                c->label_size = label_size;
+        }
+#ifdef HAVE_SELINUX
+        else {
+                char *con;
+
+                /* If we got no SELinux label passed in, let's try to acquire one */
+
+                if (getpidcon(c->pid, &con) >= 0) {
+                        free_and_replace(c->label, con);
+                        c->label_size = strlen(c->label);
+                }
+        }
+#endif
+
+        return 0;
+}
+
+static int client_context_read_cgroup(Server *s, ClientContext *c, const char *unit_id) {
+        char *t = NULL;
+        int r;
+
+        assert(c);
+
+        /* Try to acquire the current cgroup path */
+        r = cg_pid_get_path_shifted(c->pid, s->cgroup_root, &t);
+        if (r < 0) {
+
+                /* If that didn't work, we use the unit ID passed in as fallback, if we have nothing cached yet */
+                if (unit_id && !c->unit) {
+                        c->unit = strdup(unit_id);
+                        if (c->unit)
+                                return 0;
+                }
+
+                return r;
+        }
+
+        /* Let's shortcut this if the cgroup path didn't change */
+        if (streq_ptr(c->cgroup, t)) {
+                free(t);
+                return 0;
+        }
+
+        free_and_replace(c->cgroup, t);
+
+        (void) cg_path_get_session(c->cgroup, &t);
+        free_and_replace(c->session, t);
+
+        if (cg_path_get_owner_uid(c->cgroup, &c->owner_uid) < 0)
+                c->owner_uid = UID_INVALID;
+
+        (void) cg_path_get_unit(c->cgroup, &t);
+        free_and_replace(c->unit, t);
+
+        (void) cg_path_get_user_unit(c->cgroup, &t);
+        free_and_replace(c->user_unit, t);
+
+        (void) cg_path_get_slice(c->cgroup, &t);
+        free_and_replace(c->slice, t);
+
+        (void) cg_path_get_user_slice(c->cgroup, &t);
+        free_and_replace(c->user_slice, t);
+
+        return 0;
+}
+
+static int client_context_read_invocation_id(
+                Server *s,
+                ClientContext *c) {
+
+        _cleanup_free_ char *escaped = NULL, *slice_path = NULL;
+        char ids[SD_ID128_STRING_MAX];
+        const char *p;
+        int r;
+
+        assert(s);
+        assert(c);
+
+        /* Read the invocation ID of a unit off a unit. It's stored in the "trusted.invocation_id" extended attribute
+         * on the cgroup path. */
+
+        if (!c->unit || !c->slice)
+                return 0;
+
+        r = cg_slice_to_path(c->slice, &slice_path);
+        if (r < 0)
+                return r;
+
+        escaped = cg_escape(c->unit);
+        if (!escaped)
+                return -ENOMEM;
+
+        p = strjoina(s->cgroup_root, "/", slice_path, "/", escaped);
+        if (!p)
+                return -ENOMEM;
+
+        r = cg_get_xattr(SYSTEMD_CGROUP_CONTROLLER, p, "trusted.invocation_id", ids, 32);
+        if (r < 0)
+                return r;
+        if (r != 32)
+                return -EINVAL;
+        ids[32] = 0;
+
+        return sd_id128_from_string(ids, &c->invocation_id);
+}
+
+static void client_context_really_refresh(
+                Server *s,
+                ClientContext *c,
+                const struct ucred *ucred,
+                const char *label, size_t label_size,
+                const char *unit_id,
+                usec_t timestamp) {
+
+        assert(s);
+        assert(c);
+        assert(pid_is_valid(c->pid));
+
+        if (timestamp == USEC_INFINITY)
+                timestamp = now(CLOCK_MONOTONIC);
+
+        client_context_read_uid_gid(c, ucred);
+        client_context_read_basic(c);
+        (void) client_context_read_label(c, label, label_size);
+
+        (void) audit_session_from_pid(c->pid, &c->auditid);
+        (void) audit_loginuid_from_pid(c->pid, &c->loginuid);
+
+        (void) client_context_read_cgroup(s, c, unit_id);
+        (void) client_context_read_invocation_id(s, c);
+
+        c->timestamp = timestamp;
+
+        if (c->in_lru) {
+                assert(c->n_ref == 0);
+                assert_se(prioq_reshuffle(s->client_contexts_lru, c, &c->lru_index) >= 0);
+        }
+}
+
+void client_context_maybe_refresh(
+                Server *s,
+                ClientContext *c,
+                const struct ucred *ucred,
+                const char *label, size_t label_size,
+                const char *unit_id,
+                usec_t timestamp) {
+
+        assert(s);
+        assert(c);
+
+        if (timestamp == USEC_INFINITY)
+                timestamp = now(CLOCK_MONOTONIC);
+
+        /* No cached data so far? Let's fill it up */
+        if (c->timestamp == USEC_INFINITY)
+                goto refresh;
+
+        /* If the data isn't pinned and if the cashed data is older than the upper limit, we flush it out
+         * entirely. This follows the logic that as long as an entry is pinned the PID reuse is unlikely. */
+        if (c->n_ref == 0 && c->timestamp + MAX_USEC < timestamp) {
+                client_context_reset(c);
+                goto refresh;
+        }
+
+        /* If the data is older than the lower limit, we refresh, but keep the old data for all we can't update */
+        if (c->timestamp + REFRESH_USEC < timestamp)
+                goto refresh;
+
+        /* If the data passed along doesn't match the cached data we also do a refresh */
+        if (ucred && uid_is_valid(ucred->uid) && c->uid != ucred->uid)
+                goto refresh;
+
+        if (ucred && gid_is_valid(ucred->gid) && c->gid != ucred->gid)
+                goto refresh;
+
+        if (label_size > 0 && (label_size != c->label_size || memcmp(label, c->label, label_size) != 0))
+                goto refresh;
+
+        return;
+
+refresh:
+        client_context_really_refresh(s, c, ucred, label, label_size, unit_id, timestamp);
+}
+
+static void client_context_try_shrink_to(Server *s, size_t limit) {
+        assert(s);
+
+        /* Bring the number of cache entries below the indicated limit, so that we can create a new entry without
+         * breaching the limit. Note that we only flush out entries that aren't pinned here. This means the number of
+         * cache entries may very well grow beyond the limit, if all entries stored remain pinned. */
+
+        while (hashmap_size(s->client_contexts) > limit) {
+                ClientContext *c;
+
+                c = prioq_pop(s->client_contexts_lru);
+                if (!c)
+                        break; /* All remaining entries are pinned, give up */
+
+                assert(c->in_lru);
+                assert(c->n_ref == 0);
+
+                c->in_lru = false;
+
+                client_context_free(s, c);
+        }
+}
+
+void client_context_flush_all(Server *s) {
+        assert(s);
+
+        /* Flush out all remaining entries. This assumes all references are already dropped. */
+
+        s->my_context = client_context_release(s, s->my_context);
+        s->pid1_context = client_context_release(s, s->pid1_context);
+
+        client_context_try_shrink_to(s, 0);
+
+        assert(prioq_size(s->client_contexts_lru) == 0);
+        assert(hashmap_size(s->client_contexts) == 0);
+
+        s->client_contexts_lru = prioq_free(s->client_contexts_lru);
+        s->client_contexts = hashmap_free(s->client_contexts);
+}
+
+static int client_context_get_internal(
+                Server *s,
+                pid_t pid,
+                const struct ucred *ucred,
+                const char *label, size_t label_len,
+                const char *unit_id,
+                bool add_ref,
+                ClientContext **ret) {
+
+        ClientContext *c;
+        int r;
+
+        assert(s);
+        assert(ret);
+
+        if (!pid_is_valid(pid))
+                return -EINVAL;
+
+        c = hashmap_get(s->client_contexts, PID_TO_PTR(pid));
+        if (c) {
+
+                if (add_ref) {
+                        if (c->in_lru) {
+                                /* The entry wasn't pinned so far, let's remove it from the LRU list then */
+                                assert(c->n_ref == 0);
+                                assert_se(prioq_remove(s->client_contexts_lru, c, &c->lru_index) >= 0);
+                                c->in_lru = false;
+                        }
+
+                        c->n_ref++;
+                }
+
+                client_context_maybe_refresh(s, c, ucred, label, label_len, unit_id, USEC_INFINITY);
+
+                *ret = c;
+                return 0;
+        }
+
+        client_context_try_shrink_to(s, CACHE_MAX-1);
+
+        r = client_context_new(s, pid, &c);
+        if (r < 0)
+                return r;
+
+        if (add_ref)
+                c->n_ref++;
+        else {
+                r = prioq_put(s->client_contexts_lru, c, &c->lru_index);
+                if (r < 0) {
+                        client_context_free(s, c);
+                        return r;
+                }
+
+                c->in_lru = true;
+        }
+
+        client_context_really_refresh(s, c, ucred, label, label_len, unit_id, USEC_INFINITY);
+
+        *ret = c;
+        return 0;
+}
+
+int client_context_get(
+                Server *s,
+                pid_t pid,
+                const struct ucred *ucred,
+                const char *label, size_t label_len,
+                const char *unit_id,
+                ClientContext **ret) {
+
+        return client_context_get_internal(s, pid, ucred, label, label_len, unit_id, false, ret);
+}
+
+int client_context_acquire(
+                Server *s,
+                pid_t pid,
+                const struct ucred *ucred,
+                const char *label, size_t label_len,
+                const char *unit_id,
+                ClientContext **ret) {
+
+        return client_context_get_internal(s, pid, ucred, label, label_len, unit_id, true, ret);
+};
+
+ClientContext *client_context_release(Server *s, ClientContext *c) {
+        assert(s);
+
+        if (!c)
+                return NULL;
+
+        assert(c->n_ref > 0);
+        assert(!c->in_lru);
+
+        c->n_ref--;
+        if (c->n_ref > 0)
+                return NULL;
+
+        /* The entry is not pinned anymore, let's add it to the LRU prioq if we can. If we can't we'll drop it
+         * right-away */
+
+        if (prioq_put(s->client_contexts_lru, c, &c->lru_index) < 0)
+                client_context_free(s, c);
+        else
+                c->in_lru = true;
+
+        return NULL;
+}
+
+void client_context_acquire_default(Server *s) {
+        int r;
+
+        assert(s);
+
+        /* Ensure that our own and PID1's contexts are always pinned. Our own context is particularly useful to
+         * generate driver messages. */
+
+        if (!s->my_context) {
+                struct ucred ucred = {
+                        .pid = getpid_cached(),
+                        .uid = getuid(),
+                        .gid = getgid(),
+                };
+
+                r = client_context_acquire(s, ucred.pid, &ucred, NULL, 0, NULL, &s->my_context);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to acquire our own context, ignoring: %m");
+        }
+
+        if (!s->pid1_context) {
+
+                r = client_context_acquire(s, 1, NULL, NULL, 0, NULL, &s->pid1_context);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to acquire PID1's context, ignoring: %m");
+
+        }
+}
diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h
new file mode 100644 (file)
index 0000000..eb1e219
--- /dev/null
@@ -0,0 +1,92 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2017 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "sd-id128.h"
+
+typedef struct ClientContext ClientContext;
+
+#include "journald-server.h"
+
+struct ClientContext {
+        unsigned n_ref;
+        unsigned lru_index;
+        usec_t timestamp;
+        bool in_lru;
+
+        pid_t pid;
+        uid_t uid;
+        gid_t gid;
+
+        char *comm;
+        char *exe;
+        char *cmdline;
+        char *capeff;
+
+        uint32_t auditid;
+        uid_t loginuid;
+
+        char *cgroup;
+        char *session;
+        uid_t owner_uid;
+
+        char *unit;
+        char *user_unit;
+
+        char *slice;
+        char *user_slice;
+
+        sd_id128_t invocation_id;
+
+        char *label;
+        size_t label_size;
+};
+
+int client_context_get(
+                Server *s,
+                pid_t pid,
+                const struct ucred *ucred,
+                const char *label, size_t label_len,
+                const char *unit_id,
+                ClientContext **ret);
+
+int client_context_acquire(
+                Server *s,
+                pid_t pid,
+                const struct ucred *ucred,
+                const char *label, size_t label_len,
+                const char *unit_id,
+                ClientContext **ret);
+
+ClientContext* client_context_release(Server *s, ClientContext *c);
+
+void client_context_maybe_refresh(
+                Server *s,
+                ClientContext *c,
+                const struct ucred *ucred,
+                const char *label, size_t label_size,
+                const char *unit_id,
+                usec_t tstamp);
+
+void client_context_acquire_default(Server *s);
+void client_context_flush_all(Server *s);
index 7fea85a5d842599bdea899a1c33aad8e61ed87b6..2be82be5f64c62004616e3a892b17cde8356d729 100644 (file)
@@ -310,7 +310,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) {
         if (cunescape_length_with_prefix(p, pl, "MESSAGE=", UNESCAPE_RELAX, &message) >= 0)
                 IOVEC_SET_STRING(iovec[n++], message);
 
-        server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, NULL, priority, 0);
+        server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, priority, 0);
 
 finish:
         for (j = 0; j < z; j++)
index abd06b1adc02ea3a2bee326d1697228317578d21..23afe59bd532cd37d1a8fd5511448d89808a8747 100644 (file)
@@ -37,6 +37,7 @@
 #include "memfd-util.h"
 #include "parse-util.h"
 #include "path-util.h"
+#include "process-util.h"
 #include "selinux-util.h"
 #include "socket-util.h"
 #include "string-util.h"
@@ -142,6 +143,7 @@ static void server_process_entry_meta(
 static int server_process_entry(
                 Server *s,
                 const void *buffer, size_t *remaining,
+                ClientContext *context,
                 const struct ucred *ucred,
                 const struct timeval *tv,
                 const char *label, size_t label_len) {
@@ -303,7 +305,7 @@ static int server_process_entry(
                         server_forward_wall(s, priority, identifier, message, ucred);
         }
 
-        server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
+        server_dispatch_message(s, iovec, n, m, context, tv, priority, object_pid);
 
 finish:
         for (j = 0; j < n; j++)  {
@@ -329,16 +331,23 @@ void server_process_native_message(
                 const struct timeval *tv,
                 const char *label, size_t label_len) {
 
-        int r;
         size_t remaining = buffer_size;
+        ClientContext *context;
+        int r;
 
         assert(s);
         assert(buffer || buffer_size == 0);
 
+        if (ucred && pid_is_valid(ucred->pid)) {
+                r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
+        }
+
         do {
                 r = server_process_entry(s,
                                          (const uint8_t*) buffer + (buffer_size - remaining), &remaining,
-                                         ucred, tv, label, label_len);
+                                         context, ucred, tv, label, label_len);
         } while (r == 0);
 }
 
index f391845ba9a9dc022cb02ddbbb54831841bc298a..feef10c2dbf5fed596a831ace9f69479f88c329d 100644 (file)
@@ -51,6 +51,7 @@
 #include "journal-internal.h"
 #include "journal-vacuum.h"
 #include "journald-audit.h"
+#include "journald-context.h"
 #include "journald-kmsg.h"
 #include "journald-native.h"
 #include "journald-rate-limit.h"
@@ -70,8 +71,8 @@
 #include "stdio-util.h"
 #include "string-table.h"
 #include "string-util.h"
-#include "user-util.h"
 #include "syslog-util.h"
+#include "user-util.h"
 
 #define USER_JOURNALS_MAX 1024
 
@@ -714,323 +715,109 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned
                 server_schedule_sync(s, priority);
 }
 
-static int get_invocation_id(const char *cgroup_root, const char *slice, const char *unit, char **ret) {
-        _cleanup_free_ char *escaped = NULL, *slice_path = NULL, *p = NULL;
-        char *copy, ids[SD_ID128_STRING_MAX];
-        int r;
-
-        /* Read the invocation ID of a unit off a unit. It's stored in the "trusted.invocation_id" extended attribute
-         * on the cgroup path. */
-
-        r = cg_slice_to_path(slice, &slice_path);
-        if (r < 0)
-                return r;
-
-        escaped = cg_escape(unit);
-        if (!escaped)
-                return -ENOMEM;
-
-        p = strjoin(cgroup_root, "/", slice_path, "/", escaped);
-        if (!p)
-                return -ENOMEM;
-
-        r = cg_get_xattr(SYSTEMD_CGROUP_CONTROLLER, p, "trusted.invocation_id", ids, 32);
-        if (r < 0)
-                return r;
-        if (r != 32)
-                return -EINVAL;
-        ids[32] = 0;
+#define IOVEC_ADD_NUMERIC_FIELD(iovec, n, value, type, isset, format, field)  \
+        if (isset(value)) {                                             \
+                char *k;                                                \
+                k = newa(char, strlen(field "=") + DECIMAL_STR_MAX(type) + 1); \
+                sprintf(k, field "=" format, value);                    \
+                IOVEC_SET_STRING(iovec[n++], k);                        \
+        }
 
-        if (!id128_is_valid(ids))
-                return -EINVAL;
+#define IOVEC_ADD_STRING_FIELD(iovec, n, value, field)                  \
+        if (!isempty(value)) {                                          \
+                char *k;                                                \
+                k = strjoina(field "=", value);                         \
+                IOVEC_SET_STRING(iovec[n++], k);                        \
+        }
 
-        copy = strdup(ids);
-        if (!copy)
-                return -ENOMEM;
+#define IOVEC_ADD_ID128_FIELD(iovec, n, value, field)                   \
+        if (!sd_id128_is_null(value)) {                                 \
+                char *k;                                                \
+                k = newa(char, strlen(field "=") + SD_ID128_STRING_MAX); \
+                sd_id128_to_string(value, stpcpy(k, field "="));        \
+                IOVEC_SET_STRING(iovec[n++], k);                        \
+        }
 
-        *ret = copy;
-        return 0;
-}
+#define IOVEC_ADD_SIZED_FIELD(iovec, n, value, value_size, field)       \
+        if (value_size > 0) {                                           \
+                char *k;                                                \
+                k = newa(char, strlen(field "=") + value_size + 1);     \
+                *((char*) mempcpy(stpcpy(k, field "="), value, value_size)) = 0; \
+                IOVEC_SET_STRING(iovec[n++], k);                        \
+        }                                                               \
 
 static void dispatch_message_real(
                 Server *s,
                 struct iovec *iovec, unsigned n, unsigned m,
-                const struct ucred *ucred,
+                const ClientContext *c,
                 const struct timeval *tv,
-                const char *label, size_t label_len,
-                const char *unit_id,
                 int priority,
-                pid_t object_pid,
-                char *cgroup) {
-
-        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)],
-                o_uid[sizeof("OBJECT_UID=") + DECIMAL_STR_MAX(uid_t)],
-                o_gid[sizeof("OBJECT_GID=") + DECIMAL_STR_MAX(gid_t)],
-                o_owner_uid[sizeof("OBJECT_SYSTEMD_OWNER_UID=") + DECIMAL_STR_MAX(uid_t)];
-        uid_t object_uid;
-        gid_t object_gid;
-        char *x;
-        int r;
-        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)],
-                o_audit_session[sizeof("OBJECT_AUDIT_SESSION=") + DECIMAL_STR_MAX(uint32_t)],
-                o_audit_loginuid[sizeof("OBJECT_AUDIT_LOGINUID=") + DECIMAL_STR_MAX(uid_t)];
-
-        uint32_t audit;
-        uid_t loginuid;
-#endif
+                pid_t object_pid) {
+
+        char source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
+        uid_t journal_uid;
+        ClientContext *o;
 
         assert(s);
         assert(iovec);
         assert(n > 0);
-        assert(n + N_IOVEC_META_FIELDS + (object_pid > 0 ? N_IOVEC_OBJECT_FIELDS : 0) <= m);
-
-        if (ucred) {
-                realuid = ucred->uid;
-
-                sprintf(pid, "_PID="PID_FMT, ucred->pid);
-                IOVEC_SET_STRING(iovec[n++], pid);
-
-                sprintf(uid, "_UID="UID_FMT, ucred->uid);
-                IOVEC_SET_STRING(iovec[n++], uid);
-
-                sprintf(gid, "_GID="GID_FMT, ucred->gid);
-                IOVEC_SET_STRING(iovec[n++], gid);
-
-                r = get_process_comm(ucred->pid, &t);
-                if (r >= 0) {
-                        x = strjoina("_COMM=", t);
-                        free(t);
-                        IOVEC_SET_STRING(iovec[n++], x);
-                }
-
-                r = get_process_exe(ucred->pid, &t);
-                if (r >= 0) {
-                        x = strjoina("_EXE=", t);
-                        free(t);
-                        IOVEC_SET_STRING(iovec[n++], x);
-                }
-
-                r = get_process_cmdline(ucred->pid, 0, false, &t);
-                if (r >= 0) {
-                        x = strjoina("_CMDLINE=", t);
-                        free(t);
-                        IOVEC_SET_STRING(iovec[n++], x);
-                }
-
-                r = get_process_capeff(ucred->pid, &t);
-                if (r >= 0) {
-                        x = strjoina("_CAP_EFFECTIVE=", t);
-                        free(t);
-                        IOVEC_SET_STRING(iovec[n++], x);
-                }
-
-#ifdef HAVE_AUDIT
-                r = audit_session_from_pid(ucred->pid, &audit);
-                if (r >= 0) {
-                        sprintf(audit_session, "_AUDIT_SESSION=%"PRIu32, audit);
-                        IOVEC_SET_STRING(iovec[n++], audit_session);
-                }
-
-                r = audit_loginuid_from_pid(ucred->pid, &loginuid);
-                if (r >= 0) {
-                        sprintf(audit_loginuid, "_AUDIT_LOGINUID="UID_FMT, loginuid);
-                        IOVEC_SET_STRING(iovec[n++], audit_loginuid);
-                }
-#endif
-
-                r = 0;
-                if (cgroup)
-                        c = cgroup;
-                else
-                        r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &c);
-
-                if (r >= 0) {
-                        _cleanup_free_ char *raw_unit = NULL, *raw_slice = NULL;
-                        char *session = NULL;
-
-                        x = strjoina("_SYSTEMD_CGROUP=", c);
-                        IOVEC_SET_STRING(iovec[n++], x);
-
-                        r = cg_path_get_session(c, &t);
-                        if (r >= 0) {
-                                session = strjoina("_SYSTEMD_SESSION=", t);
-                                free(t);
-                                IOVEC_SET_STRING(iovec[n++], session);
-                        }
-
-                        if (cg_path_get_owner_uid(c, &owner) >= 0) {
-                                owner_valid = true;
-
-                                sprintf(owner_uid, "_SYSTEMD_OWNER_UID="UID_FMT, owner);
-                                IOVEC_SET_STRING(iovec[n++], owner_uid);
-                        }
-
-                        if (cg_path_get_unit(c, &raw_unit) >= 0) {
-                                x = strjoina("_SYSTEMD_UNIT=", raw_unit);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        } else if (unit_id && !session) {
-                                x = strjoina("_SYSTEMD_UNIT=", unit_id);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
+        assert(n + N_IOVEC_META_FIELDS + (pid_is_valid(object_pid) ? N_IOVEC_OBJECT_FIELDS : 0) <= m);
 
-                        if (cg_path_get_user_unit(c, &t) >= 0) {
-                                x = strjoina("_SYSTEMD_USER_UNIT=", t);
-                                free(t);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        } else if (unit_id && session) {
-                                x = strjoina("_SYSTEMD_USER_UNIT=", unit_id);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
-
-                        if (cg_path_get_slice(c, &raw_slice) >= 0) {
-                                x = strjoina("_SYSTEMD_SLICE=", raw_slice);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
-
-                        if (cg_path_get_user_slice(c, &t) >= 0) {
-                                x = strjoina("_SYSTEMD_USER_SLICE=", t);
-                                free(t);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
-
-                        if (raw_slice && raw_unit) {
-                                if (get_invocation_id(s->cgroup_root, raw_slice, raw_unit, &t) >= 0) {
-                                        x = strjoina("_SYSTEMD_INVOCATION_ID=", t);
-                                        free(t);
-                                        IOVEC_SET_STRING(iovec[n++], x);
-                                }
-                        }
+        if (c) {
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->pid, pid_t, pid_is_valid, PID_FMT, "_PID");
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->uid, uid_t, uid_is_valid, UID_FMT, "_UID");
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->gid, gid_t, gid_is_valid, GID_FMT, "_GID");
 
-                        if (!cgroup)
-                                free(c);
-                } else if (unit_id) {
-                        x = strjoina("_SYSTEMD_UNIT=", unit_id);
-                        IOVEC_SET_STRING(iovec[n++], x);
-                }
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM");
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE");
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->cmdline, "_CMDLINE");
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE");
 
-#ifdef HAVE_SELINUX
-                if (mac_selinux_use()) {
-                        if (label) {
-                                x = alloca(strlen("_SELINUX_CONTEXT=") + label_len + 1);
+                IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
 
-                                *((char*) mempcpy(stpcpy(x, "_SELINUX_CONTEXT="), label, label_len)) = 0;
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        } else {
-                                char *con;
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "_AUDIT_SESSION");
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->loginuid, uid_t, uid_is_valid, UID_FMT, "_AUDIT_LOGINUID");
 
-                                if (getpidcon(ucred->pid, &con) >= 0) {
-                                        x = strjoina("_SELINUX_CONTEXT=", con);
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP");
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->session, "_SYSTEMD_SESSION");
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->owner_uid, uid_t, uid_is_valid, UID_FMT, "_SYSTEMD_OWNER_UID");
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT");
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->user_unit, "_SYSTEMD_USER_UNIT");
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->slice, "_SYSTEMD_SLICE");
+                IOVEC_ADD_STRING_FIELD(iovec, n, c->user_slice, "_SYSTEMD_USER_SLICE");
 
-                                        freecon(con);
-                                        IOVEC_SET_STRING(iovec[n++], x);
-                                }
-                        }
-                }
-#endif
+                IOVEC_ADD_ID128_FIELD(iovec, n, c->invocation_id, "_SYSTEMD_INVOCATION_ID");
         }
-        assert(n <= m);
 
-        if (object_pid) {
-                r = get_process_uid(object_pid, &object_uid);
-                if (r >= 0) {
-                        sprintf(o_uid, "OBJECT_UID="UID_FMT, object_uid);
-                        IOVEC_SET_STRING(iovec[n++], o_uid);
-                }
-
-                r = get_process_gid(object_pid, &object_gid);
-                if (r >= 0) {
-                        sprintf(o_gid, "OBJECT_GID="GID_FMT, object_gid);
-                        IOVEC_SET_STRING(iovec[n++], o_gid);
-                }
-
-                r = get_process_comm(object_pid, &t);
-                if (r >= 0) {
-                        x = strjoina("OBJECT_COMM=", t);
-                        free(t);
-                        IOVEC_SET_STRING(iovec[n++], x);
-                }
-
-                r = get_process_exe(object_pid, &t);
-                if (r >= 0) {
-                        x = strjoina("OBJECT_EXE=", t);
-                        free(t);
-                        IOVEC_SET_STRING(iovec[n++], x);
-                }
-
-                r = get_process_cmdline(object_pid, 0, false, &t);
-                if (r >= 0) {
-                        x = strjoina("OBJECT_CMDLINE=", t);
-                        free(t);
-                        IOVEC_SET_STRING(iovec[n++], x);
-                }
-
-#ifdef HAVE_AUDIT
-                r = audit_session_from_pid(object_pid, &audit);
-                if (r >= 0) {
-                        sprintf(o_audit_session, "OBJECT_AUDIT_SESSION=%"PRIu32, audit);
-                        IOVEC_SET_STRING(iovec[n++], o_audit_session);
-                }
+        assert(n <= m);
 
-                r = audit_loginuid_from_pid(object_pid, &loginuid);
-                if (r >= 0) {
-                        sprintf(o_audit_loginuid, "OBJECT_AUDIT_LOGINUID="UID_FMT, loginuid);
-                        IOVEC_SET_STRING(iovec[n++], o_audit_loginuid);
-                }
-#endif
+        if (pid_is_valid(object_pid) && client_context_get(s, object_pid, NULL, NULL, 0, NULL, &o) >= 0) {
 
-                r = cg_pid_get_path_shifted(object_pid, s->cgroup_root, &c);
-                if (r >= 0) {
-                        x = strjoina("OBJECT_SYSTEMD_CGROUP=", c);
-                        IOVEC_SET_STRING(iovec[n++], x);
-
-                        r = cg_path_get_session(c, &t);
-                        if (r >= 0) {
-                                x = strjoina("OBJECT_SYSTEMD_SESSION=", t);
-                                free(t);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->pid, pid_t, pid_is_valid, PID_FMT, "OBJECT_PID");
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->uid, uid_t, uid_is_valid, UID_FMT, "OBJECT_UID");
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->gid, gid_t, gid_is_valid, GID_FMT, "OBJECT_GID");
 
-                        if (cg_path_get_owner_uid(c, &owner) >= 0) {
-                                sprintf(o_owner_uid, "OBJECT_SYSTEMD_OWNER_UID="UID_FMT, owner);
-                                IOVEC_SET_STRING(iovec[n++], o_owner_uid);
-                        }
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->comm, "OBJECT_COMM");
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->exe, "OBJECT_EXE");
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->cmdline, "OBJECT_CMDLINE");
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
 
-                        if (cg_path_get_unit(c, &t) >= 0) {
-                                x = strjoina("OBJECT_SYSTEMD_UNIT=", t);
-                                free(t);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
+                IOVEC_ADD_SIZED_FIELD(iovec, n, o->label, o->label_size, "OBJECT_SELINUX_CONTEXT");
 
-                        if (cg_path_get_user_unit(c, &t) >= 0) {
-                                x = strjoina("OBJECT_SYSTEMD_USER_UNIT=", t);
-                                free(t);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
-
-                        if (cg_path_get_slice(c, &t) >= 0) {
-                                x = strjoina("OBJECT_SYSTEMD_SLICE=", t);
-                                free(t);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "OBJECT_AUDIT_SESSION");
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->loginuid, uid_t, uid_is_valid, UID_FMT, "OBJECT_AUDIT_LOGINUID");
 
-                        if (cg_path_get_user_slice(c, &t) >= 0) {
-                                x = strjoina("OBJECT_SYSTEMD_USER_SLICE=", t);
-                                free(t);
-                                IOVEC_SET_STRING(iovec[n++], x);
-                        }
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->cgroup, "OBJECT_SYSTEMD_CGROUP");
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->session, "OBJECT_SYSTEMD_SESSION");
+                IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->owner_uid, uid_t, uid_is_valid, UID_FMT, "OBJECT_SYSTEMD_OWNER_UID");
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->unit, "OBJECT_SYSTEMD_UNIT");
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->user_unit, "OBJECT_SYSTEMD_USER_UNIT");
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->slice, "OBJECT_SYSTEMD_SLICE");
+                IOVEC_ADD_STRING_FIELD(iovec, n, o->user_slice, "OBJECT_SYSTEMD_USER_SLICE");
 
-                        free(c);
-                }
+                IOVEC_ADD_ID128_FIELD(iovec, n, o->invocation_id, "OBJECT_SYSTEMD_INVOCATION_ID=");
         }
+
         assert(n <= m);
 
         if (tv) {
@@ -1052,16 +839,16 @@ static void dispatch_message_real(
 
         assert(n <= m);
 
-        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)
+        if (s->split_mode == SPLIT_UID && c && uid_is_valid(c->uid))
+                /* Split up strictly by (non-root) UID */
+                journal_uid = c->uid;
+        else if (s->split_mode == SPLIT_LOGIN && c && c->uid > 0 && uid_is_valid(c->owner_uid))
                 /* Split up by login 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;
+                journal_uid = c->owner_uid;
         else
                 journal_uid = 0;
 
@@ -1069,11 +856,11 @@ static void dispatch_message_real(
 }
 
 void server_driver_message(Server *s, const char *message_id, const char *format, ...) {
+
         struct iovec iovec[N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS];
         unsigned n = 0, m;
-        int r;
         va_list ap;
-        struct ucred ucred = {};
+        int r;
 
         assert(s);
         assert(format);
@@ -1095,12 +882,8 @@ void server_driver_message(Server *s, const char *message_id, const char *format
         /* Error handling below */
         va_end(ap);
 
-        ucred.pid = getpid_cached();
-        ucred.uid = getuid();
-        ucred.gid = getgid();
-
         if (r >= 0)
-                dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0, NULL);
+                dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), s->my_context, NULL, LOG_INFO, 0);
 
         while (m < n)
                 free(iovec[m++].iov_base);
@@ -1114,24 +897,20 @@ void server_driver_message(Server *s, const char *message_id, const char *format
                 n = 3;
                 IOVEC_SET_STRING(iovec[n++], "PRIORITY=4");
                 IOVEC_SET_STRING(iovec[n++], buf);
-                dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0, NULL);
+                dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), s->my_context, NULL, LOG_INFO, 0);
         }
 }
 
 void server_dispatch_message(
                 Server *s,
                 struct iovec *iovec, unsigned n, unsigned m,
-                const struct ucred *ucred,
+                ClientContext *c,
                 const struct timeval *tv,
-                const char *label, size_t label_len,
-                const char *unit_id,
                 int priority,
                 pid_t object_pid) {
 
-        int rl, r;
-        _cleanup_free_ char *path = NULL;
         uint64_t available = 0;
-        char *c = NULL;
+        int rl;
 
         assert(s);
         assert(iovec || n == 0);
@@ -1147,45 +926,21 @@ void server_dispatch_message(
         if (s->storage == STORAGE_NONE)
                 return;
 
-        if (!ucred)
-                goto finish;
-
-        r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &path);
-        if (r < 0)
-                goto finish;
+        if (c && c->unit) {
+                (void) determine_space(s, &available, NULL);
 
-        /* example: /user/lennart/3/foobar
-         *          /system/dbus.service/foobar
-         *
-         * So let's cut of everything past the third /, since that is
-         * where user directories start */
+                rl = journal_rate_limit_test(s->rate_limit, c->unit, priority & LOG_PRIMASK, available);
+                if (rl == 0)
+                        return;
 
-        c = strchr(path, '/');
-        if (c) {
-                c = strchr(c+1, '/');
-                if (c) {
-                        c = strchr(c+1, '/');
-                        if (c)
-                                *c = 0;
-                }
+                /* Write a suppression message if we suppressed something */
+                if (rl > 1)
+                        server_driver_message(s, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_DROPPED_STR,
+                                              LOG_MESSAGE("Suppressed %u messages from %s", rl - 1, c->unit),
+                                              NULL);
         }
 
-        (void) determine_space(s, &available, NULL);
-        rl = journal_rate_limit_test(s->rate_limit, path, priority & LOG_PRIMASK, available);
-        if (rl == 0)
-                return;
-
-        /* Write a suppression message if we suppressed something */
-        if (rl > 1)
-                server_driver_message(s, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_DROPPED_STR,
-                                      LOG_MESSAGE("Suppressed %u messages from %s", rl - 1, path),
-                                      NULL);
-
-finish:
-        /* restore cgroup path for logging */
-        if (c)
-                *c = '/';
-        dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid, path);
+        dispatch_message_real(s, iovec, n, m, c, tv, priority, object_pid);
 }
 
 int server_flush_to_var(Server *s, bool require_flag_file) {
@@ -1335,9 +1090,8 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
                 return -EIO;
         }
 
-        /* Try to get the right size, if we can. (Not all
-         * sockets support SIOCINQ, hence we just try, but
-         * don't rely on it. */
+        /* Try to get the right size, if we can. (Not all sockets support SIOCINQ, hence we just try, but don't rely on
+         * it.) */
         (void) ioctl(fd, SIOCINQ, &v);
 
         /* Fix it up, if it is too small. We use the same fixed value as auditd here. Awful! */
@@ -2102,6 +1856,8 @@ int server_init(Server *s) {
 
         (void) server_connect_notify(s);
 
+        (void) client_context_acquire_default(s);
+
         return system_journal_open(s, false);
 }
 
@@ -2133,6 +1889,8 @@ void server_done(Server *s) {
         while (s->stdout_streams)
                 stdout_stream_free(s->stdout_streams);
 
+        client_context_flush_all(s);
+
         if (s->system_journal)
                 (void) journal_file_close(s->system_journal);
 
index 882bcead8d72888146e4b58b2063cb8930b7ec36..6a4549b1ba16bbef4fd4f14aee2c267be0428005 100644 (file)
@@ -28,9 +28,11 @@ typedef struct Server Server;
 
 #include "hashmap.h"
 #include "journal-file.h"
+#include "journald-context.h"
 #include "journald-rate-limit.h"
 #include "journald-stream.h"
 #include "list.h"
+#include "prioq.h"
 
 typedef enum Storage {
         STORAGE_AUTO,
@@ -166,6 +168,13 @@ struct Server {
         usec_t watchdog_usec;
 
         usec_t last_realtime_clock;
+
+        /* Caching of client metadata */
+        Hashmap *client_contexts;
+        Prioq *client_contexts_lru;
+
+        ClientContext *my_context; /* the context of journald itself */
+        ClientContext *pid1_context; /* the context of PID 1 */
 };
 
 #define SERVER_MACHINE_ID(s) ((s)->machine_id_field + strlen("_MACHINE_ID="))
@@ -176,7 +185,7 @@ struct Server {
 #define N_IOVEC_OBJECT_FIELDS 14
 #define N_IOVEC_PAYLOAD_FIELDS 15
 
-void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid);
+void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, ClientContext *c, const struct timeval *tv, int priority, pid_t object_pid);
 void server_driver_message(Server *s, const char *message_id, const char *format, ...) _printf_(3,0) _sentinel_;
 
 /* gperf lookup function */
index ec10d7aedf5268b33ab4cfac6966f1bcb24154a4..f074f0476fc7434fc5d3288fc3c29bec0819e491 100644 (file)
@@ -34,6 +34,7 @@
 #include "fileio.h"
 #include "io-util.h"
 #include "journald-console.h"
+#include "journald-context.h"
 #include "journald-kmsg.h"
 #include "journald-server.h"
 #include "journald-stream.h"
@@ -41,6 +42,7 @@
 #include "journald-wall.h"
 #include "mkdir.h"
 #include "parse-util.h"
+#include "process-util.h"
 #include "selinux-util.h"
 #include "socket-util.h"
 #include "stdio-util.h"
@@ -87,6 +89,8 @@ struct StdoutStream {
 
         char *state_file;
 
+        ClientContext *context;
+
         LIST_FIELDS(StdoutStream, stdout_stream);
         LIST_FIELDS(StdoutStream, stdout_stream_notify_queue);
 };
@@ -96,6 +100,10 @@ void stdout_stream_free(StdoutStream *s) {
                 return;
 
         if (s->server) {
+
+                if (s->context)
+                        client_context_release(s->server, s->context);
+
                 assert(s->server->n_stdout_streams > 0);
                 s->server->n_stdout_streams--;
                 LIST_REMOVE(stdout_stream, s->server->stdout_streams, s);
@@ -233,7 +241,7 @@ static int stdout_stream_log(StdoutStream *s, const char *p) {
         char syslog_facility[sizeof("SYSLOG_FACILITY=")-1 + DECIMAL_STR_MAX(int) + 1];
         _cleanup_free_ char *message = NULL, *syslog_identifier = NULL;
         unsigned n = 0;
-        size_t label_len;
+        int r;
 
         assert(s);
         assert(p);
@@ -278,8 +286,15 @@ static int stdout_stream_log(StdoutStream *s, const char *p) {
         if (message)
                 IOVEC_SET_STRING(iovec[n++], message);
 
-        label_len = s->label ? strlen(s->label) : 0;
-        server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, s->label, label_len, s->unit_id, priority, 0);
+        if (s->context)
+                (void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY);
+        else if (pid_is_valid(s->ucred.pid)) {
+                r = client_context_acquire(s->server, s->ucred.pid, &s->ucred, s->label, strlen_ptr(s->label), s->unit_id, &s->context);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to acquire client context, ignoring: %m");
+        }
+
+        server_dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), s->context, NULL, priority, 0);
         return 0;
 }
 
index 17f855e96731cd4c9b340a802864affc9a776534..a03c36df34764ee320301e5dfb0538b314b820db 100644 (file)
@@ -325,11 +325,12 @@ void server_process_syslog_message(
         char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)],
              syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
         const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL;
-        struct iovec iovec[N_IOVEC_META_FIELDS + 6];
-        unsigned n = 0;
-        int priority = LOG_USER | LOG_INFO;
         _cleanup_free_ char *identifier = NULL, *pid = NULL;
+        struct iovec iovec[N_IOVEC_META_FIELDS + 6];
+        int priority = LOG_USER | LOG_INFO, r;
+        ClientContext *context = NULL;
         const char *orig;
+        unsigned n = 0;
 
         assert(s);
         assert(buf);
@@ -376,7 +377,13 @@ void server_process_syslog_message(
         if (message)
                 IOVEC_SET_STRING(iovec[n++], message);
 
-        server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0);
+        if (ucred && pid_is_valid(ucred->pid)) {
+                r = client_context_get(s, ucred->pid, ucred, label, label_len, NULL, &context);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m", ucred->pid);
+        }
+
+        server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), context, tv, priority, 0);
 }
 
 int server_open_syslog_socket(Server *s) {
index 582f83afb9228d624173aca3f12ef6cf08eb23c0..b95efc2041195106687cfaa9ea59a5891d5733d5 100644 (file)
@@ -59,24 +59,26 @@ journal_internal_sources += [audit_type_to_name]
 ############################################################
 
 libjournal_core_sources = files('''
-        journald-kmsg.c
-        journald-kmsg.h
-        journald-syslog.c
-        journald-syslog.h
-        journald-stream.c
-        journald-stream.h
-        journald-server.c
-        journald-server.h
+        journald-audit.c
+        journald-audit.h
         journald-console.c
         journald-console.h
-        journald-wall.c
-        journald-wall.h
+        journald-context.c
+        journald-context.h
+        journald-kmsg.c
+        journald-kmsg.h
         journald-native.c
         journald-native.h
-        journald-audit.c
-        journald-audit.h
         journald-rate-limit.c
         journald-rate-limit.h
+        journald-server.c
+        journald-server.h
+        journald-stream.c
+        journald-stream.h
+        journald-syslog.c
+        journald-syslog.h
+        journald-wall.c
+        journald-wall.h
         journal-internal.h
 '''.split())