summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2017-07-31 20:01:05 +0200
committerGitHub <noreply@github.com>2017-07-31 20:01:05 +0200
commit6b43d079a251f11ec70ac91579ecc24f3448b963 (patch)
tree9de58289d618e16a8e1ebab6769476bb9440f7fc
parent3a0bf6d6aa08a267aefd5d5620fb6bee2556affb (diff)
parent22e3a02b9d618bbebcf987bc1411acda367271ec (diff)
downloadsystemd-6b43d079a251f11ec70ac91579ecc24f3448b963.tar.gz
Merge pull request #6392 from poettering/journal-cache
add limited metadata caching to journald and other journal improvements
-rw-r--r--man/systemd-escape.xml2
-rw-r--r--src/basic/alloc-util.c27
-rw-r--r--src/basic/alloc-util.h10
-rw-r--r--src/basic/audit-util.c4
-rw-r--r--src/basic/audit-util.h4
-rw-r--r--src/basic/bus-label.h4
-rw-r--r--src/basic/escape.c2
-rw-r--r--src/basic/hexdecoct.c3
-rw-r--r--src/basic/parse-util.c2
-rw-r--r--src/basic/process-util.c30
-rw-r--r--src/basic/process-util.h4
-rw-r--r--src/basic/string-util.c6
-rw-r--r--src/basic/string-util.h7
-rw-r--r--src/core/execute.c24
-rw-r--r--src/escape/escape.c2
-rw-r--r--src/journal/journald-audit.c2
-rw-r--r--src/journal/journald-context.c588
-rw-r--r--src/journal/journald-context.h92
-rw-r--r--src/journal/journald-kmsg.c2
-rw-r--r--src/journal/journald-native.c15
-rw-r--r--src/journal/journald-rate-limit.c7
-rw-r--r--src/journal/journald-server.c450
-rw-r--r--src/journal/journald-server.h11
-rw-r--r--src/journal/journald-stream.c40
-rw-r--r--src/journal/journald-syslog.c15
-rw-r--r--src/journal/meson.build26
-rw-r--r--src/libsystemd/sd-bus/bus-creds.c4
-rw-r--r--src/login/logind-dbus.c10
-rw-r--r--src/login/logind-session.c11
-rw-r--r--src/nss-myhostname/nss-myhostname.c2
-rw-r--r--src/shared/logs-show.c7
-rw-r--r--src/systemctl/systemctl.c2
-rw-r--r--src/test/test-string-util.c7
33 files changed, 988 insertions, 434 deletions
diff --git a/man/systemd-escape.xml b/man/systemd-escape.xml
index bb4c7e48e5..fb20d2d94f 100644
--- a/man/systemd-escape.xml
+++ b/man/systemd-escape.xml
@@ -45,7 +45,7 @@
<refnamediv>
<refname>systemd-escape</refname>
- <refpurpose>Escape strings for usage in system unit names</refpurpose>
+ <refpurpose>Escape strings for usage in systemd unit names</refpurpose>
</refnamediv>
<refsynopsisdiv>
diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c
index b540dcddf5..948389f276 100644
--- a/src/basic/alloc-util.c
+++ b/src/basic/alloc-util.c
@@ -25,16 +25,31 @@
#include "util.h"
void* memdup(const void *p, size_t l) {
- void *r;
+ void *ret;
- assert(p);
+ assert(l == 0 || p);
+
+ ret = malloc(l);
+ if (!ret)
+ return NULL;
+
+ memcpy(ret, p, l);
+ return ret;
+}
+
+void* memdup_suffix0(const void*p, size_t l) {
+ void *ret;
+
+ assert(l == 0 || p);
+
+ /* The same as memdup() but place a safety NUL byte after the allocated memory */
- r = malloc(l);
- if (!r)
+ ret = malloc(l + 1);
+ if (!ret)
return NULL;
- memcpy(r, p, l);
- return r;
+ *((uint8_t*) mempcpy(ret, p, l)) = 0;
+ return ret;
}
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h
index a44dd473c1..0a89691bae 100644
--- a/src/basic/alloc-util.h
+++ b/src/basic/alloc-util.h
@@ -36,6 +36,8 @@
#define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
+#define newdup_suffix0(t, p, n) ((t*) memdup_suffix0_multiply(p, sizeof(t), (n)))
+
#define malloc0(n) (calloc(1, (n)))
static inline void *mfree(void *memory) {
@@ -52,6 +54,7 @@ static inline void *mfree(void *memory) {
})
void* memdup(const void *p, size_t l) _alloc_(2);
+void* memdup_suffix0(const void*p, size_t l) _alloc_(2);
static inline void freep(void *p) {
free(*(void**) p);
@@ -84,6 +87,13 @@ _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, si
return memdup(p, size * need);
}
+_alloc_(2, 3) static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t need) {
+ if (size_multiply_overflow(size, need))
+ return NULL;
+
+ return memdup_suffix0(p, size * need);
+}
+
void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size);
void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c
index d1c9695973..24a6c8a936 100644
--- a/src/basic/audit-util.c
+++ b/src/basic/audit-util.c
@@ -54,7 +54,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) {
if (r < 0)
return r;
- if (u == AUDIT_SESSION_INVALID || u <= 0)
+ if (!audit_session_is_valid(u))
return -ENODATA;
*id = u;
@@ -81,7 +81,7 @@ int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
if (r < 0)
return r;
- *uid = (uid_t) u;
+ *uid = u;
return 0;
}
diff --git a/src/basic/audit-util.h b/src/basic/audit-util.h
index e048503991..3088951326 100644
--- a/src/basic/audit-util.h
+++ b/src/basic/audit-util.h
@@ -29,3 +29,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id);
int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
bool use_audit(void);
+
+static inline bool audit_session_is_valid(uint32_t id) {
+ return id > 0 && id != AUDIT_SESSION_INVALID;
+}
diff --git a/src/basic/bus-label.h b/src/basic/bus-label.h
index 62fb2c450c..600268b767 100644
--- a/src/basic/bus-label.h
+++ b/src/basic/bus-label.h
@@ -23,9 +23,11 @@
#include <stdlib.h>
#include <string.h>
+#include "string-util.h"
+
char *bus_label_escape(const char *s);
char *bus_label_unescape_n(const char *f, size_t l);
static inline char *bus_label_unescape(const char *f) {
- return bus_label_unescape_n(f, f ? strlen(f) : 0);
+ return bus_label_unescape_n(f, strlen_ptr(f));
}
diff --git a/src/basic/escape.c b/src/basic/escape.c
index 85e4b5282e..22b8b04156 100644
--- a/src/basic/escape.c
+++ b/src/basic/escape.c
@@ -314,7 +314,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
/* Undoes C style string escaping, and optionally prefixes it. */
- pl = prefix ? strlen(prefix) : 0;
+ pl = strlen_ptr(prefix);
r = new(char, pl+length+1);
if (!r)
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index 2d6e377f0a..766770389c 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "hexdecoct.h"
#include "macro.h"
+#include "string-util.h"
#include "util.h"
char octchar(int x) {
@@ -569,7 +570,7 @@ static int base64_append_width(char **prefix, int plen,
lines = (len + width - 1) / width;
- slen = sep ? strlen(sep) : 0;
+ slen = strlen_ptr(sep);
t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
if (!t)
return -ENOMEM;
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 4532f222c8..89bb667c5f 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -59,7 +59,7 @@ int parse_pid(const char *s, pid_t* ret_pid) {
if ((unsigned long) pid != ul)
return -ERANGE;
- if (pid <= 0)
+ if (!pid_is_valid(pid))
return -ERANGE;
*ret_pid = pid;
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index ce6a59c8a4..f5bd6c9487 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -388,7 +388,7 @@ int is_kernel_thread(pid_t pid) {
bool eof;
FILE *f;
- if (pid == 0 || pid == 1) /* pid 1, and we ourselves certainly aren't a kernel thread */
+ if (pid == 0 || pid == 1 || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */
return 0;
assert(pid > 1);
@@ -471,6 +471,9 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
assert(field);
assert(uid);
+ if (pid < 0)
+ return -EINVAL;
+
p = procfs_file_alloca(pid, "status");
f = fopen(p, "re");
if (!f) {
@@ -498,10 +501,22 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
}
int get_process_uid(pid_t pid, uid_t *uid) {
+
+ if (pid == 0 || pid == getpid_cached()) {
+ *uid = getuid();
+ return 0;
+ }
+
return get_process_id(pid, "Uid:", uid);
}
int get_process_gid(pid_t pid, gid_t *gid) {
+
+ if (pid == 0 || pid == getpid_cached()) {
+ *gid = getgid();
+ return 0;
+ }
+
assert_cc(sizeof(uid_t) == sizeof(gid_t));
return get_process_id(pid, "Gid:", gid);
}
@@ -577,7 +592,7 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
assert(pid >= 0);
assert(_ppid);
- if (pid == 0) {
+ if (pid == 0 || pid == getpid_cached()) {
*_ppid = getppid();
return 0;
}
@@ -775,6 +790,9 @@ bool pid_is_unwaited(pid_t pid) {
if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
return true;
+ if (pid == getpid_cached())
+ return true;
+
if (kill(pid, 0) >= 0)
return true;
@@ -792,6 +810,9 @@ bool pid_is_alive(pid_t pid) {
if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */
return true;
+ if (pid == getpid_cached())
+ return true;
+
r = get_process_state(pid);
if (r == -ESRCH || r == 'Z')
return false;
@@ -803,7 +824,10 @@ int pid_from_same_root_fs(pid_t pid) {
const char *root;
if (pid < 0)
- return 0;
+ return false;
+
+ if (pid == 0 || pid == getpid_cached())
+ return true;
root = procfs_file_alloca(pid, "root");
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 17746b4ebf..b45d60dbd1 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -118,6 +118,10 @@ static inline bool ioprio_priority_is_valid(int i) {
return i >= 0 && i < IOPRIO_BE_NR;
}
+static inline bool pid_is_valid(pid_t p) {
+ return p > 0;
+}
+
int ioprio_parse_priority(const char *s, int *ret);
pid_t getpid_cached(void);
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index cd58ef97ef..5808b1280e 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -215,7 +215,7 @@ char *strnappend(const char *s, const char *suffix, size_t b) {
}
char *strappend(const char *s, const char *suffix) {
- return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+ return strnappend(s, suffix, strlen_ptr(suffix));
}
char *strjoin_real(const char *x, ...) {
@@ -558,7 +558,7 @@ bool nulstr_contains(const char *nulstr, const char *needle) {
char* strshorten(char *s, size_t l) {
assert(s);
- if (l < strlen(s))
+ if (strnlen(s, l+1) > l)
s[l] = 0;
return s;
@@ -707,7 +707,7 @@ char *strextend(char **x, ...) {
assert(x);
- l = f = *x ? strlen(*x) : 0;
+ l = f = strlen_ptr(*x);
va_start(ap, x);
for (;;) {
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index e8a0836538..34eb952ce9 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -200,3 +200,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase);
#define _cleanup_string_free_erase_ _cleanup_(string_free_erasep)
bool string_is_safe(const char *p) _pure_;
+
+static inline size_t strlen_ptr(const char *s) {
+ if (!s)
+ return 0;
+
+ return strlen(s);
+}
diff --git a/src/core/execute.c b/src/core/execute.c
index 48b84815ca..7481588b08 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -278,7 +278,7 @@ static int open_null_as(int flags, int nfd) {
}
static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
- union sockaddr_union sa = {
+ static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
.un.sun_path = "/run/systemd/journal/stdout",
};
@@ -286,36 +286,32 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
gid_t oldgid = GID_INVALID;
int r;
- if (gid != GID_INVALID) {
+ if (gid_is_valid(gid)) {
oldgid = getgid();
- r = setegid(gid);
- if (r < 0)
+ if (setegid(gid) < 0)
return -errno;
}
- if (uid != UID_INVALID) {
+ if (uid_is_valid(uid)) {
olduid = getuid();
- r = seteuid(uid);
- if (r < 0) {
+ if (seteuid(uid) < 0) {
r = -errno;
goto restore_gid;
}
}
- r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
- if (r < 0)
- r = -errno;
+ r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0 ? -errno : 0;
/* If we fail to restore the uid or gid, things will likely
fail later on. This should only happen if an LSM interferes. */
- if (uid != UID_INVALID)
+ if (uid_is_valid(uid))
(void) seteuid(olduid);
restore_gid:
- if (gid != GID_INVALID)
+ if (gid_is_valid(gid))
(void) setegid(oldgid);
return r;
@@ -360,8 +356,8 @@ static int connect_logger_as(
"%i\n"
"%i\n"
"%i\n",
- context->syslog_identifier ? context->syslog_identifier : ident,
- unit->id,
+ context->syslog_identifier ?: ident,
+ MANAGER_IS_SYSTEM(unit->manager) ? unit->id : "",
context->syslog_priority,
!!context->syslog_level_prefix,
output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
diff --git a/src/escape/escape.c b/src/escape/escape.c
index 89e885d47c..5518c2a6fa 100644
--- a/src/escape/escape.c
+++ b/src/escape/escape.c
@@ -38,7 +38,7 @@ static bool arg_path = false;
static void help(void) {
printf("%s [OPTIONS...] [NAME...]\n\n"
- "Escape strings for usage in system unit names.\n\n"
+ "Escape strings for usage in systemd unit names.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --suffix=SUFFIX Unit suffix to append to escaped strings\n"
diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index a433c91c54..38ac3befdd 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -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
index 0000000000..10e9615f23
--- /dev/null
+++ b/src/journal/journald-context.c
@@ -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
index 0000000000..eb1e21926a
--- /dev/null
+++ b/src/journal/journald-context.h
@@ -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);
diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c
index 7fea85a5d8..2be82be5f6 100644
--- a/src/journal/journald-kmsg.c
+++ b/src/journal/journald-kmsg.c
@@ -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++)
diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
index abd06b1adc..23afe59bd5 100644
--- a/src/journal/journald-native.c
+++ b/src/journal/journald-native.c
@@ -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);
}
diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c
index f48639cf58..a3404222e0 100644
--- a/src/journal/journald-rate-limit.c
+++ b/src/journal/journald-rate-limit.c
@@ -216,6 +216,13 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
assert(id);
+ /* Returns:
+ *
+ * 0 → the log message shall be suppressed,
+ * 1 + n → the log message shall be permitted, and n messages were dropped from the peer before
+ * < 0 → error
+ */
+
if (!r)
return 1;
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index f391845ba9..feef10c2db 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -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);
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index 882bcead8d..6a4549b1ba 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -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 */
diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c
index 77551dc14b..f074f0476f 100644
--- a/src/journal/journald-stream.c
+++ b/src/journal/journald-stream.c
@@ -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,11 +42,13 @@
#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"
#include "string-util.h"
#include "syslog-util.h"
+#include "unit-name.h"
#define STDOUT_STREAMS_MAX 4096
@@ -86,6 +89,8 @@ struct StdoutStream {
char *state_file;
+ ClientContext *context;
+
LIST_FIELDS(StdoutStream, stdout_stream);
LIST_FIELDS(StdoutStream, stdout_stream_notify_queue);
};
@@ -95,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);
@@ -232,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);
@@ -277,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;
}
@@ -295,9 +311,7 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
switch (s->state) {
case STDOUT_STREAM_IDENTIFIER:
- if (isempty(p))
- s->identifier = NULL;
- else {
+ if (!isempty(p)) {
s->identifier = strdup(p);
if (!s->identifier)
return log_oom();
@@ -307,14 +321,12 @@ static int stdout_stream_line(StdoutStream *s, char *p) {
return 0;
case STDOUT_STREAM_UNIT_ID:
- if (s->ucred.uid == 0) {
- if (isempty(p))
- s->unit_id = NULL;
- else {
- s->unit_id = strdup(p);
- if (!s->unit_id)
- return log_oom();
- }
+ if (s->ucred.uid == 0 &&
+ unit_name_is_valid(p, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) {
+
+ s->unit_id = strdup(p);
+ if (!s->unit_id)
+ return log_oom();
}
s->state = STDOUT_STREAM_PRIORITY;
diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c
index 17f855e967..a03c36df34 100644
--- a/src/journal/journald-syslog.c
+++ b/src/journal/journald-syslog.c
@@ -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) {
diff --git a/src/journal/meson.build b/src/journal/meson.build
index 582f83afb9..b95efc2041 100644
--- a/src/journal/meson.build
+++ b/src/journal/meson.build
@@ -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())
diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c
index 649fcdba44..a05b4215fb 100644
--- a/src/libsystemd/sd-bus/bus-creds.c
+++ b/src/libsystemd/sd-bus/bus-creds.c
@@ -570,7 +570,7 @@ _public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessio
if (!(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID))
return -ENODATA;
- if (c->audit_session_id == AUDIT_SESSION_INVALID)
+ if (!audit_session_is_valid(c->audit_session_id))
return -ENXIO;
*sessionid = c->audit_session_id;
@@ -584,7 +584,7 @@ _public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) {
if (!(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID))
return -ENODATA;
- if (c->audit_login_uid == UID_INVALID)
+ if (!uid_is_valid(c->audit_login_uid))
return -ENXIO;
*uid = c->audit_login_uid;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index c9b7d99818..cd22ff4fca 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -88,7 +88,7 @@ int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid,
assert(message);
assert(ret);
- if (uid == UID_INVALID) {
+ if (!uid_is_valid(uid)) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
/* Note that we get the owner UID of the session, not the actual client UID here! */
@@ -767,8 +767,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
if (hashmap_size(m->sessions) >= m->sessions_max)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max);
- audit_session_from_pid(leader, &audit_id);
- if (audit_id > 0) {
+ (void) audit_session_from_pid(leader, &audit_id);
+ if (audit_session_is_valid(audit_id)) {
/* Keep our session IDs and the audit session IDs in sync */
if (asprintf(&id, "%"PRIu32, audit_id) < 0)
@@ -780,7 +780,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
* ID */
if (hashmap_get(m->sessions, id)) {
log_warning("Existing logind session ID %s used by new audit session, ignoring", id);
- audit_id = 0;
+ audit_id = AUDIT_SESSION_INVALID;
id = mfree(id);
}
@@ -1132,7 +1132,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
- if (uid == UID_INVALID) {
+ if (!uid_is_valid(uid)) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
/* Note that we get the owner UID of the session, not the actual client UID here! */
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 42dfecaffb..3778bb7d70 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -45,6 +45,7 @@
#include "terminal-util.h"
#include "user-util.h"
#include "util.h"
+#include "process-util.h"
#define RELEASE_USEC (20*USEC_PER_SEC)
@@ -82,6 +83,7 @@ Session* session_new(Manager *m, const char *id) {
s->manager = m;
s->fifo_fd = -1;
s->vtfd = -1;
+ s->audit_id = AUDIT_SESSION_INVALID;
return s;
}
@@ -280,10 +282,10 @@ int session_save(Session *s) {
if (!s->vtnr)
fprintf(f, "POSITION=%u\n", s->position);
- if (s->leader > 0)
+ if (pid_is_valid(s->leader))
fprintf(f, "LEADER="PID_FMT"\n", s->leader);
- if (s->audit_id > 0)
+ if (audit_session_is_valid(s->audit_id))
fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
if (dual_timestamp_is_set(&s->timestamp))
@@ -459,9 +461,8 @@ int session_load(Session *s) {
}
if (leader) {
- k = parse_pid(leader, &s->leader);
- if (k >= 0)
- audit_session_from_pid(s->leader, &s->audit_id);
+ if (parse_pid(leader, &s->leader) >= 0)
+ (void) audit_session_from_pid(s->leader, &s->audit_id);
}
if (type) {
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index 0570fde592..869d233d49 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -211,7 +211,7 @@ static enum nss_status fill_in_hostent(
c++;
l_canonical = strlen(canonical);
- l_additional = additional ? strlen(additional) : 0;
+ l_additional = strlen_ptr(additional);
ms = ALIGN(l_canonical+1)+
(additional ? ALIGN(l_additional+1) : 0) +
sizeof(char*) +
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index 02ae4265c6..54516cfb96 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -95,13 +95,12 @@ static int parse_field(const void *data, size_t length, const char *field, char
return 0;
nl = length - fl;
- buf = new(char, nl+1);
+
+
+ buf = newdup_suffix0(char, (const char*) data + fl, nl);
if (!buf)
return log_oom();
- memcpy(buf, (const char*) data + fl, nl);
- buf[nl] = 0;
-
free(*target);
*target = buf;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 55fce62480..36675dc499 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2010,7 +2010,7 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n)
for (m = machine_infos; m < machine_infos + n; m++) {
namelen = MAX(namelen, strlen(m->name) + (m->is_host ? sizeof(" (host)") - 1 : 0));
- statelen = MAX(statelen, m->state ? strlen(m->state) : 0);
+ statelen = MAX(statelen, strlen_ptr(m->state));
failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units));
jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs));
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
index 4b3e924cfb..604701ff7a 100644
--- a/src/test/test-string-util.c
+++ b/src/test/test-string-util.c
@@ -336,6 +336,12 @@ static void test_first_word(void) {
assert_se(!first_word("Hellooo", "Hello"));
}
+static void test_strlen_ptr(void) {
+ assert_se(strlen_ptr("foo") == 3);
+ assert_se(strlen_ptr("") == 0);
+ assert_se(strlen_ptr(NULL) == 0);
+}
+
int main(int argc, char *argv[]) {
test_string_erase();
test_ascii_strcasecmp_n();
@@ -358,6 +364,7 @@ int main(int argc, char *argv[]) {
test_in_charset();
test_split_pair();
test_first_word();
+ test_strlen_ptr();
return 0;
}