about summary refs log tree commit diff
path: root/shared/systemd/src
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2020-10-05 22:27:18 +0200
committerMichael Biebl <biebl@debian.org>2020-10-05 22:27:18 +0200
commitaafc1dbe4712c86189bbc1d4d54ad8cb4c69be7e (patch)
treea2a9bb4d007339a0b1304540388230ccedac32bd /shared/systemd/src
parente7b44ef4c80907346ec7492a09c45277459924fc (diff)
New upstream version 1.27.90 upstream/1.27.90
Diffstat (limited to 'shared/systemd/src')
-rw-r--r--shared/systemd/src/basic/env-file.c10
-rw-r--r--shared/systemd/src/basic/env-util.c4
-rw-r--r--shared/systemd/src/basic/escape.c14
-rw-r--r--shared/systemd/src/basic/escape.h14
-rw-r--r--shared/systemd/src/basic/extract-word.c26
-rw-r--r--shared/systemd/src/basic/extract-word.h7
-rw-r--r--shared/systemd/src/basic/fd-util.c18
-rw-r--r--shared/systemd/src/basic/fd-util.h10
-rw-r--r--shared/systemd/src/basic/fileio.c120
-rw-r--r--shared/systemd/src/basic/fileio.h16
-rw-r--r--shared/systemd/src/basic/fs-util.c115
-rw-r--r--shared/systemd/src/basic/fs-util.h5
-rw-r--r--shared/systemd/src/basic/hashmap.c131
-rw-r--r--shared/systemd/src/basic/hashmap.h96
-rw-r--r--shared/systemd/src/basic/in-addr-util.c86
-rw-r--r--shared/systemd/src/basic/in-addr-util.h14
-rw-r--r--shared/systemd/src/basic/io-util.c4
-rw-r--r--shared/systemd/src/basic/list.h15
-rw-r--r--shared/systemd/src/basic/log.h13
-rw-r--r--shared/systemd/src/basic/macro.h18
-rw-r--r--shared/systemd/src/basic/memory-util.h1
-rw-r--r--shared/systemd/src/basic/missing_socket.h4
-rw-r--r--shared/systemd/src/basic/missing_stat.h136
-rw-r--r--shared/systemd/src/basic/missing_syscall.h634
-rw-r--r--shared/systemd/src/basic/parse-util.c7
-rw-r--r--shared/systemd/src/basic/parse-util.h2
-rw-r--r--shared/systemd/src/basic/path-util.c71
-rw-r--r--shared/systemd/src/basic/path-util.h20
-rw-r--r--shared/systemd/src/basic/process-util.c2
-rw-r--r--shared/systemd/src/basic/process-util.h12
-rw-r--r--shared/systemd/src/basic/random-util.c47
-rw-r--r--shared/systemd/src/basic/random-util.h2
-rw-r--r--shared/systemd/src/basic/set.h36
-rw-r--r--shared/systemd/src/basic/siphash24.h30
-rw-r--r--shared/systemd/src/basic/socket-util.c278
-rw-r--r--shared/systemd/src/basic/socket-util.h57
-rw-r--r--shared/systemd/src/basic/stat-util.c57
-rw-r--r--shared/systemd/src/basic/stat-util.h22
-rw-r--r--shared/systemd/src/basic/string-table.c4
-rw-r--r--shared/systemd/src/basic/string-util.c106
-rw-r--r--shared/systemd/src/basic/string-util.h39
-rw-r--r--shared/systemd/src/basic/strv.c115
-rw-r--r--shared/systemd/src/basic/strv.h29
-rw-r--r--shared/systemd/src/basic/time-util.c166
-rw-r--r--shared/systemd/src/basic/time-util.h28
-rw-r--r--shared/systemd/src/basic/tmpfile-util.c2
-rw-r--r--shared/systemd/src/basic/utf8.c22
-rw-r--r--shared/systemd/src/basic/utf8.h7
-rw-r--r--shared/systemd/src/shared/dns-domain.c21
-rw-r--r--shared/systemd/src/shared/dns-domain.h7
50 files changed, 1822 insertions, 878 deletions
diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c
index 30ee135a..1e93f18e 100644
--- a/shared/systemd/src/basic/env-file.c
+++ b/shared/systemd/src/basic/env-file.c
@@ -211,17 +211,21 @@ static int parse_env_file_internal(
                 case DOUBLE_QUOTE_VALUE_ESCAPE:
                         state = DOUBLE_QUOTE_VALUE;
 
-                        if (c == '"') {
+                        if (strchr(SHELL_NEED_ESCAPE, c)) {
+                                /* If this is a char that needs escaping, just unescape it. */
                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
                                         return -ENOMEM;
-                                value[n_value++] = '"';
-                        } else if (!strchr(NEWLINE, c)) {
+                                value[n_value++] = c;
+                        } else if (c != '\n') {
+                                /* If other char than what needs escaping, keep the "\" in place, like the
+                                 * real shell does. */
                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+3))
                                         return -ENOMEM;
                                 value[n_value++] = '\\';
                                 value[n_value++] = c;
                         }
 
+                        /* Escaped newlines (aka "continuation lines") are eaten up entirely */
                         break;
 
                 case COMMENT:
diff --git a/shared/systemd/src/basic/env-util.c b/shared/systemd/src/basic/env-util.c
index d16890d9..11f4b29b 100644
--- a/shared/systemd/src/basic/env-util.c
+++ b/shared/systemd/src/basic/env-util.c
@@ -690,7 +690,7 @@ char **replace_env_argv(char **argv, char **env) {
                         if (e) {
                                 int r;
 
-                                r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
+                                r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
                                 if (r < 0) {
                                         ret[k] = NULL;
                                         strv_free(ret);
@@ -744,7 +744,6 @@ int getenv_bool(const char *p) {
         return parse_boolean(e);
 }
 
-#if 0 /* NM_IGNORED */
 int getenv_bool_secure(const char *p) {
         const char *e;
 
@@ -754,4 +753,3 @@ int getenv_bool_secure(const char *p) {
 
         return parse_boolean(e);
 }
-#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/escape.c b/shared/systemd/src/basic/escape.c
index 1ea0b6a6..1261fd48 100644
--- a/shared/systemd/src/basic/escape.c
+++ b/shared/systemd/src/basic/escape.c
@@ -77,7 +77,7 @@ int cescape_char(char c, char *buf) {
         return buf - buf_old;
 }
 
-char *cescape_length(const char *s, size_t n) {
+char* cescape_length(const char *s, size_t n) {
         const char *f;
         char *r, *t;
 
@@ -98,7 +98,7 @@ char *cescape_length(const char *s, size_t n) {
         return r;
 }
 
-char *cescape(const char *s) {
+char* cescape(const char *s) {
         assert(s);
 
         return cescape_length(s, strlen(s));
@@ -363,7 +363,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
         return t - r;
 }
 
-char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
+char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
         char *ans, *t, *prev, *prev2;
         const char *f;
 
@@ -430,7 +430,7 @@ char *xescape_full(const char *s, const char *bad, size_t console_width, bool ei
         return ans;
 }
 
-char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
+char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
         if (eight_bit)
                 return xescape_full(str, "", console_width, true);
         else
@@ -438,7 +438,7 @@ char *escape_non_printable_full(const char *str, size_t console_width, bool eigh
 }
 #endif /* NM_IGNORED */
 
-char *octescape(const char *s, size_t len) {
+char* octescape(const char *s, size_t len) {
         char *r, *t;
         const char *f;
 
@@ -466,7 +466,7 @@ char *octescape(const char *s, size_t len) {
 
 }
 
-static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) {
+static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) {
         assert(bad);
 
         for (; *s; s++) {
@@ -485,7 +485,7 @@ static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, b
         return t;
 }
 
-char *shell_escape(const char *s, const char *bad) {
+char* shell_escape(const char *s, const char *bad) {
         char *r, *t;
 
         r = new(char, strlen(s)*2+1);
diff --git a/shared/systemd/src/basic/escape.h b/shared/systemd/src/basic/escape.h
index 0b00b116..fa267813 100644
--- a/shared/systemd/src/basic/escape.h
+++ b/shared/systemd/src/basic/escape.h
@@ -43,8 +43,8 @@ typedef enum EscapeStyle {
                                         * syntax (a string enclosed in $'') instead of plain quotes. */
 } EscapeStyle;
 
-char *cescape(const char *s);
-char *cescape_length(const char *s, size_t n);
+char* cescape(const char *s);
+char* cescape_length(const char *s, size_t n);
 int cescape_char(char c, char *buf);
 
 int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret);
@@ -56,12 +56,12 @@ static inline int cunescape(const char *s, UnescapeFlags flags, char **ret) {
 }
 int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
 
-char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits);
-static inline char *xescape(const char *s, const char *bad) {
+char* xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits);
+static inline char* xescape(const char *s, const char *bad) {
         return xescape_full(s, bad, SIZE_MAX, false);
 }
-char *octescape(const char *s, size_t len);
-char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit);
+char* octescape(const char *s, size_t len);
+char* escape_non_printable_full(const char *str, size_t console_width, bool eight_bit);
 
-char *shell_escape(const char *s, const char *bad);
+char* shell_escape(const char *s, const char *bad);
 char* shell_maybe_quote(const char *s, EscapeStyle style);
diff --git a/shared/systemd/src/basic/extract-word.c b/shared/systemd/src/basic/extract-word.c
index 62f015f3..e32b4c7f 100644
--- a/shared/systemd/src/basic/extract-word.c
+++ b/shared/systemd/src/basic/extract-word.c
@@ -16,6 +16,7 @@
 #include "log.h"
 #include "macro.h"
 #include "string-util.h"
+#include "strv.h"
 #include "utf8.h"
 
 int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
@@ -88,25 +89,30 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
                                 return -EINVAL;
                         }
 
-                        if (flags & EXTRACT_CUNESCAPE) {
+                        if (flags & (EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS)) {
                                 bool eight_bit = false;
                                 char32_t u;
 
-                                r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false);
-                                if (r < 0) {
-                                        if (flags & EXTRACT_CUNESCAPE_RELAX) {
-                                                s[sz++] = '\\';
-                                                s[sz++] = c;
-                                        } else
-                                                return -EINVAL;
-                                } else {
+                                if ((flags & EXTRACT_CUNESCAPE) &&
+                                    (r = cunescape_one(*p, (size_t) -1, &u, &eight_bit, false)) >= 0) {
+                                        /* A valid escaped sequence */
+                                        assert(r >= 1);
+
                                         (*p) += r - 1;
 
                                         if (eight_bit)
                                                 s[sz++] = u;
                                         else
                                                 sz += utf8_encode_unichar(s + sz, u);
-                                }
+                                } else if ((flags & EXTRACT_UNESCAPE_SEPARATORS) &&
+                                           strchr(separators, **p))
+                                        /* An escaped separator char */
+                                        s[sz++] = c;
+                                else if (flags & EXTRACT_CUNESCAPE_RELAX) {
+                                        s[sz++] = '\\';
+                                        s[sz++] = c;
+                                } else
+                                        return -EINVAL;
                         } else
                                 s[sz++] = c;
 
diff --git a/shared/systemd/src/basic/extract-word.h b/shared/systemd/src/basic/extract-word.h
index e2d43389..f028577c 100644
--- a/shared/systemd/src/basic/extract-word.h
+++ b/shared/systemd/src/basic/extract-word.h
@@ -7,9 +7,10 @@ typedef enum ExtractFlags {
         EXTRACT_RELAX                    = 1 << 0,
         EXTRACT_CUNESCAPE                = 1 << 1,
         EXTRACT_CUNESCAPE_RELAX          = 1 << 2,
-        EXTRACT_UNQUOTE                  = 1 << 3,
-        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 4,
-        EXTRACT_RETAIN_ESCAPE            = 1 << 5,
+        EXTRACT_UNESCAPE_SEPARATORS      = 1 << 3,
+        EXTRACT_UNQUOTE                  = 1 << 4,
+        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5,
+        EXTRACT_RETAIN_ESCAPE            = 1 << 6,
 } ExtractFlags;
 
 int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c
index a401663e..525d3828 100644
--- a/shared/systemd/src/basic/fd-util.c
+++ b/shared/systemd/src/basic/fd-util.c
@@ -379,6 +379,7 @@ bool fdname_is_valid(const char *s) {
         return p - s < 256;
 }
 
+#if 0 /* NM_IGNORED */
 int fd_get_path(int fd, char **ret) {
         char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         int r;
@@ -389,18 +390,15 @@ int fd_get_path(int fd, char **ret) {
                 /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
                  * things debuggable and distinguish the two. */
 
-                if (access("/proc/self/fd/", F_OK) < 0)
-                        /* /proc is not available or not set up properly, we're most likely in some chroot
-                         * environment. */
-                        return errno == ENOENT ? -EOPNOTSUPP : -errno;
-
+                if (proc_mounted() == 0)
+                        return -ENOSYS;  /* /proc is not available or not set up properly, we're most likely in some chroot
+                                          * environment. */
                 return -EBADF; /* The directory exists, hence it's the fd that doesn't. */
         }
 
         return r;
 }
 
-#if 0 /* NM_IGNORED */
 int move_fd(int from, int to, int cloexec) {
         int r;
 
@@ -736,8 +734,7 @@ int fd_duplicate_data_fd(int fd) {
                 if (f != 0)
                         return -errno;
 
-                safe_close(copy_fd);
-                copy_fd = TAKE_FD(tmp_fd);
+                CLOSE_AND_REPLACE(copy_fd, tmp_fd);
 
                 remains = mfree(remains);
                 remains_size = 0;
@@ -872,8 +869,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
                                 goto finish;
                         }
 
-                        safe_close(null_fd);
-                        null_fd = copy;
+                        CLOSE_AND_REPLACE(null_fd, copy);
                 }
         }
 
@@ -983,7 +979,7 @@ int read_nr_open(void) {
                         return v;
         }
 
-        /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */
+        /* If we fail, fall back to the hard-coded kernel limit of 1024 * 1024. */
         return 1024 * 1024;
 }
 #endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/fd-util.h b/shared/systemd/src/basic/fd-util.h
index e490753c..93ce95cd 100644
--- a/shared/systemd/src/basic/fd-util.h
+++ b/shared/systemd/src/basic/fd-util.h
@@ -93,6 +93,16 @@ static inline int make_null_stdio(void) {
                 _fd_;                           \
         })
 
+/* Like free_and_replace(), but for file descriptors */
+#define CLOSE_AND_REPLACE(a, b)                 \
+        ({                                      \
+                int *_fdp_ = &(a);              \
+                safe_close(*_fdp_);             \
+                *_fdp_ = TAKE_FD(b);            \
+                0;                              \
+        })
+
+
 int fd_reopen(int fd, int flags);
 
 int read_nr_open(void);
diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c
index 30903263..664b894a 100644
--- a/shared/systemd/src/basic/fileio.c
+++ b/shared/systemd/src/basic/fileio.c
@@ -24,6 +24,7 @@
 #include "mkdir.h"
 #include "parse-util.h"
 #include "path-util.h"
+#include "socket-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "tmpfile-util.h"
@@ -486,13 +487,12 @@ int read_full_stream_full(
         assert(f);
         assert(ret_contents);
         assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
-        assert(!(flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) || ret_size);
 
         n_next = LINE_MAX; /* Start size */
 
         fd = fileno(f);
-        if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
-                        * optimize our buffering) */
+        if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's
+                        * optimize our buffering */
 
                 if (fstat(fd, &st) < 0)
                         return -errno;
@@ -509,7 +509,7 @@ int read_full_stream_full(
                         if (st.st_size > 0)
                                 n_next = st.st_size + 1;
 
-                        if (flags & READ_FULL_FILE_SECURE)
+                        if (flags & READ_FULL_FILE_WARN_WORLD_READABLE)
                                 (void) warn_file_is_world_accessible(filename, &st, NULL, 0);
                 }
         }
@@ -539,21 +539,18 @@ int read_full_stream_full(
 
                 errno = 0;
                 k = fread(buf + l, 1, n - l, f);
-                if (k > 0)
-                        l += k;
+
+                assert(k <= n - l);
+                l += k;
 
                 if (ferror(f)) {
                         r = errno_or_else(EIO);
                         goto finalize;
                 }
-
                 if (feof(f))
                         break;
 
-                /* We aren't expecting fread() to return a short read outside
-                 * of (error && eof), assert buffer is full and enlarge buffer.
-                 */
-                assert(l == n);
+                assert(k > 0); /* we can't have read zero bytes because that would have been EOF */
 
                 /* Safety check */
                 if (n >= READ_FULL_BYTES_MAX) {
@@ -565,12 +562,21 @@ int read_full_stream_full(
         }
 
         if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) {
+                _cleanup_free_ void *decoded = NULL;
+                size_t decoded_size;
+
                 buf[l++] = 0;
                 if (flags & READ_FULL_FILE_UNBASE64)
-                        r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
+                        r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size);
                 else
-                        r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
-                goto finalize;
+                        r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size);
+                if (r < 0)
+                        goto finalize;
+
+                if (flags & READ_FULL_FILE_SECURE)
+                        explicit_bzero_safe(buf, n);
+                free_and_replace(buf, decoded);
+                n = l = decoded_size;
         }
 
         if (!ret_size) {
@@ -607,8 +613,54 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag
         assert(contents);
 
         r = xfopenat(dir_fd, filename, "re", 0, &f);
-        if (r < 0)
-                return r;
+        if (r < 0) {
+                _cleanup_close_ int dfd = -1, sk = -1;
+                union sockaddr_union sa;
+
+                /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */
+                if (r != -ENXIO)
+                        return r;
+
+                /* If this is enabled, let's try to connect to it */
+                if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET))
+                        return -ENXIO;
+
+                if (dir_fd == AT_FDCWD)
+                        r = sockaddr_un_set_path(&sa.un, filename);
+                else {
+                        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+
+                        /* If we shall operate relative to some directory, then let's use O_PATH first to
+                         * open the socket inode, and then connect to it via /proc/self/fd/. We have to do
+                         * this since there's not connectat() that takes a directory fd as first arg. */
+
+                        dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC);
+                        if (dfd < 0)
+                                return -errno;
+
+                        xsprintf(procfs_path, "/proc/self/fd/%i", dfd);
+                        r = sockaddr_un_set_path(&sa.un, procfs_path);
+                }
+                if (r < 0)
+                        return r;
+
+                sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+                if (sk < 0)
+                        return -errno;
+
+                if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+                        return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is
+                                                                     * not a socket after all */
+
+                if (shutdown(sk, SHUT_WR) < 0)
+                        return -errno;
+
+                f = fdopen(sk, "r");
+                if (!f)
+                        return -errno;
+
+                TAKE_FD(sk);
+        }
 
         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
@@ -894,6 +946,42 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
 
         return search_and_fopen_internal(path, mode, root, s, _f);
 }
