about summary refs log tree commit diff
path: root/src/libnm-systemd-shared
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2022-02-14 19:23:28 +0100
committerMichael Biebl <biebl@debian.org>2022-02-14 19:23:28 +0100
commit2f94dba7385fd0e0ef19a06eb4a2fcf6c43d7946 (patch)
treee2222f5577115985dd52044d2991253403cdd952 /src/libnm-systemd-shared
parent88c227d90a6b7b388c5c85d72802a0ca8f05ed5c (diff)
New upstream version 1.35.91 upstream/1.35.91
Diffstat (limited to 'src/libnm-systemd-shared')
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/filesystems.h3
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/hmac.h3
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h2
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/netif-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.h51
-rw-r--r--src/libnm-systemd-shared/src/basic/cgroup-util.h28
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c22
-rw-r--r--src/libnm-systemd-shared/src/basic/errno-util.h38
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c216
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h56
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c261
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.h15
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c24
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.h5
-rw-r--r--src/libnm-systemd-shared/src/basic/format-util.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c188
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/hostname-util.c3
-rw-r--r--src/libnm-systemd-shared/src/basic/hostname-util.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c125
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/inotify-util.c18
-rw-r--r--src/libnm-systemd-shared/src/basic/list.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/log.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h27
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_syscall.h66
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c38
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c156
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h47
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.c18
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.c20
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c60
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.h5
-rw-r--r--src/libnm-systemd-shared/src/basic/sort-util.h22
-rw-r--r--src/libnm-systemd-shared/src/basic/sparse-endian.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c51
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.c88
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h52
-rw-r--r--src/libnm-systemd-shared/src/basic/strxcpyx.c67
-rw-r--r--src/libnm-systemd-shared/src/basic/strxcpyx.h33
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c12
-rw-r--r--src/libnm-systemd-shared/src/basic/tmpfile-util.c25
-rw-r--r--src/libnm-systemd-shared/src/basic/umask-util.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.c39
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/util.c66
-rw-r--r--src/libnm-systemd-shared/src/basic/util.h46
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h72
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c8
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h4
-rw-r--r--src/libnm-systemd-shared/src/fundamental/type.h22
-rw-r--r--src/libnm-systemd-shared/src/fundamental/types-fundamental.h39
-rw-r--r--src/libnm-systemd-shared/src/shared/dns-domain.c4
-rw-r--r--src/libnm-systemd-shared/src/shared/dns-domain.h4
58 files changed, 1472 insertions, 768 deletions
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/filesystems.h b/src/libnm-systemd-shared/sd-adapt-shared/filesystems.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/libnm-systemd-shared/sd-adapt-shared/filesystems.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/hmac.h b/src/libnm-systemd-shared/sd-adapt-shared/hmac.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/libnm-systemd-shared/sd-adapt-shared/hmac.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h b/src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h
index 637892c2..a5dce206 100644
--- a/src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h
+++ b/src/libnm-systemd-shared/sd-adapt-shared/missing_magic.h
@@ -1,3 +1,5 @@
 #pragma once
 
 /* dummy header */
+
+#include <linux/magic.h>
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h b/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/libnm-systemd-shared/sd-adapt-shared/netif-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index e587fe79..65d51756 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -22,20 +22,25 @@ typedef void (*free_func_t)(void *p);
 
 #define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
 
+#define alloca_safe(n)                                                  \
+        ({                                                              \
+                size_t _nn_ = n;                                        \
+                assert(_nn_ <= ALLOCA_MAX);                             \
+                alloca(_nn_ == 0 ? 1 : _nn_);                           \
+        })                                                              \
+
 #define newa(t, n)                                                      \
         ({                                                              \
                 size_t _n_ = n;                                         \
                 assert(!size_multiply_overflow(sizeof(t), _n_));        \
-                assert(sizeof(t)*_n_ <= ALLOCA_MAX);                    \
-                (t*) alloca((sizeof(t)*_n_) ?: 1);                      \
+                (t*) alloca_safe(sizeof(t)*_n_);                        \
         })
 
 #define newa0(t, n)                                                     \
         ({                                                              \
                 size_t _n_ = n;                                         \
                 assert(!size_multiply_overflow(sizeof(t), _n_));        \
-                assert(sizeof(t)*_n_ <= ALLOCA_MAX);                    \
-                (t*) alloca0((sizeof(t)*_n_) ?: 1);                     \
+                (t*) alloca0((sizeof(t)*_n_));                          \
         })
 
 #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
@@ -44,12 +49,6 @@ typedef void (*free_func_t)(void *p);
 
 #define malloc0(n) (calloc(1, (n) ?: 1))
 
-#define mfree(memory)                           \
-        ({                                      \
-                free(memory);                   \
-                (typeof(memory)) NULL;          \
-        })
-
 #define free_and_replace(a, b)                  \
         ({                                      \
                 typeof(a)* _a = &(a);           \
@@ -67,8 +66,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
         ({                                      \
                 void *_q_;                      \
                 size_t _l_ = l;                 \
-                assert(_l_ <= ALLOCA_MAX);      \
-                _q_ = alloca(_l_ ?: 1);         \
+                _q_ = alloca_safe(_l_);         \
                 memcpy_safe(_q_, p, _l_);       \
         })
 
@@ -76,8 +74,7 @@ void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, s
         ({                                      \
                 void *_q_;                      \
                 size_t _l_ = l;                 \
-                assert(_l_ <= ALLOCA_MAX);      \
-                _q_ = alloca(_l_ + 1);          \
+                _q_ = alloca_safe(_l_ + 1);     \
                 ((uint8_t*) _q_)[_l_] = 0;      \
                 memcpy_safe(_q_, p, _l_);       \
         })
@@ -144,9 +141,8 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
         ({                                              \
                 char *_new_;                            \
                 size_t _len_ = n;                       \
-                assert(_len_ <= ALLOCA_MAX);            \
-                _new_ = alloca(_len_ ?: 1);             \
-                (void *) memset(_new_, 0, _len_);       \
+                _new_ = alloca_safe(_len_);             \
+                memset(_new_, 0, _len_);                \
         })
 
 /* It's not clear what alignment glibc/gcc alloca() guarantee, hence provide a guaranteed safe version */
@@ -155,8 +151,7 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
                 void *_ptr_;                                            \
                 size_t _mask_ = (align) - 1;                            \
                 size_t _size_ = size;                                   \
-                assert(_size_ <= ALLOCA_MAX);                           \
-                _ptr_ = alloca((_size_ + _mask_) ?: 1);                 \
+                _ptr_ = alloca_safe(_size_ + _mask_);                   \
                 (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_);         \
         })
 
@@ -165,7 +160,7 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
                 void *_new_;                                            \
                 size_t _xsize_ = (size);                                \
                 _new_ = alloca_align(_xsize_, (align));                 \
-                (void*)memset(_new_, 0, _xsize_);                       \
+                memset(_new_, 0, _xsize_);                              \
         })
 
 #if HAS_FEATURE_MEMORY_SANITIZER
@@ -193,3 +188,19 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
                 __builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
                 MALLOC_SIZEOF_SAFE(x)/sizeof((x)[0]),                   \
                 VOID_0))
+
+
+/* These are like strdupa()/strndupa(), but honour ALLOCA_MAX */
+#define strdupa_safe(s)                                                 \
+        ({                                                              \
+                const char *_t = (s);                                   \
+                (char*) memdupa_suffix0(_t, strlen(_t));                \
+        })
+
+#define strndupa_safe(s, n)                                             \
+        ({                                                              \
+                const char *_t = (s);                                   \
+                (char*) memdupa_suffix0(_t, strnlen(_t, (n)));          \
+        })
+
+#include "memory-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h
index 90ccd2c0..461c01b3 100644
--- a/src/libnm-systemd-shared/src/basic/cgroup-util.h
+++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h
@@ -2,6 +2,7 @@
 #pragma once
 
 #include <dirent.h>
+#include <fcntl.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -33,6 +34,9 @@ typedef enum CGroupController {
         CGROUP_CONTROLLER_BPF_FOREIGN,
         CGROUP_CONTROLLER_BPF_SOCKET_BIND,
         CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
+        /* The BPF hook implementing RestrictFileSystems= is not defined here.
+         * It's applied as late as possible in exec_child() so we don't block
+         * our own unit setup code. */
 
         _CGROUP_CONTROLLER_MAX,
         _CGROUP_CONTROLLER_INVALID = -EINVAL,
@@ -123,6 +127,20 @@ static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) {
             (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX);
 }
 
+/* Special values for the special {blkio,io}.bfq.weight attribute */
+#define CGROUP_BFQ_WEIGHT_INVALID UINT64_MAX
+#define CGROUP_BFQ_WEIGHT_MIN UINT64_C(1)
+#define CGROUP_BFQ_WEIGHT_MAX UINT64_C(1000)
+#define CGROUP_BFQ_WEIGHT_DEFAULT UINT64_C(100)
+
+/* Convert the normal io.weight value to io.bfq.weight */
+static inline uint64_t BFQ_WEIGHT(uint64_t io_weight) {
+        return
+            io_weight <= CGROUP_WEIGHT_DEFAULT ?
+            CGROUP_BFQ_WEIGHT_DEFAULT - (CGROUP_WEIGHT_DEFAULT - io_weight) * (CGROUP_BFQ_WEIGHT_DEFAULT - CGROUP_BFQ_WEIGHT_MIN) / (CGROUP_WEIGHT_DEFAULT - CGROUP_WEIGHT_MIN) :
+            CGROUP_BFQ_WEIGHT_DEFAULT + (io_weight - CGROUP_WEIGHT_DEFAULT) * (CGROUP_BFQ_WEIGHT_MAX - CGROUP_BFQ_WEIGHT_DEFAULT) / (CGROUP_WEIGHT_MAX - CGROUP_WEIGHT_DEFAULT);
+}
+
 /* Special values for the blkio.weight attribute */
 #define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX
 #define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10)
@@ -236,6 +254,7 @@ int cg_is_empty_recursive(const char *controller, const char *path);
 
 int cg_get_root_path(char **path);
 
+int cg_path_get_cgroupid(const char *path, uint64_t *ret);
 int cg_path_get_session(const char *path, char **session);
 int cg_path_get_owner_uid(const char *path, uid_t *uid);
 int cg_path_get_unit(const char *path, char **unit);
@@ -311,3 +330,12 @@ typedef enum ManagedOOMPreference {
 
 const char* managed_oom_preference_to_string(ManagedOOMPreference a) _const_;
 ManagedOOMPreference managed_oom_preference_from_string(const char *s) _pure_;
+
+/* The structure to pass to name_to_handle_at() on cgroupfs2 */
+typedef union {
+        struct file_handle file_handle;
+        uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
+} cg_file_handle;
+
+#define CG_FILE_HANDLE_INIT { .file_handle.handle_bytes = sizeof(uint64_t) }
+#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index 431862f6..f47ddefe 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -10,6 +10,7 @@
 
 #include "alloc-util.h"
 #include "env-util.h"
+#include "errno-util.h"
 #include "escape.h"
 #include "extract-word.h"
 #include "macro.h"
@@ -397,7 +398,7 @@ int strv_env_replace_consume(char ***l, char *p) {
                 return -EINVAL;
         }
 
-        name = strndupa(p, t - p);
+        name = strndupa_safe(p, t - p);
 
         STRV_FOREACH(f, *l)
                 if (env_entry_has_name(*f, name)) {
@@ -484,7 +485,7 @@ char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
         if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
                 const char *t;
 
-                t = strndupa(name, k);
+                t = strndupa_safe(name, k);
                 return getenv(t);
         };
 
@@ -791,15 +792,12 @@ int getenv_bool_secure(const char *p) {
 
 #if 0 /* NM_IGNORED */
 int set_unset_env(const char *name, const char *value, bool overwrite) {
-        int r;
+        assert(name);
 
         if (value)
-                r = setenv(name, value, overwrite);
-        else
-                r = unsetenv(name);
-        if (r < 0)
-                return -errno;
-        return 0;
+                return RET_NERRNO(setenv(name, value, overwrite));
+
+        return RET_NERRNO(unsetenv(name));
 }
 
 int putenv_dup(const char *assignment, bool override) {
@@ -809,12 +807,10 @@ int putenv_dup(const char *assignment, bool override) {
         if (!e)
                 return -EINVAL;
 
-        n = strndupa(assignment, e - assignment);
+        n = strndupa_safe(assignment, e - assignment);
 
         /* This is like putenv(), but uses setenv() so that our memory doesn't become part of environ[]. */
-        if (setenv(n, e + 1, override) < 0)
-                return -errno;
-        return 0;
+        return RET_NERRNO(setenv(n, e + 1, override));
 }
 
 int setenv_systemd_exec_pid(bool update_only) {
diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h
index 3f2d0af5..09abf0b7 100644
--- a/src/libnm-systemd-shared/src/basic/errno-util.h
+++ b/src/libnm-systemd-shared/src/basic/errno-util.h
@@ -13,7 +13,7 @@ static inline void _reset_errno_(int *saved_errno) {
         errno = *saved_errno;
 }
 
-#define PROTECT_ERRNO                                                   \
+#define PROTECT_ERRNO                           \
         _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
 
 #define UNPROTECT_ERRNO                         \
@@ -31,6 +31,29 @@ static inline int negative_errno(void) {
         return -errno;
 }
 
+static inline int RET_NERRNO(int ret) {
+
+        /* Helper to wrap system calls in to make them return negative errno errors. This brings system call
+         * error handling in sync with how we usually handle errors in our own code, i.e. with immediate
+         * returning of negative errno. Usage is like this:
+         *
+         *     …
+         *     r = RET_NERRNO(unlink(t));
+         *     …
+         *
+         * or
+         *
+         *     …
+         *     fd = RET_NERRNO(open("/etc/fstab", O_RDONLY|O_CLOEXEC));
+         *     …
+         */
+
+        if (ret < 0)
+                return negative_errno();
+
+        return ret;
+}
+
 static inline const char *strerror_safe(int error) {
         /* 'safe' here does NOT mean thread safety. */
         return strerror(abs(error)); /* lgtm [cpp/potentially-dangerous-function] */
@@ -47,6 +70,13 @@ static inline int errno_or_else(int fallback) {
         return -abs(fallback);
 }
 
+/* For send()/recv() or read()/write(). */
+static inline bool ERRNO_IS_TRANSIENT(int r) {
+        return IN_SET(abs(r),
+                      EAGAIN,
+                      EINTR);
+}
+
 /* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
  *
  * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases.  See the
@@ -77,10 +107,8 @@ static inline bool ERRNO_IS_DISCONNECT(int r) {
  * the accept(2) man page. */
 static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
         return ERRNO_IS_DISCONNECT(r) ||
-                IN_SET(abs(r),
-                       EAGAIN,
-                       EINTR,
-                       EOPNOTSUPP);
+                ERRNO_IS_TRANSIENT(r) ||
+                abs(r) == EOPNOTSUPP;
 }
 
 /* Resource exhaustion, could be our fault or general system trouble */
diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c
index 93288563..4dfb9a16 100644
--- a/src/libnm-systemd-shared/src/basic/escape.c
+++ b/src/libnm-systemd-shared/src/basic/escape.c
@@ -547,7 +547,7 @@ char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
         return str_realloc(buf);
 }
 
-char* quote_command_line(char **argv) {
+char* quote_command_line(char **argv, ShellEscapeFlags flags) {
         _cleanup_free_ char *result = NULL;
 
         assert(argv);
@@ -556,7 +556,7 @@ char* quote_command_line(char **argv) {
         STRV_FOREACH(a, argv) {
                 _cleanup_free_ char *t = NULL;
 
-                t = shell_maybe_quote(*a, SHELL_ESCAPE_EMPTY);
+                t = shell_maybe_quote(*a, flags);
                 if (!t)
                         return NULL;
 
@@ -564,6 +564,6 @@ char* quote_command_line(char **argv) {
                         return NULL;
         }
 
-        return TAKE_PTR(result);
+        return str_realloc(TAKE_PTR(result));
 }
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h
index d490510d..318da6f2 100644
--- a/src/libnm-systemd-shared/src/basic/escape.h
+++ b/src/libnm-systemd-shared/src/basic/escape.h
@@ -69,4 +69,4 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
 
 char* shell_escape(const char *s, const char *bad);
 char* shell_maybe_quote(const char *s, ShellEscapeFlags flags);
-char* quote_command_line(char **argv);
+char* quote_command_line(char **argv, ShellEscapeFlags flags);
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
index c73dcff1..4504b6be 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -9,20 +9,29 @@
 #include <sys/types.h>
 
 #include "ether-addr-util.h"
+#include "hexdecoct.h"
 #include "macro.h"
 #include "string-util.h"
 
-char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]) {
+char *hw_addr_to_string_full(
+                const struct hw_addr_data *addr,
+                HardwareAddressToStringFlags flags,
+                char buffer[static HW_ADDR_TO_STRING_MAX]) {
+
         assert(addr);
         assert(buffer);
         assert(addr->length <= HW_ADDR_MAX_SIZE);
 
-        for (size_t i = 0; i < addr->length; i++) {
-                sprintf(&buffer[3*i], "%02"PRIx8, addr->bytes[i]);
-                if (i < addr->length - 1)
-                        buffer[3*i + 2] = ':';
+        for (size_t i = 0, j = 0; i < addr->length; i++) {
+                buffer[j++] = hexchar(addr->bytes[i] >> 4);
+                buffer[j++] = hexchar(addr->bytes[i] & 0x0f);
+                if (!FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON))
+                        buffer[j++] = ':';
         }
 
+        buffer[addr->length == 0 || FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON) ?
+               addr->length * 2 :
+               addr->length * 3 - 1] = '\0';
         return buffer;
 }
 
@@ -39,7 +48,7 @@ int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b)
         return memcmp(a->bytes, b->bytes, a->length);
 }
 
-static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
+void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
         assert(p);
         assert(state);
 
@@ -48,6 +57,7 @@ static void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *stat
 }
 
 DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free);
 
 char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
         assert(addr);