+
+int chase_symlinks_and_fopen_unlocked(
+                const char *path,
+                const char *root,
+                unsigned chase_flags,
+                const char *open_flags,
+                FILE **ret_file,
+                char **ret_path) {
+
+        _cleanup_close_ int fd = -1;
+        _cleanup_free_ char *final_path = NULL;
+        int mode_flags, r;
+        FILE *f;
+
+        assert(path);
+        assert(open_flags);
+        assert(ret_file);
+
+        mode_flags = mode_to_flags(open_flags);
+        if (mode_flags < 0)
+                return mode_flags;
+
+        fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL);
+        if (fd < 0)
+                return fd;
+
+        r = fdopen_unlocked(fd, open_flags, &f);
+        if (r < 0)
+                return r;
+        TAKE_FD(fd);
+
+        *ret_file = f;
+        if (ret_path)
+                *ret_path = TAKE_PTR(final_path);
+        return 0;
+}
 #endif /* NM_IGNORED */
 
 int fflush_and_check(FILE *f) {
diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h
index e2830b79..9cba5a90 100644
--- a/shared/systemd/src/basic/fileio.h
+++ b/shared/systemd/src/basic/fileio.h
@@ -32,9 +32,11 @@ typedef enum {
 } WriteStringFileFlags;
 
 typedef enum {
-        READ_FULL_FILE_SECURE   = 1 << 0,
-        READ_FULL_FILE_UNBASE64 = 1 << 1,
-        READ_FULL_FILE_UNHEX    = 1 << 2,
+        READ_FULL_FILE_SECURE              = 1 << 0, /* erase any buffers we employ internally, after use */
+        READ_FULL_FILE_UNBASE64            = 1 << 1, /* base64 decode what we read */
+        READ_FULL_FILE_UNHEX               = 1 << 2, /* hex decode what we read */
+        READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */
+        READ_FULL_FILE_CONNECT_SOCKET      = 1 << 4, /* if socket inode, connect to it and read off it */
 } ReadFullFileFlags;
 
 int fopen_unlocked(const char *path, const char *options, FILE **ret);
@@ -79,6 +81,14 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r
 int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f);
 int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f);
 
+int chase_symlinks_and_fopen_unlocked(
+                const char *path,
+                const char *root,
+                unsigned chase_flags,
+                const char *open_flags,
+                FILE **ret_file,
+                char **ret_path);
+
 int fflush_and_check(FILE *f);
 int fflush_sync_and_check(FILE *f);
 
diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c
index 75df913a..e50252cb 100644
--- a/shared/systemd/src/basic/fs-util.c
+++ b/shared/systemd/src/basic/fs-util.c
@@ -123,7 +123,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
         if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()… */
                 return -errno;
 
-        /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fallback to the racy TOCTOU
+        /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fall back to the racy TOCTOU
          * vulnerable accessat(F_OK) check followed by classic, replacing renameat(), we have nothing better. */
 
         if (faccessat(newdirfd, newpath, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
@@ -234,6 +234,7 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
 int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
         bool do_chown, do_chmod;
         struct stat st;
+        int r;
 
         /* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no
          * point in time the access mode is above the old access mode under the old ownership or the new
@@ -264,65 +265,23 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
         if (do_chown && do_chmod) {
                 mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
 
-                if (((minimal ^ st.st_mode) & 07777) != 0)
-                        if (fchmod_opath(fd, minimal & 07777) < 0)
-                                return -errno;
+                if (((minimal ^ st.st_mode) & 07777) != 0) {
+                        r = fchmod_opath(fd, minimal & 07777);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         if (do_chown)
                 if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0)
                         return -errno;
 
-        if (do_chmod)
-                if (fchmod_opath(fd, mode & 07777) < 0)
-                        return -errno;
-
-        return do_chown || do_chmod;
-}
-
-int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid) {
-        bool do_chown, do_chmod;
-        struct stat st;
-
-        assert(path);
-
-        /* Change ownership and access mode of the specified path, see description of fchmod_and_chown().
-         * Should only be used on trusted paths. */
-
-        if (lstat(path, &st) < 0)
-                return -errno;
-
-        do_chown =
-                (uid != UID_INVALID && st.st_uid != uid) ||
-                (gid != GID_INVALID && st.st_gid != gid);
-
-        do_chmod =
-                !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
-                ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) ||
-                 do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown()
-                             * modifies the access mode too */
-
-        if (mode == MODE_INVALID)
-                mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */
-        else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0)
-                return -EINVAL; /* insist on the right file type if it was specified */
-
-        if (do_chown && do_chmod) {
-                mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
-
-                if (((minimal ^ st.st_mode) & 07777) != 0)
-                        if (chmod(path, minimal & 07777) < 0)
-                                return -errno;
+        if (do_chmod) {
+                r = fchmod_opath(fd, mode & 07777);
+                if (r < 0)
+                        return r;
         }
 
-        if (do_chown)
-                if (lchown(path, uid, gid) < 0)
-                        return -errno;
-
-        if (do_chmod)
-                if (chmod(path, mode & 07777) < 0)
-                        return -errno;
-
         return do_chown || do_chmod;
 }
 
@@ -358,6 +317,25 @@ int fchmod_opath(int fd, mode_t m) {
         return 0;
 }
 
+int futimens_opath(int fd, const struct timespec ts[2]) {
+        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+
+        /* Similar to fchmod_path() but for futimens() */
+
+        xsprintf(procfs_path, "/proc/self/fd/%i", fd);
+        if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                if (proc_mounted() == 0)
+                        return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
+
+                return -ENOENT;
+        }
+
+        return 0;
+}
+
 int stat_warn_permissions(const char *path, const struct stat *st) {
         assert(path);
         assert(st);
@@ -771,7 +749,7 @@ static int log_unsafe_transition(int a, int b, const char *path, unsigned flags)
 
         return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
                                  "Detected unsafe path transition %s %s %s during canonicalization of %s.",
-                                 n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path);
+                                 strna(n1), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), path);
 }
 
 static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
@@ -784,7 +762,7 @@ static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
 
         return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
                                  "Detected autofs mount point %s during canonicalization of %s.",
-                                 n1, path);
+                                 strna(n1), path);
 }
 
 int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) {
@@ -1304,16 +1282,25 @@ int chase_symlinks_and_stat(
 
 int access_fd(int fd, int mode) {
         char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
-        int r;
 
         /* Like access() but operates on an already open fd */
 
         xsprintf(p, "/proc/self/fd/%i", fd);
-        r = access(p, mode);
-        if (r < 0)
-                return -errno;
+        if (access(p, mode) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
 
-        return r;
+                /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's
+                 * make things debuggable and distinguish the two. */
+
+                if (proc_mounted() == 0)
+                        return -ENOSYS;  /* /proc is not available or not set up properly, we're most likely in some chroot
+                                          * environment. */
+
+                return -EBADF; /* The directory exists, hence it's the fd that doesn't. */
+        }
+
+        return 0;
 }
 
 void unlink_tempfilep(char (*p)[]) {
@@ -1434,6 +1421,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
         return 0;
 }
 
+#if 0 /* NM_IGNORED */
 int fsync_directory_of_file(int fd) {
         _cleanup_free_ char *path = NULL;
         _cleanup_close_ int dfd = -1;
@@ -1447,9 +1435,9 @@ int fsync_directory_of_file(int fd) {
         if (r < 0) {
                 log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
                                 fd,
-                                r == -EOPNOTSUPP ? ", ignoring" : "");
+                                r == -ENOSYS ? ", ignoring" : "");
 
-                if (r == -EOPNOTSUPP)
+                if (r == -ENOSYS)
                         /* If /proc is not available, we're most likely running in some
                          * chroot environment, and syncing the directory is not very
                          * important in that case. Let's just silently do nothing. */
@@ -1470,6 +1458,7 @@ int fsync_directory_of_file(int fd) {
 
         return 0;
 }
+#endif /* NM_IGNORED */
 
 int fsync_full(int fd) {
         int r, q;
@@ -1587,7 +1576,7 @@ static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
 
         d = opendir(p);
         if (!d) {
-                if (errno == ENOENT) /* Doesn't have slaves */
+                if (errno == ENOENT) /* Doesn't have underlying devices */
                         return false;
 
                 return -errno;
@@ -1603,7 +1592,7 @@ static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
                         if (errno != 0)
                                 return -errno;
 
-                        break; /* No more slaves */
+                        break; /* No more underlying devices */
                 }
 
                 q = path_join(p, de->d_name);
diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h
index b184570f..241cc6ef 100644
--- a/shared/systemd/src/basic/fs-util.h
+++ b/shared/systemd/src/basic/fs-util.h
@@ -34,11 +34,12 @@ int readlink_and_make_absolute(const char *p, char **r);
 
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
 int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid);
-int chmod_and_chown_unsafe(const char *path, mode_t mode, uid_t uid, gid_t gid);
 
 int fchmod_umask(int fd, mode_t mode);
 int fchmod_opath(int fd, mode_t m);
 
+int futimens_opath(int fd, const struct timespec ts[2]);
+
 int fd_warn_permissions(const char *path, int fd);
 int stat_warn_permissions(const char *path, const struct stat *st);
 
@@ -80,7 +81,7 @@ enum {
         CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
         CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
         CHASE_NO_AUTOFS   = 1 << 2, /* Return -EREMOTE if autofs mount point found */
-        CHASE_SAFE        = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */
+        CHASE_SAFE        = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
         CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
         CHASE_STEP        = 1 << 5, /* Just execute a single step of the normalization */
         CHASE_NOFOLLOW    = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c
index c0296bde..b57814e6 100644
--- a/shared/systemd/src/basic/hashmap.c
+++ b/shared/systemd/src/basic/hashmap.c
@@ -3,6 +3,7 @@
 #include "nm-sd-adapt-shared.h"
 
 #include <errno.h>
+#include <pthread.h>
 #include <stdint.h>
 #include <stdlib.h>
 
@@ -21,7 +22,6 @@
 #include "strv.h"
 
 #if ENABLE_DEBUG_HASHMAP
-#include <pthread.h>
 #include "list.h"
 #endif
 
@@ -191,7 +191,6 @@ assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3));
  * a handful of directly stored entries in a hashmap. When a hashmap
  * outgrows direct storage, it gets its own key for indirect storage. */
 static uint8_t shared_hash_key[HASH_KEY_SIZE];
-static bool shared_hash_key_initialized;
 
 /* Fields that all hashmap/set types must have */
 struct HashmapBase {
@@ -325,12 +324,12 @@ static void n_entries_dec(HashmapBase *h) {
                 h->n_direct_entries--;
 }
 
-static void *storage_ptr(HashmapBase *h) {
+static void* storage_ptr(HashmapBase *h) {
         return h->has_indirect ? h->indirect.storage
                                : h->direct.storage;
 }
 
-static uint8_t *hash_key(HashmapBase *h) {
+static uint8_t* hash_key(HashmapBase *h) {
         return h->has_indirect ? h->indirect.hash_key
                                : shared_hash_key;
 }
@@ -373,16 +372,16 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
         memcpy(hash_key, current, sizeof(current));
 }
 
-static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) {
+static struct hashmap_base_entry* bucket_at(HashmapBase *h, unsigned idx) {
         return (struct hashmap_base_entry*)
                 ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size);
 }
 
-static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) {
+static struct plain_hashmap_entry* plain_bucket_at(Hashmap *h, unsigned idx) {
         return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
 }
 
-static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
+static struct ordered_hashmap_entry* ordered_bucket_at(OrderedHashmap *h, unsigned idx) {
         return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx);
 }
 
@@ -390,13 +389,13 @@ static struct set_entry *set_bucket_at(Set *h, unsigned idx) {
         return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx);
 }
 
-static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) {
+static struct ordered_hashmap_entry* bucket_at_swap(struct swap_entries *swap, unsigned idx) {
         return &swap->e[idx - _IDX_SWAP_BEGIN];
 }
 
 /* Returns a pointer to the bucket at index idx.
  * Understands real indexes and swap indexes, hence "_virtual". */
-static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
+static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_entries *swap,
                                                     unsigned idx) {
         if (idx < _IDX_SWAP_BEGIN)
                 return bucket_at(h, idx);
@@ -407,7 +406,7 @@ static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_
         assert_not_reached("Invalid index");
 }
 
-static dib_raw_t *dib_raw_ptr(HashmapBase *h) {
+static dib_raw_t* dib_raw_ptr(HashmapBase *h) {
         return (dib_raw_t*)
                 ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h));
 }
@@ -505,7 +504,7 @@ static unsigned prev_idx(HashmapBase *h, unsigned idx) {
         return (n_buckets(h) + idx - 1U) % n_buckets(h);
 }
 
-static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
+static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) {
         switch (h->type) {
 
         case HASHMAP_TYPE_PLAIN:
@@ -732,16 +731,12 @@ bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **ke
         return true;
 }
 
-bool set_iterate(const Set *s, Iterator *i, void **value) {
-        return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL);
-}
-
 #define HASHMAP_FOREACH_IDX(idx, h, i) \
         for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \
              (idx != IDX_NIL); \
              (idx) = hashmap_iterate_entry((h), &(i)))
 
-IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h) {
+IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h) {
         IteratedCache *cache;
 
         assert(h);
@@ -770,7 +765,11 @@ static void reset_direct_storage(HashmapBase *h) {
         memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
 }
 
-static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+static void shared_hash_key_initialize(void) {
+        random_bytes(shared_hash_key, sizeof(shared_hash_key));
+}
+
+static struct HashmapBase* hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type  HASHMAP_DEBUG_PARAMS) {
         HashmapBase *h;
         const struct hashmap_type_info *hi = &hashmap_type_info[type];
         bool up;
@@ -792,10 +791,8 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu
 
         reset_direct_storage(h);
 
-        if (!shared_hash_key_initialized) {
-                random_bytes(shared_hash_key, sizeof(shared_hash_key));
-                shared_hash_key_initialized= true;
-        }
+        static pthread_once_t once = PTHREAD_ONCE_INIT;
+        assert_se(pthread_once(&once, shared_hash_key_initialize) == 0);
 
 #if ENABLE_DEBUG_HASHMAP
         h->debug.func = func;
@@ -810,19 +807,19 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu
 }
 
 Hashmap *_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return (Hashmap*)        hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+        return (Hashmap*)        hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN  HASHMAP_DEBUG_PASS_ARGS);
 }
 
 OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+        return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED  HASHMAP_DEBUG_PASS_ARGS);
 }
 
 Set *_set_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return (Set*)            hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+        return (Set*)            hashmap_base_new(hash_ops, HASHMAP_TYPE_SET  HASHMAP_DEBUG_PASS_ARGS);
 }
 
 static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
-                                         enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+                                         enum HashmapType type  HASHMAP_DEBUG_PARAMS) {
         HashmapBase *q;
 
         assert(h);
@@ -830,7 +827,7 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops
         if (*h)
                 return 0;
 
-        q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
+        q = hashmap_base_new(hash_ops, type  HASHMAP_DEBUG_PASS_ARGS);
         if (!q)
                 return -ENOMEM;
 
@@ -839,15 +836,25 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops
 }
 
 int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN  HASHMAP_DEBUG_PASS_ARGS);
 }
 
 int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED  HASHMAP_DEBUG_PASS_ARGS);
 }
 
 int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+        return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET  HASHMAP_DEBUG_PASS_ARGS);
+}
+
+int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value  HASHMAP_DEBUG_PARAMS) {
+        int r;
+
+        r = _ordered_hashmap_ensure_allocated(h, hash_ops  HASHMAP_DEBUG_PASS_ARGS);
+        if (r < 0)
+                return r;
+
+        return ordered_hashmap_put(*h, key, value);
 }
 
 static void hashmap_free_no_clear(HashmapBase *h) {
@@ -868,7 +875,7 @@ static void hashmap_free_no_clear(HashmapBase *h) {
                 free(h);
 }
 
-HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
+HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
         if (h) {
                 _hashmap_clear(h, default_free_key, default_free_value);
                 hashmap_free_no_clear(h);
@@ -1249,6 +1256,30 @@ int set_put(Set *s, const void *key) {
         return hashmap_put_boldly(s, hash, &swap, true);
 }
 
+int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key  HASHMAP_DEBUG_PARAMS) {
+        int r;
+
+        r = _set_ensure_allocated(s, hash_ops  HASHMAP_DEBUG_PASS_ARGS);
+        if (r < 0)
+                return r;
+
+        return set_put(*s, key);
+}
+
+int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key  HASHMAP_DEBUG_PARAMS) {
+        int r;
+
+        r = _set_ensure_put(s, hash_ops, key  HASHMAP_DEBUG_PASS_ARGS);
+        if (r <= 0) {
+                if (hash_ops && hash_ops->free_key)
+                        hash_ops->free_key(key);
+                else
+                        free(key);
+        }
+
+        return r;
+}
+
 int hashmap_replace(Hashmap *h, const void *key, void *value) {
         struct swap_entries swap;
         struct plain_hashmap_entry *e;
@@ -1301,7 +1332,7 @@ int hashmap_update(Hashmap *h, const void *key, void *value) {
         return 0;
 }
 
-void *_hashmap_get(HashmapBase *h, const void *key) {
+void* _hashmap_get(HashmapBase *h, const void *key) {
         struct hashmap_base_entry *e;
         unsigned hash, idx;
 
@@ -1317,7 +1348,7 @@ void *_hashmap_get(HashmapBase *h, const void *key) {
         return entry_value(h, e);
 }
 
-void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
+void* hashmap_get2(Hashmap *h, const void *key, void **key2) {
         struct plain_hashmap_entry *e;
         unsigned hash, idx;
 
@@ -1346,7 +1377,7 @@ bool _hashmap_contains(HashmapBase *h, const void *key) {
         return bucket_scan(h, hash, key) != IDX_NIL;
 }
 
-void *_hashmap_remove(HashmapBase *h, const void *key) {
+void* _hashmap_remove(HashmapBase *h, const void *key) {
         struct hashmap_base_entry *e;
         unsigned hash, idx;
         void *data;
@@ -1366,7 +1397,7 @@ void *_hashmap_remove(HashmapBase *h, const void *key) {
         return data;
 }
 
-void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
+void* hashmap_remove2(Hashmap *h, const void *key, void **rkey) {
         struct plain_hashmap_entry *e;
         unsigned hash, idx;
         void *data;
@@ -1484,7 +1515,7 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_
         return 0;
 }
 
-void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
+void* _hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
         struct hashmap_base_entry *e;
         unsigned hash, idx;
 
@@ -1514,7 +1545,7 @@ static unsigned find_first_entry(HashmapBase *h) {
         return hashmap_iterate_entry(h, &i);
 }
 
-void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
+void* _hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
         struct hashmap_base_entry *e;
         void *key, *data;
         unsigned idx;
@@ -1689,13 +1720,13 @@ int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
         return 0;
 }
 
-HashmapBase *_hashmap_copy(HashmapBase *h) {
+HashmapBase* _hashmap_copy(HashmapBase *h  HASHMAP_DEBUG_PARAMS) {
         HashmapBase *copy;
         int r;
 
         assert(h);
 
-        copy = hashmap_base_new(h->hash_ops, h->type  HASHMAP_DEBUG_SRC_ARGS);
+        copy = hashmap_base_new(h->hash_ops, h->type  HASHMAP_DEBUG_PASS_ARGS);
         if (!copy)
                 return NULL;
 
@@ -1711,15 +1742,13 @@ HashmapBase *_hashmap_copy(HashmapBase *h) {
                 assert_not_reached("Unknown hashmap type");
         }
 
-        if (r < 0) {
-                _hashmap_free(copy, false, false);
-                return NULL;
-        }
+        if (r < 0)
+                return _hashmap_free(copy, false, false);
 
         return copy;
 }
 
-char **_hashmap_get_strv(HashmapBase *h) {
+char** _hashmap_get_strv(HashmapBase *h) {
         char **sv;
         Iterator i;
         unsigned idx, n;
@@ -1736,7 +1765,7 @@ char **_hashmap_get_strv(HashmapBase *h) {
         return sv;
 }
 
-void *ordered_hashmap_next(OrderedHashmap *h, const void *key) {
+void* ordered_hashmap_next(OrderedHashmap *h, const void *key) {
         struct ordered_hashmap_entry *e;
         unsigned hash, idx;
 
@@ -1768,10 +1797,10 @@ int set_consume(Set *s, void *value) {
 }
 
 #if 0 /* NM_IGNORED */
-int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) {
+int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v  HASHMAP_DEBUG_PARAMS) {
         int r;
 
-        r = hashmap_ensure_allocated(h, &string_hash_ops_free_free);
+        r = _hashmap_ensure_allocated(h, &string_hash_ops_free_free  HASHMAP_DEBUG_PASS_ARGS);
         if (r < 0)
                 return r;
 
@@ -1803,14 +1832,14 @@ int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) {
 }
 #endif /* NM_IGNORED */
 
-int set_put_strdup(Set **s, const char *p) {
+int _set_put_strdup(Set **s, const char *p  HASHMAP_DEBUG_PARAMS) {
         char *c;
         int r;
 
         assert(s);
         assert(p);
 
-        r = set_ensure_allocated(s, &string_hash_ops_free);
+        r = _set_ensure_allocated(s, &string_hash_ops_free  HASHMAP_DEBUG_PASS_ARGS);
         if (r < 0)
                 return r;
 
@@ -1824,14 +1853,14 @@ int set_put_strdup(Set **s, const char *p) {
         return set_consume(*s, c);
 }
 
-int set_put_strdupv(Set **s, char **l) {
+int _set_put_strdupv(Set **s, char **l  HASHMAP_DEBUG_PARAMS) {
         int n = 0, r;
         char **i;
 
         assert(s);
 
         STRV_FOREACH(i, l) {
-                r = set_put_strdup(s, *i);
+                r = _set_put_strdup(s, *i  HASHMAP_DEBUG_PASS_ARGS);
                 if (r < 0)
                         return r;
 
@@ -1943,7 +1972,7 @@ int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void
         return 0;
 }
 
-IteratedCache *iterated_cache_free(IteratedCache *cache) {
+IteratedCache* iterated_cache_free(IteratedCache *cache) {
         if (cache) {
                 free(cache->keys.ptr);
                 free(cache->values.ptr);
diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h
index 230d3222..890f90a9 100644
--- a/shared/systemd/src/basic/hashmap.h
+++ b/shared/systemd/src/basic/hashmap.h
@@ -83,8 +83,8 @@ typedef struct {
 # define HASHMAP_DEBUG_PASS_ARGS
 #endif
 
-Hashmap *_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
-OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+Hashmap* _hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
 #define hashmap_new(ops) _hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
 #define ordered_hashmap_new(ops) _ordered_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
 
@@ -96,56 +96,55 @@ OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DE
                 0;                              \
         })
 
-HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
-static inline Hashmap *hashmap_free(Hashmap *h) {
+HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+static inline Hashmap* hashmap_free(Hashmap *h) {
         return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
 }
-static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
+static inline OrderedHashmap* ordered_hashmap_free(OrderedHashmap *h) {
         return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
 }
 
-static inline Hashmap *hashmap_free_free(Hashmap *h) {
+static inline Hashmap* hashmap_free_free(Hashmap *h) {
         return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
 }
-static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
+static inline OrderedHashmap* ordered_hashmap_free_free(OrderedHashmap *h) {
         return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
 }
 
-static inline Hashmap *hashmap_free_free_key(Hashmap *h) {
+static inline Hashmap* hashmap_free_free_key(Hashmap *h) {
         return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
 }
-static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) {
+static inline OrderedHashmap* ordered_hashmap_free_free_key(OrderedHashmap *h) {
         return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
 }
 
-static inline Hashmap *hashmap_free_free_free(Hashmap *h) {
+static inline Hashmap* hashmap_free_free_free(Hashmap *h) {
         return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
 }
-static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
+static inline OrderedHashmap* ordered_hashmap_free_free_free(OrderedHashmap *h) {
         return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
 }
 
-IteratedCache *iterated_cache_free(IteratedCache *cache);
+IteratedCache* iterated_cache_free(IteratedCache *cache);
 int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries);
 
-HashmapBase *_hashmap_copy(HashmapBase *h);
-static inline Hashmap *hashmap_copy(Hashmap *h) {
-        return (Hashmap*) _hashmap_copy(HASHMAP_BASE(h));
-}
-static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
-        return (OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h));
-}
+HashmapBase* _hashmap_copy(HashmapBase *h  HASHMAP_DEBUG_PARAMS);
+#define hashmap_copy(h) ((Hashmap*) _hashmap_copy(HASHMAP_BASE(h)  HASHMAP_DEBUG_SRC_ARGS))
+#define ordered_hashmap_copy(h) ((OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h)  HASHMAP_DEBUG_SRC_ARGS))
 
 int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
 int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
 #define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
 #define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
 
-IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h);
-static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) {
+int _ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value  HASHMAP_DEBUG_PARAMS);
+#define ordered_hashmap_ensure_put(s, ops, key, value) _ordered_hashmap_ensure_put(s, ops, key, value  HASHMAP_DEBUG_SRC_ARGS)
+
+IteratedCache* _hashmap_iterated_cache_new(HashmapBase *h);
+static inline IteratedCache* hashmap_iterated_cache_new(Hashmap *h) {
         return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));
 }
-static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) {
+static inline IteratedCache* ordered_hashmap_iterated_cache_new(OrderedHashmap *h) {
         return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));
 }
 
@@ -154,7 +153,8 @@ static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *
         return hashmap_put(PLAIN_HASHMAP(h), key, value);
 }
 
-int hashmap_put_strdup(Hashmap **h, const char *k, const char *v);
+int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v  HASHMAP_DEBUG_PARAMS);
+#define hashmap_put_strdup(h, k, v) _hashmap_put_strdup(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
 
 int hashmap_update(Hashmap *h, const void *key, void *value);
 static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
@@ -166,7 +166,7 @@ static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, vo
         return hashmap_replace(PLAIN_HASHMAP(h), key, value);
 }
 
-void *_hashmap_get(HashmapBase *h, const void *key);
+void* _hashmap_get(HashmapBase *h, const void *key);
 static inline void *hashmap_get(Hashmap *h, const void *key) {
         return _hashmap_get(HASHMAP_BASE(h), key);
 }
@@ -174,7 +174,7 @@ static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
         return _hashmap_get(HASHMAP_BASE(h), key);
 }
 
-void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
+void* hashmap_get2(Hashmap *h, const void *key, void **rkey);
 static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) {
         return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
 }
@@ -187,7 +187,7 @@ static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key)
         return _hashmap_contains(HASHMAP_BASE(h), key);
 }
 
-void *_hashmap_remove(HashmapBase *h, const void *key);
+void* _hashmap_remove(HashmapBase *h, const void *key);
 static inline void *hashmap_remove(Hashmap *h, const void *key) {
         return _hashmap_remove(HASHMAP_BASE(h), key);
 }
@@ -195,17 +195,17 @@ static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
         return _hashmap_remove(HASHMAP_BASE(h), key);
 }
 
-void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
+void* hashmap_remove2(Hashmap *h, const void *key, void **rkey);
 static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) {
         return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
 }
 
-void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value);
+void* _hashmap_remove_value(HashmapBase *h, const void *key, void *value);
 static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
         return _hashmap_remove_value(HASHMAP_BASE(h), key, value);
 }
 
-static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
+static inline void* ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
         return hashmap_remove_value(PLAIN_HASHMAP(h), key, value);
 }
 
@@ -391,13 +391,13 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
         })
 
 /* no hashmap_next */
-void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
+void* ordered_hashmap_next(OrderedHashmap *h, const void *key);
 
-char **_hashmap_get_strv(HashmapBase *h);
-static inline char **hashmap_get_strv(Hashmap *h) {
+char** _hashmap_get_strv(HashmapBase *h);
+static inline char** hashmap_get_strv(Hashmap *h) {
         return _hashmap_get_strv(HASHMAP_BASE(h));
 }
-static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
+static inline char** ordered_hashmap_get_strv(OrderedHashmap *h) {
         return _hashmap_get_strv(HASHMAP_BASE(h));
 }
 
@@ -407,17 +407,25 @@ static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
  * the entries were inserted.
  * It is safe to remove the current entry.
  */
-#define HASHMAP_FOREACH(e, h, i) \
-        for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); )
-
-#define ORDERED_HASHMAP_FOREACH(e, h, i) \
-        for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); )
-
-#define HASHMAP_FOREACH_KEY(e, k, h, i) \
-        for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
-
-#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
-        for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); )
+#define _HASHMAP_FOREACH(e, h, i) \
+        for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), NULL); )
+#define HASHMAP_FOREACH(e, h) \
+        _HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ))
+
+#define _ORDERED_HASHMAP_FOREACH(e, h, i) \
+        for (Iterator i = ITERATOR_FIRST; ordered_hashmap_iterate((h), &i, (void**)&(e), NULL); )
+#define ORDERED_HASHMAP_FOREACH(e, h) \
+        _ORDERED_HASHMAP_FOREACH(e, h, UNIQ_T(i, UNIQ))
+
+#define _HASHMAP_FOREACH_KEY(e, k, h, i) \
+        for (Iterator i = ITERATOR_FIRST; hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
+#define HASHMAP_FOREACH_KEY(e, k, h) \
+        _HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
+
+#define _ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \
+        for (Iterator i = ITERATOR_FIRST; ordered_hashmap_iterate((h), &i, (void**)&(e), (const void**) &(k)); )
+#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h) \
+        _ORDERED_HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c
index cddc269f..1ea3e7ff 100644
--- a/shared/systemd/src/basic/in-addr-util.c
+++ b/shared/systemd/src/basic/in-addr-util.c
@@ -16,6 +16,7 @@
 #include "macro.h"
 #include "parse-util.h"
 #include "random-util.h"
+#include "string-util.h"
 #include "strxcpyx.h"
 #include "util.h"
 
@@ -55,6 +56,16 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
         return -EAFNOSUPPORT;
 }
 
+bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a) {
+        assert(a);
+
+        /* ff02::1 */
+        return be32toh(a->s6_addr32[0]) == UINT32_C(0xff020000) &&
+                a->s6_addr32[1] == 0 &&
+                a->s6_addr32[2] == 0 &&
+                be32toh(a->s6_addr32[3]) == UINT32_C(0x00000001);
+}
+
 int in_addr_is_multicast(int family, const union in_addr_union *u) {
         assert(u);
 
@@ -67,6 +78,12 @@ int in_addr_is_multicast(int family, const union in_addr_union *u) {
         return -EAFNOSUPPORT;
 }
 
+bool in4_addr_is_local_multicast(const struct in_addr *a) {
+        assert(a);
+
+        return (be32toh(a->s_addr) & UINT32_C(0xffffff00)) == UINT32_C(0xe0000000);
+}
+
 bool in4_addr_is_localhost(const struct in_addr *a) {
         assert(a);
 
@@ -109,11 +126,7 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_
                 return in4_addr_equal(&a->in, &b->in);
 
         if (family == AF_INET6)
-                return
-                        a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
-                        a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
-                        a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
-                        a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
+                return IN6_ARE_ADDR_EQUAL(&a->in6, &b->in6);
 
         return -EAFNOSUPPORT;
 }
@@ -411,44 +424,59 @@ int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned
 }
 #endif /* NM_IGNORED */
 
-int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
-        _cleanup_free_ char *x = NULL;
-        size_t l;
+int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) {
+        _cleanup_free_ char *ip_str = NULL, *x = NULL;
         int r;
 
+        assert(IN_SET(family, AF_INET, AF_INET6));
         assert(u);
         assert(ret);
 
         /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
          * handle IPv6 link-local addresses. */
 
-        if (family != AF_INET6)
-                goto fallback;
-        if (ifindex <= 0)
-                goto fallback;
-
-        r = in_addr_is_link_local(family, u);
+        r = in_addr_to_string(family, u, &ip_str);
         if (r < 0)
                 return r;
-        if (r == 0)
-                goto fallback;
 
-        l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1;
-        x = new(char, l);
-        if (!x)
-                return -ENOMEM;
+        if (family == AF_INET6) {
+                r = in_addr_is_link_local(family, u);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        ifindex = 0;
+        } else
+                ifindex = 0; /* For IPv4 address, ifindex is always ignored. */
 
-        errno = 0;
-        if (!inet_ntop(family, u, x, l))
-                return errno_or_else(EINVAL);
+        if (port == 0 && ifindex == 0 && isempty(server_name)) {
+                *ret = TAKE_PTR(ip_str);
+                return 0;
+        }
 
-        sprintf(strchr(x, 0), "%%%i", ifindex);
+        const char *separator = isempty(server_name) ? "" : "#";
+        server_name = strempty(server_name);
+
+        if (port > 0) {
+                if (family == AF_INET6) {
+                        if (ifindex > 0)
+                                r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name);
+                        else
+                                r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name);
+                } else
+                        r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name);
+        } else {
+                if (ifindex > 0)
+                        r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name);
+                else {
+                        x = strjoin(ip_str, separator, server_name);
+                        r = x ? 0 : -ENOMEM;
+                }
+        }
+        if (r < 0)
+                return -ENOMEM;
 
         *ret = TAKE_PTR(x);
         return 0;
-
-fallback:
-        return in_addr_to_string(family, u, ret);
 }
 
 int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
@@ -750,13 +778,13 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct
 
 DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func);
 
-static void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
+void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) {
         assert(addr);
 
         siphash24_compress(addr, sizeof(*addr), state);
 }
 
-static int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
+int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
         return memcmp(a, b, sizeof(*a));
 }
 
diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h
index 90d79a5e..45c93a00 100644
--- a/shared/systemd/src/basic/in-addr-util.h
+++ b/shared/systemd/src/basic/in-addr-util.h
@@ -12,6 +12,7 @@
 union in_addr_union {
         struct in_addr in;
         struct in6_addr in6;
+        uint8_t bytes[CONST_MAX(sizeof(struct in_addr), sizeof(struct in6_addr))];
 };
 
 struct in_addr_data {
@@ -26,10 +27,12 @@ int in_addr_is_multicast(int family, const union in_addr_union *u);
 
 bool in4_addr_is_link_local(const struct in_addr *a);
 int in_addr_is_link_local(int family, const union in_addr_union *u);
+bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
 
 bool in4_addr_is_localhost(const struct in_addr *a);
 int in_addr_is_localhost(int family, const union in_addr_union *u);
 
+bool in4_addr_is_local_multicast(const struct in_addr *a);
 bool in4_addr_is_non_local(const struct in_addr *a);
 
 bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
@@ -40,7 +43,13 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u
 int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen);
 int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
 int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
-int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
+int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);
+static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
+        return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret);
+}
+static inline int in_addr_port_to_string(int family, const union in_addr_union *u, uint16_t port, char **ret) {
+        return in_addr_port_ifindex_name_to_string(family, u, port, 0, NULL, ret);
+}
 int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
 int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
 
@@ -73,5 +82,8 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
  * See also oss-fuzz#11344. */
 #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
 
+void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state);
+int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b);
+
 extern const struct hash_ops in_addr_data_hash_ops;
 extern const struct hash_ops in6_addr_hash_ops;
diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c
index f1df3fac..8b0e354a 100644
--- a/shared/systemd/src/basic/io-util.c
+++ b/shared/systemd/src/basic/io-util.c
@@ -158,10 +158,8 @@ int pipe_eof(int fd) {
         int r;
 
         r = fd_wait_for_event(fd, POLLIN, 0);
-        if (r < 0)
+        if (r <= 0)
                 return r;
-        if (r == 0)
-                return 0;
 
         return !!(r & POLLHUP);
 }
diff --git a/shared/systemd/src/basic/list.h b/shared/systemd/src/basic/list.h
index f7f97000..b62c3749 100644
--- a/shared/systemd/src/basic/list.h
+++ b/shared/systemd/src/basic/list.h
@@ -169,3 +169,18 @@
 
 #define LIST_IS_EMPTY(head)                                             \
         (!(head))