@@ -93,75 +103,163 @@ static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *sta
 }
 
 DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free);
 
-int ether_addr_from_string(const char *s, struct ether_addr *ret) {
-        size_t pos = 0, n, field;
-        char sep = '\0';
-        const char *hex = HEXDIGITS, *hexoff;
-        size_t x;
-        bool touched;
-
-#define parse_fields(v)                                         \
-        for (field = 0; field < ELEMENTSOF(v); field++) {       \
-                touched = false;                                \
-                for (n = 0; n < (2 * sizeof(v[0])); n++) {      \
-                        if (s[pos] == '\0')                     \
-                                break;                          \
-                        hexoff = strchr(hex, s[pos]);           \
-                        if (!hexoff)                            \
-                                break;                          \
-                        assert(hexoff >= hex);                  \
-                        x = hexoff - hex;                       \
-                        if (x >= 16)                            \
-                                x -= 6; /* A-F */               \
-                        assert(x < 16);                         \
-                        touched = true;                         \
-                        v[field] <<= 4;                         \
-                        v[field] += x;                          \
-                        pos++;                                  \
-                }                                               \
-                if (!touched)                                   \
-                        return -EINVAL;                         \
-                if (field < (ELEMENTSOF(v)-1)) {                \
-                        if (s[pos] != sep)                      \
-                                return -EINVAL;                 \
-                        else                                    \
-                                pos++;                          \
-                }                                               \
-        }
+static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) {
+        const char *hex = HEXDIGITS, *p;
+        uint16_t data = 0;
+        bool cont;
 
         assert(s);
-        assert(ret);
+        assert(*s);
+        assert(IN_SET(len, 1, 2));
+        assert(buf);
 
-        s += strspn(s, WHITESPACE);
-        sep = s[strspn(s, hex)];
+        p = *s;
 
-        if (sep == '.') {
-                uint16_t shorts[3] = { 0 };
+        for (size_t i = 0; i < len * 2; i++) {
+                const char *hexoff;
+                size_t x;
 
-                parse_fields(shorts);
+                if (*p == '\0' || *p == sep) {
+                        if (i == 0)
+                                return -EINVAL;
+                        break;
+                }
 
-                if (s[pos] != '\0')
+                hexoff = strchr(hex, *p);
+                if (!hexoff)
                         return -EINVAL;
 
-                for (n = 0; n < ELEMENTSOF(shorts); n++) {
-                        ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8);
-                        ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff);
+                assert(hexoff >= hex);
+                x = hexoff - hex;
+                if (x >= 16)
+                        x -= 6; /* A-F */
+
+                assert(x < 16);
+                data <<= 4;
+                data += x;
+
+                p++;
+        }
+
+        if (*p != '\0' && *p != sep)
+                return -EINVAL;
+
+        switch (len) {
+        case 1:
+                buf[0] = data;
+                break;
+        case 2:
+                buf[0] = (data & 0xff00) >> 8;
+                buf[1] = data & 0xff;
+                break;
+        default:
+                assert_not_reached();
+        }
+
+        cont = *p == sep;
+        *s = p + cont;
+        return cont;
+}
+
+int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) {
+        size_t field_size, max_len, len = 0;
+        uint8_t bytes[HW_ADDR_MAX_SIZE];
+        char sep;
+        int r;
+
+        assert(s);
+        assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX);
+        assert(ret);
+
+        /* This accepts the following formats:
+         *
+         * Dot separated 2 bytes format: xxyy.zzaa.bbcc
+         * Colon separated 1 bytes format: xx:yy:zz:aa:bb:cc
+         * Hyphen separated 1 bytes format: xx-yy-zz-aa-bb-cc
+         *
+         * Moreover, if expected_len == 0, 4, or 16, this also accepts:
+         *
+         * IPv4 format: used by IPv4 tunnel, e.g. ipgre
+         * IPv6 format: used by IPv6 tunnel, e.g. ip6gre
+         *
+         * The expected_len argument controls the length of acceptable addresses:
+         *
+         * 0: accepts 4 (AF_INET), 16 (AF_INET6), 6 (ETH_ALEN), or 20 (INFINIBAND_ALEN).
+         * SIZE_MAX: accepts arbitrary length, but at least one separator must be included.
+         * Otherwise: accepts addresses with matching length.
+         */
+
+        if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) {
+                union in_addr_union a;
+                int family;
+
+                if (expected_len == 0)
+                        r = in_addr_from_string_auto(s, &family, &a);
+                else {
+                        family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
+                        r = in_addr_from_string(family, s, &a);
+                }
+                if (r >= 0) {
+                        ret->length = FAMILY_ADDRESS_SIZE(family);
+                        memcpy(ret->bytes, a.bytes, ret->length);
+                        return 0;
                 }
+        }
 
-        } else if (IN_SET(sep, ':', '-')) {
-                struct ether_addr out = ETHER_ADDR_NULL;
+        max_len =
+                expected_len == 0 ? INFINIBAND_ALEN :
+                expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len;
+        sep = s[strspn(s, HEXDIGITS)];
 
-                parse_fields(out.ether_addr_octet);
+        if (sep == '.')
+                field_size = 2;
+        else if (IN_SET(sep, ':', '-'))
+                field_size = 1;
+        else
+                return -EINVAL;
 
-                if (s[pos] != '\0')
-                        return -EINVAL;
+        if (max_len % field_size != 0)
+                return -EINVAL;
 
-                for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++)
-                        ret->ether_addr_octet[n] = out.ether_addr_octet[n];
+        for (size_t i = 0; i < max_len / field_size; i++) {
+                r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size);
+                if (r < 0)
+                        return r;
+                if (r == 0) {
+                        len = (i + 1) * field_size;
+                        break;
+                }
+        }
 
-        } else
+        if (len == 0)
                 return -EINVAL;
 
+        if (expected_len == 0) {
+                if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN))
+                        return -EINVAL;
+        } else if (expected_len != SIZE_MAX) {
+                if (len != expected_len)
+                        return -EINVAL;
+        }
+
+        ret->length = len;
+        memcpy(ret->bytes, bytes, ret->length);
+        return 0;
+}
+
+int parse_ether_addr(const char *s, struct ether_addr *ret) {
+        struct hw_addr_data a;
+        int r;
+
+        assert(s);
+        assert(ret);
+
+        r = parse_hw_addr_full(s, ETH_ALEN, &a);
+        if (r < 0)
+                return r;
+
+        *ret = a.ether;
         return 0;
 }
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
index 794fc55b..32f45fe8 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -6,6 +6,9 @@
 #include <stdbool.h>
 
 #include "hash-funcs.h"
+#include "in-addr-util.h"
+#include "macro.h"
+#include "memory-util.h"
 
 /* This is MAX_ADDR_LEN as defined in linux/netdevice.h, but net/if_arp.h
  * defines a macro of the same name with a much lower size. */
@@ -16,29 +19,51 @@ struct hw_addr_data {
         union {
                 struct ether_addr ether;
                 uint8_t infiniband[INFINIBAND_ALEN];
+                struct in_addr in;
+                struct in6_addr in6;
                 uint8_t bytes[HW_ADDR_MAX_SIZE];
         };
 };
 
+int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret);
+static inline int parse_hw_addr(const char *s, struct hw_addr_data *ret) {
+        return parse_hw_addr_full(s, 0, ret);
+}
+int parse_ether_addr(const char *s, struct ether_addr *ret);
+
+typedef enum HardwareAddressToStringFlags {
+        HW_ADDR_TO_STRING_NO_COLON = 1 << 0,
+} HardwareAddressToStringFlags;
+
 #define HW_ADDR_TO_STRING_MAX (3*HW_ADDR_MAX_SIZE)
-char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_STRING_MAX]);
+char *hw_addr_to_string_full(
+                const struct hw_addr_data *addr,
+                HardwareAddressToStringFlags flags,
+                char buffer[static HW_ADDR_TO_STRING_MAX]);
+static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buffer[static HW_ADDR_TO_STRING_MAX]) {
+        return hw_addr_to_string_full(addr, 0, buffer);
+}
 
 /* Note: the lifetime of the compound literal is the immediately surrounding block,
  * see C11 §6.5.2.5, and
  * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
-#define HW_ADDR_TO_STR(hw_addr) hw_addr_to_string((hw_addr), (char[HW_ADDR_TO_STRING_MAX]){})
+#define HW_ADDR_TO_STR_FULL(hw_addr, flags) hw_addr_to_string_full((hw_addr), flags, (char[HW_ADDR_TO_STRING_MAX]){})
+#define HW_ADDR_TO_STR(hw_addr) HW_ADDR_TO_STR_FULL(hw_addr, 0)
 
 #define HW_ADDR_NULL ((const struct hw_addr_data){})
 
+void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state);
 int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
 static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
         return hw_addr_compare(a, b) == 0;
 }
 static inline bool hw_addr_is_null(const struct hw_addr_data *addr) {
-        return hw_addr_equal(addr, &HW_ADDR_NULL);
+        assert(addr);
+        return addr->length == 0 || memeqzero(addr->bytes, addr->length);
 }
 
 extern const struct hash_ops hw_addr_hash_ops;
+extern const struct hash_ops hw_addr_hash_ops_free;
 
 #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X"
 #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5]
@@ -60,6 +85,29 @@ static inline bool ether_addr_is_null(const struct ether_addr *addr) {
         return ether_addr_equal(addr, &ETHER_ADDR_NULL);
 }
 
-int ether_addr_from_string(const char *s, struct ether_addr *ret);
+static inline bool ether_addr_is_broadcast(const struct ether_addr *addr) {
+        assert(addr);
+        return memeqbyte(0xff, addr->ether_addr_octet, ETH_ALEN);
+}
+
+static inline bool ether_addr_is_multicast(const struct ether_addr *addr) {
+        assert(addr);
+        return FLAGS_SET(addr->ether_addr_octet[0], 0x01);
+}
+
+static inline bool ether_addr_is_unicast(const struct ether_addr *addr) {
+        return !ether_addr_is_multicast(addr);
+}
+
+static inline bool ether_addr_is_local(const struct ether_addr *addr) {
+        /* Determine if the Ethernet address is locally-assigned one (IEEE 802) */
+        assert(addr);
+        return FLAGS_SET(addr->ether_addr_octet[0], 0x02);
+}
+
+static inline bool ether_addr_is_global(const struct ether_addr *addr) {
+        return !ether_addr_is_local(addr);
+}
 
 extern const struct hash_ops ether_addr_hash_ops;
+extern const struct hash_ops ether_addr_hash_ops_free;
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index ee2a4caf..c3df68de 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -154,10 +154,7 @@ int fd_nonblock(int fd, bool nonblock) {
         if (nflags == flags)
                 return 0;
 
-        if (fcntl(fd, F_SETFL, nflags) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(fcntl(fd, F_SETFL, nflags));
 }
 
 int fd_cloexec(int fd, bool cloexec) {
@@ -173,10 +170,7 @@ int fd_cloexec(int fd, bool cloexec) {
         if (nflags == flags)
                 return 0;
 
-        if (fcntl(fd, F_SETFD, nflags) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(fcntl(fd, F_SETFD, nflags));
 }
 
 #if 0 /* NM_IGNORED */
@@ -190,7 +184,7 @@ _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
         return false;
 }
 
-static int get_max_fd(void) {
+int get_max_fd(void) {
         struct rlimit rl;
         rlim_t m;
 
@@ -211,37 +205,147 @@ static int get_max_fd(void) {
         return (int) (m - 1);
 }
 
-int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
-        static bool have_close_range = true; /* Assume we live in the future */
+static int close_all_fds_frugal(const int except[], size_t n_except) {
+        int max_fd, r = 0;
+
+        assert(n_except == 0 || except);
+
+        /* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
+         * and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
+         * but when we assume we are called from signal handler context, then use this simpler call
+         * instead. */
+
+        max_fd = get_max_fd();
+        if (max_fd < 0)
+                return max_fd;
+
+        /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
+         * spin the CPU for a long time. */
+        if (max_fd > MAX_FD_LOOP_LIMIT)
+                return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
+                                       "Refusing to loop over %d potential fds.",
+                                       max_fd);
+
+        for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
+                int q;
+
+                if (fd_in_set(fd, except, n_except))
+                        continue;
+
+                q = close_nointr(fd);
+                if (q < 0 && q != -EBADF && r >= 0)
+                        r = q;
+        }
+
+        return r;
+}
+
+static bool have_close_range = true; /* Assume we live in the future */
+
+static int close_all_fds_special_case(const int except[], size_t n_except) {
+        assert(n_except == 0 || except);
+
+        /* Handles a few common special cases separately, since they are common and can be optimized really
+         * nicely, since we won't need sorting for them. Returns > 0 if the special casing worked, 0
+         * otherwise. */
+
+        if (!have_close_range)
+                return 0;
+
+        switch (n_except) {
+
+        case 0:
+                /* Close everything. Yay! */
+
+                if (close_range(3, -1, 0) >= 0)
+                        return 1;
+
+                if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
+                        have_close_range = false;
+                        return 0;
+                }
+
+                return -errno;
+
+        case 1:
+                /* Close all but exactly one, then we don't need no sorting. This is a pretty common
+                 * case, hence let's handle it specially. */
+
+                if ((except[0] <= 3 || close_range(3, except[0]-1, 0) >= 0) &&
+                    (except[0] >= INT_MAX || close_range(MAX(3, except[0]+1), -1, 0) >= 0))
+                        return 1;
+
+                if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
+                        have_close_range = false;
+                        return 0;
+                }
+
+                return -errno;
+
+        default:
+                return 0;
+        }
+}
+
+int close_all_fds_without_malloc(const int except[], size_t n_except) {
+        int r;
+
+        assert(n_except == 0 || except);
+
+        r = close_all_fds_special_case(except, n_except);
+        if (r < 0)
+                return r;
+        if (r > 0) /* special case worked! */
+                return 0;
+
+        return close_all_fds_frugal(except, n_except);
+}
+
+int close_all_fds(const int except[], size_t n_except) {
         _cleanup_closedir_ DIR *d = NULL;
         int r = 0;
 
         assert(n_except == 0 || except);
 
+        r = close_all_fds_special_case(except, n_except);
+        if (r < 0)
+                return r;
+        if (r > 0) /* special case worked! */
+                return 0;
+
         if (have_close_range) {
+                _cleanup_free_ int *sorted_malloc = NULL;
+                size_t n_sorted;
+                int *sorted;
+
                 /* In the best case we have close_range() to close all fds between a start and an end fd,
                  * which we can use on the "inverted" exception array, i.e. all intervals between all
                  * adjacent pairs from the sorted exception array. This changes loop complexity from O(n)
                  * where n is number of open fds to O(m⋅log(m)) where m is the number of fds to keep
                  * open. Given that we assume n ≫ m that's preferable to us. */
 
-                if (n_except == 0) {
-                        /* Close everything. Yay! */
+                assert(n_except < SIZE_MAX);
+                n_sorted = n_except + 1;
 
-                        if (close_range(3, -1, 0) >= 0)
-                                return 0;
+                if (n_sorted > 64) /* Use heap for large numbers of fds, stack otherwise */
+                        sorted = sorted_malloc = new(int, n_sorted);
+                else
+                        sorted = newa(int, n_sorted);
 
-                        if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
-                                have_close_range = false;
-                        else
-                                return -errno;
+                if (sorted) {
+                        memcpy(sorted, except, n_except * sizeof(int));
 
-                } else {
-                        typesafe_qsort(except, n_except, cmp_int);
+                        /* Let's add fd 2 to the list of fds, to simplify the loop below, as this
+                         * allows us to cover the head of the array the same way as the body */
+                        sorted[n_sorted-1] = 2;
 
-                        for (size_t i = 0; i < n_except; i++) {
-                                int start = i == 0 ? 2 : MAX(except[i-1], 2); /* The first three fds shall always remain open */
-                                int end = MAX(except[i], 2);
+                        typesafe_qsort(sorted, n_sorted, cmp_int);
+
+                        for (size_t i = 0; i < n_sorted-1; i++) {
+                                int start, end;
+
+                                start = MAX(sorted[i], 2); /* The first three fds shall always remain open */
+                                end = MAX(sorted[i+1], 2);
 
                                 assert(end >= start);
 
@@ -250,83 +354,58 @@ int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) {
 
                                 /* Close everything between the start and end fds (both of which shall stay open) */
                                 if (close_range(start + 1, end - 1, 0) < 0) {
-                                        if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
-                                                have_close_range = false;
-                                        else
+                                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
                                                 return -errno;
-                                        goto opendir_fallback;
+
+                                        have_close_range = false;
+                                        break;
                                 }
                         }
 
-                        /* The loop succeeded. Let's now close everything beyond the end */
+                        if (have_close_range) {
+                                /* The loop succeeded. Let's now close everything beyond the end */
 
-                        if (except[n_except-1] >= INT_MAX) /* Don't let the addition below overflow */
-                                return 0;
+                                if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
+                                        return 0;
 
-                        int start = MAX(except[n_except-1], 2);
+                                if (close_range(sorted[n_sorted-1] + 1, -1, 0) >= 0)
+                                        return 0;
 
-                        if (close_range(start + 1, -1, 0) >= 0)
-                                return 0;
+                                if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
+                                        return -errno;
 
-                        if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno))
                                 have_close_range = false;
-                        else
-                                return -errno;
+                        }
                 }
-        }
-
-        /* Fallback for when close_range() is not supported */
- opendir_fallback:
-        d = allow_alloc ? opendir("/proc/self/fd") : NULL;
-        if (d) {
-                struct dirent *de;
-
-                FOREACH_DIRENT(de, d, return -errno) {
-                        int fd = -1, q;
-
-                        if (safe_atoi(de->d_name, &fd) < 0)
-                                /* Let's better ignore this, just in case */
-                                continue;
 
-                        if (fd < 3)
-                                continue;
-
-                        if (fd == dirfd(d))
-                                continue;
+                /* Fallback on OOM or if close_range() is not supported */
+        }
 
-                        if (fd_in_set(fd, except, n_except))
-                                continue;
+        d = opendir("/proc/self/fd");
+        if (!d)
+                return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */
 
-                        q = close_nointr(fd);
-                        if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
-                                r = q;
-                }
+        FOREACH_DIRENT(de, d, return -errno) {
+                int fd = -1, q;
 
-                return r;
-        }
-
-        /* Fallback for when /proc isn't available (for example in chroots) or when we cannot allocate by
-         * brute-forcing through the file descriptor table. */
+                if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+                        continue;
 
-        int max_fd = get_max_fd();
-        if (max_fd < 0)
-                return max_fd;
+                if (safe_atoi(de->d_name, &fd) < 0)
+                        /* Let's better ignore this, just in case */
+                        continue;
 
-        /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
-         * spin the CPU for a long time. */
-        if (max_fd > MAX_FD_LOOP_LIMIT)
-                return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
-                                       "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
-                                       max_fd);
+                if (fd < 3)
+                        continue;
 