+
+/* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */
+#define LIST_JOIN(name,a,b)                                             \
+        do {                                                            \
+                assert(b);                                              \
+                if (!(a))                                               \
+                        (a) = (b);                                      \
+                else {                                                  \
+                        typeof(*(a)) *_head = (b), *_tail;              \
+                        LIST_FIND_TAIL(name, (a), _tail);               \
+                        _tail->name##_next = _head;                     \
+                        _head->name##_prev = _tail;                     \
+                }                                                       \
+                (b) = NULL;                                             \
+        } while (false)
diff --git a/shared/systemd/src/basic/log.h b/shared/systemd/src/basic/log.h
index 3725b08f..c2ffbb5d 100644
--- a/shared/systemd/src/basic/log.h
+++ b/shared/systemd/src/basic/log.h
@@ -30,7 +30,7 @@ typedef enum LogTarget{
         LOG_TARGET_JOURNAL_OR_KMSG,
         LOG_TARGET_SYSLOG,
         LOG_TARGET_SYSLOG_OR_KMSG,
-        LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
+        LOG_TARGET_AUTO, /* console if stderr is not journal, JOURNAL_OR_KMSG otherwise */
         LOG_TARGET_NULL,
         _LOG_TARGET_MAX,
         _LOG_TARGET_INVALID = -1
@@ -61,10 +61,13 @@ void log_show_location(bool b);
 bool log_get_show_location(void) _pure_;
 void log_show_time(bool b);
 bool log_get_show_time(void) _pure_;
+void log_show_tid(bool b);
+bool log_get_show_tid(void) _pure_;
 
 int log_show_color_from_string(const char *e);
 int log_show_location_from_string(const char *e);
 int log_show_time_from_string(const char *e);
+int log_show_tid_from_string(const char *e);
 
 LogTarget log_get_target(void) _pure_;
 #if 0 /* NM_IGNORED */
@@ -89,8 +92,11 @@ void log_close(void);
 void log_forget_fds(void);
 
 void log_parse_environment_realm(LogRealm realm);
+void log_parse_environment_cli_realm(LogRealm realm);
 #define log_parse_environment() \
         log_parse_environment_realm(LOG_REALM)
+#define log_parse_environment_cli() \
+        log_parse_environment_cli_realm(LOG_REALM)
 
 #if 0 /* NM_IGNORED */
 int log_dispatch_internal(
@@ -245,12 +251,12 @@ void log_assert_failed_return_realm(
 #define log_full_errno(level, error, ...)                               \
         log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__)
 
-#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__)
+#define log_full(level, ...) (void) log_full_errno((level), 0, __VA_ARGS__)
 
 int log_emergency_level(void);
 
 /* Normal logging */
-#define log_debug(...)     log_full(LOG_DEBUG,   __VA_ARGS__)
+#define log_debug(...)     log_full_errno(LOG_DEBUG, 0, __VA_ARGS__)
 #define log_info(...)      log_full(LOG_INFO,    __VA_ARGS__)
 #define log_notice(...)    log_full(LOG_NOTICE,  __VA_ARGS__)
 #define log_warning(...)   log_full(LOG_WARNING, __VA_ARGS__)
@@ -359,3 +365,4 @@ int log_syntax_invalid_utf8_internal(
 #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
 
 void log_setup_service(void);
+void log_setup_cli(void);
diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h
index 0c2eb894..f872d41f 100644
--- a/shared/systemd/src/basic/macro.h
+++ b/shared/systemd/src/basic/macro.h
@@ -295,6 +295,15 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
                 UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
         })
 
+/* evaluates to (void) if _A or _B are not constant or of different types */
+#define CONST_MIN(_A, _B) \
+        (__builtin_choose_expr(                                         \
+                __builtin_constant_p(_A) &&                             \
+                __builtin_constant_p(_B) &&                             \
+                __builtin_types_compatible_p(typeof(_A), typeof(_B)),   \
+                ((_A) < (_B)) ? (_A) : (_B),                            \
+                VOID_0))
+
 #define MIN3(x, y, z)                                   \
         ({                                              \
                 const typeof(x) _c = MIN(x, y);         \
@@ -538,6 +547,15 @@ static inline int __coverity_check_and_return__(int condition) {
                 (y) = (_t);                        \
         } while (false)
 
+#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
+#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
+
+/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */
+#define FOREACH_POINTER(p, x, ...)                                                      \
+        for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \
+             p != (typeof(p)) (void*) -1;                                               \
+             p = *(++_l))
+
 /* Define C11 thread_local attribute even on older gcc compiler
  * version */
 #ifndef thread_local
diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h
index a6a2ccdb..4f596cff 100644
--- a/shared/systemd/src/basic/memory-util.h
+++ b/shared/systemd/src/basic/memory-util.h
@@ -13,6 +13,7 @@
 size_t page_size(void) _pure_;
 #define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
 #define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1))
+#define PAGE_OFFSET(l) ((l) & (page_size() - 1))
 
 /* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
 static inline void memcpy_safe(void *dst, const void *src, size_t n) {
diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h
index 647e56f1..fe4d35bd 100644
--- a/shared/systemd/src/basic/missing_socket.h
+++ b/shared/systemd/src/basic/missing_socket.h
@@ -65,6 +65,10 @@ struct sockaddr_vm {
 #define IP_TRANSPARENT 19
 #endif
 
+#ifndef IPV6_FREEBIND
+#define IPV6_FREEBIND 78
+#endif
+
 /* linux/sockios.h */
 #ifndef SIOCGSKNS
 #define SIOCGSKNS 0x894C
diff --git a/shared/systemd/src/basic/missing_stat.h b/shared/systemd/src/basic/missing_stat.h
index 7d89a7bb..5d59a214 100644
--- a/shared/systemd/src/basic/missing_stat.h
+++ b/shared/systemd/src/basic/missing_stat.h
@@ -9,45 +9,129 @@
 #include <linux/stat.h>
 #endif
 
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+/* Thew newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
+#define STATX_DEFINITION {                      \
+        __u32 stx_mask;                         \
+        __u32 stx_blksize;                      \
+        __u64 stx_attributes;                   \
+        __u32 stx_nlink;                        \
+        __u32 stx_uid;                          \
+        __u32 stx_gid;                          \
+        __u16 stx_mode;                         \
+        __u16 __spare0[1];                      \
+        __u64 stx_ino;                          \
+        __u64 stx_size;                         \
+        __u64 stx_blocks;                       \
+        __u64 stx_attributes_mask;              \
+        struct statx_timestamp stx_atime;       \
+        struct statx_timestamp stx_btime;       \
+        struct statx_timestamp stx_ctime;       \
+        struct statx_timestamp stx_mtime;       \
+        __u32 stx_rdev_major;                   \
+        __u32 stx_rdev_minor;                   \
+        __u32 stx_dev_major;                    \
+        __u32 stx_dev_minor;                    \
+        __u64 stx_mnt_id;                       \
+        __u64 __spare2;                         \
+        __u64 __spare3[12];                     \
+}
+
 #if !HAVE_STRUCT_STATX
 struct statx_timestamp {
         __s64 tv_sec;
         __u32 tv_nsec;
         __s32 __reserved;
 };
-struct statx {
-        __u32 stx_mask;
-        __u32 stx_blksize;
-        __u64 stx_attributes;
-        __u32 stx_nlink;
-        __u32 stx_uid;
-        __u32 stx_gid;
-        __u16 stx_mode;
-        __u16 __spare0[1];
-        __u64 stx_ino;
-        __u64 stx_size;
-        __u64 stx_blocks;
-        __u64 stx_attributes_mask;
-        struct statx_timestamp stx_atime;
-        struct statx_timestamp stx_btime;
-        struct statx_timestamp stx_ctime;
-        struct statx_timestamp stx_mtime;
-        __u32 stx_rdev_major;
-        __u32 stx_rdev_minor;
-        __u32 stx_dev_major;
-        __u32 stx_dev_minor;
-        __u64 __spare2[14];
-};
+
+struct statx STATX_DEFINITION;
 #endif
+
+/* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc
+ * defines an older definition */
+struct new_statx STATX_DEFINITION;
 #endif /* NM_IGNORED */
 
 /* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_BTIME
-#define STATX_BTIME 0x00000800U
+#ifndef AT_STATX_SYNC_AS_STAT
+#define AT_STATX_SYNC_AS_STAT 0x0000
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef AT_STATX_FORCE_SYNC
+#define AT_STATX_FORCE_SYNC 0x2000
 #endif
 
 /* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
 #ifndef AT_STATX_DONT_SYNC
 #define AT_STATX_DONT_SYNC 0x4000
 #endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_TYPE
+#define STATX_TYPE 0x00000001U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_MODE
+#define STATX_MODE 0x00000002U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_NLINK
+#define STATX_NLINK 0x00000004U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_UID
+#define STATX_UID 0x00000008U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_GID
+#define STATX_GID 0x00000010U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_ATIME
+#define STATX_ATIME 0x00000020U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_MTIME
+#define STATX_MTIME 0x00000040U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_CTIME
+#define STATX_CTIME 0x00000080U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_INO
+#define STATX_INO 0x00000100U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_SIZE
+#define STATX_SIZE 0x00000200U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_BLOCKS
+#define STATX_BLOCKS 0x00000400U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_BTIME
+#define STATX_BTIME 0x00000800U
+#endif
+
+/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */
+#ifndef STATX_MNT_ID
+#define STATX_MNT_ID 0x00001000U
+#endif
+
+/* 80340fe3605c0e78cfe496c3b3878be828cfdbfe (5.8) */
+#ifndef STATX_ATTR_MOUNT_ROOT
+#define STATX_ATTR_MOUNT_ROOT 0x00002000 /* Root of a mount */
+#endif
diff --git a/shared/systemd/src/basic/missing_syscall.h b/shared/systemd/src/basic/missing_syscall.h
index 4c42e665..4aed6e7c 100644
--- a/shared/systemd/src/basic/missing_syscall.h
+++ b/shared/systemd/src/basic/missing_syscall.h
@@ -35,39 +35,49 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old)
 
 /* ======================================================================= */
 
-#if !HAVE_MEMFD_CREATE
+#if defined __x86_64__
+#  define systemd_NR_memfd_create 319
+#elif defined __arm__
+#  define systemd_NR_memfd_create 385
+#elif defined __aarch64__
+#  define systemd_NR_memfd_create 279
+#elif defined(__powerpc__)
+#  define systemd_NR_memfd_create 360
+#elif defined __s390__
+#  define systemd_NR_memfd_create 350
+#elif defined _MIPS_SIM
+#  if _MIPS_SIM == _MIPS_SIM_ABI32
+#    define systemd_NR_memfd_create 4354
+#  endif
+#  if _MIPS_SIM == _MIPS_SIM_NABI32
+#    define systemd_NR_memfd_create 6318
+#  endif
+#  if _MIPS_SIM == _MIPS_SIM_ABI64
+#    define systemd_NR_memfd_create 5314
+#  endif
+#elif defined __i386__
+#  define systemd_NR_memfd_create 356
+#elif defined __arc__
+#  define systemd_NR_memfd_create 279
+#else
+#  warning "memfd_create() syscall number unknown for your architecture"
+#endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_memfd_create && __NR_memfd_create >= 0)
-#    if defined __NR_memfd_create
-#      undef __NR_memfd_create
-#    endif
-#    if defined __x86_64__
-#      define __NR_memfd_create 319
-#    elif defined __arm__
-#      define __NR_memfd_create 385
-#    elif defined __aarch64__
-#      define __NR_memfd_create 279
-#    elif defined __s390__
-#      define __NR_memfd_create 350
-#    elif defined _MIPS_SIM
-#      if _MIPS_SIM == _MIPS_SIM_ABI32
-#        define __NR_memfd_create 4354
-#      endif
-#      if _MIPS_SIM == _MIPS_SIM_NABI32
-#        define __NR_memfd_create 6318
-#      endif
-#      if _MIPS_SIM == _MIPS_SIM_ABI64
-#        define __NR_memfd_create 5314
-#      endif
-#    elif defined __i386__
-#      define __NR_memfd_create 356
-#    elif defined __arc__
-#      define __NR_memfd_create 279
-#    else
-#      warning "__NR_memfd_create unknown for your architecture"
-#    endif
+#if defined __NR_memfd_create && __NR_memfd_create >= 0
+#  if defined systemd_NR_memfd_create
+assert_cc(__NR_memfd_create == systemd_NR_memfd_create);
+#  endif
+#else
+#  if defined __NR_memfd_create
+#    undef __NR_memfd_create
 #  endif
+#  if defined systemd_NR_memfd_create
+#    define __NR_memfd_create systemd_NR_memfd_create
+#  endif
+#endif
 
+#if !HAVE_MEMFD_CREATE
 static inline int missing_memfd_create(const char *name, unsigned int flags) {
 #  ifdef __NR_memfd_create
         return syscall(__NR_memfd_create, name, flags);
@@ -82,45 +92,53 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
 
 /* ======================================================================= */
 
-#if !HAVE_GETRANDOM
+#if defined __x86_64__
+#  define systemd_NR_getrandom 318
+#elif defined(__i386__)
+#  define systemd_NR_getrandom 355
+#elif defined(__arm__)
+#  define systemd_NR_getrandom 384
+#elif defined(__aarch64__)
+#  define systemd_NR_getrandom 278
+#elif defined(__ia64__)
+#  define systemd_NR_getrandom 1339
+#elif defined(__m68k__)
+#  define systemd_NR_getrandom 352
+#elif defined(__s390x__)
+#  define systemd_NR_getrandom 349
+#elif defined(__powerpc__)
+#  define systemd_NR_getrandom 359
+#elif defined _MIPS_SIM
+#  if _MIPS_SIM == _MIPS_SIM_ABI32
+#    define systemd_NR_getrandom 4353
+#  endif
+#  if _MIPS_SIM == _MIPS_SIM_NABI32
+#    define systemd_NR_getrandom 6317
+#  endif
+#  if _MIPS_SIM == _MIPS_SIM_ABI64
+#    define systemd_NR_getrandom 5313
+#  endif
+#elif defined(__arc__)
+#  define systemd_NR_getrandom 278
+#else
+#  warning "getrandom() syscall number unknown for your architecture"
+#endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_getrandom && __NR_getrandom >= 0)
-#    if defined __NR_getrandom
-#      undef __NR_getrandom
-#    endif
-#    if defined __x86_64__
-#      define __NR_getrandom 318
-#    elif defined(__i386__)
-#      define __NR_getrandom 355
-#    elif defined(__arm__)
-#      define __NR_getrandom 384
-#   elif defined(__aarch64__)
-#      define __NR_getrandom 278
-#    elif defined(__ia64__)
-#      define __NR_getrandom 1339
-#    elif defined(__m68k__)
-#      define __NR_getrandom 352
-#    elif defined(__s390x__)
-#      define __NR_getrandom 349
-#    elif defined(__powerpc__)
-#      define __NR_getrandom 359
-#    elif defined _MIPS_SIM
-#      if _MIPS_SIM == _MIPS_SIM_ABI32
-#        define __NR_getrandom 4353
-#      endif
-#      if _MIPS_SIM == _MIPS_SIM_NABI32
-#        define __NR_getrandom 6317
-#      endif
-#      if _MIPS_SIM == _MIPS_SIM_ABI64
-#        define __NR_getrandom 5313
-#      endif
-#    elif defined(__arc__)
-#      define __NR_getrandom 278
-#    else
-#      warning "__NR_getrandom unknown for your architecture"
-#    endif
+#if defined __NR_getrandom && __NR_getrandom >= 0
+#  if defined systemd_NR_getrandom
+assert_cc(__NR_getrandom == systemd_NR_getrandom);
+#  endif
+#else
+#  if defined __NR_getrandom
+#    undef __NR_getrandom
+#  endif
+#  if defined systemd_NR_getrandom
+#    define __NR_getrandom systemd_NR_getrandom
 #  endif
+#endif
 
+#if !HAVE_GETRANDOM
 static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) {
 #  ifdef __NR_getrandom
         return syscall(__NR_getrandom, buffer, count, flags);
@@ -135,9 +153,14 @@ static inline int missing_getrandom(void *buffer, size_t count, unsigned flags)
 
 /* ======================================================================= */
 
+/* The syscall has been defined since forever, but the glibc wrapper was missing. */
 #if !HAVE_GETTID
 static inline pid_t missing_gettid(void) {
+#  if defined __NR_gettid && __NR_gettid >= 0
         return (pid_t) syscall(__NR_gettid);
+#  else
+#    error "__NR_gettid not defined"
+#  endif
 }
 
 #  define gettid missing_gettid
@@ -145,27 +168,39 @@ static inline pid_t missing_gettid(void) {
 
 /* ======================================================================= */
 
-#if !HAVE_NAME_TO_HANDLE_AT
+#if defined(__x86_64__)
+#  define systemd_NR_name_to_handle_at 303
+#elif defined(__i386__)
+#  define systemd_NR_name_to_handle_at 341
+#elif defined(__arm__)
+#  define systemd_NR_name_to_handle_at 370
+#elif defined __aarch64__
+#  define systemd_NR_name_to_handle_at 264
+#elif defined(__powerpc__)
+#  define systemd_NR_name_to_handle_at 345
+#elif defined __s390__ || defined __s390x__
+#  define systemd_NR_name_to_handle_at 335
+#elif defined(__arc__)
+#  define systemd_NR_name_to_handle_at 264
+#else
+#  warning "name_to_handle_at number is not defined"
+#endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_name_to_handle_at && __NR_name_to_handle_at >= 0)
-#    if defined __NR_name_to_handle_at
-#      undef __NR_name_to_handle_at
-#    endif
-#    if defined(__x86_64__)
-#      define __NR_name_to_handle_at 303
-#    elif defined(__i386__)
-#      define __NR_name_to_handle_at 341
-#    elif defined(__arm__)
-#      define __NR_name_to_handle_at 370
-#    elif defined(__powerpc__)
-#      define __NR_name_to_handle_at 345
-#    elif defined(__arc__)
-#      define __NR_name_to_handle_at 264
-#    else
-#      error "__NR_name_to_handle_at is not defined"
-#    endif
+#if defined __NR_name_to_handle_at && __NR_name_to_handle_at >= 0
+#  if defined systemd_NR_name_to_handle_at
+assert_cc(__NR_name_to_handle_at == systemd_NR_name_to_handle_at);
 #  endif
+#else
+#  if defined __NR_name_to_handle_at
+#    undef __NR_name_to_handle_at
+#  endif
+#  if defined systemd_NR_name_to_handle_at
+#    define __NR_name_to_handle_at systemd_NR_name_to_handle_at
+#  endif
+#endif
 
+#if !HAVE_NAME_TO_HANDLE_AT
 struct file_handle {
         unsigned int handle_bytes;
         int handle_type;
@@ -186,23 +221,39 @@ static inline int missing_name_to_handle_at(int fd, const char *name, struct fil
 
 /* ======================================================================= */
 
-#if !HAVE_SETNS
+#if defined __aarch64__
+#  define systemd_NR_setns 268
+#elif defined __arm__
+#  define systemd_NR_setns 375
+#elif defined(__x86_64__)
+#  define systemd_NR_setns 308
+#elif defined(__i386__)
+#  define systemd_NR_setns 346
+#elif defined(__powerpc__)
+#  define systemd_NR_setns 350
+#elif defined __s390__ || defined __s390x__
+#  define systemd_NR_setns 339
+#elif defined(__arc__)
+#  define systemd_NR_setns 268
+#else
+#  warning "setns() syscall number unknown for your architecture"
+#endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_setns && __NR_setns >= 0)
-#    if defined __NR_setns
-#      undef __NR_setns
-#    endif
-#    if defined(__x86_64__)
-#      define __NR_setns 308
-#    elif defined(__i386__)
-#      define __NR_setns 346
-#    elif defined(__arc__)
-#      define __NR_setns 268
-#    else
-#      error "__NR_setns is not defined"
-#    endif
+#if defined __NR_setns && __NR_setns >= 0
+#  if defined systemd_NR_setns
+assert_cc(__NR_setns == systemd_NR_setns);
 #  endif
+#else
+#  if defined __NR_setns
+#    undef __NR_setns
+#  endif
+#  if defined systemd_NR_setns
+#    define __NR_setns systemd_NR_setns
+#  endif
+#endif
 
+#if !HAVE_SETNS
 static inline int missing_setns(int fd, int nstype) {
 #  ifdef __NR_setns
         return syscall(__NR_setns, fd, nstype);
@@ -227,41 +278,49 @@ static inline pid_t raw_getpid(void) {
 
 /* ======================================================================= */
 
-#if !HAVE_RENAMEAT2
+#if defined __x86_64__
+#  define systemd_NR_renameat2 316
+#elif defined __arm__
+#  define systemd_NR_renameat2 382
+#elif defined __aarch64__
+#  define systemd_NR_renameat2 276
+#elif defined _MIPS_SIM
+#  if _MIPS_SIM == _MIPS_SIM_ABI32
+#    define systemd_NR_renameat2 4351
+#  endif
+#  if _MIPS_SIM == _MIPS_SIM_NABI32
+#    define systemd_NR_renameat2 6315
+#  endif
+#  if _MIPS_SIM == _MIPS_SIM_ABI64
+#    define systemd_NR_renameat2 5311
+#  endif
+#elif defined __i386__
+#  define systemd_NR_renameat2 353
+#elif defined __powerpc64__
+#  define systemd_NR_renameat2 357
+#elif defined __s390__ || defined __s390x__
+#  define systemd_NR_renameat2 347
+#elif defined __arc__
+#  define systemd_NR_renameat2 276
+#else
+#  warning "renameat2() syscall number unknown for your architecture"
+#endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_renameat2 && __NR_renameat2 >= 0)
-#    if defined __NR_renameat2
-#      undef __NR_renameat2
-#    endif
-#    if defined __x86_64__
-#      define __NR_renameat2 316
-#    elif defined __arm__
-#      define __NR_renameat2 382
-#    elif defined __aarch64__
-#      define __NR_renameat2 276
-#    elif defined _MIPS_SIM
-#      if _MIPS_SIM == _MIPS_SIM_ABI32
-#        define __NR_renameat2 4351
-#      endif
-#      if _MIPS_SIM == _MIPS_SIM_NABI32
-#        define __NR_renameat2 6315
-#      endif
-#      if _MIPS_SIM == _MIPS_SIM_ABI64
-#        define __NR_renameat2 5311
-#      endif
-#    elif defined __i386__
-#      define __NR_renameat2 353
-#    elif defined __powerpc64__
-#      define __NR_renameat2 357
-#    elif defined __s390__ || defined __s390x__
-#      define __NR_renameat2 347
-#    elif defined __arc__
-#      define __NR_renameat2 276
-#    else
-#      warning "__NR_renameat2 unknown for your architecture"
-#    endif
+#if defined __NR_renameat2 && __NR_renameat2 >= 0
+#  if defined systemd_NR_renameat2
+assert_cc(__NR_renameat2 == systemd_NR_renameat2);
 #  endif
+#else
+#  if defined __NR_renameat2
+#    undef __NR_renameat2
+#  endif
+#  if defined systemd_NR_renameat2
+#    define __NR_renameat2 systemd_NR_renameat2
+#  endif
+#endif
 
+#if !HAVE_RENAMEAT2
 static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
 #  ifdef __NR_renameat2
         return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
@@ -328,31 +387,39 @@ static inline key_serial_t missing_request_key(const char *type, const char *des
 
 /* ======================================================================= */
 
-#if !HAVE_COPY_FILE_RANGE
+#if defined(__x86_64__)
+#  define systemd_NR_copy_file_range 326
+#elif defined(__i386__)
+#  define systemd_NR_copy_file_range 377
+#elif defined __s390__
+#  define systemd_NR_copy_file_range 375
+#elif defined __arm__
+#  define systemd_NR_copy_file_range 391
+#elif defined __aarch64__
+#  define systemd_NR_copy_file_range 285
+#elif defined __powerpc__
+#  define systemd_NR_copy_file_range 379
+#elif defined __arc__
+#  define systemd_NR_copy_file_range 285
+#else
+#  warning "copy_file_range() syscall number unknown for your architecture"
+#endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_copy_file_range && __NR_copy_file_range >= 0)
-#    if defined __NR_copy_file_range
-#      undef __NR_copy_file_range
-#    endif
-#    if defined(__x86_64__)
-#      define __NR_copy_file_range 326
-#    elif defined(__i386__)
-#      define __NR_copy_file_range 377
-#    elif defined __s390__
-#      define __NR_copy_file_range 375
-#    elif defined __arm__
-#      define __NR_copy_file_range 391
-#    elif defined __aarch64__
-#      define __NR_copy_file_range 285
-#    elif defined __powerpc__
-#      define __NR_copy_file_range 379
-#    elif defined __arc__
-#      define __NR_copy_file_range 285
-#    else
-#      warning "__NR_copy_file_range not defined for your architecture"
-#    endif
+#if defined __NR_copy_file_range && __NR_copy_file_range >= 0
+#  if defined systemd_NR_copy_file_range
+assert_cc(__NR_copy_file_range == systemd_NR_copy_file_range);
 #  endif
+#else
+#  if defined __NR_copy_file_range
+#    undef __NR_copy_file_range
+#  endif
+#  if defined systemd_NR_copy_file_range
+#    define __NR_copy_file_range systemd_NR_copy_file_range
+#  endif
+#endif
 
+#if !HAVE_COPY_FILE_RANGE
 static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
                                               int fd_out, loff_t *off_out,
                                               size_t len,
@@ -370,31 +437,41 @@ static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
 
 /* ======================================================================= */
 
-#if !HAVE_BPF
+#if defined __i386__
+#  define systemd_NR_bpf 357
+#elif defined __x86_64__
+#  define systemd_NR_bpf 321
+#elif defined __aarch64__
+#  define systemd_NR_bpf 280
+#elif defined __arm__
+#  define systemd_NR_bpf 386
+#elif defined(__powerpc__)
+#  define systemd_NR_bpf 361
+#elif defined __sparc__
+#  define systemd_NR_bpf 349
+#elif defined __s390__
+#  define systemd_NR_bpf 351
+#elif defined __tilegx__
+#  define systemd_NR_bpf 280
+#else
+#  warning "bpf() syscall number unknown for your architecture"
+#endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_bpf && __NR_bpf >= 0)
-#    if defined __NR_bpf
-#      undef __NR_bpf
-#    endif
-#    if defined __i386__
-#      define __NR_bpf 357
-#    elif defined __x86_64__
-#      define __NR_bpf 321
-#    elif defined __aarch64__
-#      define __NR_bpf 280
-#    elif defined __arm__
-#      define __NR_bpf 386
-#    elif defined __sparc__
-#      define __NR_bpf 349
-#    elif defined __s390__
-#      define __NR_bpf 351
-#    elif defined __tilegx__
-#      define __NR_bpf 280
-#    else
-#      warning "__NR_bpf not defined for your architecture"
-#    endif
+#if defined __NR_bpf && __NR_bpf >= 0
+#  if defined systemd_NR_bpf
+assert_cc(__NR_bpf == systemd_NR_bpf);
+#  endif
+#else
+#  if defined __NR_bpf
+#    undef __NR_bpf
 #  endif
+#  if defined systemd_NR_bpf
+#    define __NR_bpf systemd_NR_bpf
+#  endif
+#endif
 
+#if !HAVE_BPF
 union bpf_attr;
 
 static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
@@ -412,69 +489,84 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
 /* ======================================================================= */
 
 #ifndef __IGNORE_pkey_mprotect
+#  if defined __i386__
+#    define systemd_NR_pkey_mprotect 380
+#  elif defined __x86_64__
+#    define systemd_NR_pkey_mprotect 329
+#  elif defined __aarch64__
+#    define systemd_NR_pkey_mprotect 288
+#  elif defined __arm__
+#    define systemd_NR_pkey_mprotect 394
+#  elif defined __powerpc__
+#    define systemd_NR_pkey_mprotect 386
+#  elif defined __s390__
+#    define systemd_NR_pkey_mprotect 384
+#  elif defined _MIPS_SIM
+#    if _MIPS_SIM == _MIPS_SIM_ABI32
+#      define systemd_NR_pkey_mprotect 4363
+#    endif
+#    if _MIPS_SIM == _MIPS_SIM_NABI32
+#      define systemd_NR_pkey_mprotect 6327
+#    endif
+#    if _MIPS_SIM == _MIPS_SIM_ABI64
+#      define systemd_NR_pkey_mprotect 5323
+#    endif
+#  else
+#    warning "pkey_mprotect() syscall number unknown for your architecture"
+#  endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_pkey_mprotect && __NR_pkey_mprotect >= 0)
+#  if defined __NR_pkey_mprotect && __NR_pkey_mprotect >= 0
+#    if defined systemd_NR_pkey_mprotect
+assert_cc(__NR_pkey_mprotect == systemd_NR_pkey_mprotect);
+#    endif
+#  else
 #    if defined __NR_pkey_mprotect
 #      undef __NR_pkey_mprotect
 #    endif
-#    if defined __i386__
-#      define __NR_pkey_mprotect 380
-#    elif defined __x86_64__
-#      define __NR_pkey_mprotect 329
-#    elif defined __arm__
-#      define __NR_pkey_mprotect 394
-#    elif defined __aarch64__
-#      define __NR_pkey_mprotect 394
-#    elif defined __powerpc__
-#      define __NR_pkey_mprotect 386
-#    elif defined __s390__
-#      define __NR_pkey_mprotect 384
-#    elif defined _MIPS_SIM
-#      if _MIPS_SIM == _MIPS_SIM_ABI32
-#        define __NR_pkey_mprotect 4363
-#      endif
-#      if _MIPS_SIM == _MIPS_SIM_NABI32
-#        define __NR_pkey_mprotect 6327
-#      endif
-#      if _MIPS_SIM == _MIPS_SIM_ABI64
-#        define __NR_pkey_mprotect 5323
-#      endif
-#    else
-#      warning "__NR_pkey_mprotect not defined for your architecture"
+#    if defined systemd_NR_pkey_mprotect
+#      define __NR_pkey_mprotect systemd_NR_pkey_mprotect
 #    endif
 #  endif
 #endif
 
 /* ======================================================================= */
 
-#if !HAVE_STATX
+#if defined __aarch64__
+#  define systemd_NR_statx 291
+#elif defined __arm__
+#  define systemd_NR_statx 397
+#elif defined __alpha__
+#  define systemd_NR_statx 522
+#elif defined __i386__ || defined __powerpc64__
+#  define systemd_NR_statx 383
+#elif defined __s390__ || defined __s390x__
+#  define systemd_NR_statx 379
+#elif defined __sparc__
+#  define systemd_NR_statx 360
+#elif defined __x86_64__
+#  define systemd_NR_statx 332
+#else
+#  warning "statx() syscall number unknown for your architecture"
+#endif
+
 /* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_statx && __NR_statx >= 0)
-#    if defined __NR_statx
-#      undef __NR_statx
-#    endif
-#    if defined __aarch64__ || defined __arm__
-#      define __NR_statx 397
-#    elif defined __alpha__
-#      define __NR_statx 522
-#    elif defined __i386__ || defined __powerpc64__
-#      define __NR_statx 383
-#    elif defined __sparc__
-#      define __NR_statx 360
-#    elif defined __x86_64__
-#      define __NR_statx 332
-#    else
-#      warning "__NR_statx not defined for your architecture"
-#    endif
+#if defined __NR_statx && __NR_statx >= 0
+#  if defined systemd_NR_statx
+assert_cc(__NR_statx == systemd_NR_statx);
+#  endif
+#else
+#  if defined __NR_statx
+#    undef __NR_statx
+#  endif
+#  if defined systemd_NR_statx
+#    define __NR_statx systemd_NR_statx
 #  endif
-
-struct statx;
 #endif
 
-/* This typedef is supposed to be always defined. */
-typedef struct statx struct_statx;
-
 #if !HAVE_STATX
+struct statx;
+
 static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) {
 #  ifdef __NR_statx
         return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
@@ -483,12 +575,18 @@ static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flag
         return -1;
 #  endif
 }
+#endif
 
-#  define statx missing_statx
+/* This typedef is supposed to be always defined. */
+typedef struct statx struct_statx;
+
+#if !HAVE_STATX
+#  define statx(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer)
 #endif
 
-#if !HAVE_SET_MEMPOLICY
+/* ======================================================================= */
 
+#if !HAVE_SET_MEMPOLICY
 enum {
         MPOL_DEFAULT,
         MPOL_PREFERRED,
@@ -517,7 +615,7 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
                            unsigned long maxnode, void *addr,
                            unsigned long flags) {
         long i;
-#  ifdef __NR_get_mempolicy
+#  if defined __NR_get_mempolicy && __NR_get_mempolicy >= 0
         i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
 #  else
         errno = ENOSYS;
@@ -526,49 +624,87 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
         return i;
 }
 
-#define get_mempolicy missing_get_mempolicy
+#  define get_mempolicy missing_get_mempolicy
 #endif
 
 #endif /* NM_IGNORED */
 
-#if !HAVE_PIDFD_OPEN
-/* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_pidfd_open && __NR_pidfd_open >= 0)
-#    if defined __NR_pidfd_open
-#      undef __NR_pidfd_open
-#    endif
-#    define __NR_pidfd_open 434
+/* ======================================================================= */
+
+/* should be always defined, see kernel 39036cd2727395c3369b1051005da74059a85317 */
+#if defined(__alpha__)
+#  define systemd_NR_pidfd_send_signal 534
+#else
+#  define systemd_NR_pidfd_send_signal 424
 #endif
-static inline int pidfd_open(pid_t pid, unsigned flags) {
-#ifdef __NR_pidfd_open
-        return syscall(__NR_pidfd_open, pid, flags);
+
+/* may be (invalid) negative number due to libseccomp, see PR 13319 */
+#if defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0
+#  if defined systemd_NR_pidfd_send_signal
+assert_cc(__NR_pidfd_send_signal == systemd_NR_pidfd_send_signal);
+#  endif
 #else
+#  if defined __NR_pidfd_send_signal
+#    undef __NR_pidfd_send_signal
+#  endif
+#  define __NR_pidfd_send_signal systemd_NR_pidfd_send_signal
+#endif
+
+#if !HAVE_PIDFD_SEND_SIGNAL
+static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
+#  ifdef __NR_pidfd_open
+        return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
+#  else
         errno = ENOSYS;
         return -1;
-#endif
+#  endif
 }
+
+#  define pidfd_send_signal missing_pidfd_send_signal
 #endif
 
-#if !HAVE_PIDFD_SEND_SIGNAL
-/* may be (invalid) negative number due to libseccomp, see PR 13319 */
-#  if ! (defined __NR_pidfd_send_signal && __NR_pidfd_send_signal >= 0)
-#    if defined __NR_pidfd_send_signal
-#      undef __NR_pidfd_send_signal
-#    endif
-#    define __NR_pidfd_send_signal 424
+/* should be always defined, see kernel 7615d9e1780e26e0178c93c55b73309a5dc093d7 */
+#if defined(__alpha__)
+#  define systemd_NR_pidfd_open 544
+#else
+#  define systemd_NR_pidfd_open 434
 #endif
-static inline int pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
-#ifdef __NR_pidfd_open
-        return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
+
+/* may be (invalid) negative number due to libseccomp, see PR 13319 */
+#if defined __NR_pidfd_open && __NR_pidfd_open >= 0
+#  if defined systemd_NR_pidfd_open
+assert_cc(__NR_pidfd_open == systemd_NR_pidfd_open);
+#  endif
 #else
+#  if defined __NR_pidfd_open
+#    undef __NR_pidfd_open
+#  endif
+#  define __NR_pidfd_open systemd_NR_pidfd_open
+#endif
+
+#if !HAVE_PIDFD_OPEN
+static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
+#  ifdef __NR_pidfd_open
+        return syscall(__NR_pidfd_open, pid, flags);
+#  else
         errno = ENOSYS;
         return -1;
-#endif
+#  endif
 }
+
+#  define pidfd_open missing_pidfd_open
 #endif
 
+/* ======================================================================= */
+
 #if !HAVE_RT_SIGQUEUEINFO
-static inline int rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) {
+static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) {
+#  if defined __NR_rt_sigqueueinfo && __NR_rt_sigqueueinfo >= 0
         return syscall(__NR_rt_sigqueueinfo, tgid, sig, info);
+#  else
+#    error "__NR_rt_sigqueueinfo not defined"
+#  endif
 }
+
+#  define rt_sigqueueinfo missing_rt_sigqueueinfo
 #endif
diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c
index 0b343b2a..6b47317a 100644
--- a/shared/systemd/src/basic/parse-util.c
+++ b/shared/systemd/src/basic/parse-util.c
@@ -18,6 +18,9 @@
 #include "missing_network.h"
 #include "parse-util.h"
 #include "process-util.h"
+#if HAVE_SECCOMP
+#include "seccomp-util.h"
+#endif
 #include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -317,6 +320,7 @@ int parse_errno(const char *t) {
         return e;
 }
 
+#if HAVE_SECCOMP
 int parse_syscall_and_errno(const char *in, char **name, int *error) {
         _cleanup_free_ char *n = NULL;
         char *p;
@@ -335,7 +339,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
 
         p = strchr(in, ':');
         if (p) {
-                e = parse_errno(p + 1);
+                e = seccomp_parse_errno_or_action(p + 1);
                 if (e < 0)
                         return e;
 
@@ -354,6 +358,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
 
         return 0;
 }
+#endif
 #endif /* NM_IGNORED */
 
 static const char *mangle_base(const char *s, unsigned *base) {
diff --git a/shared/systemd/src/basic/parse-util.h b/shared/systemd/src/basic/parse-util.h
index 9a516ce5..2cee65c4 100644
--- a/shared/systemd/src/basic/parse-util.h
+++ b/shared/systemd/src/basic/parse-util.h
@@ -19,7 +19,9 @@ int parse_mtu(int family, const char *s, uint32_t *ret);
 int parse_size(const char *t, uint64_t base, uint64_t *size);
 int parse_range(const char *t, unsigned *lower, unsigned *upper);
 int parse_errno(const char *t);
+#if HAVE_SECCOMP
 int parse_syscall_and_errno(const char *in, char **name, int *error);
+#endif
 
 #define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30)
 #define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29)
diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c
index 45477bc2..644829b2 100644
--- a/shared/systemd/src/basic/path-util.c
+++ b/shared/systemd/src/basic/path-util.c
@@ -16,6 +16,7 @@
 
 #include "alloc-util.h"
 #include "extract-word.h"
+#include "fd-util.h"
 #include "fs-util.h"
 #include "glob-util.h"
 #include "log.h"
@@ -29,15 +30,7 @@
 #include "time-util.h"
 #include "utf8.h"
 
-bool path_is_absolute(const char *p) {
-        return p[0] == '/';
-}
-
 #if 0 /* NM_IGNORED */
-bool is_path(const char *p) {
-        return !!strchr(p, '/');
-}
-
 int path_split_and_make_absolute(const char *p, char ***ret) {
         char **l;
         int r;
@@ -601,9 +594,9 @@ char* path_join_internal(const char *first, ...) {
 }
 
 #if 0 /* NM_IGNORED */
-int find_binary(const char *name, char **ret) {
+int find_executable_full(const char *name, bool use_path_envvar, char **ret) {
         int last_error, r;
-        const char *p;
+        const char *p = NULL;
 
         assert(name);
 
@@ -620,11 +613,10 @@ int find_binary(const char *name, char **ret) {
                 return 0;
         }
 
-        /**
-         * Plain getenv, not secure_getenv, because we want
-         * to actually allow the user to pick the binary.
-         */
-        p = getenv("PATH");
+        if (use_path_envvar)
+                /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
+                 * binary. */
+                p = getenv("PATH");
         if (!p)
                 p = DEFAULT_PATH;
 
@@ -647,14 +639,24 @@ int find_binary(const char *name, char **ret) {
                         return -ENOMEM;
 
                 if (access(j, X_OK) >= 0) {
-                        /* Found it! */
+                        _cleanup_free_ char *with_dash;
 
-                        if (ret) {
-                                *ret = path_simplify(j, false);
-                                j = NULL;
-                        }
+                        with_dash = strjoin(j, "/");
+                        if (!with_dash)
+                                return -ENOMEM;
 
-                        return 0;
+                        /* If this passes, it must be a directory, and so should be skipped. */
+                        if (access(with_dash, X_OK) >= 0)
+                                continue;
+
+                        /* We can't just `continue` inverting this case, since we need to update last_error. */
+                        if (errno == ENOTDIR) {
+                                /* Found it! */
+                                if (ret)
+                                        *ret = path_simplify(TAKE_PTR(j), false);
+
+                                return 0;
+                        }
                 }
 
                 /* PATH entries which we don't have access to are ignored, as per tradition. */
@@ -700,18 +702,17 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd
         return changed;
 }
 
-static int binary_is_good(const char *binary) {
+static int executable_is_good(const char *executable) {
         _cleanup_free_ char *p = NULL, *d = NULL;
         int r;
 
-        r = find_binary(binary, &p);
+        r = find_executable(executable, &p);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
                 return r;
 
-        /* An fsck that is linked to /bin/true is a non-existent
-         * fsck */
+        /* An fsck that is linked to /bin/true is a non-existent fsck */
 
         r = readlink_malloc(p, &d);
         if (r == -EINVAL) /* not a symlink */
@@ -734,19 +735,7 @@ int fsck_exists(const char *fstype) {
                 return -EINVAL;
 
         checker = strjoina("fsck.", fstype);
-        return binary_is_good(checker);
-}
-
-int mkfs_exists(const char *fstype) {
-        const char *mkfs;
-
-        assert(fstype);
-
-        if (streq(fstype, "auto"))
-                return -EINVAL;
-
-        mkfs = strjoina("mkfs.", fstype);
-        return binary_is_good(mkfs);
+        return executable_is_good(checker);
 }
 
 int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
@@ -1154,4 +1143,10 @@ bool prefixed_path_strv_contains(char **l, const char *path) {
 
         return false;
 }
+
+bool credential_name_valid(const char *s) {
+        /* We want that credential names are both valid in filenames (since that's our primary way to pass
+         * them around) and as fdnames (which is how we might want to pass them around eventually) */
+        return filename_is_valid(s) && fdname_is_valid(s);
+}
 #endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h
index aba8ad00..6c265519 100644
--- a/shared/systemd/src/basic/path-util.h
+++ b/shared/systemd/src/basic/path-util.h
@@ -44,9 +44,17 @@
 #  define DEFAULT_USER_PATH DEFAULT_PATH
 #endif
 
-bool is_path(const char *p) _pure_;
+static inline bool is_path(const char *p) {
+        assert(p);
+        return strchr(p, '/');
+}
+
+static inline bool path_is_absolute(const char *p) {
+        assert(p);
+        return p[0] == '/';
+}
+
 int path_split_and_make_absolute(const char *p, char ***ret);
-bool path_is_absolute(const char *p) _pure_;
 char* path_make_absolute(const char *p, const char *prefix);
 int safe_getcwd(char **ret);
 int path_make_absolute_cwd(const char *p, char **ret);
@@ -82,12 +90,14 @@ int path_strv_make_absolute_cwd(char **l);
 char** path_strv_resolve(char **l, const char *root);
 char** path_strv_resolve_uniq(char **l, const char *root);
 
-int find_binary(const char *name, char **filename);
+int find_executable_full(const char *name, bool use_path_envvar, char **ret);
+static inline int find_executable(const char *name, char **ret) {
+        return find_executable_full(name, true, ret);
+}
 
 bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
 
 int fsck_exists(const char *fstype);
-int mkfs_exists(const char *fstype);
 
 /* Iterates through the path prefixes of the specified path, going up
  * the tree, to root. Also returns "" (and not "/"!) for the root
@@ -176,3 +186,5 @@ static inline const char *empty_to_root(const char *path) {
 
 bool path_strv_contains(char **l, const char *path);
 bool prefixed_path_strv_contains(char **l, const char *path);
+
+bool credential_name_valid(const char *s);
diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c
index 6dcb26fb..03ca04e1 100644
--- a/shared/systemd/src/basic/process-util.c
+++ b/shared/systemd/src/basic/process-util.c
@@ -1344,7 +1344,7 @@ int safe_fork_full(
 
                 ppid = getppid();
                 if (ppid == 0)
-                        /* Parent is in a differn't PID namespace. */;
+                        /* Parent is in a different PID namespace. */;
                 else if (ppid != original_pid) {
                         log_debug("Parent died early, raising SIGTERM.");
                         (void) raise(SIGTERM);
diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h
index 05012377..314a8de1 100644
--- a/shared/systemd/src/basic/process-util.h
+++ b/shared/systemd/src/basic/process-util.h
@@ -20,14 +20,16 @@
 #define procfs_file_alloca(pid, field)                                  \
         ({                                                              \
                 pid_t _pid_ = (pid);                                    \
-                const char *_r_;                                        \
+                const char *_field_ = (field);                          \
+                char *_r_;                                              \
                 if (_pid_ == 0) {                                       \
-                        _r_ = ("/proc/self/" field);                    \
+                        _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \
+                        strcpy(stpcpy(_r_, "/proc/self/"), _field_);    \
                 } else {                                                \
-                        _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
-                        sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
+                        _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \
+                        sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \
                 }                                                       \
-                _r_;                                                    \
+                (const char*) _r_;                                      \
         })
 
 typedef enum ProcessCmdlineFlags {
diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c
index 512e7af9..9551e762 100644
--- a/shared/systemd/src/basic/random-util.c
+++ b/shared/systemd/src/basic/random-util.c
@@ -9,11 +9,13 @@
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/random.h>
 #include <pthread.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/time.h>
 
 #if HAVE_SYS_AUXV_H
@@ -21,6 +23,7 @@
 #endif
 
 #include "alloc-util.h"
+#include "env-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -77,7 +80,7 @@ int rdrand(unsigned long *ret) {
          *           hash functions for its hash tables, with a seed generated randomly. The hash tables
          *           systemd employs watch the fill level closely and reseed if necessary. This allows use of
          *           a low quality RNG initially, as long as it improves should a hash table be under attack:
-         *           the attacker after all needs to to trigger many collisions to exploit it for the purpose
+         *           the attacker after all needs to trigger many collisions to exploit it for the purpose
          *           of DoS, but if doing so improves the seed the attack surface is reduced as the attack
          *           takes place.
          *
@@ -116,6 +119,15 @@ int rdrand(unsigned long *ret) {
 #endif
 
                 have_rdrand = !!(ecx & bit_RDRND);
+
+                if (have_rdrand > 0) {
+                        /* Allow disabling use of RDRAND with SYSTEMD_RDRAND=0
+                           If it is unset getenv_bool_secure will return a negative value. */
+                        if (getenv_bool_secure("SYSTEMD_RDRAND") == 0) {
+                                have_rdrand = false;
+                                return -EOPNOTSUPP;
+                        }
+                }
         }
 
         if (have_rdrand == 0)
@@ -448,4 +460,37 @@ size_t random_pool_size(void) {
         /* Use the minimum as default, if we can't retrieve the correct value */
         return RANDOM_POOL_SIZE_MIN;
 }
+
+int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
+        int r;
+
+        assert(fd >= 0);
+        assert(seed && size > 0);
+
+        if (credit) {
+                _cleanup_free_ struct rand_pool_info *info = NULL;
+
+                /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any
+                 * chance for confusion here. */
+                if (size > INT_MAX / 8)
+                        return -EOVERFLOW;
+
+                info = malloc(offsetof(struct rand_pool_info, buf) + size);
+                if (!info)
+                        return -ENOMEM;
+
+                info->entropy_count = size * 8;
+                info->buf_size = size;
+                memcpy(info->buf, seed, size);
+
+                if (ioctl(fd, RNDADDENTROPY, info) < 0)
+                        return -errno;
+        } else {
+                r = loop_write(fd, seed, size, false);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
 #endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/random-util.h b/shared/systemd/src/basic/random-util.h
index d8e067d9..7824ffac 100644
--- a/shared/systemd/src/basic/random-util.h
+++ b/shared/systemd/src/basic/random-util.h
@@ -38,3 +38,5 @@ int rdrand(unsigned long *ret);
 #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
 
 size_t random_pool_size(void);
+
+int random_write_entropy(int fd, const void *seed, size_t size, bool credit);
diff --git a/shared/systemd/src/basic/set.h b/shared/systemd/src/basic/set.h
index 621e83bf..7170eea8 100644
--- a/shared/systemd/src/basic/set.h
+++ b/shared/systemd/src/basic/set.h
@@ -13,22 +13,20 @@
                 0;                              \
         })
 
-Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
 #define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
 
-static inline Set *set_free(Set *s) {
+static inline Set* set_free(Set *s) {
         return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL);
 }
 
-static inline Set *set_free_free(Set *s) {
+static inline Set* set_free_free(Set *s) {
         return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL);
 }
 
 /* no set_free_free_free */
 
-static inline Set *set_copy(Set *s) {
-        return (Set*) _hashmap_copy(HASHMAP_BASE(s));
-}
+#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h)  HASHMAP_DEBUG_SRC_ARGS))
 
 int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
 #define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
@@ -36,7 +34,7 @@ int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG
 int set_put(Set *s, const void *key);
 /* no set_update */
 /* no set_replace */
-static inline void *set_get(const Set *s, void *key) {
+static inline void *set_get(const Set *s, const void *key) {
         return _hashmap_get(HASHMAP_BASE((Set *) s), key);
 }
 /* no set_get2 */
@@ -79,7 +77,9 @@ static inline unsigned set_buckets(const Set *s) {
         return _hashmap_buckets(HASHMAP_BASE((Set *) s));
 }
 
-bool set_iterate(const Set *s, Iterator *i, void **value);
+static inline bool set_iterate(const Set *s, Iterator *i, void **value) {
+        return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL);
+}
 
 static inline void set_clear(Set *s) {
         _hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
@@ -120,13 +120,25 @@ static inline char **set_get_strv(Set *s) {
         return _hashmap_get_strv(HASHMAP_BASE(s));
 }
 
+int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key  HASHMAP_DEBUG_PARAMS);
+#define set_ensure_put(s, hash_ops, key) _set_ensure_put(s, hash_ops, key  HASHMAP_DEBUG_SRC_ARGS)
+
+int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key  HASHMAP_DEBUG_PARAMS);
+#define set_ensure_consume(s, hash_ops, key) _set_ensure_consume(s, hash_ops, key  HASHMAP_DEBUG_SRC_ARGS)
+
 int set_consume(Set *s, void *value);
-int set_put_strdup(Set **s, const char *p);
-int set_put_strdupv(Set **s, char **l);
+
+int _set_put_strdup(Set **s, const char *p  HASHMAP_DEBUG_PARAMS);
+#define set_put_strdup(s, p) _set_put_strdup(s, p  HASHMAP_DEBUG_SRC_ARGS)
+int _set_put_strdupv(Set **s, char **l  HASHMAP_DEBUG_PARAMS);
+#define set_put_strdupv(s, l) _set_put_strdupv(s, l  HASHMAP_DEBUG_SRC_ARGS)
+
 int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags);
 
-#define SET_FOREACH(e, s, i) \
-        for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); )
+#define _SET_FOREACH(e, s, i) \
+        for (Iterator i = ITERATOR_FIRST; set_iterate((s), &i, (void**)&(e)); )
+#define SET_FOREACH(e, s) \
+        _SET_FOREACH(e, s, UNIQ_T(i, UNIQ))
 
 #define SET_FOREACH_MOVE(e, d, s)                                       \
         for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); )
diff --git a/shared/systemd/src/basic/siphash24.h b/shared/systemd/src/basic/siphash24.h
index c4e919df..474d9c9d 100644
--- a/shared/systemd/src/basic/siphash24.h
+++ b/shared/systemd/src/basic/siphash24.h
@@ -6,6 +6,8 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "time-util.h"
+
 #if 0 /* NM_IGNORED */
 struct siphash {
         uint64_t v0;
@@ -17,39 +19,55 @@ struct siphash {
 };
 #else /* NM_IGNORED */
 struct siphash {
-	CSipHash _csiphash;
+    CSipHash _csiphash;
 };
 
 static inline void
 siphash24_init (struct siphash *state, const uint8_t k[16])
 {
-	c_siphash_init ((CSipHash *) state, k);
+    c_siphash_init ((CSipHash *) state, k);
 }
 
 static inline void
 siphash24_compress (const void *in, size_t inlen, struct siphash *state)
 {
-	c_siphash_append ((CSipHash *) state, in, inlen);
+    c_siphash_append ((CSipHash *) state, in, inlen);
 }
 
 static inline uint64_t
 siphash24_finalize (struct siphash *state)
 {
-	return c_siphash_finalize ((CSipHash *) state);
+    return c_siphash_finalize ((CSipHash *) state);
 }
 
 static inline uint64_t
 siphash24 (const void *in, size_t inlen, const uint8_t k[16])
 {
-	return c_siphash_hash (k, in, inlen);
+    return c_siphash_hash (k, in, inlen);
 }
 #endif /* NM_IGNORED */
 
 void siphash24_init(struct siphash *state, const uint8_t k[static 16]);
 void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
-void siphash24_compress_boolean(bool in, struct siphash *state);
 #define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
 
+static inline void siphash24_compress_boolean(bool in, struct siphash *state) {
+        uint8_t i = in;
+
+        siphash24_compress(&i, sizeof i, state);
+}
+
+static inline void siphash24_compress_usec_t(usec_t in, struct siphash *state) {
+        siphash24_compress(&in, sizeof in, state);
+}
+
+static inline void siphash24_compress_string(const char *in, struct siphash *state) {
+        if (!in)
+                return;
+
+        siphash24_compress(in, strlen(in), state);
+}
+
 uint64_t siphash24_finalize(struct siphash *state);
 
 uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[static 16]);
diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c
index 9ced3a12..01dce3ab 100644
--- a/shared/systemd/src/basic/socket-util.c
+++ b/shared/systemd/src/basic/socket-util.c
@@ -30,6 +30,7 @@
 #include "macro.h"
 #include "memory-util.h"
 #include "missing_socket.h"
+#include "missing_network.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
@@ -73,7 +74,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
                 if (a->sockaddr.in.sin_port == 0)
                         return -EINVAL;
 
-                if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM))
+                if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM))
                         return -EINVAL;
 
                 return 0;
@@ -85,7 +86,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
                 if (a->sockaddr.in6.sin6_port == 0)
                         return -EINVAL;
 
-                if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM))
+                if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM))
                         return -EINVAL;
 
                 return 0;
@@ -110,7 +111,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
                                 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
                                         return -EINVAL;
                         } else {
-                                /* If there's no embedded NUL byte, then then the size needs to match the whole
+                                /* If there's no embedded NUL byte, then the size needs to match the whole
                                  * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful,
                                  * and considers both equivalent: getsockname() even extends sockaddr_un beyond its
                                  * size if the path is non NUL terminated.)*/
@@ -119,7 +120,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
                         }
                 }
 
-                if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET))
+                if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET))
                         return -EINVAL;
 
                 return 0;
@@ -129,7 +130,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
                 if (a->size != sizeof(struct sockaddr_nl))
                         return -EINVAL;
 
-                if (!IN_SET(a->type, SOCK_RAW, SOCK_DGRAM))
+                if (!IN_SET(a->type, 0, SOCK_RAW, SOCK_DGRAM))
                         return -EINVAL;
 
                 return 0;
@@ -138,7 +139,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
                 if (a->size != sizeof(struct sockaddr_vm))
                         return -EINVAL;
 
-                if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM))
+                if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM))
                         return -EINVAL;
 
                 return 0;
@@ -404,19 +405,23 @@ int sockaddr_pretty(
                         if (r < 0)
                                 return -ENOMEM;
                 } else {
-                        char a[INET6_ADDRSTRLEN];
+                        char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1];
 
                         inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