-        for (int fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
-                int q;
+                if (fd == dirfd(d))
+                        continue;
 
                 if (fd_in_set(fd, except, n_except))
                         continue;
 
                 q = close_nointr(fd);
-                if (q < 0 && q != -EBADF && r >= 0)
+                if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
                         r = q;
         }
 
@@ -652,7 +731,7 @@ finish:
 }
 
 int fd_reopen(int fd, int flags) {
-        int new_fd;
+        int new_fd, r;
 
         /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
          * turn O_RDWR fds into O_RDONLY fds.
@@ -661,15 +740,28 @@ int fd_reopen(int fd, int flags) {
          *
          * This implicitly resets the file read index to 0. */
 
+        if (FLAGS_SET(flags, O_DIRECTORY)) {
+                /* If we shall reopen the fd as directory we can just go via "." and thus bypass the whole
+                 * magic /proc/ directory, and make ourselves independent of that being mounted. */
+                new_fd = openat(fd, ".", flags);
+                if (new_fd < 0)
+                        return -errno;
+
+                return new_fd;
+        }
+
         new_fd = open(FORMAT_PROC_FD_PATH(fd), flags);
         if (new_fd < 0) {
                 if (errno != ENOENT)
                         return -errno;
 
-                if (proc_mounted() == 0)
+                r = proc_mounted();
+                if (r == 0)
                         return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
 
-                return -ENOENT;
+                return r > 0 ? -EBADF : -ENOENT; /* If /proc/ is definitely around then this means the fd is
+                                                  * not valid, otherwise let's propagate the original
+                                                  * error */
         }
 
         return new_fd;
@@ -710,9 +802,6 @@ int btrfs_defrag_fd(int fd) {
         if (r < 0)
                 return r;
 
-        if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL));
 }
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h
index 2382d52d..f5cfcb4e 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.h
+++ b/src/libnm-systemd-shared/src/basic/fd-util.h
@@ -57,10 +57,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
 int fd_nonblock(int fd, bool nonblock);
 int fd_cloexec(int fd, bool cloexec);
 
-int close_all_fds_full(int except[], size_t n_except, bool allow_alloc);
-static inline int close_all_fds(int except[], size_t n_except) {
-        return close_all_fds_full(except, n_except, true);
-}
+int get_max_fd(void);
+
+int close_all_fds(const int except[], size_t n_except);
+int close_all_fds_without_malloc(const int except[], size_t n_except);
 
 int same_fd(int a, int b);
 
@@ -91,9 +91,10 @@ static inline int make_null_stdio(void) {
 /* Like TAKE_PTR() but for file descriptors, resetting them to -1 */
 #define TAKE_FD(fd)                             \
         ({                                      \
-                int _fd_ = (fd);                \
-                (fd) = -1;                      \
-                _fd_;                           \
+                int *_fd_ = &(fd);              \
+                int _ret_ = *_fd_;              \
+                *_fd_ = -1;                     \
+                _ret_;                          \
         })
 
 /* Like free_and_replace(), but for file descriptors */
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 322638a2..c66bc67b 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -169,7 +169,7 @@ int write_string_stream_ts(
                  * it won't be equal to the new value. */
                 if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 &&
                     streq_skip_trailing_chars(line, t, NEWLINE)) {
-                        log_debug("No change in value '%s', supressing write", line);
+                        log_debug("No change in value '%s', suppressing write", line);
                         return 0;
                 }
 
@@ -551,12 +551,25 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
         return !truncated;
 }
 
-int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
+int read_virtual_file_at(
+                int dir_fd,
+                const char *filename,
+                size_t max_size,
+                char **ret_contents,
+                size_t *ret_size) {
+
         _cleanup_close_ int fd = -1;
 
-        assert(filename);
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
 
-        fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
+        if (!filename) {
+                if (dir_fd == AT_FDCWD)
+                        return -EBADF;
+
+                return read_virtual_file_fd(dir_fd, max_size, ret_contents, ret_size);
+        }
+
+        fd = openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
@@ -934,6 +947,9 @@ DIR *xopendirat(int fd, const char *name, int flags) {
 
         assert(!(flags & O_CREAT));
 
+        if (fd == AT_FDCWD && flags == 0)
+                return opendir(name);
+
         nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
         if (nfd < 0)
                 return NULL;
diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h
index 899def94..cea3dd89 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.h
+++ b/src/libnm-systemd-shared/src/basic/fileio.h
@@ -69,7 +69,10 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
 }
 
 int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
-int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
+int read_virtual_file_at(int dir_fd, const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
+static inline int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
+        return read_virtual_file_at(AT_FDCWD, filename, max_size, ret_contents, ret_size);
+}
 static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
         return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
 }
diff --git a/src/libnm-systemd-shared/src/basic/format-util.h b/src/libnm-systemd-shared/src/basic/format-util.h
index 7dd422b9..7db8b61d 100644
--- a/src/libnm-systemd-shared/src/basic/format-util.h
+++ b/src/libnm-systemd-shared/src/basic/format-util.h
@@ -26,11 +26,15 @@ assert_cc(sizeof(gid_t) == sizeof(uint32_t));
 #  error Unknown time_t size
 #endif
 
-#if defined __x86_64__ && defined __ILP32__
+#if 0 /* NM_IGNORED */
+#if SIZEOF_TIMEX_MEMBER == 8
 #  define PRI_TIMEX PRIi64
-#else
+#elif SIZEOF_TIMEX_MEMBER == 4
 #  define PRI_TIMEX "li"
+#else
+#  error Unknown timex member size
 #endif
+#endif /* NM_IGNORED */
 
 #if SIZEOF_RLIM_T == 8
 #  define RLIM_FMT "%" PRIu64
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index 3f309868..b524a6fd 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -14,6 +14,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "hostname-util.h"
 #include "log.h"
 #include "macro.h"
 #include "missing_fcntl.h"
@@ -31,18 +32,13 @@
 #include "strv.h"
 #include "time-util.h"
 #include "tmpfile-util.h"
+#include "umask-util.h"
 #include "user-util.h"
 #include "util.h"
 
 int unlink_noerrno(const char *path) {
         PROTECT_ERRNO;
-        int r;
-
-        r = unlink(path);
-        if (r < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(unlink(path));
 }
 
 #if 0 /* NM_IGNORED */
@@ -59,7 +55,7 @@ int rmdir_parents(const char *path, const char *stop) {
         if (!path_is_safe(stop))
                 return -EINVAL;
 
-        p = strdupa(path);
+        p = strdupa_safe(path);
 
         for (;;) {
                 char *slash = NULL;
@@ -99,8 +95,8 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
          * want — though not atomic (i.e. for a short period both the new and the old filename will exist). */
         if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) {
 
-                if (unlinkat(olddirfd, oldpath, 0) < 0) {
-                        r = -errno; /* Backup errno before the following unlinkat() alters it */
+                r = RET_NERRNO(unlinkat(olddirfd, oldpath, 0));
+                if (r < 0) {
                         (void) unlinkat(newdirfd, newpath, 0);
                         return r;
                 }
@@ -119,10 +115,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
         if (errno != ENOENT)
                 return -errno;
 
-        if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(renameat(olddirfd, oldpath, newdirfd, newpath));
 }
 #endif /* NM_IGNORED */
 
@@ -290,14 +283,9 @@ int fchmod_and_chown_with_fallback(int fd, const char *path, mode_t mode, uid_t
 }
 
 int fchmod_umask(int fd, mode_t m) {
-        mode_t u;
-        int r;
-
-        u = umask(0777);
-        r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
-        umask(u);
+        _cleanup_umask_ mode_t u = umask(0777);
 
-        return r;
+        return RET_NERRNO(fchmod(fd, m & (~u)));
 }
 
 int fchmod_opath(int fd, mode_t m) {
@@ -550,7 +538,6 @@ int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) {
 int get_files_in_directory(const char *path, char ***list) {
         _cleanup_strv_free_ char **l = NULL;
         _cleanup_closedir_ DIR *d = NULL;
-        struct dirent *de;
         size_t n = 0;
 
         assert(path);
@@ -830,7 +817,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
 
 int open_parent(const char *path, int flags, mode_t mode) {
         _cleanup_free_ char *parent = NULL;
-        int fd, r;
+        int r;
 
         r = path_extract_directory(path, &parent);
         if (r < 0)
@@ -844,11 +831,7 @@ int open_parent(const char *path, int flags, mode_t mode) {
         else if (!FLAGS_SET(flags, O_TMPFILE))
                 flags |= O_DIRECTORY|O_RDONLY;
 
-        fd = open(parent, flags, mode);
-        if (fd < 0)
-                return -errno;
-
-        return fd;
+        return RET_NERRNO(open(parent, flags, mode));
 }
 #endif /* NM_IGNORED */
 
@@ -962,3 +945,152 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size) {
 
         return -EINTR;
 }
+
+int parse_cifs_service(
+                const char *s,
+                char **ret_host,
+                char **ret_service,
+                char **ret_path) {
+
+        _cleanup_free_ char *h = NULL, *ss = NULL, *x = NULL;
+        const char *p, *e, *d;
+        char delimiter;
+
+        /* Parses a CIFS service in form of //host/service/path… and splitting it in three parts. The last
+         * part is optional, in which case NULL is returned there. To maximize compatibility syntax with
+         * backslashes instead of slashes is accepted too. */
+
+        if (!s)
+                return -EINVAL;
+
+        p = startswith(s, "//");
+        if (!p) {
+                p = startswith(s, "\\\\");
+                if (!p)
+                        return -EINVAL;
+        }
+
+        delimiter = s[0];
+        e = strchr(p, delimiter);
+        if (!e)
+                return -EINVAL;
+
+        h = strndup(p, e - p);
+        if (!h)
+                return -ENOMEM;
+
+        if (!hostname_is_valid(h, 0))
+                return -EINVAL;
+
+        e++;
+
+        d = strchrnul(e, delimiter);
+
+        ss = strndup(e, d - e);
+        if (!ss)
+                return -ENOMEM;
+
+        if (!filename_is_valid(ss))
+                return -EINVAL;
+
+        if (!isempty(d)) {
+                x = strdup(skip_leading_chars(d, CHAR_TO_STR(delimiter)));
+                if (!x)
+                        return -EINVAL;
+
+                /* Make sure to convert Windows-style "\" → Unix-style / */
+                for (char *i = x; *i; i++)
+                        if (*i == delimiter)
+                                *i = '/';
+
+                if (!path_is_valid(x))
+                        return -EINVAL;
+
+                path_simplify(x);
+                if (!path_is_normalized(x))
+                        return -EINVAL;
+        }
+
+        if (ret_host)
+                *ret_host = TAKE_PTR(h);
+        if (ret_service)
+                *ret_service = TAKE_PTR(ss);
+        if (ret_path)
+                *ret_path = TAKE_PTR(x);
+
+        return 0;
+}
+
+int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
+        _cleanup_close_ int fd = -1, parent_fd = -1;
+        _cleanup_free_ char *fname = NULL;
+        bool made;
+        int r;
+
+        /* Creates a directory with mkdirat() and then opens it, in the "most atomic" fashion we can
+         * do. Guarantees that the returned fd refers to a directory. If O_EXCL is specified will fail if the
+         * dir already exists. Otherwise will open an existing dir, but only if it is one.  */
+
+        if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
+                return -EINVAL;
+        if ((flags & O_ACCMODE) != O_RDONLY)
+                return -EINVAL;
+
+        /* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
+         * flags actually make sense to specify: O_CLOEXEC, O_EXCL, O_NOATIME, O_PATH */
+
+        if (isempty(path))
+                return -EINVAL;
+
+        if (!filename_is_valid(path)) {
+                _cleanup_free_ char *parent = NULL;
+
+                /* If this is not a valid filename, it's a path. Let's open the parent directory then, so
+                 * that we can pin it, and operate below it. */
+
+                r = path_extract_directory(path, &parent);
+                if (r < 0)
+                        return r;
+
+                r = path_extract_filename(path, &fname);
+                if (r < 0)
+                        return r;
+
+                parent_fd = openat(dirfd, parent, O_PATH|O_DIRECTORY|O_CLOEXEC);
+                if (parent_fd < 0)
+                        return -errno;
+
+                dirfd = parent_fd;
+                path = fname;
+        }
+
+        r = RET_NERRNO(mkdirat(dirfd, path, mode));
+        if (r == -EEXIST) {
+                if (FLAGS_SET(flags, O_EXCL))
+                        return -EEXIST;
+
+                made = false;
+        } else if (r < 0)
+                return r;
+        else
+                made = true;
+
+        fd = RET_NERRNO(openat(dirfd, path, (flags & ~O_EXCL)|O_DIRECTORY|O_NOFOLLOW));
+        if (fd < 0) {
+                if (fd == -ENOENT)  /* We got ENOENT? then someone else immediately removed it after we
+                                     * created it. In that case let's return immediately without unlinking
+                                     * anything, because there simply isn't anything to unlink anymore. */
+                        return -ENOENT;
+                if (fd == -ELOOP)   /* is a symlink? exists already → created by someone else, don't unlink */
+                        return -EEXIST;
+                if (fd == -ENOTDIR) /* not a directory? exists already → created by someone else, don't unlink */
+                        return -EEXIST;
+
+                if (made)
+                        (void) unlinkat(dirfd, path, AT_REMOVEDIR);
+
+                return fd;
+        }
+
+        return TAKE_FD(fd);
+}
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h
index f8a7657a..0bbb3f62 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.h
+++ b/src/libnm-systemd-shared/src/basic/fs-util.h
@@ -47,7 +47,7 @@ int fd_warn_permissions(const char *path, int fd);
 int stat_warn_permissions(const char *path, const struct stat *st);
 
 #define laccess(path, mode)                                             \
-        (faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) < 0 ? -errno : 0)
+        RET_NERRNO(faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW))
 
 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode);
 int touch(const char *path);
@@ -106,3 +106,7 @@ static inline int conservative_rename(const char *oldpath, const char *newpath)
 }
 
 int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
+
+int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path);
+
+int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c
index 3063cc7c..1d1c2f51 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.c
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.c
@@ -49,8 +49,7 @@ int gethostname_full(GetHostnameFlags flags, char **ret) {
         assert_se(uname(&u) >= 0);
 
         s = u.nodename;
-        if (isempty(s) ||
-            (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && streq(s, "(none)")) ||
+        if (isempty(s) || streq(s, "(none)") ||
             (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) ||
             (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) {
                 if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT))
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h
index 0d1574db..d435bed5 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.h
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.h
@@ -9,10 +9,9 @@
 #include "strv.h"
 
 typedef enum GetHostnameFlags {
-        GET_HOSTNAME_ALLOW_NONE       = 1 << 0, /* accepts "(none)". */
-        GET_HOSTNAME_ALLOW_LOCALHOST  = 1 << 1, /* accepts "localhost" or friends. */
-        GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */
-        GET_HOSTNAME_SHORT            = 1 << 3, /* kills the FQDN part if present. */
+        GET_HOSTNAME_ALLOW_LOCALHOST  = 1 << 0, /* accepts "localhost" or friends. */
+        GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 1, /* use default hostname if no hostname is set. */
+        GET_HOSTNAME_SHORT            = 1 << 2, /* kills the FQDN part if present. */
 } GetHostnameFlags;
 
 int gethostname_full(GetHostnameFlags flags, char **ret);
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c
index bd4dd8fc..1bf0764d 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -629,65 +629,120 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
         return 0;
 }
 
-#if 0 /* NM_IGNORED */
-int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
+int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen) {
+        struct in_addr mask;
+
         assert(addr);
 
-        if (family == AF_INET) {
-                struct in_addr mask;
+        if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen))
+                return -EINVAL;
 
-                if (!in4_addr_prefixlen_to_netmask(&mask, prefixlen))
-                        return -EINVAL;
+        addr->s_addr &= mask.s_addr;
+        return 0;
+}
 
-                addr->in.s_addr &= mask.s_addr;
-                return 0;
-        }
+int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen) {
+        unsigned i;
 
-        if (family == AF_INET6) {
-                unsigned i;
+        for (i = 0; i < 16; i++) {
+                uint8_t mask;
 
-                for (i = 0; i < 16; i++) {
-                        uint8_t mask;
+                if (prefixlen >= 8) {
+                        mask = 0xFF;
+                        prefixlen -= 8;
+                } else if (prefixlen > 0) {
+                        mask = 0xFF << (8 - prefixlen);
+                        prefixlen = 0;
+                } else {
+                        assert(prefixlen == 0);
+                        mask = 0;
+                }
 
-                        if (prefixlen >= 8) {
-                                mask = 0xFF;
-                                prefixlen -= 8;
-                        } else {
-                                mask = 0xFF << (8 - prefixlen);
-                                prefixlen = 0;
-                        }
+                addr->s6_addr[i] &= mask;
+        }
 
-                        addr->in6.s6_addr[i] &= mask;
-                }
+        return 0;
+}
 
-                return 0;
+#if 0 /* NM_IGNORED */
+int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) {
+        assert(addr);
+
+        switch (family) {
+        case AF_INET:
+                return in4_addr_mask(&addr->in, prefixlen);
+        case AF_INET6:
+                return in6_addr_mask(&addr->in6, prefixlen);
+        default:
+                return -EAFNOSUPPORT;
         }
+}
 
-        return -EAFNOSUPPORT;
+int in4_addr_prefix_covers(
+                const struct in_addr *prefix,
+                unsigned char prefixlen,
+                const struct in_addr *address) {
+
+        struct in_addr masked_prefix, masked_address;
+        int r;
+
+        assert(prefix);
+        assert(address);
+
+        masked_prefix = *prefix;
+        r = in4_addr_mask(&masked_prefix, prefixlen);
+        if (r < 0)
+                return r;
+
+        masked_address = *address;
+        r = in4_addr_mask(&masked_address, prefixlen);
+        if (r < 0)
+                return r;
+
+        return in4_addr_equal(&masked_prefix, &masked_address);
 }
 
-int in_addr_prefix_covers(int family,
-                          const union in_addr_union *prefix,
-                          unsigned char prefixlen,
-                          const union in_addr_union *address) {
+int in6_addr_prefix_covers(
+                const struct in6_addr *prefix,
+                unsigned char prefixlen,
+                const struct in6_addr *address) {
 
-        union in_addr_union masked_prefix, masked_address;
+        struct in6_addr masked_prefix, masked_address;
         int r;
 
         assert(prefix);
         assert(address);
 
         masked_prefix = *prefix;
-        r = in_addr_mask(family, &masked_prefix, prefixlen);
+        r = in6_addr_mask(&masked_prefix, prefixlen);
         if (r < 0)
                 return r;
 
         masked_address = *address;
-        r = in_addr_mask(family, &masked_address, prefixlen);
+        r = in6_addr_mask(&masked_address, prefixlen);
         if (r < 0)
                 return r;
 
-        return in_addr_equal(family, &masked_prefix, &masked_address);
+        return in6_addr_equal(&masked_prefix, &masked_address);
+}
+
+int in_addr_prefix_covers(
+                int family,
+                const union in_addr_union *prefix,
+                unsigned char prefixlen,
+                const union in_addr_union *address) {
+
+        assert(prefix);
+        assert(address);
+
+        switch (family) {
+        case AF_INET:
+                return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
+        case AF_INET6:
+                return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
+        default:
+                return -EAFNOSUPPORT;
+        }
 }
 
 int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) {
@@ -855,4 +910,10 @@ int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
 }
 
 DEFINE_HASH_OPS(in6_addr_hash_ops, struct in6_addr, in6_addr_hash_func, in6_addr_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+        in6_addr_hash_ops_free,
+        struct in6_addr,
+        in6_addr_hash_func,
+        in6_addr_compare_func,
+        free);
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h
index c74b0d51..0178391e 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h
@@ -89,7 +89,11 @@ unsigned char in4_addr_netmask_to_prefixlen(const struct in_addr *addr);
 struct in_addr* in4_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen);
 int in4_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen);
 int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask);
+int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
+int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
 int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
+int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
+int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
 int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
 int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
 int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
@@ -110,6 +114,13 @@ static inline size_t FAMILY_ADDRESS_SIZE(int family) {
         return family == AF_INET6 ? 16 : 4;
 }
 
+#define FAMILY_ADDRESS_SIZE_SAFE(f)                                     \
+        ({                                                              \
+                int _f = (f);                                           \
+                _f == AF_INET ? sizeof(struct in_addr) :                \
+                _f == AF_INET6 ? sizeof(struct in6_addr) : 0;           \
+        })
+
 /* Workaround for clang, explicitly specify the maximum-size element here.
  * See also oss-fuzz#11344. */
 #define IN_ADDR_NULL ((union in_addr_union) { .in6 = {} })
@@ -119,6 +130,7 @@ 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;
+extern const struct hash_ops in6_addr_hash_ops_free;
 
 #define IPV4_ADDRESS_FMT_STR     "%u.%u.%u.%u"
 #define IPV4_ADDRESS_FMT_VAL(address)              \
diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.c b/src/libnm-systemd-shared/src/basic/inotify-util.c
index cc2a92ad..59e03e62 100644
--- a/src/libnm-systemd-shared/src/basic/inotify-util.c
+++ b/src/libnm-systemd-shared/src/basic/inotify-util.c
@@ -4,14 +4,26 @@
 
 #include "fd-util.h"
 #include "inotify-util.h"
+#include "stat-util.h"
 
 int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
-        int wd;
+        int wd, r;
 
         /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
         wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask);
-        if (wd < 0)
-                return -errno;
+        if (wd < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                /* Didn't work with ENOENT? If so, then either /proc/ isn't mounted, or the fd is bad */
+                r = proc_mounted();
+                if (r == 0)
+                        return -ENOSYS;
+                if (r > 0)
+                        return -EBADF;
+
+                return -ENOENT; /* OK, no clue, let's propagate the original error */
+        }
 
         return wd;
 }
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index e488fff9..f827e721 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -134,7 +134,7 @@
         } while (false)
 
 #define LIST_JUST_US(name,item)                                         \
-        (!(item)->name##_prev && !(item)->name##_next)                  \
+        (!(item)->name##_prev && !(item)->name##_next)
 
 #define LIST_FOREACH(name,i,head)                                       \
         for ((i) = (head); (i); (i) = (i)->name##_next)
@@ -181,3 +181,12 @@
                 }                                                       \
                 (b) = NULL;                                             \
         } while (false)
+
+#define LIST_POP(name, a)                                               \
+        ({                                                              \
+                typeof(a)* _a = &(a);                                   \
+                typeof(a) _p = *_a;                                     \
+                if (_p)                                                 \
+                        LIST_REMOVE(name, *_a, _p);                     \
+                _p;                                                     \
+        })
diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h
index b11eac21..88d278fd 100644
--- a/src/libnm-systemd-shared/src/basic/log.h
+++ b/src/libnm-systemd-shared/src/basic/log.h
@@ -27,10 +27,14 @@ typedef enum LogTarget{
         _LOG_TARGET_INVALID = -EINVAL,
 } LogTarget;
 
-/* Note to readers: << and >> have lower precedence than & and | */
+/* This log level disables logging completely. It can only be passed to log_set_max_level() and cannot be
+ * used a regular log level. */
+#define LOG_NULL (LOG_EMERG - 1)
+
+/* Note to readers: << and >> have lower precedence (are evaluated earlier) than & and | */
 #define SYNTHETIC_ERRNO(num)                (1 << 30 | (num))
 #define IS_SYNTHETIC_ERRNO(val)             ((val) >> 30 & 1)
-#define ERRNO_VALUE(val)                    (abs(val) & 255)
+#define ERRNO_VALUE(val)                    (abs(val) & ~(1 << 30))
 
 /* The callback function to be invoked when syntax warnings are seen
  * in the unit files. */
@@ -90,6 +94,7 @@ int log_open(void);
 void log_close(void);
 void log_forget_fds(void);
 
+void log_parse_environment_variables(void);
 void log_parse_environment(void);
 
 #if 0 /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index 33d28332..79437775 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -25,7 +25,7 @@
 #define _public_ __attribute__((__visibility__("default")))
 #define _hidden_ __attribute__((__visibility__("hidden")))
 #define _weakref_(x) __attribute__((__weakref__(#x)))
-#define _alignas_(x) __attribute__((__aligned__(__alignof(x))))
+#define _alignas_(x) __attribute__((__aligned__(__alignof__(x))))
 #define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
 #define _warn_unused_result_ __attribute__((__warn_unused_result__))
 
@@ -154,24 +154,6 @@
 #define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
 #define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
 
-static inline size_t ALIGN_TO(size_t l, size_t ali) {
-        /* Check that alignment is exponent of 2 */
-#if SIZE_MAX == UINT_MAX
-        assert(__builtin_popcount(ali) == 1);
-#elif SIZE_MAX == ULONG_MAX
-        assert(__builtin_popcountl(ali) == 1);
-#elif SIZE_MAX == ULLONG_MAX
-        assert(__builtin_popcountll(ali) == 1);
-#else
-#error "Unexpected size_t"
-#endif
-
-        if (l > SIZE_MAX - (ali - 1))
-                return SIZE_MAX; /* indicate overflow */
-
-        return ((l + ali - 1) & ~(ali - 1));
-}
-
 #define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
 
 /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
@@ -355,13 +337,6 @@ static inline int __coverity_check_and_return__(int condition) {
                 ans;                                    \
         })
 