+                        if (sa->in6.sin6_scope_id != 0)
+                                format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX);
 
                         if (include_port) {
                                 r = asprintf(&p,
-                                             "[%s]:%u",
+                                             "[%s]:%u%s%s",
                                              a,
-                                             be16toh(sa->in6.sin6_port));
+                                             be16toh(sa->in6.sin6_port),
+                                             sa->in6.sin6_scope_id != 0 ? "%" : "",
+                                             sa->in6.sin6_scope_id != 0 ? ifname : "");
                                 if (r < 0)
                                         return -ENOMEM;
                         } else {
-                                p = strdup(a);
+                                p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a);
                                 if (!p)
                                         return -ENOMEM;
                         }
@@ -622,40 +627,64 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b
         return false;
 }
 
-int fd_inc_sndbuf(int fd, size_t n) {
+int fd_set_sndbuf(int fd, size_t n, bool increase) {
         int r, value;
         socklen_t l = sizeof(value);
 
+        if (n > INT_MAX)
+                return -ERANGE;
+
         r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
-        if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+        if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2)
                 return 0;
 
-        /* If we have the privileges we will ignore the kernel limit. */
+        /* First, try to set the buffer size with SO_SNDBUF. */
+        r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n);
+        if (r < 0)
+                return r;
 
-        if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n) < 0) {
-                r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n);
-                if (r < 0)
-                        return r;
-        }
+        /* SO_SNDBUF above may set to the kernel limit, instead of the requested size.
+         * So, we need to check the actual buffer size here. */
+        l = sizeof(value);
+        r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+        if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2)
+                return 1;
+
+        /* If we have the privileges we will ignore the kernel limit. */
+        r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n);
+        if (r < 0)
+                return r;
 
         return 1;
 }
 
-int fd_inc_rcvbuf(int fd, size_t n) {
+int fd_set_rcvbuf(int fd, size_t n, bool increase) {
         int r, value;
         socklen_t l = sizeof(value);
 
+        if (n > INT_MAX)
+                return -ERANGE;
+
         r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
-        if (r >= 0 && l == sizeof(value) && (size_t) value >= n*2)
+        if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2)
                 return 0;
 
-        /* If we have the privileges we will ignore the kernel limit. */
+        /* First, try to set the buffer size with SO_RCVBUF. */
+        r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n);
+        if (r < 0)
+                return r;
 
-        if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n) < 0) {
-                r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n);
-                if (r < 0)
-                        return r;
-        }
+        /* SO_RCVBUF above may set to the kernel limit, instead of the requested size.
+         * So, we need to check the actual buffer size here. */
+        l = sizeof(value);
+        r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+        if (r >= 0 && l == sizeof(value) && increase ? (size_t) value >= n*2 : (size_t) value == n*2)
+                return 1;
+
+        /* If we have the privileges we will ignore the kernel limit. */
+        r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n);
+        if (r < 0)
+                return r;
 
         return 1;
 }
@@ -669,17 +698,19 @@ static const char* const ip_tos_table[] = {
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
 
-bool ifname_valid_full(const char *p, bool alternative) {
+bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
         bool numeric = true;
 
         /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources
          * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We
          * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */
 
+        assert(!(flags & ~_IFNAME_VALID_ALL));
+
         if (isempty(p))
                 return false;
 
-        if (alternative) {
+        if (flags & IFNAME_VALID_ALTERNATIVE) {
                 if (strlen(p) >= ALTIFNAMSIZ)
                         return false;
         } else {
@@ -690,22 +721,27 @@ bool ifname_valid_full(const char *p, bool alternative) {
         if (dot_or_dot_dot(p))
                 return false;
 
-        while (*p) {
-                if ((unsigned char) *p >= 127U)
+        for (const char *t = p; *t; t++) {
+                if ((unsigned char) *t >= 127U)
                         return false;
 
-                if ((unsigned char) *p <= 32U)
+                if ((unsigned char) *t <= 32U)
                         return false;
 
-                if (IN_SET(*p, ':', '/'))
+                if (IN_SET(*t, ':', '/'))
                         return false;
 
-                numeric = numeric && (*p >= '0' && *p <= '9');
-                p++;
+                numeric = numeric && (*t >= '0' && *t <= '9');
         }
 
-        if (numeric)
-                return false;
+        if (numeric) {
+                if (!(flags & IFNAME_VALID_NUMERIC))
+                        return false;
+
+                /* Verify that the number is well-formatted and in range. */
+                if (parse_ifindex(p) < 0)
+                        return false;
+        }
 
         return true;
 }
@@ -1081,6 +1117,7 @@ int sockaddr_un_unlink(const struct sockaddr_un *sa) {
 
         return 1;
 }
+#endif /* NM_IGNORED */
 
 int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
         size_t l;
@@ -1094,12 +1131,10 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
          * reference paths in the abstract namespace that include NUL bytes in the name. */
 
         l = strlen(path);
-        if (l == 0)
+        if (l < 2)
                 return -EINVAL;
         if (!IN_SET(path[0], '/', '@'))
                 return -EINVAL;
-        if (path[1] == 0)
-                return -EINVAL;
 
         /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than
          * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket
@@ -1125,7 +1160,6 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
                 return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */
         }
 }
-#endif /* NM_IGNORED */
 
 int socket_bind_to_ifname(int fd, const char *ifname) {
         assert(fd >= 0);
@@ -1140,6 +1174,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
 
 int socket_bind_to_ifindex(int fd, int ifindex) {
         char ifname[IF_NAMESIZE + 1];
+        int r;
 
         assert(fd >= 0);
 
@@ -1151,10 +1186,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
                 return 0;
         }
 
-        if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0)
-                return 0;
-        if (errno != ENOPROTOOPT)
-                return -errno;
+        r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
+        if (r != -ENOPROTOOPT)
+                return r;
 
         /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
         if (!format_ifname(ifindex, ifname))
@@ -1183,13 +1217,28 @@ ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
         return n;
 }
 
-int socket_pass_pktinfo(int fd, bool b) {
+int socket_get_family(int fd, int *ret) {
         int af;
         socklen_t sl = sizeof(af);
 
         if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0)
                 return -errno;
 
+        if (sl != sizeof(af))
+                return -EINVAL;
+
+        return af;
+}
+
+int socket_set_recvpktinfo(int fd, int af, bool b) {
+        int r;
+
+        if (af == AF_UNSPEC) {
+                r = socket_get_family(fd, &af);
+                if (r < 0)
+                        return r;
+        }
+
         switch (af) {
 
         case AF_INET:
@@ -1205,3 +1254,142 @@ int socket_pass_pktinfo(int fd, bool b) {
                 return -EAFNOSUPPORT;
         }
 }
+
+int socket_set_recverr(int fd, int af, bool b) {
+        int r;
+
+        if (af == AF_UNSPEC) {
+                r = socket_get_family(fd, &af);
+                if (r < 0)
+                        return r;
+        }
+
+        switch (af) {
+
+        case AF_INET:
+                return setsockopt_int(fd, IPPROTO_IP, IP_RECVERR, b);
+
+        case AF_INET6:
+                return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVERR, b);
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
+
+int socket_set_recvttl(int fd, int af, bool b) {
+        int r;
+
+        if (af == AF_UNSPEC) {
+                r = socket_get_family(fd, &af);
+                if (r < 0)
+                        return r;
+        }
+
+        switch (af) {
+
+        case AF_INET:
+                return setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, b);
+
+        case AF_INET6:
+                return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, b);
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
+
+int socket_set_ttl(int fd, int af, int ttl) {
+        int r;
+
+        if (af == AF_UNSPEC) {
+                r = socket_get_family(fd, &af);
+                if (r < 0)
+                        return r;
+        }
+
+        switch (af) {
+
+        case AF_INET:
+                return setsockopt_int(fd, IPPROTO_IP, IP_TTL, ttl);
+
+        case AF_INET6:
+                return setsockopt_int(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, ttl);
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
+
+int socket_set_unicast_if(int fd, int af, int ifi) {
+        be32_t ifindex_be = htobe32(ifi);
+        int r;
+
+        if (af == AF_UNSPEC) {
+                r = socket_get_family(fd, &af);
+                if (r < 0)
+                        return r;
+        }
+
+        switch (af) {
+
+        case AF_INET:
+                if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0)
+                        return -errno;
+
+                return 0;
+
+        case AF_INET6:
+                if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0)
+                        return -errno;
+
+                return 0;
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
+
+int socket_set_freebind(int fd, int af, bool b) {
+        int r;
+
+        if (af == AF_UNSPEC) {
+                r = socket_get_family(fd, &af);
+                if (r < 0)
+                        return r;
+        }
+
+        switch (af) {
+
+        case AF_INET:
+                return setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, b);
+
+        case AF_INET6:
+                return setsockopt_int(fd, IPPROTO_IPV6, IPV6_FREEBIND, b);
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
+
+int socket_set_transparent(int fd, int af, bool b) {
+        int r;
+
+        if (af == AF_UNSPEC) {
+                r = socket_get_family(fd, &af);
+                if (r < 0)
+                        return r;
+        }
+
+        switch (af) {
+
+        case AF_INET:
+                return setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, b);
+
+        case AF_INET6:
+                return setsockopt_int(fd, IPPROTO_IPV6, IPV6_TRANSPARENT, b);
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h
index 5e5cf731..1ece9118 100644
--- a/shared/systemd/src/basic/socket-util.h
+++ b/shared/systemd/src/basic/socket-util.h
@@ -120,15 +120,26 @@ int netlink_family_from_string(const char *s) _pure_;
 
 bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b);
 
-int fd_inc_sndbuf(int fd, size_t n);
-int fd_inc_rcvbuf(int fd, size_t n);
+int fd_set_sndbuf(int fd, size_t n, bool increase);
+static inline int fd_inc_sndbuf(int fd, size_t n) {
+        return fd_set_sndbuf(fd, n, true);
+}
+int fd_set_rcvbuf(int fd, size_t n, bool increase);
+static inline int fd_inc_rcvbuf(int fd, size_t n) {
+        return fd_set_rcvbuf(fd, n, true);
+}
 
 int ip_tos_to_string_alloc(int i, char **s);
 int ip_tos_from_string(const char *s);
 
-bool ifname_valid_full(const char *p, bool alternative);
+typedef enum {
+      IFNAME_VALID_ALTERNATIVE = 1 << 0,
+      IFNAME_VALID_NUMERIC     = 1 << 1,
+      _IFNAME_VALID_ALL        = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
+} IfnameValidFlags;
+bool ifname_valid_full(const char *p, IfnameValidFlags flags);
 static inline bool ifname_valid(const char *p) {
-        return ifname_valid_full(p, false);
+        return ifname_valid_full(p, 0);
 }
 bool address_label_valid(const char *p);
 
@@ -207,6 +218,35 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
                          strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
         })
 
+#define SOCKADDR_LEN(sa)                                                \
+        ({                                                              \
+                const union sockaddr_union *__sa = &(sa);               \
+                size_t _len;                                            \
+                switch(__sa->sa.sa_family) {                            \
+                case AF_INET:                                           \
+                        _len = sizeof(struct sockaddr_in);              \
+                        break;                                          \
+                case AF_INET6:                                          \
+                        _len = sizeof(struct sockaddr_in6);             \
+                        break;                                          \
+                case AF_UNIX:                                           \
+                        _len = SOCKADDR_UN_LEN(__sa->un);               \
+                        break;                                          \
+                case AF_PACKET:                                         \
+                        _len = SOCKADDR_LL_LEN(__sa->ll);               \
+                        break;                                          \
+                case AF_NETLINK:                                        \
+                        _len = sizeof(struct sockaddr_nl);              \
+                        break;                                          \
+                case AF_VSOCK:                                          \
+                        _len = sizeof(struct sockaddr_vm);              \
+                        break;                                          \
+                default:                                                \
+                        assert_not_reached("invalid socket family");    \
+                }                                                       \
+                _len;                                                   \
+        })
+
 int socket_ioctl_fd(void);
 
 int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path);
@@ -223,4 +263,11 @@ int socket_bind_to_ifindex(int fd, int ifindex);
 
 ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags);
 
-int socket_pass_pktinfo(int fd, bool b);
+int socket_get_family(int fd, int *ret);
+int socket_set_recvpktinfo(int fd, int af, bool b);
+int socket_set_recverr(int fd, int af, bool b);
+int socket_set_recvttl(int fd, int af, bool b);
+int socket_set_ttl(int fd, int af, int ttl);
+int socket_set_unicast_if(int fd, int af, int ifi);
+int socket_set_freebind(int fd, int af, bool b);
+int socket_set_transparent(int fd, int af, bool b);
diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c
index 5c51e13a..6d393041 100644
--- a/shared/systemd/src/basic/stat-util.c
+++ b/shared/systemd/src/basic/stat-util.c
@@ -17,6 +17,7 @@
 #include "macro.h"
 #include "missing_fs.h"
 #include "missing_magic.h"
+#include "missing_syscall.h"
 #include "parse-util.h"
 #include "stat-util.h"
 #include "string-util.h"
@@ -420,4 +421,60 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
                 a->st_ino == b->st_ino &&
                 (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
 }
+
+int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
+        static bool avoid_statx = false;
+        struct stat st;
+
+        if (!avoid_statx) {
+                if (statx(dfd, path, flags, mask, sx) < 0) {
+                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM)
+                                return -errno;
+
+                        /* If statx() is not supported or if we see EPERM (which might indicate seccomp
+                         * filtering or so), let's do a fallback. Not that on EACCES we'll not fall back,
+                         * since that is likely an indication of fs access issues, which we should
+                         * propagate */
+                } else
+                        return 0;
+
+                avoid_statx = true;
+        }
+
+        /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
+         * OK to ignore */
+        if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW|
+                      AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0)
+                return -EOPNOTSUPP;
+
+        if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0)
+                return -errno;
+
+        *sx = (struct statx) {
+                .stx_mask = STATX_TYPE|STATX_MODE|
+                STATX_NLINK|STATX_UID|STATX_GID|
+                STATX_ATIME|STATX_MTIME|STATX_CTIME|
+                STATX_INO|STATX_SIZE|STATX_BLOCKS,
+                .stx_blksize = st.st_blksize,
+                .stx_nlink = st.st_nlink,
+                .stx_uid = st.st_uid,
+                .stx_gid = st.st_gid,
+                .stx_mode = st.st_mode,
+                .stx_ino = st.st_ino,
+                .stx_size = st.st_size,
+                .stx_blocks = st.st_blocks,
+                .stx_rdev_major = major(st.st_rdev),
+                .stx_rdev_minor = minor(st.st_rdev),
+                .stx_dev_major = major(st.st_dev),
+                .stx_dev_minor = minor(st.st_dev),
+                .stx_atime.tv_sec = st.st_atim.tv_sec,
+                .stx_atime.tv_nsec = st.st_atim.tv_nsec,
+                .stx_mtime.tv_sec = st.st_mtim.tv_sec,
+                .stx_mtime.tv_nsec = st.st_mtim.tv_nsec,
+                .stx_ctime.tv_sec = st.st_ctim.tv_sec,
+                .stx_ctime.tv_nsec = st.st_ctim.tv_nsec,
+        };
+
+        return 0;
+}
 #endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h
index 59aedcb7..26ecd635 100644
--- a/shared/systemd/src/basic/stat-util.h
+++ b/shared/systemd/src/basic/stat-util.h
@@ -10,6 +10,7 @@
 #include <sys/vfs.h>
 
 #include "macro.h"
+#include "missing_stat.h"
 
 int is_symlink(const char *path);
 int is_dir(const char *path, bool follow);
@@ -91,3 +92,24 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret
 int proc_mounted(void);
 
 bool stat_inode_unmodified(const struct stat *a, const struct stat *b);
+
+int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx);
+
+#if HAS_FEATURE_MEMORY_SANITIZER
+#  warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
+#  define STRUCT_STATX_DEFINE(var)              \
+        struct statx var = {}
+#  define STRUCT_NEW_STATX_DEFINE(var)          \
+        union {                                 \
+                struct statx sx;                \
+                struct new_statx nsx;           \
+        } var = {}
+#else
+#  define STRUCT_STATX_DEFINE(var)              \
+        struct statx var
+#  define STRUCT_NEW_STATX_DEFINE(var)          \
+        union {                                 \
+                struct statx sx;                \
+                struct new_statx nsx;           \
+        } var
+#endif
diff --git a/shared/systemd/src/basic/string-table.c b/shared/systemd/src/basic/string-table.c
index 14ae6308..46014b18 100644
--- a/shared/systemd/src/basic/string-table.c
+++ b/shared/systemd/src/basic/string-table.c
@@ -6,12 +6,10 @@
 #include "string-util.h"
 
 ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
-        size_t i;
-
         if (!key)
                 return -1;
 
-        for (i = 0; i < len; ++i)
+        for (size_t i = 0; i < len; ++i)
                 if (streq_ptr(table[i], key))
                         return (ssize_t) i;
 
diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c
index c30e88c5..aea13dcb 100644
--- a/shared/systemd/src/basic/string-util.c
+++ b/shared/systemd/src/basic/string-util.c
@@ -10,12 +10,14 @@
 
 #include "alloc-util.h"
 #include "escape.h"
+#include "extract-word.h"
 #include "fileio.h"
 #include "gunicode.h"
 #include "locale-util.h"
 #include "macro.h"
 #include "memory-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "terminal-util.h"
 #include "utf8.h"
 #include "util.h"
@@ -112,83 +114,6 @@ char* first_word(const char *s, const char *word) {
         return (char*) p;
 }
 
-static size_t strcspn_escaped(const char *s, const char *reject) {
-        bool escaped = false;
-        int n;
-
-        for (n = 0; s[n] != '\0'; n++) {
-                if (escaped)
-                        escaped = false;
-                else if (s[n] == '\\')
-                        escaped = true;
-                else if (strchr(reject, s[n]))
-                        break;
-        }
-
-        return n;
-}
-
-/* Split a string into words. */
-const char* split(
-                const char **state,
-                size_t *l,
-                const char *separator,
-                SplitFlags flags) {
-
-        const char *current;
-
-        assert(state);
-        assert(l);
-
-        if (!separator)
-                separator = WHITESPACE;
-
-        current = *state;
-
-        if (*current == '\0') /* already at the end? */
-                return NULL;
-
-        current += strspn(current, separator); /* skip leading separators */
-        if (*current == '\0') { /* at the end now? */
-                *state = current;
-                return NULL;
-        }
-
-        if (FLAGS_SET(flags, SPLIT_QUOTES)) {
-
-                if (strchr(QUOTES, *current)) {
-                        /* We are looking at a quote */
-                        *l = strcspn_escaped(current + 1, CHAR_TO_STR(*current));
-                        if (current[*l + 1] != *current ||
-                            (current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) {
-                                /* right quote missing or garbage at the end */
-                                if (FLAGS_SET(flags, SPLIT_RELAX)) {
-                                        *state = current + *l + 1 + (current[*l + 1] != '\0');
-                                        return current + 1;
-                                }
-                                *state = current;
-                                return NULL;
-                        }
-                        *state = current++ + *l + 2;
-
-                } else {
-                        /* We are looking at a something that is not a quote */
-                        *l = strcspn_escaped(current, separator);
-                        if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) {
-                                /* unfinished escape */
-                                *state = current;
-                                return NULL;
-                        }
-                        *state = current + *l;
-                }
-        } else {
-                *l = strcspn(current, separator);
-                *state = current + *l;
-        }
-
-        return current;
-}
-
 char *strnappend(const char *s, const char *suffix, size_t b) {
         size_t a;
         char *r;
@@ -1216,4 +1141,31 @@ int string_extract_line(const char *s, size_t i, char **ret) {
                 c++;
         }
 }
+
+int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) {
+        /* In the default mode with no separators specified, we split on whitespace and
+         * don't coalesce separators. */
+        const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
+
+        const char *found = NULL;
+
+        for (const char *p = string;;) {
+                _cleanup_free_ char *w = NULL;
+                int r;
+
+                r = extract_first_word(&p, &w, separators, flags);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                found = strv_find(words, w);
+                if (found)
+                        break;
+        }
+
+        if (ret_word)
+                *ret_word = found;
+        return !!found;
+}
 #endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h
index 09131455..cefbda35 100644
--- a/shared/systemd/src/basic/string-util.h
+++ b/shared/systemd/src/basic/string-util.h
@@ -108,24 +108,6 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_;
 
 char *first_word(const char *s, const char *word) _pure_;
 
-typedef enum SplitFlags {
-        SPLIT_QUOTES                     = 0x01 << 0,
-        SPLIT_RELAX                      = 0x01 << 1,
-} SplitFlags;
-
-/* Smelly. Do not use this anymore. Use extract_first_word() instead! */
-const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
-
-/* Similar, don't use this anymore */
-#define FOREACH_WORD(word, length, s, state)                            \
-        _FOREACH_WORD(word, length, s, WHITESPACE, 0, state)
-
-#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state)       \
-        _FOREACH_WORD(word, length, s, separator, 0, state)
-
-#define _FOREACH_WORD(word, length, s, separator, flags, state)         \
-        for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags)))
-
 char *strnappend(const char *s, const char *suffix, size_t length);
 
 char *strjoin_real(const char *x, ...) _sentinel_;