-#define UPDATE_FLAG(orig, flag, b)                      \
-        ((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
-#define SET_FLAG(v, flag, b) \
-        (v) = UPDATE_FLAG(v, flag, b)
-#define FLAGS_SET(v, flags) \
-        ((~(v) & (flags)) == 0)
-
 #define SWAP_TWO(x, y) do {                        \
                 typeof(x) _t = (x);                \
                 (x) = (y);                         \
diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h
index aeb994f0..41c83ec5 100644
--- a/src/libnm-systemd-shared/src/basic/missing_syscall.h
+++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h
@@ -20,6 +20,7 @@
 #include <asm/sgidefs.h>
 #endif
 
+#include "macro.h"
 #include "missing_keyctl.h"
 #include "missing_stat.h"
 #include "missing_syscall_def.h"
@@ -79,7 +80,8 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
 /* ======================================================================= */
 
 #if !HAVE_GETRANDOM
-static inline int missing_getrandom(void *buffer, size_t count, unsigned flags) {
+/* glibc says getrandom() returns ssize_t */
+static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
 #  ifdef __NR_getrandom
         return syscall(__NR_getrandom, buffer, count, flags);
 #  else
@@ -466,8 +468,52 @@ struct mount_attr {
 struct mount_attr;
 #endif
 
+#ifndef MOUNT_ATTR_RDONLY
+#define MOUNT_ATTR_RDONLY       0x00000001 /* Mount read-only */
+#endif
+
+#ifndef MOUNT_ATTR_NOSUID
+#define MOUNT_ATTR_NOSUID       0x00000002 /* Ignore suid and sgid bits */
+#endif
+
+#ifndef MOUNT_ATTR_NODEV
+#define MOUNT_ATTR_NODEV        0x00000004 /* Disallow access to device special files */
+#endif
+
+#ifndef MOUNT_ATTR_NOEXEC
+#define MOUNT_ATTR_NOEXEC       0x00000008 /* Disallow program execution */
+#endif
+
+#ifndef MOUNT_ATTR__ATIME
+#define MOUNT_ATTR__ATIME       0x00000070 /* Setting on how atime should be updated */
+#endif
+
+#ifndef MOUNT_ATTR_RELATIME
+#define MOUNT_ATTR_RELATIME     0x00000000 /* - Update atime relative to mtime/ctime. */
+#endif
+
+#ifndef MOUNT_ATTR_NOATIME
+#define MOUNT_ATTR_NOATIME      0x00000010 /* - Do not update access times. */
+#endif
+
+#ifndef MOUNT_ATTR_STRICTATIME
+#define MOUNT_ATTR_STRICTATIME  0x00000020 /* - Always perform atime updates */
+#endif
+
+#ifndef MOUNT_ATTR_NODIRATIME
+#define MOUNT_ATTR_NODIRATIME   0x00000080 /* Do not update directory access times */
+#endif
+
 #ifndef MOUNT_ATTR_IDMAP
-#define MOUNT_ATTR_IDMAP 0x00100000
+#define MOUNT_ATTR_IDMAP        0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
+#endif
+
+#ifndef MOUNT_ATTR_NOSYMFOLLOW
+#define MOUNT_ATTR_NOSYMFOLLOW  0x00200000 /* Do not follow symlinks */
+#endif
+
+#ifndef MOUNT_ATTR_SIZE_VER0
+#define MOUNT_ATTR_SIZE_VER0    32 /* sizeof first published struct */
 #endif
 
 #ifndef AT_RECURSIVE
@@ -545,4 +591,20 @@ static inline int missing_move_mount(
 
 #  define move_mount missing_move_mount
 #endif
+
+/* ======================================================================= */
+
+#if !HAVE_GETDENTS64
+
+static inline ssize_t missing_getdents64(int fd, void *buffer, size_t length) {
+#  if defined __NR_getdents64 && __NR_getdents64 >= 0
+        return syscall(__NR_getdents64, fd, buffer, length);
+#  else
+        errno = ENOSYS;
+        return -1;
+#  endif
+}
+
+#  define getdents64 missing_getdents64
+#endif
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index f81bf1da..4ba2657c 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -651,7 +651,7 @@ int parse_ip_port(const char *s, uint16_t *ret) {
         uint16_t l;
         int r;
 
-        r = safe_atou16(s, &l);
+        r = safe_atou16_full(s, SAFE_ATO_REFUSE_LEADING_WHITESPACE, &l);
         if (r < 0)
                 return r;
 
@@ -711,7 +711,7 @@ int parse_dev(const char *s, dev_t *ret) {
         if (s[n] != ':')
                 return -EINVAL;
 
-        major = strndupa(s, n);
+        major = strndupa_safe(s, n);
         r = safe_atou(major, &x);
         if (r < 0)
                 return r;
@@ -772,7 +772,7 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
         if (!d)
                 return -EINVAL;
 
-        i_str = strndupa(s, d - s);
+        i_str = strndupa_safe(s, d - s);
         f_str = d + 1;
 
         r = safe_atolu_full(i_str, 10, &i);
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index 92c55048..f391848f 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -1246,8 +1246,6 @@ char *file_in_same_dir(const char *path, const char *filename) {
 }
 
 bool hidden_or_backup_file(const char *filename) {
-        const char *p;
-
         assert(filename);
 
         if (filename[0] == '.' ||
@@ -1257,24 +1255,25 @@ bool hidden_or_backup_file(const char *filename) {
             endswith(filename, "~"))
                 return true;
 
-        p = strrchr(filename, '.');
-        if (!p)
+        const char *dot = strrchr(filename, '.');
+        if (!dot)
                 return false;
 
-        /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
-         * with always new suffixes and that everybody else should just adjust to that, then it really should be on
-         * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
-         * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
-         * string. Specifically: there's now:
+        /* Please, let's not add more entries to the list below. If external projects think it's a good idea
+         * to come up with always new suffixes and that everybody else should just adjust to that, then it
+         * really should be on them. Hence, in future, let's not add any more entries. Instead, let's ask
+         * those packages to instead adopt one of the generic suffixes/prefixes for hidden files or backups,
+         * possibly augmented with an additional string. Specifically: there's now:
          *
          *    The generic suffixes "~" and ".bak" for backup files
          *    The generic prefix "." for hidden files
          *
-         * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
-         * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
+         * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old",
+         * ".foopkg-dist" or so registered, let's refuse that and ask them to use ".foopkg.new",
+         * ".foopkg.old" or ".foopkg~" instead.
          */
 
-        return STR_IN_SET(p + 1,
+        return STR_IN_SET(dot + 1,
                           "rpmnew",
                           "rpmsave",
                           "rpmorig",
@@ -1296,15 +1295,16 @@ bool hidden_or_backup_file(const char *filename) {
 
 bool is_device_path(const char *path) {
 
-        /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */
+        /* Returns true for paths that likely refer to a device, either by path in sysfs or to something in
+         * /dev. */
 
         return PATH_STARTSWITH_SET(path, "/dev/", "/sys/");
 }
 
 bool valid_device_node_path(const char *path) {
 
-        /* Some superficial checks whether the specified path is a valid device node path, all without looking at the
-         * actual device node. */
+        /* Some superficial checks whether the specified path is a valid device node path, all without
+         * looking at the actual device node. */
 
         if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/"))
                 return false;
@@ -1318,8 +1318,8 @@ bool valid_device_node_path(const char *path) {
 bool valid_device_allow_pattern(const char *path) {
         assert(path);
 
-        /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny=
-         * accept it */
+        /* Like valid_device_node_path(), but also allows full-subsystem expressions like those accepted by
+         * DeviceAllow= and DeviceDeny=. */
 
         if (STARTSWITH_SET(path, "block-", "char-"))
                 return true;
@@ -1412,8 +1412,8 @@ bool dot_or_dot_dot(const char *path) {
 #if 0 /* NM_IGNORED */
 bool empty_or_root(const char *path) {
 
-        /* For operations relative to some root directory, returns true if the specified root directory is redundant,
-         * i.e. either / or NULL or the empty string or any equivalent. */
+        /* For operations relative to some root directory, returns true if the specified root directory is
+         * redundant, i.e. either / or NULL or the empty string or any equivalent. */
 
         if (isempty(path))
                 return true;
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 96d0912c..70564bd3 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -187,13 +187,13 @@ static int get_process_cmdline_nulstr(
         return r;
 }
 
-int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line) {
+int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret) {
         _cleanup_free_ char *t = NULL;
         size_t k;
         char *ans;
 
-        assert(line);
         assert(pid >= 0);
+        assert(ret);
 
         /* Retrieve and format a commandline. See above for discussion of retrieval options.
          *
@@ -220,25 +220,23 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
                 assert(!(flags & PROCESS_CMDLINE_USE_LOCALE));
 
                 _cleanup_strv_free_ char **args = NULL;
+                char **p;
 
                 args = strv_parse_nulstr(t, k);
                 if (!args)
                         return -ENOMEM;
 
-                for (size_t i = 0; args[i]; i++) {
-                        char *e;
-
-                        e = shell_maybe_quote(args[i], shflags);
-                        if (!e)
-                                return -ENOMEM;
+                /* Drop trailing empty strings. See issue #21186. */
+                STRV_FOREACH_BACKWARDS(p, args) {
+                        if (!isempty(*p))
+                                break;
 
-                        free_and_replace(args[i], e);
+                        *p = mfree(*p);
                 }
 
-                ans = strv_join(args, " ");
+                ans = quote_command_line(args, shflags);
                 if (!ans)
                         return -ENOMEM;
-
         } else {
                 /* Arguments are separated by NULs. Let's replace those with spaces. */
                 for (size_t i = 0; i < k - 1; i++)
@@ -257,7 +255,7 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
                 ans = str_realloc(ans);
         }
 
-        *line = ans;
+        *ret = ans;
         return 0;
 }
 
@@ -464,29 +462,29 @@ int is_kernel_thread(pid_t pid) {
         return !!(flags & PF_KTHREAD);
 }
 
-int get_process_capeff(pid_t pid, char **capeff) {
+int get_process_capeff(pid_t pid, char **ret) {
         const char *p;
         int r;
 
-        assert(capeff);
         assert(pid >= 0);
+        assert(ret);
 
         p = procfs_file_alloca(pid, "status");
 
-        r = get_proc_field(p, "CapEff", WHITESPACE, capeff);
+        r = get_proc_field(p, "CapEff", WHITESPACE, ret);
         if (r == -ENOENT)
                 return -ESRCH;
 
         return r;
 }
 
-static int get_process_link_contents(const char *proc_file, char **name) {
+static int get_process_link_contents(const char *proc_file, char **ret) {
         int r;
 
         assert(proc_file);
-        assert(name);
+        assert(ret);
 
-        r = readlink_malloc(proc_file, name);
+        r = readlink_malloc(proc_file, ret);
         if (r == -ENOENT)
                 return -ESRCH;
         if (r < 0)
@@ -495,32 +493,33 @@ static int get_process_link_contents(const char *proc_file, char **name) {
         return 0;
 }
 
-int get_process_exe(pid_t pid, char **name) {
+int get_process_exe(pid_t pid, char **ret) {
         const char *p;
         char *d;
         int r;
 
         assert(pid >= 0);
+        assert(ret);
 
         p = procfs_file_alloca(pid, "exe");
-        r = get_process_link_contents(p, name);
+        r = get_process_link_contents(p, ret);
         if (r < 0)
                 return r;
 
-        d = endswith(*name, " (deleted)");
+        d = endswith(*ret, " (deleted)");
         if (d)
                 *d = '\0';
 
         return 0;
 }
 
-static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
+static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
         _cleanup_fclose_ FILE *f = NULL;
         const char *p;
         int r;
 
         assert(field);
-        assert(uid);
+        assert(ret);
 
         if (pid < 0)
                 return -EINVAL;
@@ -550,60 +549,62 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
 
                         l[strcspn(l, WHITESPACE)] = 0;
 
-                        return parse_uid(l, uid);
+                        return parse_uid(l, ret);
                 }
         }
 
         return -EIO;
 }
 
-int get_process_uid(pid_t pid, uid_t *uid) {
+int get_process_uid(pid_t pid, uid_t *ret) {
 
         if (pid == 0 || pid == getpid_cached()) {
-                *uid = getuid();
+                *ret = getuid();
                 return 0;
         }
 
-        return get_process_id(pid, "Uid:", uid);
+        return get_process_id(pid, "Uid:", ret);
 }
 
-int get_process_gid(pid_t pid, gid_t *gid) {
+int get_process_gid(pid_t pid, gid_t *ret) {
 
         if (pid == 0 || pid == getpid_cached()) {
-                *gid = getgid();
+                *ret = getgid();
                 return 0;
         }
 
         assert_cc(sizeof(uid_t) == sizeof(gid_t));
-        return get_process_id(pid, "Gid:", gid);
+        return get_process_id(pid, "Gid:", ret);
 }
 
-int get_process_cwd(pid_t pid, char **cwd) {
+int get_process_cwd(pid_t pid, char **ret) {
         const char *p;
 
         assert(pid >= 0);
+        assert(ret);
 
         if (pid == 0 || pid == getpid_cached())
-                return safe_getcwd(cwd);
+                return safe_getcwd(ret);
 
         p = procfs_file_alloca(pid, "cwd");
 
-        return get_process_link_contents(p, cwd);
+        return get_process_link_contents(p, ret);
 }
 
-int get_process_root(pid_t pid, char **root) {
+int get_process_root(pid_t pid, char **ret) {
         const char *p;
 
         assert(pid >= 0);
+        assert(ret);
 
         p = procfs_file_alloca(pid, "root");
 
-        return get_process_link_contents(p, root);
+        return get_process_link_contents(p, ret);
 }
 
 #define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
 
-int get_process_environ(pid_t pid, char **env) {
+int get_process_environ(pid_t pid, char **ret) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *outcome = NULL;
         size_t sz = 0;
@@ -611,7 +612,7 @@ int get_process_environ(pid_t pid, char **env) {
         int r;
 
         assert(pid >= 0);
-        assert(env);
+        assert(ret);
 
         p = procfs_file_alloca(pid, "environ");
 
@@ -643,7 +644,7 @@ int get_process_environ(pid_t pid, char **env) {
         }
 
         outcome[sz] = '\0';
-        *env = TAKE_PTR(outcome);
+        *ret = TAKE_PTR(outcome);
 
         return 0;
 }
@@ -702,13 +703,13 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
         return 0;
 }
 
-int get_process_umask(pid_t pid, mode_t *umask) {
+int get_process_umask(pid_t pid, mode_t *ret) {
         _cleanup_free_ char *m = NULL;
         const char *p;
         int r;
 
-        assert(umask);
         assert(pid >= 0);
+        assert(ret);
 
         p = procfs_file_alloca(pid, "status");
 
@@ -716,7 +717,7 @@ int get_process_umask(pid_t pid, mode_t *umask) {
         if (r == -ENOENT)
                 return -ESRCH;
 
-        return parse_mode(m, umask);
+        return parse_mode(m, ret);
 }
 
 int wait_for_terminate(pid_t pid, siginfo_t *status) {
@@ -830,7 +831,7 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
                 if (n >= until)
                         break;
 
-                r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0;
+                r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)));
                 /* Assuming we woke due to the child exiting. */
                 if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
                         if (status.si_pid == pid) {
@@ -863,8 +864,8 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
 void sigkill_wait(pid_t pid) {
         assert(pid > 1);
 
-        if (kill(pid, SIGKILL) >= 0)
-                (void) wait_for_terminate(pid, NULL);
+        (void) kill(pid, SIGKILL);
+        (void) wait_for_terminate(pid, NULL);
 }
 
 void sigkill_waitp(pid_t *pid) {
@@ -881,14 +882,14 @@ void sigkill_waitp(pid_t *pid) {
 void sigterm_wait(pid_t pid) {
         assert(pid > 1);
 
-        if (kill_and_sigcont(pid, SIGTERM) >= 0)
-                (void) wait_for_terminate(pid, NULL);
+        (void) kill_and_sigcont(pid, SIGTERM);
+        (void) wait_for_terminate(pid, NULL);
 }
 
 int kill_and_sigcont(pid_t pid, int sig) {
         int r;
 
-        r = kill(pid, sig) < 0 ? -errno : 0;
+        r = RET_NERRNO(kill(pid, sig));
 
         /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
          * affected by a process being suspended anyway. */
@@ -1064,8 +1065,8 @@ unsigned long personality_from_string(const char *p) {
 
         if (architecture == native_architecture())
                 return PER_LINUX;
-#ifdef SECONDARY_ARCHITECTURE
-        if (architecture == SECONDARY_ARCHITECTURE)
+#ifdef ARCHITECTURE_SECONDARY
+        if (architecture == ARCHITECTURE_SECONDARY)
                 return PER_LINUX32;
 #endif
 
@@ -1077,9 +1078,9 @@ const char* personality_to_string(unsigned long p) {
 
         if (p == PER_LINUX)
                 architecture = native_architecture();
-#ifdef SECONDARY_ARCHITECTURE
+#ifdef ARCHITECTURE_SECONDARY
         else if (p == PER_LINUX32)
-                architecture = SECONDARY_ARCHITECTURE;
+                architecture = ARCHITECTURE_SECONDARY;
 #endif
 
         if (architecture < 0)
@@ -1150,23 +1151,6 @@ int pid_compare_func(const pid_t *a, const pid_t *b) {
         /* Suitable for usage in qsort() */
         return CMP(*a, *b);
 }
-
-int ioprio_parse_priority(const char *s, int *ret) {
-        int i, r;
-
-        assert(s);
-        assert(ret);
-
-        r = safe_atoi(s, &i);
-        if (r < 0)
-                return r;
-
-        if (!ioprio_priority_is_valid(i))
-                return -EINVAL;
-
-        *ret = i;
-        return 0;
-}
 #endif /* NM_IGNORED */
 
 /* The cached PID, possible values:
@@ -1256,7 +1240,7 @@ static void restore_sigsetp(sigset_t **ssp) {
 
 int safe_fork_full(
                 const char *name,
-                int except_fds[],
+                const int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
                 pid_t *ret_pid) {
@@ -1451,7 +1435,7 @@ int safe_fork_full(
 int namespace_fork(
                 const char *outer_name,
                 const char *inner_name,
-                int except_fds[],
+                const int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
                 int pidns_fd,
@@ -1467,8 +1451,7 @@ int namespace_fork(
          * process. This ensures that we are fully a member of the destination namespace, with pidns an all, so that
          * /proc/self/fd works correctly. */
 
-        r = safe_fork_full(outer_name, except_fds, n_except_fds,
-                           (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
+        r = safe_fork_full(outer_name, except_fds, n_except_fds, (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid);
         if (r < 0)
                 return r;
         if (r == 0) {
@@ -1513,7 +1496,7 @@ int set_oom_score_adjust(int value) {
 }
 
 int get_oom_score_adjust(int *ret) {
-        _cleanup_free_ char *t;
+        _cleanup_free_ char *t = NULL;
         int r, a;
 
         r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL);
@@ -1628,14 +1611,27 @@ bool invoked_as(char *argv[], const char *token) {
         return strstr(last_path_component(argv[0]), token);
 }
 
-static const char *const ioprio_class_table[] = {
-        [IOPRIO_CLASS_NONE] = "none",
-        [IOPRIO_CLASS_RT] = "realtime",
-        [IOPRIO_CLASS_BE] = "best-effort",
-        [IOPRIO_CLASS_IDLE] = "idle",
-};
+_noreturn_ void freeze(void) {
+        log_close();
+
+        /* Make sure nobody waits for us (i.e. on one of our sockets) anymore. Note that we use
+         * close_all_fds_without_malloc() instead of plain close_all_fds() here, since we want this function
+         * to be compatible with being called from signal handlers. */
+        (void) close_all_fds_without_malloc(NULL, 0);
+
+        /* Let's not freeze right away, but keep reaping zombies. */
+        for (;;) {
+                siginfo_t si = {};
+
+                if (waitid(P_ALL, 0, &si, WEXITED) < 0 && errno != EINTR)
+                        break;
+        }
+
+        /* waitid() failed with an unexpected error, things are really borked. Freeze now! */
+        for (;;)
+                pause();
+}
 
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
 
 static const char *const sigchld_code_table[] = {
         [CLD_EXITED] = "exited",
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index bc16d328..a957cab2 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -14,7 +14,6 @@
 #include "alloc-util.h"
 #include "format-util.h"
 #include "macro.h"
-#include "missing_ioprio.h"
 #include "time-util.h"
 
 #define procfs_file_alloca(pid, field)                                  \
@@ -39,17 +38,17 @@ typedef enum ProcessCmdlineFlags {
         PROCESS_CMDLINE_QUOTE_POSIX   = 1 << 3,
 } ProcessCmdlineFlags;
 
-int get_process_comm(pid_t pid, char **name);
-int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **line);
-int get_process_exe(pid_t pid, char **name);
-int get_process_uid(pid_t pid, uid_t *uid);
-int get_process_gid(pid_t pid, gid_t *gid);
-int get_process_capeff(pid_t pid, char **capeff);
-int get_process_cwd(pid_t pid, char **cwd);
-int get_process_root(pid_t pid, char **root);
-int get_process_environ(pid_t pid, char **environ);
-int get_process_ppid(pid_t pid, pid_t *ppid);
-int get_process_umask(pid_t pid, mode_t *umask);
+int get_process_comm(pid_t pid, char **ret);
+int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags, char **ret);
+int get_process_exe(pid_t pid, char **ret);
+int get_process_uid(pid_t pid, uid_t *ret);
+int get_process_gid(pid_t pid, gid_t *ret);
+int get_process_capeff(pid_t pid, char **ret);
+int get_process_cwd(pid_t pid, char **ret);
+int get_process_root(pid_t pid, char **ret);
+int get_process_environ(pid_t pid, char **ret);
+int get_process_ppid(pid_t pid, pid_t *ret);
+int get_process_umask(pid_t pid, mode_t *ret);
 
 int wait_for_terminate(pid_t pid, siginfo_t *status);
 
@@ -97,9 +96,6 @@ const char *personality_to_string(unsigned long);
 int safe_personality(unsigned long p);
 int opinionated_personality(unsigned long *ret);
 
-int ioprio_class_to_string_alloc(int i, char **s);
-int ioprio_class_from_string(const char *s);
-
 const char *sigchld_code_to_string(int i) _const_;
 int sigchld_code_from_string(const char *s) _pure_;
 
@@ -131,21 +127,11 @@ static inline bool sched_priority_is_valid(int i) {
         return i >= 0 && i <= sched_get_priority_max(SCHED_RR);
 }
 
-static inline bool ioprio_class_is_valid(int i) {
-        return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE);
-}
-
-static inline bool ioprio_priority_is_valid(int i) {
-        return i >= 0 && i < IOPRIO_BE_NR;
-}
-
 static inline bool pid_is_valid(pid_t p) {
         return p > 0;
 }
 #endif /* NM_IGNORED */
 
-int ioprio_parse_priority(const char *s, int *ret);
-
 pid_t getpid_cached(void);
 void reset_cached_pid(void);
 
@@ -168,13 +154,13 @@ typedef enum ForkFlags {
         FORK_NEW_USERNS         = 1 << 13, /* Run child in its own user namespace */
 } ForkFlags;
 
-int safe_fork_full(const char *name, int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
+int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
 
 static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
         return safe_fork_full(name, NULL, 0, flags, ret_pid);
 }
 
-int namespace_fork(const char *outer_name, const char *inner_name, int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
+int namespace_fork(const char *outer_name, const char *inner_name, const int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid);
 
 int set_oom_score_adjust(int value);
 int get_oom_score_adjust(int *ret);
@@ -194,8 +180,9 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
 /* Like TAKE_PTR() but for child PIDs, resetting them to 0 */
 #define TAKE_PID(pid)                           \
         ({                                      \
-                pid_t _pid_ = (pid);            \
-                (pid) = 0;                      \
+                pid_t *_ppid_ = &(pid);         \
+                pid_t _pid_ = *_ppid_;          \
+                *_ppid_ = 0;                    \
                 _pid_;                          \
         })
 
@@ -204,3 +191,5 @@ int pidfd_get_pid(int fd, pid_t *ret);
 int setpriority_closest(int priority);
 
 bool invoked_as(char *argv[], const char *token);
+
+_noreturn_ void freeze(void);
diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c
index 063784dc..9832f8ef 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.c
+++ b/src/libnm-systemd-shared/src/basic/random-util.c
@@ -163,7 +163,6 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
         static int have_syscall = -1;
         _cleanup_close_ int fd = -1;
         bool got_some = false;
-        int r;
 
         /* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from
          * the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK
@@ -222,25 +221,26 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
         if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
 
                 for (;;) {
+                        ssize_t l;
 #if !HAVE_GETRANDOM
                         /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround.
                          * If you don't compile against a libc that provides getrandom(), you don't get it. */
-                        r = -1;
+                        l = -1;
                         errno = ENOSYS;
 #else
-                        r = getrandom(p, n,
+                        l = getrandom(p, n,
                                       (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) |
                                       (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0));
 #endif
-                        if (r > 0) {
+                        if (l > 0) {
                                 have_syscall = true;
 
-                                if ((size_t) r == n)
+                                if ((size_t) l == n)
                                         return 0; /* Yay, success! */
 
-                                assert((size_t) r < n);
-                                p = (uint8_t*) p + r;
-                                n -= r;
+                                assert((size_t) l < n);
+                                p = (uint8_t*) p + l;
+                                n -= l;
 
                                 if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
                                         /* Fill in the remaining bytes using pseudo-random values */
@@ -257,7 +257,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
                                 /* Fill in the rest with /dev/urandom */
                                 break;
 
-                        } else if (r == 0) {
+                        } else if (l == 0) {
                                 have_syscall = true;
                                 return -EIO;
 
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c
index f96eb4d4..6d8c9920 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.c
+++ b/src/libnm-systemd-shared/src/basic/signal-util.c
@@ -5,6 +5,7 @@
 #include <errno.h>
 #include <stdarg.h>
 
+#include "errno-util.h"
 #include "macro.h"
 #include "parse-util.h"
 #include "signal-util.h"
@@ -42,10 +43,7 @@ int reset_signal_mask(void) {
         if (sigemptyset(&ss) < 0)
                 return -errno;
 
-        if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(sigprocmask(SIG_SETMASK, &ss, NULL));
 }
 
 int sigaction_many_internal(const struct sigaction *sa, ...) {
@@ -122,7 +120,7 @@ int sigprocmask_many(int how, sigset_t *old, ...) {
         return 0;
 }
 
-static const char *const __signal_table[] = {
+static const char *const static_signal_table[] = {
         [SIGHUP] = "HUP",
         [SIGINT] = "INT",
         [SIGQUIT] = "QUIT",
@@ -158,13 +156,13 @@ static const char *const __signal_table[] = {
         [SIGSYS] = "SYS"
 };
 
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
 
 const char *signal_to_string(int signo) {
         static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)];
         const char *name;
 
-        name = __signal_to_string(signo);
+        name = static_signal_to_string(signo);
         if (name)
                 return name;
 
@@ -193,7 +191,7 @@ int signal_from_string(const char *s) {
                 s += 3;
 
         /* Check that the input is a signal name. */
-        signo = __signal_from_string(s);
+        signo = static_signal_from_string(s);
         if (signo > 0)
                 return signo;
 
@@ -250,11 +248,7 @@ int signal_is_blocked(int sig) {
         if (r != 0)
                 return -r;
 
-        r = sigismember(&ss, sig);
-        if (r < 0)
-                return -errno;
-
-        return r;
+        return RET_NERRNO(sigismember(&ss, sig));
 }
 
 int pop_pending_signal_internal(int sig, ...) {
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index 49a4df44..6478fdee 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -405,6 +405,41 @@ const union in_addr_union *sockaddr_in_addr(const struct sockaddr *_sa) {
         }
 }
 
+int sockaddr_set_in_addr(
+                union sockaddr_union *u,
+                int family,
+                const union in_addr_union *a,
+                uint16_t port) {
+
+        assert(u);
+        assert(a);
+
+        switch (family) {
+
+        case AF_INET:
+                u->in = (struct sockaddr_in) {
+                        .sin_family = AF_INET,
+                        .sin_addr = a->in,
+                        .sin_port = htobe16(port),
+                };
+
+                return 0;
+
+        case AF_INET6:
+                u->in6 = (struct sockaddr_in6) {
+                        .sin6_family = AF_INET6,
+                        .sin6_addr = a->in6,
+                        .sin6_port = htobe16(port),
+                };
+
+                return 0;
+
+        default:
+                return -EAFNOSUPPORT;
+
+        }
+}
+
 int sockaddr_pretty(
                 const struct sockaddr *_sa,
                 socklen_t salen,
@@ -556,7 +591,7 @@ int getpeername_pretty(int fd, bool include_port, char **ret) {
                 return -errno;
 
         if (sa.sa.sa_family == AF_UNIX) {
-                struct ucred ucred = {};
+                struct ucred ucred = UCRED_INVALID;
 
                 /* UNIX connection sockets are anonymous, so let's use
                  * PID/UID as pretty credentials instead */
@@ -1238,10 +1273,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
 
         /* Call with NULL to drop binding */
 
-        if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)));
 }
 
 int socket_bind_to_ifindex(int fd, int ifindex) {
@@ -1250,13 +1282,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
 
         assert(fd >= 0);
 
-        if (ifindex <= 0) {
+        if (ifindex <= 0)
                 /* Drop binding */
-                if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0)
-                        return -errno;
-
-                return 0;
-        }
+                return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
 
         r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
         if (r != -ENOPROTOOPT)
@@ -1345,16 +1373,10 @@ int socket_set_unicast_if(int fd, int af, int ifi) {
         switch (af) {
 
         case AF_INET:
-                if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0)
-                        return -errno;
-
-                return 0;
+                return RET_NERRNO(setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)));
 
         case AF_INET6:
-                if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)) < 0)
-                        return -errno;
-
-                return 0;
+                return RET_NERRNO(setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be)));
 
         default:
                 return -EAFNOSUPPORT;
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index 4caa27c8..ddea5350 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -15,6 +15,7 @@
 #include <sys/un.h>
 
 #include "errno-util.h"
+#include "in-addr-util.h"
 #include "macro.h"
 #include "missing_network.h"
 #include "missing_socket.h"
@@ -108,6 +109,7 @@ bool socket_ipv6_is_enabled(void);
 
 int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
 const union in_addr_union *sockaddr_in_addr(const struct sockaddr *sa);
+int sockaddr_set_in_addr(union sockaddr_union *u, int family, const union in_addr_union *a, uint16_t port);
 
 int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
 int getpeername_pretty(int fd, bool include_port, char **ret);
@@ -331,3 +333,6 @@ static inline int socket_set_recvfragsize(int fd, int af, bool b) {
 #endif /* NM_IGNORED */
 
 int socket_get_mtu(int fd, int af, size_t *ret);
+
+/* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
+#define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }
diff --git a/src/libnm-systemd-shared/src/basic/sort-util.h b/src/libnm-systemd-shared/src/basic/sort-util.h
index a8984fc1..28c06c40 100644
--- a/src/libnm-systemd-shared/src/basic/sort-util.h
+++ b/src/libnm-systemd-shared/src/basic/sort-util.h
@@ -5,14 +5,20 @@
 
 #include "macro.h"
 
+/* This is the same as glibc's internal __compar_d_fn_t type. glibc exports a public comparison_fn_t, for the
+ * external type __compar_fn_t, but doesn't do anything similar for __compar_d_fn_t. Let's hence do that
+ * ourselves, picking a name that is obvious, but likely enough to not clash with glibc's choice of naming if
+ * they should ever add one. */
+typedef int (*comparison_userdata_fn_t)(const void *, const void *, void *);
+
 void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
-                 __compar_d_fn_t compar, void *arg);
+                 comparison_userdata_fn_t compar, void *arg);
 
 #define typesafe_bsearch_r(k, b, n, func, userdata)                     \
         ({                                                              \
                 const typeof(b[0]) *_k = k;                             \
                 int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
-                xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \
+                xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
         })
 
 /**
@@ -20,7 +26,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
  * that only if nmemb > 0.
  */
 static inline void* bsearch_safe(const void *key, const void *base,
-                                 size_t nmemb, size_t size, __compar_fn_t compar) {
+                                 size_t nmemb, size_t size, comparison_fn_t compar) {
         if (nmemb <= 0)
                 return NULL;
 
@@ -32,14 +38,14 @@ static inline void* bsearch_safe(const void *key, const void *base,
         ({                                                              \
                 const typeof(b[0]) *_k = k;                             \
                 int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
-                bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \
+                bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
         })
 
 /**
  * Normal qsort requires base to be nonnull. Here were require
  * that only if nmemb > 0.
  */
-static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
+static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
         if (nmemb <= 1)
                 return;
 
@@ -52,11 +58,11 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_f
 #define typesafe_qsort(p, n, func)                                      \
         ({                                                              \
                 int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
-                _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
+                _qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
         })
 
 #if 0 /* NM_IGNORED */
-static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
+static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
         if (nmemb <= 1)
                 return;
 
@@ -67,7 +73,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_
 #define typesafe_qsort_r(p, n, func, userdata)                          \
         ({                                                              \
                 int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
-                qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \
+                qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
         })
 #endif /* NM_IGNORED */
 
diff --git a/src/libnm-systemd-shared/src/basic/sparse-endian.h b/src/libnm-systemd-shared/src/basic/sparse-endian.h
index 9583dda9..c795d3da 100644
--- a/src/libnm-systemd-shared/src/basic/sparse-endian.h
+++ b/src/libnm-systemd-shared/src/basic/sparse-endian.h
@@ -55,9 +55,9 @@ typedef uint64_t __sd_bitwise be64_t;
 #undef le64toh
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-#define bswap_16_on_le(x) __bswap_16(x)
-#define bswap_32_on_le(x) __bswap_32(x)
-#define bswap_64_on_le(x) __bswap_64(x)
+#define bswap_16_on_le(x) bswap_16(x)
+#define bswap_32_on_le(x) bswap_32(x)
+#define bswap_64_on_le(x) bswap_64(x)
 #define bswap_16_on_be(x) (x)
 #define bswap_32_on_be(x) (x)
 #define bswap_64_on_be(x) (x)
@@ -65,9 +65,9 @@ typedef uint64_t __sd_bitwise be64_t;
 #define bswap_16_on_le(x) (x)
 #define bswap_32_on_le(x) (x)
 #define bswap_64_on_le(x) (x)
-#define bswap_16_on_be(x) __bswap_16(x)
-#define bswap_32_on_be(x) __bswap_32(x)
-#define bswap_64_on_be(x) __bswap_64(x)
+#define bswap_16_on_be(x) bswap_16(x)
+#define bswap_32_on_be(x) bswap_32(x)
+#define bswap_64_on_be(x) bswap_64(x)
 #endif
 
 static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); }
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index 133828bc..8126ce08 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -15,10 +15,13 @@
 #include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "filesystems.h"
+#include "fs-util.h"
 #include "macro.h"
 #include "missing_fs.h"
 #include "missing_magic.h"
 #include "missing_syscall.h"
+#include "nulstr-util.h"
 #include "parse-util.h"
 #include "stat-util.h"
 #include "string-util.h"
@@ -75,27 +78,41 @@ int is_device_node(const char *path) {
 
 int dir_is_empty_at(int dir_fd, const char *path) {
         _cleanup_close_ int fd = -1;
-        _cleanup_closedir_ DIR *d = NULL;
+        /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("."  +
+         * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */
+        DEFINE_DIRENT_BUFFER(buffer, 3);
         struct dirent *de;
+        ssize_t n;
 
         if (path) {
+                assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+
                 fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
                 if (fd < 0)
                         return -errno;
+        } else if (dir_fd == AT_FDCWD) {
+                fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+                if (fd < 0)
+                        return -errno;
         } else {
-                /* Note that DUPing is not enough, as the internal pointer
-                 * would still be shared and moved by FOREACH_DIRENT. */
-                fd = fd_reopen(dir_fd, O_CLOEXEC);
+                /* Note that DUPing is not enough, as the internal pointer would still be shared and moved
+                 * getedents64(). */
+                assert(dir_fd >= 0);
+
+                fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
                 if (fd < 0)
                         return fd;
         }
 
-        d = take_fdopendir(&fd);
-        if (!d)
+        n = getdents64(fd, &buffer, sizeof(buffer));
+        if (n < 0)
                 return -errno;
 
-        FOREACH_DIRENT(de, d, return -errno)
-                return 0;
+        msan_unpoison(&buffer, n);
+
+        FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n)
+                if (!dot_or_dot_dot(de->d_name))
+                        return 0;
 
         return 1;
 }
@@ -178,7 +195,6 @@ int files_same(const char *filea, const char *fileb, int flags) {
                a.st_ino == b.st_ino;
 }
 
-#if 0 /* NM_IGNORED */
 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
         assert(s);
         assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
@@ -186,6 +202,7 @@ bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
         return F_TYPE_EQUAL(s->f_type, magic_value);
 }
 
+#if 0 /* NM_IGNORED */
 int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
         struct statfs s;
 
@@ -194,6 +211,7 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
 
         return is_fs_type(&s, magic_value);
 }
+#endif /* NM_IGNORE */
 
 int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
         struct statfs s;
@@ -204,20 +222,13 @@ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
         return is_fs_type(&s, magic_value);
 }
 
+#if 0 /* NM_IGNORE */
 bool is_temporary_fs(const struct statfs *s) {
-        return is_fs_type(s, TMPFS_MAGIC) ||
-                is_fs_type(s, RAMFS_MAGIC);
+        return fs_in_group(s, FILESYSTEM_SET_TEMPORARY);
 }
 
 bool is_network_fs(const struct statfs *s) {
-        return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
-                is_fs_type(s, CODA_SUPER_MAGIC) ||
-                is_fs_type(s, NCP_SUPER_MAGIC) ||
-                is_fs_type(s, NFS_SUPER_MAGIC) ||
-                is_fs_type(s, SMB_SUPER_MAGIC) ||
-                is_fs_type(s, V9FS_MAGIC) ||
-                is_fs_type(s, AFS_SUPER_MAGIC) ||
-                is_fs_type(s, OCFS2_SUPER_MAGIC);
+        return fs_in_group(s, FILESYSTEM_SET_NETWORK);
 }
 
 int fd_is_temporary_fs(int fd) {
@@ -395,6 +406,7 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret
 
         return 0;
 }
+#endif /* NM_IGNORED */
 
 int proc_mounted(void) {
         int r;
@@ -408,6 +420,7 @@ int proc_mounted(void) {
         return r;
 }
 
+#if 0 /* NM_IGNORED */
 bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
 
         /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h
index 775d694c..a1d88fbb 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.h
+++ b/src/libnm-systemd-shared/src/basic/string-util.h
@@ -60,6 +60,10 @@ static inline const char *empty_to_null(const char *p) {
         return isempty(p) ? NULL : p;
 }
 
+static inline const char *empty_to_na(const char *p) {
+        return isempty(p) ? "n/a" : p;
+}
+
 static inline const char *empty_to_dash(const char *str) {
         return isempty(str) ? "-" : str;
 }
diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c
index 1d840923..3e0faf95 100644
--- a/src/libnm-systemd-shared/src/basic/strv.c
+++ b/src/libnm-systemd-shared/src/basic/strv.c
@@ -18,7 +18,7 @@
 #include "string-util.h"
 #include "strv.h"
 
-char *strv_find(char * const *l, const char *name) {
+char* strv_find(char * const *l, const char *name) {
         char * const *i;
 
         assert(name);
@@ -30,7 +30,7 @@ char *strv_find(char * const *l, const char *name) {
         return NULL;
 }
 
-char *strv_find_case(char * const *l, const char *name) {
+char* strv_find_case(char * const *l, const char *name) {
         char * const *i;
 
         assert(name);
@@ -42,7 +42,7 @@ char *strv_find_case(char * const *l, const char *name) {
         return NULL;
 }
 
-char *strv_find_prefix(char * const *l, const char *name) {
+char* strv_find_prefix(char * const *l, const char *name) {
         char * const *i;
 
         assert(name);
@@ -54,7 +54,7 @@ char *strv_find_prefix(char * const *l, const char *name) {
         return NULL;
 }
 
-char *strv_find_startswith(char * const *l, const char *name) {
+char* strv_find_startswith(char * const *l, const char *name) {
         char * const *i, *e;
 
         assert(name);
@@ -71,19 +71,17 @@ char *strv_find_startswith(char * const *l, const char *name) {
         return NULL;
 }
 
-char **strv_free(char **l) {
-        char **k;
-
+char** strv_free(char **l) {
         if (!l)
                 return NULL;
 
-        for (k = l; *k; k++)
+        for (char **k = l; *k; k++)
                 free(*k);
 
         return mfree(l);
 }
 
-char **strv_free_erase(char **l) {
+char** strv_free_erase(char **l) {
         char **i;
 
         STRV_FOREACH(i, l)
@@ -92,7 +90,7 @@ char **strv_free_erase(char **l) {
         return mfree(l);
 }
 
-char **strv_copy(char * const *l) {
+char** strv_copy(char * const *l) {
         char **r, **k;
 
         k = r = new(char*, strv_length(l) + 1);
@@ -124,7 +122,7 @@ size_t strv_length(char * const *l) {
         return n;
 }
 
-char **strv_new_ap(const char *x, va_list ap) {
+char** strv_new_ap(const char *x, va_list ap) {
         _cleanup_strv_free_ char **a = NULL;
         size_t n = 0, i = 0;
         va_list aq;
@@ -163,7 +161,7 @@ char **strv_new_ap(const char *x, va_list ap) {
         return TAKE_PTR(a);
 }
 
-char **strv_new_internal(const char *x, ...) {
+char** strv_new_internal(const char *x, ...) {
         char **r;
         va_list ap;
 
@@ -176,7 +174,7 @@ char **strv_new_internal(const char *x, ...) {
 
 int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
         char * const *s, **t;
-        size_t p, q, i = 0, j;
+        size_t p, q, i = 0;
 
         assert(a);
 
@@ -197,7 +195,6 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
         *a = t;
 
         STRV_FOREACH(s, b) {
-
                 if (filter_duplicates && strv_contains(t, *s))
                         continue;
 
@@ -214,7 +211,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
         return (int) i;
 
 rollback:
-        for (j = 0; j < i; j++)
+        for (size_t j = 0; j < i; j++)
                 free(t[p + j]);
 
         t[p] = NULL;
@@ -289,7 +286,6 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
                         return -ENOMEM;
 
                 l[n++] = TAKE_PTR(word);
-
                 l[n] = NULL;
         }
 
@@ -376,7 +372,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
 }
 #endif /* NM_IGNORED */
 
-char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
+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;
@@ -483,7 +479,7 @@ int strv_push_pair(char ***l, char *a, char *b) {
 
 int strv_insert(char ***l, size_t position, char *value) {
         char **c;
-        size_t n, m, i;
+        size_t n, m;
 
         if (!value)
                 return 0;
@@ -500,18 +496,14 @@ int strv_insert(char ***l, size_t position, char *value) {
         if (!c)
                 return -ENOMEM;
 
-        for (i = 0; i < position; i++)
+        for (size_t i = 0; i < position; i++)
                 c[i] = (*l)[i];
         c[position] = value;
-        for (i = position; i < n; i++)
+        for (size_t i = position; i < n; i++)
                 c[i+1] = (*l)[i];
-
         c[n+1] = NULL;
 
-        free(*l);
-        *l = c;
-
-        return 0;
+        return free_and_replace(*l, c);
 }
 
 int strv_consume(char ***l, char *value) {
@@ -608,7 +600,7 @@ int strv_extend_front(char ***l, const char *value) {
         return 0;
 }
 
-char **strv_uniq(char **l) {
+char** strv_uniq(char **l) {
         char **i;
 
         /* Drops duplicate entries. The first identical string will be
@@ -630,7 +622,7 @@ bool strv_is_uniq(char * const *l) {
         return true;
 }
 
-char **strv_remove(char **l, const char *s) {
+char** strv_remove(char **l, const char *s) {
         char **f, **t;
 
         if (!l)
@@ -651,7 +643,7 @@ char **strv_remove(char **l, const char *s) {
         return l;
 }
 
-char **strv_parse_nulstr(const char *s, size_t l) {
+char** strv_parse_nulstr(const char *s, size_t l) {
         /* l is the length of the input data, which will be split at NULs into
          * elements of the resulting strv. Hence, the number of items in the resulting strv
          * will be equal to one plus the number of NUL bytes in the l bytes starting at s,
@@ -663,7 +655,6 @@ char **strv_parse_nulstr(const char *s, size_t l) {
          * empty strings in s.
          */
 
-        const char *p;
         size_t c = 0, i = 0;
         char **v;
 
@@ -672,7 +663,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
         if (l <= 0)
                 return new0(char*, 1);
 
-        for (p = s; p < s + l; p++)
+        for (const char *p = s; p < s + l; p++)
                 if (*p == 0)
                         c++;
 
@@ -683,8 +674,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
         if (!v)
                 return NULL;
 
-        p = s;
-        while (p < s + l) {
+        for (const char *p = s; p < s + l; ) {
                 const char *e;
 
                 e = memchr(p, 0, s + l - p);
@@ -709,7 +699,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
 }
 
 #if 0 /* NM_IGNORED */
-char **strv_split_nulstr(const char *s) {
+char** strv_split_nulstr(const char *s) {
         const char *i;
         char **r = NULL;
 
@@ -786,7 +776,7 @@ static int str_compare(char * const *a, char * const *b) {
         return strcmp(*a, *b);
 }
 
-char **strv_sort(char **l) {
+char** strv_sort(char **l) {
         typesafe_qsort(l, strv_length(l), str_compare);
         return l;
 }
@@ -836,21 +826,21 @@ int strv_extendf(char ***l, const char *format, ...) {
         return strv_consume(l, x);
 }
 
-char **strv_reverse(char **l) {
-        size_t n, i;
+char** strv_reverse(char **l) {
+        size_t n;
 
         n = strv_length(l);
         if (n <= 1)
                 return l;
 
-        for (i = 0; i < n / 2; i++)
+        for (size_t i = 0; i < n / 2; i++)
                 SWAP_TWO(l[i], l[n-1-i]);
 
         return l;
 }
 
 #if 0 /* NM_IGNORED */
-char **strv_shell_escape(char **l, const char *bad) {
+char** strv_shell_escape(char **l, const char *bad) {
         char **s;
 
         /* Escapes every character in every string in l that is in bad,
@@ -882,19 +872,7 @@ bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *
         return false;
 }
 
-char ***strv_free_free(char ***l) {
-        char ***i;
-
-        if (!l)
-                return NULL;
-
-        for (i = l; *i; i++)
-                strv_free(*i);
-
-        return mfree(l);
-}
-
-char **strv_skip(char **l, size_t n) {
+char** strv_skip(char **l, size_t n) {
 
         while (n > 0) {
                 if (strv_isempty(l))
@@ -907,7 +885,7 @@ char **strv_skip(char **l, size_t n) {
 }
 
 int strv_extend_n(char ***l, const char *value, size_t n) {
-        size_t i, j, k;
+        size_t i, k;
         char **nl;
 
         assert(l);
@@ -934,15 +912,15 @@ int strv_extend_n(char ***l, const char *value, size_t n) {
                 if (!nl[i])
                         goto rollback;
         }
-
         nl[i] = NULL;
+
         return 0;
 
 rollback:
-        for (j = k; j < i; j++)
+        for (size_t j = k; j < i; j++)
                 free(nl[j]);
-
         nl[k] = NULL;
+
         return -ENOMEM;
 }
 
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index a56ef941..092d40c8 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -13,23 +13,23 @@
 #include "macro.h"
 #include "string-util.h"
 
-char *strv_find(char * const *l, const char *name) _pure_;
-char *strv_find_case(char * const *l, const char *name) _pure_;
-char *strv_find_prefix(char * const *l, const char *name) _pure_;
-char *strv_find_startswith(char * const *l, const char *name) _pure_;
+char* strv_find(char * const *l, const char *name) _pure_;
+char* strv_find_case(char * const *l, const char *name) _pure_;
+char* strv_find_prefix(char * const *l, const char *name) _pure_;
+char* strv_find_startswith(char * const *l, const char *name) _pure_;
 
 #define strv_contains(l, s) (!!strv_find((l), (s)))
 #define strv_contains_case(l, s) (!!strv_find_case((l), (s)))
 
-char **strv_free(char **l);
+char** strv_free(char **l);
 DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
 #define _cleanup_strv_free_ _cleanup_(strv_freep)
 
-char **strv_free_erase(char **l);
+char** strv_free_erase(char **l);
 DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
 #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
 
-char **strv_copy(char * const *l);
+char** strv_copy(char * const *l);
 size_t strv_length(char * const *l) _pure_;
 
 int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
@@ -50,8 +50,8 @@ int strv_consume(char ***l, char *value);
 int strv_consume_pair(char ***l, char *a, char *b);
 int strv_consume_prepend(char ***l, char *value);
 
-char **strv_remove(char **l, const char *s);
-char **strv_uniq(char **l);
+char** strv_remove(char **l, const char *s);
+char** strv_uniq(char **l);
 bool strv_is_uniq(char * const *l);
 
 int strv_compare(char * const *a, char * const *b);
@@ -59,8 +59,8 @@ static inline bool strv_equal(char * const *a, char * const *b) {
         return strv_compare(a, b) == 0;
 }
 
-char **strv_new_internal(const char *x, ...) _sentinel_;
-char **strv_new_ap(const char *x, va_list ap);
+char** strv_new_internal(const char *x, ...) _sentinel_;
+char** strv_new_ap(const char *x, va_list ap);
 #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL)
 
 #define STRV_IGNORE ((const char *) POINTER_MAX)
@@ -74,7 +74,7 @@ static inline bool strv_isempty(char * const *l) {
 }
 
 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) {
+static inline char** strv_split(const char *s, const char *separators) {
         char **ret;
 
         if (strv_split_full(&ret, s, separators, 0) < 0)
@@ -87,7 +87,7 @@ int strv_split_and_extend_full(char ***t, const char *s, const char *separators,
 #define strv_split_and_extend(t, s, sep, dup) strv_split_and_extend_full(t, s, sep, dup, 0)
 
 int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags);
-static inline char **strv_split_newlines(const char *s) {
+static inline char** strv_split_newlines(const char *s) {
         char **ret;
 
         if (strv_split_newlines_full(&ret, s, 0) < 0)
@@ -101,13 +101,13 @@ static inline char **strv_split_newlines(const char *s) {
  * 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);
+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_full(l, separator, NULL, false);
 }
 
-char **strv_parse_nulstr(const char *s, size_t l);
-char **strv_split_nulstr(const char *s);
+char** strv_parse_nulstr(const char *s, size_t l);
+char** strv_split_nulstr(const char *s);
 int strv_make_nulstr(char * const *l, char **p, size_t *n);
 
 static inline int strv_from_nulstr(char ***a, const char *nulstr) {
@@ -136,7 +136,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
 #define STRV_FOREACH_PAIR(x, y, l)               \
         for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
 
-char **strv_sort(char **l);
+char** strv_sort(char **l);
 void strv_print(char * const *l);
 
 #define strv_from_stdarg_alloca(first)                          \
@@ -206,13 +206,16 @@ void strv_print(char * const *l);
                 _found;                                         \
         })
 
-#define FOREACH_STRING(x, y, ...)                                       \
-        for (char **_l = STRV_MAKE(({ x = y; }), ##__VA_ARGS__);        \
+#define _FOREACH_STRING(uniq, x, y, ...)                                \
+        for (char **UNIQ_T(l, uniq) = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \
              x;                                                         \
-             x = *(++_l))
+             x = *(++UNIQ_T(l, uniq)))
 
-char **strv_reverse(char **l);
-char **strv_shell_escape(char **l, const char *bad);
+#define FOREACH_STRING(x, y, ...)                       \
+        _FOREACH_STRING(UNIQ, x, y, ##__VA_ARGS__)
+
+char** strv_reverse(char **l);
+char** strv_shell_escape(char **l, const char *bad);
 
 bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos);
 static inline bool strv_fnmatch(char* const* patterns, const char *s) {
@@ -225,10 +228,7 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i
                strv_fnmatch_full(patterns, s, flags, NULL);
 }
 
-char ***strv_free_free(char ***l);
-DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free);
-
-char **strv_skip(char **l, size_t n);
+char** strv_skip(char **l, size_t n);
 
 int strv_extend_n(char ***l, const char *value, size_t n);
 
diff --git a/src/libnm-systemd-shared/src/basic/strxcpyx.c b/src/libnm-systemd-shared/src/basic/strxcpyx.c
index 39aebb88..2788dd82 100644
--- a/src/libnm-systemd-shared/src/basic/strxcpyx.c
+++ b/src/libnm-systemd-shared/src/basic/strxcpyx.c
@@ -17,57 +17,73 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "string-util.h"
 #include "strxcpyx.h"
 
-size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) {
+size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated) {
+        bool truncated = false;
+
         assert(dest);
         assert(src);
 
-        if (size == 0)
+        if (size == 0) {
+                if (ret_truncated)
+                        *ret_truncated = len > 0;
                 return 0;
+        }
 
         if (len >= size) {
                 if (size > 1)
                         *dest = mempcpy(*dest, src, size-1);
                 size = 0;
+                truncated = true;
         } else if (len > 0) {
                 *dest = mempcpy(*dest, src, len);
                 size -= len;
         }
 
+        if (ret_truncated)
+                *ret_truncated = truncated;
+
         *dest[0] = '\0';
         return size;
 }
 
-size_t strpcpy(char **dest, size_t size, const char *src) {
+size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated) {
         assert(dest);
         assert(src);
 
-        return strnpcpy(dest, size, src, strlen(src));
+        return strnpcpy_full(dest, size, src, strlen(src), ret_truncated);
 }
 
-size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
+size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) {
+        bool truncated = false;
         va_list va;
         int i;
 
         assert(dest);
         assert(src);
 
-        if (size == 0)
-                return 0;
-
         va_start(va, src);
         i = vsnprintf(*dest, size, src, va);
-        if (i < (int)size) {
+        va_end(va);
+
+        if (i < (int) size) {
                 *dest += i;
                 size -= i;
-        } else
+        } else {
                 size = 0;
-        va_end(va);
+                truncated = i > 0;
+        }
+
+        if (ret_truncated)
+                *ret_truncated = truncated;
+
         return size;
 }
 
-size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
+size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) {
+        bool truncated = false;
         va_list va;
 
         assert(dest);
@@ -75,31 +91,38 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
 
         va_start(va, src);
         do {
-                size = strpcpy(dest, size, src);
+                bool t;
+
+                size = strpcpy_full(dest, size, src, &t);
+                truncated = truncated || t;
                 src = va_arg(va, char *);
         } while (src);
         va_end(va);
+
+        if (ret_truncated)
+                *ret_truncated = truncated;
         return size;
 }
 
-size_t strnscpy(char *dest, size_t size, const char *src, size_t len) {
+size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated) {
         char *s;
 
         assert(dest);
         assert(src);
 
         s = dest;
-        return strnpcpy(&s, size, src, len);
+        return strnpcpy_full(&s, size, src, len, ret_truncated);
 }
 
-size_t strscpy(char *dest, size_t size, const char *src) {
+size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated) {
         assert(dest);
         assert(src);
 
-        return strnscpy(dest, size, src, strlen(src));
+        return strnscpy_full(dest, size, src, strlen(src), ret_truncated);
 }
 
-size_t strscpyl(char *dest, size_t size, const char *src, ...) {
+size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) {
+        bool truncated = false;
         va_list va;
         char *s;
 
@@ -109,10 +132,16 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) {
         va_start(va, src);
         s = dest;
         do {
-                size = strpcpy(&s, size, src);
+                bool t;
+
+                size = strpcpy_full(&s, size, src, &t);
+                truncated = truncated || t;
                 src = va_arg(va, char *);
         } while (src);
         va_end(va);
 
+        if (ret_truncated)
+                *ret_truncated = truncated;
+
         return size;
 }
diff --git a/src/libnm-systemd-shared/src/basic/strxcpyx.h b/src/libnm-systemd-shared/src/basic/strxcpyx.h
index cdef492d..4a648ed0 100644
--- a/src/libnm-systemd-shared/src/basic/strxcpyx.h
+++ b/src/libnm-systemd-shared/src/basic/strxcpyx.h
@@ -1,14 +1,33 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <stdbool.h>
 #include <stddef.h>
 
 #include "macro.h"
 
-size_t strnpcpy(char **dest, size_t size, const char *src, size_t len);
-size_t strpcpy(char **dest, size_t size, const char *src);
-size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4);
-size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_;
-size_t strnscpy(char *dest, size_t size, const char *src, size_t len);
-size_t strscpy(char *dest, size_t size, const char *src);
-size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_;
+size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated);
+static inline size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) {
+        return strnpcpy_full(dest, size, src, len, NULL);
+}
+size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated);
+static inline size_t strpcpy(char **dest, size_t size, const char *src) {
+        return strpcpy_full(dest, size, src, NULL);
+}
+size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _printf_(4, 5);
+#define strpcpyf(dest, size, src, ...) \
+        strpcpyf_full((dest), (size), NULL, (src), ##__VA_ARGS__)
+size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_;
+#define strpcpyl(dest, size, src, ...) \
+        strpcpyl_full((dest), (size), NULL, (src), ##__VA_ARGS__)
+size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated);
+static inline size_t strnscpy(char *dest, size_t size, const char *src, size_t len) {
+        return strnscpy_full(dest, size, src, len, NULL);
+}
+size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated);
+static inline size_t strscpy(char *dest, size_t size, const char *src) {
+        return strscpy_full(dest, size, src, NULL);
+}
+size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_;
+#define strscpyl(dest, size, src, ...) \
+        strscpyl_full(dest, size, NULL, src, ##__VA_ARGS__)
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 21e7b24a..d7ea2b38 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -271,7 +271,6 @@ struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n)  {
         return ts;
 }
 
-#if 0 /* NM_IGNORED */
 usec_t timeval_load(const struct timeval *tv) {
         assert(tv);
 
@@ -286,6 +285,7 @@ usec_t timeval_load(const struct timeval *tv) {
                 (usec_t) tv->tv_usec;
 }
 
+#if 0 /* NM_IGNORED */
 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
         assert(tv);
 
@@ -676,7 +676,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
                         goto finish;
 
                 } else if ((k = endswith(t, " ago"))) {
-                        t = strndupa(t, k - t);
+                        t = strndupa_safe(t, k - t);
 
                         r = parse_sec(t, &minus);
                         if (r < 0)
@@ -685,7 +685,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
                         goto finish;
 
                 } else if ((k = endswith(t, " left"))) {
-                        t = strndupa(t, k - t);
+                        t = strndupa_safe(t, k - t);
 
                         r = parse_sec(t, &plus);
                         if (r < 0)
@@ -697,7 +697,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
                 /* See if the timestamp is suffixed with UTC */
                 utc = endswith_no_case(t, " UTC");
                 if (utc)
-                        t = strndupa(t, utc - t);
+                        t = strndupa_safe(t, utc - t);
                 else {
                         const char *e = NULL;
                         int j;
@@ -728,7 +728,7 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
 
                         if (IN_SET(j, 0, 1)) {
                                 /* Found one of the two timezones specified. */
-                                t = strndupa(t, e - t - 1);
+                                t = strndupa_safe(t, e - t - 1);
                                 dst = j;
                                 tzn = tzname[j];
                         }
@@ -929,7 +929,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
 
                 /* Cut off the timezone if we don't need it. */
                 if (with_tz)
-                        t = strndupa(t, last_space - t);
+                        t = strndupa_safe(t, last_space - t);
 
                 shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);
 
diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c
index 8a6404d3..dc5a49e2 100644
--- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c
+++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c
@@ -67,16 +67,9 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
 
 /* This is much like mkostemp() but is subject to umask(). */
 int mkostemp_safe(char *pattern) {
-        int fd = -1;  /* avoid false maybe-uninitialized warning */
-
         assert(pattern);
-
-        RUN_WITH_UMASK(0077)
-                fd = mkostemp(pattern, O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        return fd;
+        BLOCK_WITH_UMASK(0077);
+        return RET_NERRNO(mkostemp(pattern, O_CLOEXEC));
 }
 
 #if 0 /* NM_IGNORED */
@@ -288,8 +281,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
 }
 
 int link_tmpfile(int fd, const char *path, const char *target) {
-        int r;
-
         assert(fd >= 0);
         assert(target);
 
@@ -300,16 +291,10 @@ int link_tmpfile(int fd, const char *path, const char *target) {
          * Note that in both cases we will not replace existing files. This is because linkat() does not support this
          * operation currently (renameat2() does), and there is no nice way to emulate this. */
 
-        if (path) {
-                r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
-                if (r < 0)
-                        return r;
-        } else {
-                if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
-                        return -errno;
-        }
+        if (path)
+                return rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
 
-        return 0;
+        return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW));
 }
 
 int mkdtemp_malloc(const char *template, char **ret) {
diff --git a/src/libnm-systemd-shared/src/basic/umask-util.h b/src/libnm-systemd-shared/src/basic/umask-util.h
index bd7c2bdb..90d18f70 100644
--- a/src/libnm-systemd-shared/src/basic/umask-util.h
+++ b/src/libnm-systemd-shared/src/basic/umask-util.h
@@ -24,3 +24,6 @@ assert_cc((S_IFMT & 0777) == 0);
         for (_cleanup_umask_ mode_t _saved_umask_ = umask(mask) | S_IFMT; \
              FLAGS_SET(_saved_umask_, S_IFMT);                          \
              _saved_umask_ &= 0777)
+
+#define BLOCK_WITH_UMASK(mask) \
+        _unused_ _cleanup_umask_ mode_t _saved_umask_ = umask(mask);
diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c
index dfa010e8..c8e39fe4 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.c
+++ b/src/libnm-systemd-shared/src/basic/utf8.c
@@ -126,11 +126,9 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
 }
 
 bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
-        const char *p;
-
         assert(str);
 
-        for (p = str; length > 0;) {
+        for (const char *p = str; length > 0;) {
                 int encoded_len, r;
                 char32_t val;
 
@@ -293,14 +291,12 @@ char *utf8_escape_non_printable_full(const char *str, size_t console_width, bool
 #endif /* NM_IGNORED */
 
 char *ascii_is_valid(const char *str) {
-        const char *p;
-
         /* Check whether the string consists of valid ASCII bytes,
          * i.e values between 0 and 127, inclusive. */
 
         assert(str);
 
-        for (p = str; *p; p++)
+        for (const char *p = str; *p; p++)
                 if ((unsigned char) *p >= 128)
                         return NULL;
 
@@ -322,6 +318,37 @@ char *ascii_is_valid_n(const char *str, size_t len) {
 }
 #endif /* NM_IGNORED */
 
+int utf8_to_ascii(const char *str, char replacement_char, char **ret) {
+        /* Convert to a string that has only ASCII chars, replacing anything that is not ASCII
+         * by replacement_char. */
+
+        _cleanup_free_ char *ans = new(char, strlen(str) + 1);
+        if (!ans)
+                return -ENOMEM;
+
+        char *q = ans;
+
+        for (const char *p = str; *p; q++) {
+                int l;
+
+                l = utf8_encoded_valid_unichar(p, SIZE_MAX);
+                if (l < 0)  /* Non-UTF-8, let's not even try to propagate the garbage */
+                        return l;
+
+                if (l == 1)
+                        *q = *p;
+                else
+                        /* non-ASCII, we need to replace it */
+                        *q = replacement_char;
+
+                p += l;
+        }
+        *q = '\0';
+
+        *ret = TAKE_PTR(ans);
+        return 0;
+}
+
 /**
  * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
  * @out_utf8: output buffer of at least 4 bytes or NULL
diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h
index b0e969f6..4a06dd62 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.h
+++ b/src/libnm-systemd-shared/src/basic/utf8.h
@@ -21,6 +21,8 @@ static inline char *utf8_is_valid(const char *s) {
 char *ascii_is_valid(const char *s) _pure_;
 char *ascii_is_valid_n(const char *str, size_t len);
 
+int utf8_to_ascii(const char *str, char replacement_char, char **ret);
+
 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)
 
diff --git a/src/libnm-systemd-shared/src/basic/util.c b/src/libnm-systemd-shared/src/basic/util.c
index ed328466..3425d0f0 100644
--- a/src/libnm-systemd-shared/src/basic/util.c
+++ b/src/libnm-systemd-shared/src/basic/util.c
@@ -8,7 +8,6 @@
 
 #include "alloc-util.h"
 #include "build.h"
-#include "dirent-util.h"
 #include "env-file.h"
 #include "env-util.h"
 #include "fd-util.h"
@@ -118,71 +117,6 @@ void in_initrd_force(bool value) {
         saved_in_initrd = value;
 }
 
-int on_ac_power(void) {
-        bool found_offline = false, found_online = false;
-        _cleanup_closedir_ DIR *d = NULL;
-        struct dirent *de;
-
-        d = opendir("/sys/class/power_supply");
-        if (!d)
-                return errno == ENOENT ? true : -errno;
-
-        FOREACH_DIRENT(de, d, return -errno) {
-                _cleanup_close_ int fd = -1, device = -1;
-                char contents[6];
-                ssize_t n;
-
-                device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (device < 0) {
-                        if (IN_SET(errno, ENOENT, ENOTDIR))
-                                continue;
-
-                        return -errno;
-                }
-
-                fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (fd < 0) {
-                        if (errno == ENOENT)
-                                continue;
-
-                        return -errno;
-                }
-
-                n = read(fd, contents, sizeof(contents));
-                if (n < 0)
-                        return -errno;
-
-                if (n != 6 || memcmp(contents, "Mains\n", 6))
-                        continue;
-
-                safe_close(fd);
-                fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-                if (fd < 0) {
-                        if (errno == ENOENT)
-                                continue;
-
-                        return -errno;
-                }
-
-                n = read(fd, contents, sizeof(contents));
-                if (n < 0)
-                        return -errno;
-
-                if (n != 2 || contents[1] != '\n')
-                        return -EIO;
-
-                if (contents[0] == '1') {
-                        found_online = true;
-                        break;
-                } else if (contents[0] == '0')
-                        found_offline = true;
-                else
-                        return -EIO;
-        }
-
-        return found_online || !found_offline;
-}
-
 int container_get_leader(const char *machine, pid_t *pid) {
         _cleanup_free_ char *s = NULL, *class = NULL;
         const char *p;
diff --git a/src/libnm-systemd-shared/src/basic/util.h b/src/libnm-systemd-shared/src/basic/util.h
index b6c51c03..dd1cb4f3 100644
--- a/src/libnm-systemd-shared/src/basic/util.h
+++ b/src/libnm-systemd-shared/src/basic/util.h
@@ -20,13 +20,24 @@ int prot_from_flags(int flags) _const_;
 bool in_initrd(void);
 void in_initrd_force(bool value);
 
-int on_ac_power(void);
-
-static inline unsigned u64log2(uint64_t n) {
+/* Note: log2(0) == log2(1) == 0 here and below. */
+
+#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0)
+#define NONCONST_LOG2ULL(x) ({                                     \
+                unsigned long long _x = (x);                       \
+                _x > 1 ? (unsigned) __builtin_clzll(_x) ^ 63U : 0; \
+        })
+#if 0 /* NM_IGNORED */
+#define LOG2ULL(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2ULL(x), NONCONST_LOG2ULL(x))
+#else /* NM_IGNORED */
+#define LOG2ULL(x) NONCONST_LOG2ULL(x)
+#endif /* NM_IGNORED */
+
+static inline unsigned log2u64(uint64_t x) {
 #if __SIZEOF_LONG_LONG__ == 8
-        return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0;
+        return LOG2ULL(x);
 #else
-#error "Wut?"
+#  error "Wut?"
 #endif
 }
 
@@ -34,26 +45,31 @@ static inline unsigned u32ctz(uint32_t n) {
 #if __SIZEOF_INT__ == 4
         return n != 0 ? __builtin_ctz(n) : 32;
 #else
-#error "Wut?"
+#  error "Wut?"
 #endif
 }
 
-static inline unsigned log2i(int x) {
-        assert(x > 0);
+#define CONST_LOG2U(x) ((x) > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1 : 0)
+#define NONCONST_LOG2U(x) ({                                             \
+                unsigned _x = (x);                                       \
+                _x > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(_x) - 1 : 0; \
+        })
+#if 0 /* NM_IGNORED */
+#define LOG2U(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2U(x), NONCONST_LOG2U(x))
+#else /* NM_IGNORED */
+#define LOG2U(x) NONCONST_LOG2U(x)
+#endif /* NM_IGNORED */
 
-        return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1;
+static inline unsigned log2i(int x) {
+        return LOG2U(x);
 }
 
 static inline unsigned log2u(unsigned x) {
-        assert(x > 0);
-
-        return sizeof(unsigned) * 8 - __builtin_clz(x) - 1;
+        return LOG2U(x);
 }
 
 static inline unsigned log2u_round_up(unsigned x) {
-        assert(x > 0);
-
-        if (x == 1)
+        if (x <= 1)
                 return 0;
 
         return log2u(x - 1) + 1;
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 20d8dabf..d597c743 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -5,13 +5,15 @@
 #include <assert.h>
 #endif
 
-#include "type.h"
+#include <limits.h>
+#include "types-fundamental.h"
 
 #define _align_(x) __attribute__((__aligned__(x)))
 #define _const_ __attribute__((__const__))
 #define _pure_ __attribute__((__pure__))
 #define _section_(x) __attribute__((__section__(x)))
 #define _packed_ __attribute__((__packed__))
+#define _retain_ __attribute__((__retain__))
 #define _used_ __attribute__((__used__))
 #define _unused_ __attribute__((__unused__))
 #define _cleanup_(x) __attribute__((__cleanup__(x)))
@@ -59,8 +61,19 @@
                 #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
                 #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
         #endif
+
+        #define memcpy(a, b, c) CopyMem((a), (b), (c))
+        #define free(a) FreePool(a)
 #endif
 
+/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
+#define ASSERT_PTR(expr)                        \
+        ({                                      \
+                typeof(expr) _expr_ = (expr);   \
+                assert(_expr_);                 \
+                _expr_;                         \
+        })
+
 #if defined(static_assert)
 #define assert_cc(expr)                                                 \
         static_assert(expr, #expr)
@@ -81,8 +94,8 @@
 #define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
 #define __ONCE(o)                                                       \
         ({                                                              \
-                static bool (o) = false;                                \
-                __sync_bool_compare_and_swap(&(o), false, true);        \
+                static sd_bool (o) = sd_false;                          \
+                __sync_bool_compare_and_swap(&(o), sd_false, sd_true);  \
         })
 
 #undef MAX
@@ -232,7 +245,7 @@
 
 #define IN_SET(x, ...)                          \
         ({                                      \
-                sd_bool _found = false;         \
+                sd_bool _found = sd_false;      \
                 /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as  \
                  * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \
                  * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that  \
@@ -241,7 +254,7 @@
                 assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
                 switch(x) {                     \
                 FOR_EACH_MAKE_CASE(__VA_ARGS__) \
-                        _found = true;          \
+                        _found = sd_true;       \
                         break;                  \
                 default:                        \
                         break;                  \
@@ -253,8 +266,9 @@
  * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
 #define TAKE_PTR(ptr)                           \
         ({                                      \
-                typeof(ptr) _ptr_ = (ptr);      \
-                (ptr) = NULL;                   \
+                typeof(ptr) *_pptr_ = &(ptr);   \
+                typeof(ptr) _ptr_ = *_pptr_;    \
+                *_pptr_ = NULL;                 \
                 _ptr_;                          \
         })
 
@@ -264,3 +278,47 @@
  * @x: a string literal.
  */
 #define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0])))
+
+#define mfree(memory)                           \
+        ({                                      \
+                free(memory);                   \
+                (typeof(memory)) NULL;          \
+        })
+
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+        /* sd-boot uses UINTN for size_t, let's make sure SIZE_MAX is correct. */
+        assert_cc(SIZE_MAX == ~(size_t)0);
+
+        /* Check that alignment is exponent of 2 */
+#if SIZE_MAX == UINT_MAX
+        assert(__builtin_popcount(ali) == 1);
+#elif SIZE_MAX == ULONG_MAX
+        assert(__builtin_popcountl(ali) == 1);
+#elif SIZE_MAX == ULLONG_MAX
+        assert(__builtin_popcountll(ali) == 1);
+#else
+        #error "Unexpected size_t"
+#endif
+
+        if (l > SIZE_MAX - (ali - 1))
+                return SIZE_MAX; /* indicate overflow */
+
+        return ((l + ali - 1) & ~(ali - 1));
+}
+
+/* Same as ALIGN_TO but callable in constant contexts. */
+#define CONST_ALIGN_TO(l, ali)                                         \
+        __builtin_choose_expr(                                         \
+                __builtin_constant_p(l) &&                             \
+                __builtin_constant_p(ali) &&                           \
+                __builtin_popcountll(ali) == 1 && /* is power of 2? */ \
+                (l <= SIZE_MAX - (ali - 1)),      /* overflow? */      \
+                ((l) + (ali) - 1) & ~((ali) - 1),                      \
+                VOID_0)
+
+#define UPDATE_FLAG(orig, flag, b)                      \
+        ((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
+#define SET_FLAG(v, flag, b) \
+        (v) = UPDATE_FLAG(v, flag, b)
+#define FLAGS_SET(v, flags) \
+        ((~(v) & (flags)) == 0)
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
index f1703926..73569765 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
@@ -10,7 +10,7 @@
 #include "string-util-fundamental.h"
 
 sd_char *startswith(const sd_char *s, const sd_char *prefix) {
-        sd_size_t l;
+        size_t l;
 
         assert(s);
         assert(prefix);
@@ -24,7 +24,7 @@ sd_char *startswith(const sd_char *s, const sd_char *prefix) {
 
 #ifndef SD_BOOT
 sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
-        sd_size_t l;
+        size_t l;
 
         assert(s);
         assert(prefix);
@@ -38,7 +38,7 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
 #endif
 
 sd_char* endswith(const sd_char *s, const sd_char *postfix) {
-        sd_size_t sl, pl;
+        size_t sl, pl;
 
         assert(s);
         assert(postfix);
@@ -59,7 +59,7 @@ sd_char* endswith(const sd_char *s, const sd_char *postfix) {
 }
 
 sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
-        sd_size_t sl, pl;
+        size_t sl, pl;
 
         assert(s);
         assert(postfix);
diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
index 7455c054..dc0c1202 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
@@ -68,10 +68,10 @@ static inline const sd_char *yes_no(sd_bool b) {
 sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
 
 /* Like startswith(), but operates on arbitrary memory blocks */
-static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) {
+static inline void *memory_startswith(const void *p, size_t sz, const sd_char *token) {
         assert(token);
 
-        sd_size_t n = strlen(token) * sizeof(sd_char);
+        size_t n = strlen(token) * sizeof(sd_char);
         if (sz < n)
                 return NULL;
 
diff --git a/src/libnm-systemd-shared/src/fundamental/type.h b/src/libnm-systemd-shared/src/fundamental/type.h
deleted file mode 100644
index f645d2de..00000000
--- a/src/libnm-systemd-shared/src/fundamental/type.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#ifdef SD_BOOT
-#include <efi.h>
-
-typedef BOOLEAN sd_bool;
-typedef CHAR16  sd_char;
-typedef INTN    sd_int;
-typedef UINTN   sd_size_t;
-
-#define true    TRUE
-#define false   FALSE
-#else
-#include <stdbool.h>
-#include <stdint.h>
-
-typedef bool    sd_bool;
-typedef char    sd_char;
-typedef int     sd_int;
-typedef size_t  sd_size_t;
-#endif
diff --git a/src/libnm-systemd-shared/src/fundamental/types-fundamental.h b/src/libnm-systemd-shared/src/fundamental/types-fundamental.h
new file mode 100644
index 00000000..5977e40c
--- /dev/null
+++ b/src/libnm-systemd-shared/src/fundamental/types-fundamental.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* This defines a number of basic types that are one thing in userspace and another in the UEFI environment,
+ * but mostly the same in concept and behaviour.
+ *
+ * Note: if the definition of these types/values has slightly different semantics in userspace and in the
+ * UEFI environment then please prefix its name with "sd_" to make clear these types have special semantics,
+ * and *we* defined them. Otherwise, if the types are effectively 100% identical in behaviour in userspace
+ * and UEFI environment you can omit the prefix. (Examples: sd_char is 8 bit in userspace and 16 bit in UEFI
+ * space hence it should have the sd_ prefix; but size_t in userspace and UINTN in UEFI environment are 100%
+ * defined the same way ultimately, hence it's OK to just define size_t as alias to UINTN in UEFI
+ * environment, so that size_t can be used everywhere, without any "sd_" prefix.)
+ *
+ * Note: we generally prefer the userspace names of types and concepts. i.e. if in doubt please name types
+ * after the userspace vocabulary, and let's keep UEFI vocabulary specific to the UEFI build environment. */
+
+#ifdef SD_BOOT
+#include <efi.h>
+
+typedef BOOLEAN sd_bool;
+typedef CHAR16  sd_char;
+typedef INTN    sd_int;
+typedef UINTN   size_t;
+
+#define sd_true  TRUE
+#define sd_false FALSE
+#else
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef bool    sd_bool;
+typedef char    sd_char;
+typedef int     sd_int;
+
+#define sd_true  true
+#define sd_false false
+
+#endif
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c
index 473f6a02..b82a9a1c 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.c
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.c
@@ -687,7 +687,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
         }
 
         /* Found it! Now generate the new name */
-        prefix = strndupa(name, saved_before - name);
+        prefix = strndupa_safe(name, saved_before - name);
 
         r = dns_name_concat(prefix, new_suffix, 0, ret);
         if (r < 0)
@@ -1037,7 +1037,7 @@ static bool dns_service_name_label_is_valid(const char *label, size_t n) {
         if (memchr(label, 0, n))
                 return false;
 
-        s = strndupa(label, n);
+        s = strndupa_safe(label, n);
         return dns_service_name_is_valid(s);
 }
 
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.h b/src/libnm-systemd-shared/src/shared/dns-domain.h
index d0195c58..c237f761 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.h
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.h
@@ -62,6 +62,10 @@ static inline int dns_name_is_valid_ldh(const char *s) {
         return 1;
 }
 
+static inline bool dns_name_is_empty(const char *s) {
+        return isempty(s) || streq(s, ".");
+}
+
 void dns_name_hash_func(const char *s, struct siphash *state);
 int dns_name_compare_func(const char *a, const char *b);
 extern const struct hash_ops dns_name_hash_ops;