@@ -135,8 +117,8 @@ char *strjoin_real(const char *x, ...) _sentinel_;
         ({                                                              \
                 const char *_appendees_[] = { a, __VA_ARGS__ };         \
                 char *_d_, *_p_;                                        \
-                size_t _len_ = 0;                                          \
-                size_t _i_;                                           \
+                size_t _len_ = 0;                                       \
+                size_t _i_;                                             \
                 for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
                         _len_ += strlen(_appendees_[_i_]);              \
                 _p_ = _d_ = newa(char, _len_ + 1);                      \
@@ -152,7 +134,6 @@ char *delete_trailing_chars(char *s, const char *bad);
 char *truncate_nl(char *s);
 
 static inline char *skip_leading_chars(const char *s, const char *bad) {
-
         if (!s)
                 return NULL;
 
@@ -231,11 +212,9 @@ REENABLE_WARNING;
 
 /* Like startswith(), but operates on arbitrary memory blocks */
 static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
-        size_t n;
-
         assert(token);
 
-        n = strlen(token);
+        size_t n = strlen(token);
         if (sz < n)
                 return NULL;
 
@@ -251,20 +230,17 @@ static inline void *memory_startswith(const void *p, size_t sz, const char *toke
  * It works only for ASCII strings.
  */
 static inline void *memory_startswith_no_case(const void *p, size_t sz, const char *token) {
-        size_t n, i;
-
         assert(token);
 
-        n = strlen(token);
+        size_t n = strlen(token);
         if (sz < n)
                 return NULL;
 
         assert(p);
 
-        for (i = 0; i < n; i++) {
+        for (size_t i = 0; i < n; i++)
                 if (ascii_tolower(((char *)p)[i]) != ascii_tolower(token[i]))
                         return NULL;
-        }
 
         return (uint8_t*) p + n;
 }
@@ -286,3 +262,8 @@ char* string_erase(char *x);
 
 int string_truncate_lines(const char *s, size_t n_lines, char **ret);
 int string_extract_line(const char *s, size_t i, char **ret);
+
+int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word);
+static inline int string_contains_word(const char *string, const char *separators, const char *word) {
+        return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
+}
diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c
index 6f812139..30d3af46 100644
--- a/shared/systemd/src/basic/strv.c
+++ b/shared/systemd/src/basic/strv.c
@@ -260,44 +260,6 @@ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
 }
 #endif /* NM_IGNORED */
 
-char **strv_split_full(const char *s, const char *separator, SplitFlags flags) {
-        const char *word, *state;
-        size_t l;
-        size_t n, i;
-        char **r;
-
-        assert(s);
-
-        if (!separator)
-                separator = WHITESPACE;
-
-        s += strspn(s, separator);
-        if (isempty(s))
-                return new0(char*, 1);
-
-        n = 0;
-        _FOREACH_WORD(word, l, s, separator, flags, state)
-                n++;
-
-        r = new(char*, n+1);
-        if (!r)
-                return NULL;
-
-        i = 0;
-        _FOREACH_WORD(word, l, s, separator, flags, state) {
-                r[i] = strndup(word, l);
-                if (!r[i]) {
-                        strv_free(r);
-                        return NULL;
-                }
-
-                i++;
-        }
-
-        r[i] = NULL;
-        return r;
-}
-
 char **strv_split_newlines(const char *s) {
         char **l;
         size_t n;
@@ -321,8 +283,7 @@ char **strv_split_newlines(const char *s) {
         return l;
 }
 
-#if 0 /* NM_IGNORED */
-int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
+int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
         _cleanup_strv_free_ char **l = NULL;
         size_t n = 0, allocated = 0;
         int r;
@@ -357,9 +318,62 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
 
         return (int) n;
 }
+
+#if 0 /* NM_IGNORED */
+int strv_split_colon_pairs(char ***t, const char *s) {
+        _cleanup_strv_free_ char **l = NULL;
+        size_t n = 0, allocated = 0;
+        int r;
+
+        assert(t);
+        assert(s);
+
+        for (;;) {
+                _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
+
+                r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                const char *p = tuple;
+                r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
+                                       &first, &second, NULL);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+                /* Enforce that at most 2 colon-separated words are contained in each group */
+                if (!isempty(p))
+                        return -EINVAL;
+
+                second_or_empty = strdup(strempty(second));
+                if (!second_or_empty)
+                        return -ENOMEM;
+
+                if (!GREEDY_REALLOC(l, allocated, n + 3))
+                        return -ENOMEM;
+
+                l[n++] = TAKE_PTR(first);
+                l[n++] = TAKE_PTR(second_or_empty);
+
+                l[n] = NULL;
+        }
+
+        if (!l) {
+                l = new0(char*, 1);
+                if (!l)
+                        return -ENOMEM;
+        }
+
+        *t = TAKE_PTR(l);
+
+        return (int) n;
+}
 #endif /* NM_IGNORED */
 
-char *strv_join_prefix(char * const *l, const char *separator, const char *prefix) {
+char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
         char * const *s;
         char *r, *e;
         size_t n, k, m;
@@ -370,11 +384,17 @@ char *strv_join_prefix(char * const *l, const char *separator, const char *prefi
         k = strlen(separator);
         m = strlen_ptr(prefix);
 
+        if (unescape_separators) /* If there separator is multi-char, we won't know how to escape it. */
+                assert(k == 1);
+
         n = 0;
         STRV_FOREACH(s, l) {
                 if (s != l)
                         n += k;
-                n += m + strlen(*s);
+
+                bool needs_escaping = unescape_separators && strchr(*s, separator[0]);
+
+                n += m + strlen(*s) * (1 + needs_escaping);
         }
 
         r = new(char, n+1);
@@ -389,7 +409,16 @@ char *strv_join_prefix(char * const *l, const char *separator, const char *prefi
                 if (prefix)
                         e = stpcpy(e, prefix);
 
-                e = stpcpy(e, *s);
+                bool needs_escaping = unescape_separators && strchr(*s, separator[0]);
+
+                if (needs_escaping)
+                        for (size_t i = 0; (*s)[i]; i++) {
+                                if ((*s)[i] == separator[0])
+                                        *(e++) = '\\';
+                                *(e++) = (*s)[i];
+                        }
+                else
+                        e = stpcpy(e, *s);
         }
 
         *e = 0;
diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h
index 2ad927bc..919fabf7 100644
--- a/shared/systemd/src/basic/strv.h
+++ b/shared/systemd/src/basic/strv.h
@@ -72,17 +72,28 @@ static inline bool strv_isempty(char * const *l) {
         return !l || !*l;
 }
 
-char **strv_split_full(const char *s, const char *separator, SplitFlags flags);
-static inline char **strv_split(const char *s, const char *separator) {
-        return strv_split_full(s, separator, 0);
-}
 char **strv_split_newlines(const char *s);
 
-int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
+int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags);
+static inline char **strv_split(const char *s, const char *separators) {
+        char **ret;
+        int r;
+
+        r = strv_split_full(&ret, s, separators, 0);
+        if (r < 0)
+                return NULL;
+
+        return ret;
+}
 
-char *strv_join_prefix(char * const *l, const char *separator, const char *prefix);
+/* Given a string containing white-space separated tuples of words themselves separated by ':',
+ * returns a vector of strings. If the second element in a tuple is missing, the corresponding
+ * string in the vector is an empty string. */
+int strv_split_colon_pairs(char ***t, const char *s);
+
+char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor);
 static inline char *strv_join(char * const *l, const char *separator) {
-        return strv_join_prefix(l, separator, NULL);
+        return strv_join_full(l, separator, NULL, false);
 }
 
 char **strv_parse_nulstr(const char *s, size_t l);
@@ -118,10 +129,6 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
 char **strv_sort(char **l);
 void strv_print(char * const *l);
 
-#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
-
-#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
-
 #define strv_from_stdarg_alloca(first)                          \
         ({                                                      \
                 char **_l;                                      \
diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c
index 71af4f21..0fac79a7 100644
--- a/shared/systemd/src/basic/time-util.c
+++ b/shared/systemd/src/basic/time-util.c
@@ -25,6 +25,7 @@
 #include "path-util.h"
 #include "process-util.h"
 #include "stat-util.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
@@ -84,43 +85,82 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
         return ts;
 }
 
+static usec_t map_clock_usec_internal(usec_t from, usec_t from_base, usec_t to_base) {
+
+        /* Maps the time 'from' between two clocks, based on a common reference point where the first clock
+         * is at 'from_base' and the second clock at 'to_base'. Basically calculates:
+         *
+         *         from - from_base + to_base
+         *
+         * But takes care of overflows/underflows and avoids signed operations. */
+
+        if (from >= from_base) { /* In the future */
+                usec_t delta = from - from_base;
+
+                if (to_base >= USEC_INFINITY - delta) /* overflow? */
+                        return USEC_INFINITY;
+
+                return to_base + delta;
+
+        } else { /* In the past */
+                usec_t delta = from_base - from;
+
+                if (to_base <= delta) /* underflow? */
+                        return 0;
+
+                return to_base - delta;
+        }
+}
+
+usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
+
+        /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
+         * onto itself */
+        if (map_clock_id(from_clock) == map_clock_id(to_clock))
+                return from;
+
+        /* Keep infinity as is */
+        if (from == USEC_INFINITY)
+                return from;
+
+        return map_clock_usec_internal(from, now(from_clock), now(to_clock));
+}
+
 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
-        int64_t delta;
         assert(ts);
 
-        if (u == USEC_INFINITY || u <= 0) {
+        if (u == USEC_INFINITY || u == 0) {
                 ts->realtime = ts->monotonic = u;
                 return ts;
         }
 
         ts->realtime = u;
-
-        delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
-        ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
-
+        ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC);
         return ts;
 }
 
 triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
-        int64_t delta;
+        usec_t nowr;
 
         assert(ts);
 
-        if (u == USEC_INFINITY || u <= 0) {
+        if (u == USEC_INFINITY || u == 0) {
                 ts->realtime = ts->monotonic = ts->boottime = u;
                 return ts;
         }
 
+        nowr = now(CLOCK_REALTIME);
+
         ts->realtime = u;
-        delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
-        ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
-        ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
+        ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC));
+        ts->boottime = clock_boottime_supported() ?
+                map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) :
+                USEC_INFINITY;
 
         return ts;
 }
 
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
-        int64_t delta;
         assert(ts);
 
         if (u == USEC_INFINITY) {
@@ -129,25 +169,28 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
         }
 
         ts->monotonic = u;
-        delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
-        ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta);
-
+        ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME);
         return ts;
 }
 
 dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
-        int64_t delta;
+        clockid_t cid;
+        usec_t nowm;
 
         if (u == USEC_INFINITY) {
                 ts->realtime = ts->monotonic = USEC_INFINITY;
                 return ts;
         }
 
-        dual_timestamp_get(ts);
-        delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
-        ts->realtime = usec_sub_signed(ts->realtime, delta);
-        ts->monotonic = usec_sub_signed(ts->monotonic, delta);
+        cid = clock_boottime_or_monotonic();
+        nowm = now(cid);
+
+        if (cid == CLOCK_MONOTONIC)
+                ts->monotonic = u;
+        else
+                ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC));
 
+        ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME));
         return ts;
 }
 
@@ -203,12 +246,28 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
         if (u == USEC_INFINITY ||
             u / USEC_PER_SEC >= TIME_T_MAX) {
                 ts->tv_sec = (time_t) -1;
-                ts->tv_nsec = (long) -1;
+                ts->tv_nsec = -1L;
                 return ts;
         }
 
         ts->tv_sec = (time_t) (u / USEC_PER_SEC);
-        ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
+        ts->tv_nsec = (long) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
+
+        return ts;
+}
+
+struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n)  {
+        assert(ts);
+
+        if (n == NSEC_INFINITY ||
+            n / NSEC_PER_SEC >= TIME_T_MAX) {
+                ts->tv_sec = (time_t) -1;
+                ts->tv_nsec = -1L;
+                return ts;
+        }
+
+        ts->tv_sec = (time_t) (n / NSEC_PER_SEC);
+        ts->tv_nsec = (long) (n % NSEC_PER_SEC);
 
         return ts;
 }
@@ -243,12 +302,11 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) {
         return tv;
 }
 
-static char *format_timestamp_internal(
+char *format_timestamp_style(
                 char *buf,
                 size_t l,
                 usec_t t,
-                bool utc,
-                bool us) {
+                TimestampStyle style) {
 
         /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
          * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
@@ -265,9 +323,27 @@ static char *format_timestamp_internal(
         struct tm tm;
         time_t sec;
         size_t n;
+        bool utc = false, us = false;
 
         assert(buf);
 
+        switch (style) {
+                case TIMESTAMP_PRETTY:
+                        break;
+                case TIMESTAMP_US:
+                        us = true;
+                        break;
+                case TIMESTAMP_UTC:
+                        utc = true;
+                        break;
+                case TIMESTAMP_US_UTC:
+                        us = true;
+                        utc = true;
+                        break;
+                default:
+                        return NULL;
+        }
+
         if (l < (size_t) (3 +                  /* week day */
                           1 + 10 +             /* space and date */
                           1 + 8 +              /* space and time */
@@ -341,22 +417,6 @@ static char *format_timestamp_internal(
         return buf;
 }
 
-char *format_timestamp(char *buf, size_t l, usec_t t) {
-        return format_timestamp_internal(buf, l, t, false, false);
-}
-
-char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
-        return format_timestamp_internal(buf, l, t, true, false);
-}
-
-char *format_timestamp_us(char *buf, size_t l, usec_t t) {
-        return format_timestamp_internal(buf, l, t, false, true);
-}
-
-char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
-        return format_timestamp_internal(buf, l, t, true, true);
-}
-
 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
         const char *s;
         usec_t n, d;
@@ -1533,4 +1593,28 @@ int time_change_fd(void) {
 
         return -errno;
 }
+
+static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = {
+        [TIMESTAMP_PRETTY] = "pretty",
+        [TIMESTAMP_US] = "us",
+        [TIMESTAMP_UTC] = "utc",
+        [TIMESTAMP_US_UTC] = "us+utc",
+};
+
+/* Use the macro for enum → string to allow for aliases */
+_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle,);
+
+/* For the string → enum mapping we use the generic implementation, but also support two aliases */
+TimestampStyle timestamp_style_from_string(const char *s) {
+        TimestampStyle t;
+
+        t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
+        if (t >= 0)
+                return t;
+        if (streq_ptr(s, "µs"))
+                return TIMESTAMP_US;
+        if (streq_ptr(s, "µs+uts"))
+                return TIMESTAMP_US_UTC;
+        return t;
+}
 #endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/time-util.h b/shared/systemd/src/basic/time-util.h
index 4c371257..cecd5efa 100644
--- a/shared/systemd/src/basic/time-util.h
+++ b/shared/systemd/src/basic/time-util.h
@@ -29,8 +29,17 @@ typedef struct triple_timestamp {
         usec_t boottime;
 } triple_timestamp;
 
-#define USEC_INFINITY ((usec_t) -1)
-#define NSEC_INFINITY ((nsec_t) -1)
+typedef enum TimestampStyle {
+        TIMESTAMP_PRETTY,
+        TIMESTAMP_US,
+        TIMESTAMP_UTC,
+        TIMESTAMP_US_UTC,
+        _TIMESTAMP_STYLE_MAX,
+        _TIMESTAMP_STYLE_INVALID = -1,
+} TimestampStyle;
+
+#define USEC_INFINITY ((usec_t) UINT64_MAX)
+#define NSEC_INFINITY ((nsec_t) UINT64_MAX)
 
 #define MSEC_PER_SEC  1000ULL
 #define USEC_PER_SEC  ((usec_t) 1000000ULL)
@@ -67,6 +76,8 @@ typedef struct triple_timestamp {
 usec_t now(clockid_t clock);
 nsec_t now_nsec(clockid_t clock);
 
+usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock);
+
 dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
@@ -101,17 +112,19 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
 usec_t timespec_load(const struct timespec *ts) _pure_;
 nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
 struct timespec *timespec_store(struct timespec *ts, usec_t u);
+struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n);
 
 usec_t timeval_load(const struct timeval *tv) _pure_;
 struct timeval *timeval_store(struct timeval *tv, usec_t u);
 
-char *format_timestamp(char *buf, size_t l, usec_t t);
-char *format_timestamp_utc(char *buf, size_t l, usec_t t);
-char *format_timestamp_us(char *buf, size_t l, usec_t t);
-char *format_timestamp_us_utc(char *buf, size_t l, usec_t t);
+char *format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style);
 char *format_timestamp_relative(char *buf, size_t l, usec_t t);
 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy);
 
+static inline char *format_timestamp(char *buf, size_t l, usec_t t) {
+        return format_timestamp_style(buf, l, t, TIMESTAMP_PRETTY);
+}
+
 int parse_timestamp(const char *t, usec_t *usec);
 
 int parse_sec(const char *t, usec_t *usec);
@@ -183,3 +196,6 @@ static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) {
 #endif
 
 int time_change_fd(void);
+
+const char* timestamp_style_to_string(TimestampStyle t) _const_;
+TimestampStyle timestamp_style_from_string(const char *s) _pure_;
diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c
index 8bbd12b0..9b3621ba 100644
--- a/shared/systemd/src/basic/tmpfile-util.c
+++ b/shared/systemd/src/basic/tmpfile-util.c
@@ -262,7 +262,7 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
         assert((flags & O_EXCL) == 0);
 
         /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
-         * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
+         * which case "ret_path" will be returned as NULL. If not possible the temporary path name used is returned in
          * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
 
         fd = open_parent(target, O_TMPFILE|flags, 0640);
diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c
index f2e27b68..8159b187 100644
--- a/shared/systemd/src/basic/utf8.c
+++ b/shared/systemd/src/basic/utf8.c
@@ -125,7 +125,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
         return 0;
 }
 
-bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
+bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
         const char *p;
 
         assert(str);
@@ -142,7 +142,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
                 r = utf8_encoded_to_unichar(p, &val);
                 if (r < 0 ||
                     unichar_is_control(val) ||
-                    (!newline && val == '\n'))
+                    (!allow_newline && val == '\n'))
                         return false;
 
                 length -= encoded_len;
@@ -152,18 +152,22 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
         return true;
 }
 
-char *utf8_is_valid(const char *str) {
-        const char *p;
+char *utf8_is_valid_n(const char *str, size_t len_bytes) {
+        /* Check if the string is composed of valid utf8 characters. If length len_bytes is given, stop after
+         * len_bytes. Otherwise, stop at NUL. */
 
         assert(str);
 
-        p = str;
-        while (*p) {
+        for (const char *p = str; len_bytes != (size_t) -1 ? (size_t) (p - str) < len_bytes : *p != '\0'; ) {
                 int len;
 
-                len = utf8_encoded_valid_unichar(p, (size_t) -1);
-                if (len < 0)
-                        return NULL;
+                if (_unlikely_(*p == '\0') && len_bytes != (size_t) -1)
+                        return NULL; /* embedded NUL */
+
+                len = utf8_encoded_valid_unichar(p,
+                                                 len_bytes != (size_t) -1 ? len_bytes - (p - str) : (size_t) -1);
+                if (_unlikely_(len < 0))
+                        return NULL; /* invalid character */
 
                 p += len;
         }
diff --git a/shared/systemd/src/basic/utf8.h b/shared/systemd/src/basic/utf8.h
index 62e99b72..f315ea0f 100644
--- a/shared/systemd/src/basic/utf8.h
+++ b/shared/systemd/src/basic/utf8.h
@@ -14,11 +14,14 @@
 
 bool unichar_is_valid(char32_t c);
 
-char *utf8_is_valid(const char *s) _pure_;
+char *utf8_is_valid_n(const char *str, size_t len_bytes) _pure_;
+static inline char *utf8_is_valid(const char *s) {
+        return utf8_is_valid_n(s, (size_t) -1);
+}
 char *ascii_is_valid(const char *s) _pure_;
 char *ascii_is_valid_n(const char *str, size_t len);
 
-bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_;
+bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) _pure_;
 #define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true)
 
 char *utf8_escape_invalid(const char *s);
diff --git a/shared/systemd/src/shared/dns-domain.c b/shared/systemd/src/shared/dns-domain.c
index 9bba7359..6d60eb58 100644
--- a/shared/systemd/src/shared/dns-domain.c
+++ b/shared/systemd/src/shared/dns-domain.c
@@ -45,8 +45,9 @@ int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags f
                                 /* Trailing dash */
                                 return -EINVAL;
 
-                        if (*n == '.')
+                        if (n[0] == '.' && (n[1] != 0 || !FLAGS_SET(flags, DNS_LABEL_LEAVE_TRAILING_DOT)))
                                 n++;
+
                         break;
                 }
 
@@ -143,7 +144,7 @@ int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags f
                 return -EINVAL;
 
         /* More than one trailing dot? */
-        if (*n == '.')
+        if (n[0] == '.' && !FLAGS_SET(flags, DNS_LABEL_LEAVE_TRAILING_DOT))
                 return -EINVAL;
 
         if (sz >= 1 && d)
@@ -1383,4 +1384,20 @@ int dns_name_is_valid_or_address(const char *name) {
 
         return dns_name_is_valid(name);
 }
+
+int dns_name_dot_suffixed(const char *name) {
+        const char *p = name;
+        int r;
+
+        for (;;) {
+                if (streq(p, "."))
+                        return true;
+
+                r = dns_label_unescape(&p, NULL, DNS_LABEL_MAX, DNS_LABEL_LEAVE_TRAILING_DOT);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return false;
+        }
+}
 #endif /* NM_IGNORED */
diff --git a/shared/systemd/src/shared/dns-domain.h b/shared/systemd/src/shared/dns-domain.h
index 88b3eb11..e4e5b1b9 100644
--- a/shared/systemd/src/shared/dns-domain.h
+++ b/shared/systemd/src/shared/dns-domain.h
@@ -25,8 +25,9 @@
 #define DNS_N_LABELS_MAX 127
 
 typedef enum DNSLabelFlags {
-        DNS_LABEL_LDH        = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */
-        DNS_LABEL_NO_ESCAPES = 1 << 1, /* Do not treat backslashes specially */
+        DNS_LABEL_LDH                = 1 << 0, /* Follow the "LDH" rule — only letters, digits, and internal hyphens. */
+        DNS_LABEL_NO_ESCAPES         = 1 << 1, /* Do not treat backslashes specially */
+        DNS_LABEL_LEAVE_TRAILING_DOT = 1 << 2, /* Leave trailing dot in place */
 } DNSLabelFlags;
 
 int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags);
@@ -112,3 +113,5 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret);
 int dns_name_apply_idna(const char *name, char **ret);
 
 int dns_name_is_valid_or_address(const char *name);
+
+int dns_name_dot_suffixed(const char *name);