about summary refs log tree commit diff
path: root/src/libnm-systemd-shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-shared')
-rw-r--r--src/libnm-systemd-shared/meson.build2
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.c1
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.h13
-rw-r--r--src/libnm-systemd-shared/src/basic/assert-util.h48
-rw-r--r--src/libnm-systemd-shared/src/basic/bitfield.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/cgroup-util.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/chattr-util.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/chattr-util.h13
-rw-r--r--src/libnm-systemd-shared/src/basic/devnum-util.h10
-rw-r--r--src/libnm-systemd-shared/src/basic/env-file.c1
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c41
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/errno-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.c25
-rw-r--r--src/libnm-systemd-shared/src/basic/escape.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c17
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c41
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c231
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/format-ifname.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/format-util.c1
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c15
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.c208
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.h128
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.c78
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.c40
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.h89
-rw-r--r--src/libnm-systemd-shared/src/basic/hexdecoct.h14
-rw-r--r--src/libnm-systemd-shared/src/basic/hostname-util.c50
-rw-r--r--src/libnm-systemd-shared/src/basic/hostname-util.h36
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c18
-rw-r--r--src/libnm-systemd-shared/src/basic/include/net/if.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/include/netinet/in.h19
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.c7
-rw-r--r--src/libnm-systemd-shared/src/basic/io-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/iovec-util.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/list.h6
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.c120
-rw-r--r--src/libnm-systemd-shared/src/basic/lock-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/log.h229
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h131
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.c16
-rw-r--r--src/libnm-systemd-shared/src/basic/memory-util.h88
-rw-r--r--src/libnm-systemd-shared/src/basic/mempool.c1
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_fcntl.h73
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_fs.h14
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_random.h21
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_socket.h57
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_stat.h139
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_syscall.h482
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_threads.h13
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_type.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_wait.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/mountpoint-util.c823
-rw-r--r--src/libnm-systemd-shared/src/basic/mountpoint-util.h87
-rw-r--r--src/libnm-systemd-shared/src/basic/namespace-util.h5
-rw-r--r--src/libnm-systemd-shared/src/basic/ordered-set.h21
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c14
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.h16
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c71
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/pidfd-util.c55
-rw-r--r--src/libnm-systemd-shared/src/basic/pidref.h22
-rw-r--r--src/libnm-systemd-shared/src/basic/prioq.c12
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c259
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h39
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.c3
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/ratelimit.h52
-rw-r--r--src/libnm-systemd-shared/src/basic/set.h31
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/signal-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c94
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.h61
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c91
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.h30
-rw-r--r--src/libnm-systemd-shared/src/basic/stdio-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.c108
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.h18
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.c58
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h10
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c5
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/tmpfile-util.c1
-rw-r--r--src/libnm-systemd-shared/src/basic/user-util.c1243
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.c25
-rw-r--r--src/libnm-systemd-shared/src/basic/utf8.h3
-rw-r--r--src/libnm-systemd-shared/src/fundamental/assert-fundamental.h141
-rw-r--r--src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h12
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h187
-rw-r--r--src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h115
-rw-r--r--src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c7
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c2
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h3
-rw-r--r--src/libnm-systemd-shared/src/shared/dns-domain.c7
99 files changed, 3873 insertions, 2511 deletions
diff --git a/src/libnm-systemd-shared/meson.build b/src/libnm-systemd-shared/meson.build
index 7a88d78d..27199546 100644
--- a/src/libnm-systemd-shared/meson.build
+++ b/src/libnm-systemd-shared/meson.build
@@ -30,6 +30,7 @@ libnm_systemd_shared = static_library(
     'src/basic/locale-util.c',
     'src/basic/memory-util.c',
     'src/basic/mempool.c',
+    'src/basic/mountpoint-util.c',
     'src/basic/ordered-set.c',
     'src/basic/parse-util.c',
     'src/basic/path-util.c',
@@ -49,6 +50,7 @@ libnm_systemd_shared = static_library(
     'src/basic/time-util.c',
     'src/basic/tmpfile-util.c',
     'src/basic/utf8.c',
+    'src/basic/user-util.c',
     'src/fundamental/sha256-fundamental.c',
     'src/fundamental/string-util-fundamental.c',
     'src/shared/dns-domain.c',
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.c b/src/libnm-systemd-shared/src/basic/alloc-util.c
index f9686666..a2c0caa1 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.c
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.c
@@ -8,7 +8,6 @@
 
 #include "alloc-util.h"
 #include "macro.h"
-#include "memory-util.h"
 
 void* memdup(const void *p, size_t l) {
         void *ret;
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index ba712982..764a364a 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -7,7 +7,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "assert-util.h"
 #include "macro.h"
+#include "memory-util.h"
 
 #if HAS_FEATURE_MEMORY_SANITIZER
 #  include <sanitizer/msan_interface.h>
@@ -119,15 +121,6 @@ _malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t need, size_t s
         return malloc(size * need ?: 1);
 }
 
-#if !HAVE_REALLOCARRAY
-_alloc_(2, 3) static inline void *reallocarray(void *p, size_t need, size_t size) {
-        if (size_multiply_overflow(size, need))
-                return NULL;
-
-        return realloc(p, size * need ?: 1);
-}
-#endif
-
 _alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t need, size_t size) {
         if (size_multiply_overflow(size, need))
                 return NULL;
@@ -275,5 +268,3 @@ _alloc_(2) static inline void *realloc0(void *p, size_t new_size) {
 
         return q;
 }
-
-#include "memory-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/assert-util.h b/src/libnm-systemd-shared/src/basic/assert-util.h
new file mode 100644
index 00000000..30860594
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/assert-util.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "assert-fundamental.h"
+#include "macro.h"
+
+/* Logging for various assertions */
+
+void log_set_assert_return_is_critical(bool b);
+bool log_get_assert_return_is_critical(void) _pure_;
+
+#if 0 /* NM_IGNORED */
+void log_assert_failed_return(const char *text, const char *file, int line, const char *func);
+#else /* NM_IGNORED */
+#define log_assert_failed_return(text, file, line, func)                         \
+    ({                                                                           \
+        log_internal(LOG_DEBUG,                                                  \
+                     0,                                                          \
+                     file,                                                       \
+                     line,                                                       \
+                     func,                                                       \
+                     "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", \
+                     text,                                                       \
+                     file,                                                       \
+                     line,                                                       \
+                     func);                                                      \
+        g_return_if_fail_warning(G_LOG_DOMAIN, G_STRFUNC, text);                 \
+        (void) 0;                                                                \
+    })
+#endif /* NM_IGNORED */
+
+#define assert_log(expr, message) ((_likely_(expr))                     \
+        ? (true)                                                        \
+        : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
+
+#define assert_return(expr, r)                                          \
+        do {                                                            \
+                if (!assert_log(expr, #expr))                           \
+                        return (r);                                     \
+        } while (false)
+
+#define assert_return_errno(expr, r, err)                               \
+        do {                                                            \
+                if (!assert_log(expr, #expr)) {                         \
+                        errno = err;                                    \
+                        return (r);                                     \
+                }                                                       \
+        } while (false)
diff --git a/src/libnm-systemd-shared/src/basic/bitfield.h b/src/libnm-systemd-shared/src/basic/bitfield.h
index 048e08d7..c6eaaa48 100644
--- a/src/libnm-systemd-shared/src/basic/bitfield.h
+++ b/src/libnm-systemd-shared/src/basic/bitfield.h
@@ -7,6 +7,7 @@
 #define _INDEX_TO_MASK(type, i, uniq)                                   \
         ({                                                              \
                 int UNIQ_T(_i, uniq) = (i);                             \
+                assert(UNIQ_T(_i, uniq) >= 0);                          \
                 assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8);       \
                 ((type)1) << UNIQ_T(_i, uniq);                          \
         })
diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h
index 77294779..269d68c5 100644
--- a/src/libnm-systemd-shared/src/basic/cgroup-util.h
+++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h
@@ -263,8 +263,7 @@ int cg_get_attribute_as_bool(const char *controller, const char *path, const cha
 int cg_get_owner(const char *path, uid_t *ret_uid);
 
 int cg_set_xattr(const char *path, const char *name, const void *value, size_t size, int flags);
-int cg_get_xattr(const char *path, const char *name, void *value, size_t size);
-int cg_get_xattr_malloc(const char *path, const char *name, char **ret);
+int cg_get_xattr_malloc(const char *path, const char *name, char **ret, size_t *ret_size);
 /* Returns negative on error, and 0 or 1 on success for the bool value */
 int cg_get_xattr_bool(const char *path, const char *name);
 int cg_remove_xattr(const char *path, const char *name);
@@ -312,10 +311,6 @@ int cg_mask_supported_subtree(const char *root, CGroupMask *ret);
 int cg_mask_from_string(const char *s, CGroupMask *ret);
 int cg_mask_to_string(CGroupMask mask, char **ret);
 
-int cg_kernel_controllers(Set **controllers);
-
-bool cg_ns_supported(void);
-bool cg_freezer_supported(void);
 bool cg_kill_supported(void);
 
 int cg_all_unified(void);
@@ -329,9 +324,6 @@ static inline int cg_unified(void) {
 const char* cgroup_controller_to_string(CGroupController c) _const_;
 CGroupController cgroup_controller_from_string(const char *s) _pure_;
 
-bool is_cgroup_fs(const struct statfs *s);
-bool fd_is_cgroup_fs(int fd);
-
 typedef enum ManagedOOMMode {
         MANAGED_OOM_AUTO,
         MANAGED_OOM_KILL,
@@ -365,4 +357,4 @@ typedef union {
                 .file_handle.handle_type = FILEID_KERNFS,       \
         }
 
-#define CG_FILE_HANDLE_CGROUPID(fh) (*(uint64_t*) (fh).file_handle.f_handle)
+#define CG_FILE_HANDLE_CGROUPID(fh) (*CAST_ALIGN_PTR(uint64_t, (fh).file_handle.f_handle))
diff --git a/src/libnm-systemd-shared/src/basic/chattr-util.c b/src/libnm-systemd-shared/src/basic/chattr-util.c
index 2a63e46d..0a27e023 100644
--- a/src/libnm-systemd-shared/src/basic/chattr-util.c
+++ b/src/libnm-systemd-shared/src/basic/chattr-util.c
@@ -6,13 +6,13 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
-#include <linux/fs.h>
 
 #include "bitfield.h"
 #include "chattr-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "fs-util.h"
+#include "log.h"
 #include "macro.h"
 #include "string-util.h"
 
diff --git a/src/libnm-systemd-shared/src/basic/chattr-util.h b/src/libnm-systemd-shared/src/basic/chattr-util.h
index 1fe38e32..472054d5 100644
--- a/src/libnm-systemd-shared/src/basic/chattr-util.h
+++ b/src/libnm-systemd-shared/src/basic/chattr-util.h
@@ -2,7 +2,6 @@
 #pragma once
 
 #include <fcntl.h>
-#include <linux/fs.h>
 #include <stdbool.h>
 #include <stddef.h>
 
@@ -41,14 +40,14 @@ typedef enum ChattrApplyFlags {
 } ChattrApplyFlags;
 
 int chattr_full(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *ret_previous, unsigned *ret_final, ChattrApplyFlags flags);
-static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask, unsigned *previous) {
-        return chattr_full(dir_fd, path, value, mask, previous, NULL, 0);
+static inline int chattr_at(int dir_fd, const char *path, unsigned value, unsigned mask) {
+        return chattr_full(dir_fd, path, value, mask, NULL, NULL, 0);
 }
-static inline int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
-        return chattr_full(fd, NULL, value, mask, previous, NULL, 0);
+static inline int chattr_fd(int fd, unsigned value, unsigned mask) {
+        return chattr_full(fd, NULL, value, mask, NULL, NULL, 0);
 }
-static inline int chattr_path(const char *path, unsigned value, unsigned mask, unsigned *previous) {
-        return chattr_full(AT_FDCWD, path, value, mask, previous, NULL, 0);
+static inline int chattr_path(const char *path, unsigned value, unsigned mask) {
+        return chattr_full(AT_FDCWD, path, value, mask, NULL, NULL, 0);
 }
 
 int read_attr_fd(int fd, unsigned *ret);
diff --git a/src/libnm-systemd-shared/src/basic/devnum-util.h b/src/libnm-systemd-shared/src/basic/devnum-util.h
index e109de99..0efca567 100644
--- a/src/libnm-systemd-shared/src/basic/devnum-util.h
+++ b/src/libnm-systemd-shared/src/basic/devnum-util.h
@@ -9,6 +9,9 @@
 
 int parse_devnum(const char *s, dev_t *ret);
 
+#define DEVNUM_MAJOR_MAX ((UINT32_C(1) << 12) - 1U)
+#define DEVNUM_MINOR_MAX ((UINT32_C(1) << 20) - 1U)
+
 /* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
  * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
  * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
@@ -18,14 +21,14 @@ int parse_devnum(const char *s, dev_t *ret);
 #define DEVICE_MAJOR_VALID(x)                                           \
         ({                                                              \
                 typeof(x) _x = (x), _y = 0;                             \
-                _x >= _y && _x < (UINT32_C(1) << 12);                   \
+                _x >= _y && _x <= DEVNUM_MAJOR_MAX;                     \
                                                                         \
         })
 
 #define DEVICE_MINOR_VALID(x)                                           \
         ({                                                              \
                 typeof(x) _x = (x), _y = 0;                             \
-                _x >= _y && _x < (UINT32_C(1) << 20);                   \
+                _x >= _y && _x <= DEVNUM_MINOR_MAX;                     \
         })
 
 int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
@@ -54,3 +57,6 @@ static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
 static inline bool devnum_is_zero(dev_t d) {
         return major(d) == 0 && minor(d) == 0;
 }
+
+#define DEVNUM_TO_PTR(u) ((void*) (uintptr_t) (u))
+#define PTR_TO_DEVNUM(p) ((dev_t) ((uintptr_t) (p)))
diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c
index 75b2febf..d705765b 100644
--- a/src/libnm-systemd-shared/src/basic/env-file.c
+++ b/src/libnm-systemd-shared/src/basic/env-file.c
@@ -9,6 +9,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "log.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tmpfile-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index 2fe7332c..8dd52c09 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -13,6 +13,7 @@
 #include "errno-util.h"
 #include "escape.h"
 #include "extract-word.h"
+#include "log.h"
 #include "macro.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -549,7 +550,7 @@ char* strv_env_get_n(char * const *l, const char *name, size_t k, ReplaceEnvFlag
                         return NULL;
 
                 t = strndupa_safe(name, k);
-                return getenv(t);
+                return secure_getenv(t);
         };
 
         return NULL;
@@ -700,7 +701,7 @@ int replace_env_full(
         _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
         const char *e, *word = format, *test_value = NULL; /* test_value is initialized to appease gcc */
         _cleanup_free_ char *s = NULL;
-        char ***pu, ***pb, *k;
+        char ***pu, ***pb;
         size_t i, len = 0; /* len is initialized to appease gcc */
         int nest = 0, r;
 
@@ -722,33 +723,24 @@ int replace_env_full(
 
                 case CURLY:
                         if (*e == '{') {
-                                k = strnappend(s, word, e-word-1);
-                                if (!k)
+                                if (!strextendn(&s, word, e-word-1))
                                         return -ENOMEM;
 
-                                free_and_replace(s, k);
-
                                 word = e-1;
                                 state = VARIABLE;
                                 nest++;
 
                         } else if (*e == '$') {
-                                k = strnappend(s, word, e-word);
-                                if (!k)
+                                if (!strextendn(&s, word, e-word))
                                         return -ENOMEM;
 
-                                free_and_replace(s, k);
-
                                 word = e+1;
                                 state = WORD;
 
                         } else if (FLAGS_SET(flags, REPLACE_ENV_ALLOW_BRACELESS) && strchr(VALID_BASH_ENV_NAME_CHARS, *e)) {
-                                k = strnappend(s, word, e-word-1);
-                                if (!k)
+                                if (!strextendn(&s, word, e-word-1))
                                         return -ENOMEM;
 
-                                free_and_replace(s, k);
-
                                 word = e-1;
                                 state = VARIABLE_RAW;
 
@@ -1121,7 +1113,7 @@ int getenv_steal_erase(const char *name, char **ret) {
          * it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for
          * testing, and given that people are likely going to misuse this, be thorough) */
 
-        e = getenv(name);
+        e = secure_getenv(name);
         if (!e) {
                 if (ret)
                         *ret = NULL;
@@ -1145,25 +1137,6 @@ int getenv_steal_erase(const char *name, char **ret) {
         return 1;
 }
 
-int set_full_environment(char **env) {
-        int r;
-
-        clearenv();
-
-        STRV_FOREACH(e, env) {
-                _cleanup_free_ char *k = NULL, *v = NULL;
-
-                r = split_pair(*e, "=", &k, &v);
-                if (r < 0)
-                        return r;
-
-                if (setenv(k, v, /* overwrite= */ true) < 0)
-                        return -errno;
-        }
-
-        return 0;
-}
-
 int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
         _cleanup_free_ char *value = NULL;
         va_list ap;
diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h
index 203ed65b..52771ecc 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.h
+++ b/src/libnm-systemd-shared/src/basic/env-util.h
@@ -82,6 +82,4 @@ int getenv_path_list(const char *name, char ***ret_paths);
 
 int getenv_steal_erase(const char *name, char **ret);
 
-int set_full_environment(char **env);
-
 int setenvf(const char *name, bool overwrite, const char *valuef, ...) _printf_(3,4);
diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h
index 02572e3b..01b4d59e 100644
--- a/src/libnm-systemd-shared/src/basic/errno-util.h
+++ b/src/libnm-systemd-shared/src/basic/errno-util.h
@@ -5,6 +5,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "assert-util.h"
 #include "macro.h"
 
 /* strerror(3) says that glibc uses a maximum length of 1024 bytes. */
diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c
index e882aced..ff1d817c 100644
--- a/src/libnm-systemd-shared/src/basic/escape.c
+++ b/src/libnm-systemd-shared/src/basic/escape.c
@@ -82,10 +82,15 @@ char* cescape_length(const char *s, size_t n) {
         const char *f;
         char *r, *t;
 
+        /* Does C style string escaping. May be reversed with cunescape(). */
+
         assert(s || n == 0);
 
-        /* Does C style string escaping. May be reversed with
-         * cunescape(). */
+        if (n == SIZE_MAX)
+                n = strlen(s);
+
+        if (n > (SIZE_MAX - 1) / 4)
+                return NULL;
 
         r = new(char, n*4 + 1);
         if (!r)
@@ -99,12 +104,6 @@ char* cescape_length(const char *s, size_t n) {
         return r;
 }
 
-char* cescape(const char *s) {
-        assert(s);
-
-        return cescape_length(s, strlen(s));
-}
-
 int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) {
         int r = 1;
 
@@ -452,7 +451,7 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl
 char* octescape(const char *s, size_t len) {
         char *buf, *t;
 
-        /* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */
+        /* Escapes \ and " chars, in \nnn style escaping. */
 
         assert(s || len == 0);
 
@@ -482,13 +481,19 @@ char* octescape(const char *s, size_t len) {
         return buf;
 }
 
-char* decescape(const char *s, const char *bad, size_t len) {
+char* decescape(const char *s, size_t len, const char *bad) {
         char *buf, *t;
 
         /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
 
         assert(s || len == 0);
 
+        if (len == SIZE_MAX)
+                len = strlen(s);
+
+        if (len > (SIZE_MAX - 1) / 4)
+                return NULL;
+
         t = buf = new(char, len * 4 + 1);
         if (!buf)
                 return NULL;
diff --git a/src/libnm-systemd-shared/src/basic/escape.h b/src/libnm-systemd-shared/src/basic/escape.h
index 65caf0db..05c27f68 100644
--- a/src/libnm-systemd-shared/src/basic/escape.h
+++ b/src/libnm-systemd-shared/src/basic/escape.h
@@ -8,7 +8,6 @@
 #include <uchar.h>
 
 #include "string-util.h"
-#include "missing_type.h"
 
 /* What characters are special in the shell? */
 /* must be escaped outside and inside double-quotes */
@@ -41,9 +40,11 @@ typedef enum ShellEscapeFlags {
         SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */
 } ShellEscapeFlags;
 
-char* cescape(const char *s);
-char* cescape_length(const char *s, size_t n);
 int cescape_char(char c, char *buf);
+char* cescape_length(const char *s, size_t n) _nonnull_if_nonzero_(1, 2);
+static inline char* cescape(const char *s) {
+        return cescape_length(s, SIZE_MAX);
+}
 
 int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul);
 
@@ -65,7 +66,7 @@ static inline char* xescape(const char *s, const char *bad) {
         return xescape_full(s, bad, SIZE_MAX, 0);
 }
 char* octescape(const char *s, size_t len);
-char* decescape(const char *s, const char *bad, size_t len);
+char* decescape(const char *s, size_t len, const char *bad) _nonnull_if_nonzero_(1, 2);
 char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags);
 
 char* shell_escape(const char *s, const char *bad);
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 2643c974..168fd329 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -10,6 +10,7 @@
 
 #include "ether-addr-util.h"
 #include "hexdecoct.h"
+#include "log.h"
 #include "macro.h"
 #include "string-util.h"
 
@@ -87,22 +88,6 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR
         return buffer;
 }
 
-int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret) {
-        char *buf;
-
-        assert(addr);
-        assert(ret);
-
-        buf = new(char, ETHER_ADDR_TO_STRING_MAX);
-        if (!buf)
-                return -ENOMEM;
-
-        ether_addr_to_string(addr, buf);
-
-        *ret = buf;
-        return 0;
-}
-
 int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
         return memcmp(a, b, ETH_ALEN);
 }
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 8ebf9c03..168c6500 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -72,7 +72,6 @@ extern const struct hash_ops hw_addr_hash_ops_free;
 
 #define ETHER_ADDR_TO_STRING_MAX (3*6)
 char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]);
-int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret);
 /* Use only as function argument, never stand-alone! */
 #define ETHER_ADDR_TO_STR(addr) ether_addr_to_string((addr), (char[ETHER_ADDR_TO_STRING_MAX]){})
 
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index e052a6a2..fd6a95e0 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -4,9 +4,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#if WANT_LINUX_FS_H
-#include <linux/fs.h>
-#endif
+#include <linux/kcmp.h>
 #include <linux/magic.h>
 #include <sys/ioctl.h>
 #include <sys/resource.h>
@@ -19,6 +17,7 @@
 #include "fileio.h"
 #include "fs-util.h"
 #include "io-util.h"
+#include "log.h"
 #include "macro.h"
 #include "missing_fcntl.h"
 #include "missing_fs.h"
@@ -31,7 +30,6 @@
 #include "sort-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
-#include "tmpfile-util.h"
 
 /* The maximum number of iterations in the loop to close descriptors in the fallback case
  * when /proc/self/fd/ is inaccessible. */
@@ -1012,13 +1010,13 @@ int fd_verify_safe_flags_full(int fd, int extra_flags) {
         if (flags < 0)
                 return -errno;
 
-        unexpected_flags = flags & ~(O_ACCMODE|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
+        unexpected_flags = flags & ~(O_ACCMODE_STRICT|O_NOFOLLOW|RAW_O_LARGEFILE|extra_flags);
         if (unexpected_flags != 0)
                 return log_debug_errno(SYNTHETIC_ERRNO(EREMOTEIO),
                                        "Unexpected flags set for extrinsic fd: 0%o",
                                        (unsigned) unexpected_flags);
 
-        return flags & (O_ACCMODE | extra_flags); /* return the flags variable, but remove the noise */
+        return flags & (O_ACCMODE_STRICT | extra_flags); /* return the flags variable, but remove the noise */
 }
 
 int read_nr_open(void) {
@@ -1095,30 +1093,27 @@ int path_is_root_at(int dir_fd, const char *path) {
 }
 
 int fds_are_same_mount(int fd1, int fd2) {
-        STRUCT_NEW_STATX_DEFINE(st1);
-        STRUCT_NEW_STATX_DEFINE(st2);
+        struct statx sx1 = {}, sx2 = {}; /* explicitly initialize the struct to make msan silent. */
         int r;
 
         assert(fd1 >= 0);
         assert(fd2 >= 0);
 
-        r = statx_fallback(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st1.sx);
-        if (r < 0)
-                return r;
+        if (statx(fd1, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx1) < 0)
+                return -errno;
 
-        r = statx_fallback(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &st2.sx);
-        if (r < 0)
-                return r;
+        if (statx(fd2, "", AT_EMPTY_PATH, STATX_TYPE|STATX_INO|STATX_MNT_ID, &sx2) < 0)
+                return -errno;
 
         /* First, compare inode. If these are different, the fd does not point to the root directory "/". */
-        if (!statx_inode_same(&st1.sx, &st2.sx))
+        if (!statx_inode_same(&sx1, &sx2))
                 return false;
 
         /* Note, statx() does not provide the mount ID and path_get_mnt_id_at() does not work when an old
          * kernel is used. In that case, let's assume that we do not have such spurious mount points in an
          * early boot stage, and silently skip the following check. */
 
-        if (!FLAGS_SET(st1.nsx.stx_mask, STATX_MNT_ID)) {
+        if (!FLAGS_SET(sx1.stx_mask, STATX_MNT_ID)) {
                 int mntid;
 
                 r = path_get_mnt_id_at_fallback(fd1, "", &mntid);
@@ -1126,11 +1121,11 @@ int fds_are_same_mount(int fd1, int fd2) {
                         return r;
                 assert(mntid >= 0);
 
-                st1.nsx.stx_mnt_id = mntid;
-                st1.nsx.stx_mask |= STATX_MNT_ID;
+                sx1.stx_mnt_id = mntid;
+                sx1.stx_mask |= STATX_MNT_ID;
         }
 
-        if (!FLAGS_SET(st2.nsx.stx_mask, STATX_MNT_ID)) {
+        if (!FLAGS_SET(sx2.stx_mask, STATX_MNT_ID)) {
                 int mntid;
 
                 r = path_get_mnt_id_at_fallback(fd2, "", &mntid);
@@ -1138,15 +1133,15 @@ int fds_are_same_mount(int fd1, int fd2) {
                         return r;
                 assert(mntid >= 0);
 
-                st2.nsx.stx_mnt_id = mntid;
-                st2.nsx.stx_mask |= STATX_MNT_ID;
+                sx2.stx_mnt_id = mntid;
+                sx2.stx_mask |= STATX_MNT_ID;
         }
 
-        return statx_mount_same(&st1.nsx, &st2.nsx);
+        return statx_mount_same(&sx1, &sx2);
 }
 
 const char* accmode_to_string(int flags) {
-        switch (flags & O_ACCMODE) {
+        switch (flags & O_ACCMODE_STRICT) {
         case O_RDONLY:
                 return "ro";
         case O_WRONLY:
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h
index 93b254c6..59c43e81 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.h
+++ b/src/libnm-systemd-shared/src/basic/fd-util.h
@@ -8,6 +8,7 @@
 #include <sys/socket.h>
 
 #include "macro.h"
+#include "memory-util.h"
 #include "missing_fcntl.h"
 #include "stdio-util.h"
 
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 78231228..0d83faf2 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -247,17 +247,13 @@ static int write_string_file_atomic_at(
         }
 
         r = fopen_temporary_at(dir_fd, fn, &f, &p);
+        if (call_label_ops_post)
+                /* If fopen_temporary_at() failed in the above, propagate the error code, and ignore failures
+                 * in label_ops_post(). */
+                RET_GATHER(r, label_ops_post(f ? fileno(f) : dir_fd, f ? NULL : fn, /* created= */ !!f));
         if (r < 0)
                 goto fail;
 
-        if (call_label_ops_post) {
-                call_label_ops_post = false;
-
-                r = label_ops_post(fileno(f), /* path= */ NULL, /* created= */ true);
-                if (r < 0)
-                        goto fail;
-        }
-
         r = write_string_stream_full(f, line, flags, ts);
         if (r < 0)
                 goto fail;
@@ -280,9 +276,6 @@ static int write_string_file_atomic_at(
         return 0;
 
 fail:
-        if (call_label_ops_post)
-                (void) label_ops_post(f ? fileno(f) : dir_fd, f ? NULL : fn, /* created= */ !!f);
-
         if (f)
                 (void) unlinkat(dir_fd, p, 0);
         return r;
@@ -296,24 +289,27 @@ int write_string_file_full(
                 const struct timespec *ts,
                 const char *label_fn) {
 
-        bool call_label_ops_post = false, made_file = false;
+        bool made_file = false;
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_close_ int fd = -EBADF;
         int r;
 
-        assert(fn);
+        assert(dir_fd == AT_FDCWD || dir_fd >= 0);
         assert(line);
 
         /* We don't know how to verify whether the file contents was already on-disk. */
         assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
 
         if (flags & WRITE_STRING_FILE_MKDIR_0755) {
+                assert(fn);
+
                 r = mkdirat_parents(dir_fd, fn, 0755);
                 if (r < 0)
                         return r;
         }
 
         if (flags & WRITE_STRING_FILE_ATOMIC) {
+                assert(fn);
                 assert(flags & WRITE_STRING_FILE_CREATE);
 
                 r = write_string_file_atomic_at(dir_fd, fn, line, flags, ts);
@@ -323,37 +319,39 @@ int write_string_file_full(
                 return r;
         }
 
-        mode_t mode = write_string_file_flags_to_mode(flags);
-
-        if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_CREATE)) {
-                r = label_ops_pre(dir_fd, label_fn ?: fn, mode);
-                if (r < 0)
-                        goto fail;
-
-                call_label_ops_post = true;
-        }
-
         /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */
-        fd = openat_report_new(
-                        dir_fd, fn, O_CLOEXEC | O_NOCTTY |
-                        (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
-                        (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
-                        (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
-                        (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
-                        mode,
-                        &made_file);
-        if (fd < 0) {
-                r = fd;
-                goto fail;
-        }
+        if (isempty(fn))
+                r = fd = fd_reopen(
+                                ASSERT_FD(dir_fd), O_CLOEXEC | O_NOCTTY |
+                                (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
+                                (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY));
+        else {
+                mode_t mode = write_string_file_flags_to_mode(flags);
+                bool call_label_ops_post = false;
+
+                if (FLAGS_SET(flags, WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_CREATE)) {
+                        r = label_ops_pre(dir_fd, label_fn ?: fn, mode);
+                        if (r < 0)
+                                goto fail;
 
-        if (call_label_ops_post) {
-                call_label_ops_post = false;
+                        call_label_ops_post = true;
+                }
 
-                r = label_ops_post(fd, /* path= */ NULL, made_file);
-                if (r < 0)
-                        goto fail;
+                r = fd = openat_report_new(
+                                dir_fd, fn, O_CLOEXEC | O_NOCTTY |
+                                (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) |
+                                (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) |
+                                (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) |
+                                (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY),
+                                mode,
+                                &made_file);
+                if (call_label_ops_post)
+                        /* If openat_report_new() failed in the above, propagate the error code, and ignore
+                         * failures in label_ops_post(). */
+                        RET_GATHER(r, label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : fn, made_file));
         }
+        if (r < 0)
+                goto fail;
 
         r = take_fdopen_unlocked(&fd, "w", &f);
         if (r < 0)
@@ -369,9 +367,6 @@ int write_string_file_full(
         return 0;
 
 fail:
-        if (call_label_ops_post)
-                (void) label_ops_post(fd >= 0 ? fd : dir_fd, fd >= 0 ? NULL : fn, made_file);
-
         if (made_file)
                 (void) unlinkat(dir_fd, fn, 0);
 
@@ -383,7 +378,7 @@ fail:
 
         /* OK, the operation failed, but let's see if the right contents in place already. If so, eat up the
          * error. */
-        if (verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)) > 0)
+        if (verify_file_at(dir_fd, fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) || (flags & WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE)) > 0)
                 return 0;
 
         return r;
@@ -445,7 +440,6 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
         size_t l, k;
         int r;
 
-        assert(fn);
         assert(blob);
 
         l = strlen(blob);
@@ -457,7 +451,7 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
         if (!buf)
                 return -ENOMEM;
 
-        r = fopen_unlocked_at(dir_fd, fn, "re", 0, &f);
+        r = fopen_unlocked_at(dir_fd, strempty(fn), "re", 0, &f);
         if (r < 0)
                 return r;
 
@@ -478,7 +472,13 @@ int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_ext
 }
 #endif /* NM_IGNORED */
 
-int read_virtual_file_fd(int fd, 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_free_ char *buf = NULL;
         size_t n, size;
         int n_retries;
@@ -495,11 +495,23 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
          * max_size specifies a limit on the bytes read. If max_size is SIZE_MAX, the full file is read. If
          * the full file is too large to read, an error is returned. For other values of max_size, *partial
          * contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on
-         * partial success, 1 if untruncated contents were read. */
+         * partial success, 1 if untruncated contents were read.
+         *
+         * Rule: for kernfs files using "seq_file" → use regular read_full_file_at()
+         *       for kernfs files using "raw" → use read_virtual_file_at()
+         */
 
-        assert(fd >= 0);
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
         assert(max_size <= READ_VIRTUAL_BYTES_MAX || max_size == SIZE_MAX);
 
+        _cleanup_close_ int fd = -EBADF;
+        if (isempty(filename))
+                fd = fd_reopen(ASSERT_FD(dir_fd), O_RDONLY | O_NOCTTY | O_CLOEXEC);
+        else
+                fd = RET_NERRNO(openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC));
+        if (fd < 0)
+                return fd;
+
         /* Limit the number of attempts to read the number of bytes returned by fstat(). */
         n_retries = 3;
 
@@ -623,31 +635,6 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
         return !truncated;
 }
 
-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 = -EBADF;
-
-        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
-
-        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;
-
-        return read_virtual_file_fd(fd, max_size, ret_contents, ret_size);
-}
-
 int read_full_stream_full(
                 FILE *f,
                 const char *filename,
@@ -727,7 +714,7 @@ int read_full_stream_full(
                 size_t k;
 
                 /* If we shall fail when reading overly large data, then read exactly one byte more than the
-                 * specified size at max, since that'll tell us if there's anymore data beyond the limit*/
+                 * specified size at max, since that'll tell us if there's anymore data beyond the limit. */
                 if (FLAGS_SET(flags, READ_FULL_FILE_FAIL_WHEN_LARGER) && n_next > size)
                         n_next = size + 1;
 
@@ -907,74 +894,46 @@ int script_get_shebang_interpreter(const char *path, char **ret) {
         return 0;
 }
 
-/**
- * Retrieve one field from a file like /proc/self/status.  pattern
- * should not include whitespace or the delimiter (':'). pattern matches only
- * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
- * zeros after the ':' will be skipped. field must be freed afterwards.
- * terminator specifies the terminating characters of the field value (not
- * included in the value).
- */
-int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
-        _cleanup_free_ char *status = NULL;
-        char *t, *f;
+int get_proc_field(const char *path, const char *key, char **ret) {
+        _cleanup_fclose_ FILE *f = NULL;
         int r;
 
-        assert(terminator);
-        assert(filename);
-        assert(pattern);
-        assert(field);
+        /* Retrieve one field from a file like /proc/self/status. "key" matches the beginning of the line
+         * and should not include whitespace or the delimiter (':').
+         * Whitespaces after the ':' will be skipped. Only the first element is returned
+         * (i.e. for /proc/meminfo line "MemTotal: 1024 kB" -> return "1024"). */
+
+        assert(path);
+        assert(key);
 
-        r = read_full_virtual_file(filename, &status, NULL);
+        r = fopen_unlocked(path, "re", &f);
+        if (r == -ENOENT && proc_mounted() == 0)
+                return -ENOSYS;
         if (r < 0)
                 return r;
 
-        t = status;
-
-        do {
-                bool pattern_ok;
-
-                do {
-                        t = strstr(t, pattern);
-                        if (!t)
-                                return -ENOENT;
-
-                        /* Check that pattern occurs in beginning of line. */
-                        pattern_ok = (t == status || t[-1] == '\n');
-
-                        t += strlen(pattern);
-
-                } while (!pattern_ok);
-
-                t += strspn(t, " \t");
-                if (!*t)
-                        return -ENOENT;
-
-        } while (*t != ':');
-
-        t++;
-
-        if (*t) {
-                t += strspn(t, " \t");
-
-                /* Also skip zeros, because when this is used for
-                 * capabilities, we don't want the zeros. This way the
-                 * same capability set always maps to the same string,
-                 * irrespective of the total capability set size. For
-                 * other numbers it shouldn't matter. */
-                t += strspn(t, "0");
-                /* Back off one char if there's nothing but whitespace
-                   and zeros */
-                if (!*t || isspace(*t))
-                        t--;
+        for (;;) {
+                 _cleanup_free_ char *line = NULL;
+
+                 r = read_line(f, LONG_LINE_MAX, &line);
+                 if (r < 0)
+                         return r;
+                 if (r == 0)
+                         return -ENODATA;
+
+                 char *l = startswith(line, key);
+                 if (l && *l == ':') {
+                         if (ret) {
+                                 char *s = strdupcspn(skip_leading_chars(l + 1, " \t"), WHITESPACE);
+                                 if (!s)
+                                         return -ENOMEM;
+
+                                 *ret = s;
+                         }
+
+                         return 0;
+                 }
         }
-
-        f = strdupcspn(t, terminator);
-        if (!f)
-                return -ENOMEM;
-
-        *field = f;
-        return 0;
 }
 
 DIR* xopendirat(int dir_fd, const char *name, int flags) {
diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h
index bd053050..eff98802 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.h
+++ b/src/libnm-systemd-shared/src/basic/fileio.h
@@ -56,6 +56,9 @@ int write_string_file_full(int dir_fd, const char *fn, const char *line, WriteSt
 static inline int write_string_file_at(int dir_fd, const char *fn, const char *line, WriteStringFileFlags flags) {
         return write_string_file_full(dir_fd, fn, line, flags, NULL, NULL);
 }
+static inline int write_string_file_fd(int dir_fd, const char *line, WriteStringFileFlags flags) {
+        return write_string_file_at(dir_fd, NULL, line, flags);
+}
 static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
         return write_string_file_at(AT_FDCWD, fn, line, flags);
 }
@@ -75,8 +78,10 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
         return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
 }
 
-int read_virtual_file_fd(int fd, 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_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) {
+        return read_virtual_file_at(fd, NULL, max_size, ret_contents, 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);
 }
@@ -90,13 +95,10 @@ static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_siz
 }
 
 int verify_file_at(int dir_fd, const char *fn, const char *blob, bool accept_extra_nl);
-static inline int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
-        return verify_file_at(AT_FDCWD, fn, blob, accept_extra_nl);
-}
 
 int script_get_shebang_interpreter(const char *path, char **ret);
 
-int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field);
+int get_proc_field(const char *path, const char *key, char **ret);
 
 DIR* xopendirat(int dir_fd, const char *name, int flags);
 
diff --git a/src/libnm-systemd-shared/src/basic/format-ifname.c b/src/libnm-systemd-shared/src/basic/format-ifname.c
index 81592998..57f39c30 100644
--- a/src/libnm-systemd-shared/src/basic/format-ifname.c
+++ b/src/libnm-systemd-shared/src/basic/format-ifname.c
@@ -3,6 +3,8 @@
 #include "nm-sd-adapt-shared.h"
 
 #include "format-ifname.h"
+#include "log.h"
+#include "stdio-util.h"
 #include "string-util.h"
 
 assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE);
diff --git a/src/libnm-systemd-shared/src/basic/format-util.c b/src/libnm-systemd-shared/src/basic/format-util.c
index b2a5d2d0..6b767ef2 100644
--- a/src/libnm-systemd-shared/src/basic/format-util.c
+++ b/src/libnm-systemd-shared/src/basic/format-util.c
@@ -64,5 +64,4 @@ char* format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
 finish:
         buf[l-1] = 0;
         return buf;
-
 }
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index 0202933d..bdc341f6 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -3,11 +3,11 @@
 #include "nm-sd-adapt-shared.h"
 
 #include <errno.h>
+#include <linux/falloc.h>
+#include <linux/magic.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <sys/file.h>
-#include <linux/falloc.h>
-#include <linux/magic.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -80,6 +80,11 @@ int rmdir_parents(const char *path, const char *stop) {
 int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
         int r;
 
+        assert(olddirfd >= 0 || olddirfd == AT_FDCWD);
+        assert(oldpath);
+        assert(newdirfd >= 0 || newdirfd == AT_FDCWD);
+        assert(newpath);
+
         /* Try the ideal approach first */
         if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
                 return 0;
@@ -792,7 +797,7 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
                 }
         }
 
-        /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */
+        /* Don't deallocate if there's nothing to deallocate or if the file is linked elsewhere */
         if (st.st_blocks == 0 || st.st_nlink > 0)
                 return 0;
 
@@ -1041,7 +1046,7 @@ int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_
 
         if (flags & ~(O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_EXCL|O_NOATIME|O_NOFOLLOW|O_PATH))
                 return -EINVAL;
-        if ((flags & O_ACCMODE) != O_RDONLY)
+        if ((flags & O_ACCMODE_STRICT) != O_RDONLY)
                 return -EINVAL;
 
         /* Note that O_DIRECTORY|O_NOFOLLOW is implied, but we allow specifying it anyway. The following
@@ -1264,7 +1269,7 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
         }
 
         if (FLAGS_SET(xopen_flags, XO_NOCOW)) {
-                r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
+                r = chattr_fd(fd, FS_NOCOW_FL, FS_NOCOW_FL);
                 if (r < 0 && !ERRNO_IS_NOT_SUPPORTED(r))
                         goto error;
         }
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h
index eb031a0c..b2c01079 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.h
+++ b/src/libnm-systemd-shared/src/basic/fs-util.h
@@ -172,3 +172,12 @@ static inline int at_flags_normalize_nofollow(int flags) {
                 flags |= AT_SYMLINK_NOFOLLOW;
         return flags;
 }
+
+static inline int at_flags_normalize_follow(int flags) {
+        if (FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW)) {
+                assert(!FLAGS_SET(flags, AT_SYMLINK_FOLLOW));
+                flags &= ~AT_SYMLINK_NOFOLLOW;
+        } else
+                flags |= AT_SYMLINK_FOLLOW;
+        return flags;
+}
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c
index d38698b6..aedd47c2 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.c
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.c
@@ -25,7 +25,7 @@ bool emoji_enabled(void) {
         return cached_emoji_enabled;
 }
 
-const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
+const char* glyph_full(Glyph code, bool force_utf) {
 
         /* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
          * conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still
@@ -34,133 +34,139 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
          * http://git.altlinux.org/people/legion/packages/kbd.git?p=kbd.git;a=blob;f=data/consolefonts/README.eurlatgr
          */
 
-        static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = {
+        static const char* const draw_table[2][_GLYPH_MAX] = {
                 /* ASCII fallback */
                 [false] = {
-                        [SPECIAL_GLYPH_TREE_VERTICAL]           = "| ",
-                        [SPECIAL_GLYPH_TREE_BRANCH]             = "|-",
-                        [SPECIAL_GLYPH_TREE_RIGHT]              = "`-",
-                        [SPECIAL_GLYPH_TREE_SPACE]              = "  ",
-                        [SPECIAL_GLYPH_TREE_TOP]                = ",-",
-                        [SPECIAL_GLYPH_VERTICAL_DOTTED]         = ":",
-                        [SPECIAL_GLYPH_HORIZONTAL_DOTTED]       = "-",
-                        [SPECIAL_GLYPH_HORIZONTAL_FAT]          = "=",
-                        [SPECIAL_GLYPH_TRIANGULAR_BULLET]       = ">",
-                        [SPECIAL_GLYPH_BLACK_CIRCLE]            = "*",
-                        [SPECIAL_GLYPH_WHITE_CIRCLE]            = "*",
-                        [SPECIAL_GLYPH_MULTIPLICATION_SIGN]     = "x",
-                        [SPECIAL_GLYPH_CIRCLE_ARROW]            = "*",
-                        [SPECIAL_GLYPH_BULLET]                  = "*",
-                        [SPECIAL_GLYPH_MU]                      = "u",
-                        [SPECIAL_GLYPH_CHECK_MARK]              = "+",
-                        [SPECIAL_GLYPH_CROSS_MARK]              = "-",
-                        [SPECIAL_GLYPH_LIGHT_SHADE]             = "-",
-                        [SPECIAL_GLYPH_DARK_SHADE]              = "X",
-                        [SPECIAL_GLYPH_FULL_BLOCK]              = "#",
-                        [SPECIAL_GLYPH_SIGMA]                   = "S",
-                        [SPECIAL_GLYPH_ARROW_UP]                = "^",
-                        [SPECIAL_GLYPH_ARROW_DOWN]              = "v",
-                        [SPECIAL_GLYPH_ARROW_LEFT]              = "<-",
-                        [SPECIAL_GLYPH_ARROW_RIGHT]             = "->",
-                        [SPECIAL_GLYPH_ELLIPSIS]                = "...",
-                        [SPECIAL_GLYPH_EXTERNAL_LINK]           = "[LNK]",
-                        [SPECIAL_GLYPH_ECSTATIC_SMILEY]         = ":-]",
-                        [SPECIAL_GLYPH_HAPPY_SMILEY]            = ":-}",
-                        [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY]   = ":-)",
-                        [SPECIAL_GLYPH_NEUTRAL_SMILEY]          = ":-|",
-                        [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
-                        [SPECIAL_GLYPH_UNHAPPY_SMILEY]          = ":-{",
-                        [SPECIAL_GLYPH_DEPRESSED_SMILEY]        = ":-[",
-                        [SPECIAL_GLYPH_LOCK_AND_KEY]            = "o-,",
-                        [SPECIAL_GLYPH_TOUCH]                   = "O=",    /* Yeah, not very convincing, can you do it better? */
-                        [SPECIAL_GLYPH_RECYCLING]               = "~",
-                        [SPECIAL_GLYPH_DOWNLOAD]                = "\\",
-                        [SPECIAL_GLYPH_SPARKLES]                = "*",
-                        [SPECIAL_GLYPH_LOW_BATTERY]             = "!",
-                        [SPECIAL_GLYPH_WARNING_SIGN]            = "!",
-                        [SPECIAL_GLYPH_RED_CIRCLE]              = "o",
-                        [SPECIAL_GLYPH_YELLOW_CIRCLE]           = "o",
-                        [SPECIAL_GLYPH_BLUE_CIRCLE]             = "o",
-                        [SPECIAL_GLYPH_GREEN_CIRCLE]            = "o",
-                        [SPECIAL_GLYPH_SUPERHERO]               = "S",
-                        [SPECIAL_GLYPH_IDCARD]                  = "@",
+                        [GLYPH_SPACE]                   = " ",
+                        [GLYPH_TREE_VERTICAL]           = "| ",
+                        [GLYPH_TREE_BRANCH]             = "|-",
+                        [GLYPH_TREE_RIGHT]              = "`-",
+                        [GLYPH_TREE_SPACE]              = "  ",
+                        [GLYPH_TREE_TOP]                = ",-",
+                        [GLYPH_VERTICAL_DOTTED]         = ":",
+                        [GLYPH_HORIZONTAL_DOTTED]       = "-",
+                        [GLYPH_HORIZONTAL_FAT]          = "=",
+                        [GLYPH_TRIANGULAR_BULLET]       = ">",
+                        [GLYPH_BLACK_CIRCLE]            = "*",
+                        [GLYPH_WHITE_CIRCLE]            = "*",
+                        [GLYPH_MULTIPLICATION_SIGN]     = "x",
+                        [GLYPH_CIRCLE_ARROW]            = "*",
+                        [GLYPH_BULLET]                  = "*",
+                        [GLYPH_MU]                      = "u",
+                        [GLYPH_CHECK_MARK]              = "+",
+                        [GLYPH_CROSS_MARK]              = "-",
+                        [GLYPH_LIGHT_SHADE]             = "-",
+                        [GLYPH_DARK_SHADE]              = "X",
+                        [GLYPH_FULL_BLOCK]              = "#",
+                        [GLYPH_SIGMA]                   = "S",
+                        [GLYPH_ARROW_UP]                = "^",
+                        [GLYPH_ARROW_DOWN]              = "v",
+                        [GLYPH_ARROW_LEFT]              = "<-",
+                        [GLYPH_ARROW_RIGHT]             = "->",
+                        [GLYPH_ELLIPSIS]                = "...",
+                        [GLYPH_EXTERNAL_LINK]           = "[LNK]",
+                        [GLYPH_ECSTATIC_SMILEY]         = ":-]",
+                        [GLYPH_HAPPY_SMILEY]            = ":-}",
+                        [GLYPH_SLIGHTLY_HAPPY_SMILEY]   = ":-)",
+                        [GLYPH_NEUTRAL_SMILEY]          = ":-|",
+                        [GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
+                        [GLYPH_UNHAPPY_SMILEY]          = ":-{",
+                        [GLYPH_DEPRESSED_SMILEY]        = ":-[",
+                        [GLYPH_LOCK_AND_KEY]            = "o-,",
+                        [GLYPH_TOUCH]                   = "O=",    /* Yeah, not very convincing, can you do it better? */
+                        [GLYPH_RECYCLING]               = "~",
+                        [GLYPH_DOWNLOAD]                = "\\",
+                        [GLYPH_SPARKLES]                = "*",
+                        [GLYPH_LOW_BATTERY]             = "!",
+                        [GLYPH_WARNING_SIGN]            = "!",
+                        [GLYPH_RED_CIRCLE]              = "o",
+                        [GLYPH_YELLOW_CIRCLE]           = "o",
+                        [GLYPH_BLUE_CIRCLE]             = "o",
+                        [GLYPH_GREEN_CIRCLE]            = "o",
+                        [GLYPH_SUPERHERO]               = "S",
+                        [GLYPH_IDCARD]                  = "@",
+                        [GLYPH_HOME]                    = "^",
                 },
 
                 /* UTF-8 */
                 [true] = {
+                        /* This exists to allow more consistent handling of optional whitespace */
+                        [GLYPH_SPACE]                   = " ",
+
                         /* The following are multiple glyphs in both ASCII and in UNICODE */
-                        [SPECIAL_GLYPH_TREE_VERTICAL]           = u8"│ ",
-                        [SPECIAL_GLYPH_TREE_BRANCH]             = u8"├─",
-                        [SPECIAL_GLYPH_TREE_RIGHT]              = u8"└─",
-                        [SPECIAL_GLYPH_TREE_SPACE]              = u8"  ",
-                        [SPECIAL_GLYPH_TREE_TOP]                = u8"┌─",
+                        [GLYPH_TREE_VERTICAL]           = UTF8("│ "),
+                        [GLYPH_TREE_BRANCH]             = UTF8("├─"),
+                        [GLYPH_TREE_RIGHT]              = UTF8("└─"),
+                        [GLYPH_TREE_SPACE]              = UTF8("  "),
+                        [GLYPH_TREE_TOP]                = UTF8("┌─"),
 
                         /* Single glyphs in both cases */
-                        [SPECIAL_GLYPH_VERTICAL_DOTTED]         = u8"┆",
-                        [SPECIAL_GLYPH_HORIZONTAL_DOTTED]       = u8"┄",
-                        [SPECIAL_GLYPH_HORIZONTAL_FAT]          = u8"━",
-                        [SPECIAL_GLYPH_TRIANGULAR_BULLET]       = u8"‣",
-                        [SPECIAL_GLYPH_BLACK_CIRCLE]            = u8"●",
-                        [SPECIAL_GLYPH_WHITE_CIRCLE]            = u8"○",
-                        [SPECIAL_GLYPH_MULTIPLICATION_SIGN]     = u8"×",
-                        [SPECIAL_GLYPH_CIRCLE_ARROW]            = u8"↻",
-                        [SPECIAL_GLYPH_BULLET]                  = u8"•",
-                        [SPECIAL_GLYPH_MU]                      = u8"μ",       /* actually called: GREEK SMALL LETTER MU */
-                        [SPECIAL_GLYPH_CHECK_MARK]              = u8"✓",
-                        [SPECIAL_GLYPH_CROSS_MARK]              = u8"✗",        /* actually called: BALLOT X */
-                        [SPECIAL_GLYPH_LIGHT_SHADE]             = u8"░",
-                        [SPECIAL_GLYPH_DARK_SHADE]              = u8"▒",
-                        [SPECIAL_GLYPH_FULL_BLOCK]              = u8"█",
-                        [SPECIAL_GLYPH_SIGMA]                   = u8"Σ",
-                        [SPECIAL_GLYPH_ARROW_UP]                = u8"↑",       /* actually called: UPWARDS ARROW */
-                        [SPECIAL_GLYPH_ARROW_DOWN]              = u8"↓",       /* actually called: DOWNWARDS ARROW */
+                        [GLYPH_VERTICAL_DOTTED]         = UTF8("┆"),
+                        [GLYPH_HORIZONTAL_DOTTED]       = UTF8("┄"),
+                        [GLYPH_HORIZONTAL_FAT]          = UTF8("━"),
+                        [GLYPH_TRIANGULAR_BULLET]       = UTF8("‣"),
+                        [GLYPH_BLACK_CIRCLE]            = UTF8("●"),
+                        [GLYPH_WHITE_CIRCLE]            = UTF8("○"),
+                        [GLYPH_MULTIPLICATION_SIGN]     = UTF8("×"),
+                        [GLYPH_CIRCLE_ARROW]            = UTF8("↻"),
+                        [GLYPH_BULLET]                  = UTF8("•"),
+                        [GLYPH_MU]                      = UTF8("μ"),       /* actually called: GREEK SMALL LETTER MU */
+                        [GLYPH_CHECK_MARK]              = UTF8("✓"),
+                        [GLYPH_CROSS_MARK]              = UTF8("✗"),        /* actually called: BALLOT X */
+                        [GLYPH_LIGHT_SHADE]             = UTF8("░"),
+                        [GLYPH_DARK_SHADE]              = UTF8("▒"),
+                        [GLYPH_FULL_BLOCK]              = UTF8("█"),
+                        [GLYPH_SIGMA]                   = UTF8("Σ"),
+                        [GLYPH_ARROW_UP]                = UTF8("↑"),       /* actually called: UPWARDS ARROW */
+                        [GLYPH_ARROW_DOWN]              = UTF8("↓"),       /* actually called: DOWNWARDS ARROW */
 
                         /* Single glyph in Unicode, two in ASCII */
-                        [SPECIAL_GLYPH_ARROW_LEFT]              = u8"←",       /* actually called: LEFTWARDS ARROW */
-                        [SPECIAL_GLYPH_ARROW_RIGHT]             = u8"→",       /* actually called: RIGHTWARDS ARROW */
+                        [GLYPH_ARROW_LEFT]              = UTF8("←"),       /* actually called: LEFTWARDS ARROW */
+                        [GLYPH_ARROW_RIGHT]             = UTF8("→"),       /* actually called: RIGHTWARDS ARROW */
 
                         /* Single glyph in Unicode, three in ASCII */
-                        [SPECIAL_GLYPH_ELLIPSIS]                = u8"…",       /* actually called: HORIZONTAL ELLIPSIS */
+                        [GLYPH_ELLIPSIS]                = UTF8("…"),       /* actually called: HORIZONTAL ELLIPSIS */
 
                         /* Three glyphs in Unicode, five in ASCII */
-                        [SPECIAL_GLYPH_EXTERNAL_LINK]           = u8"[🡕]",      /* actually called: NORTH EAST SANS-SERIF ARROW, enclosed in [] */
+                        [GLYPH_EXTERNAL_LINK]           = UTF8("[🡕]"),      /* actually called: NORTH EAST SANS-SERIF ARROW, enclosed in [] */
 
                         /* These smileys are a single glyph in Unicode, and three in ASCII */
-                        [SPECIAL_GLYPH_ECSTATIC_SMILEY]         = u8"😇",       /* actually called: SMILING FACE WITH HALO */
-                        [SPECIAL_GLYPH_HAPPY_SMILEY]            = u8"😀",       /* actually called: GRINNING FACE */
-                        [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY]   = u8"🙂",       /* actually called: SLIGHTLY SMILING FACE */
-                        [SPECIAL_GLYPH_NEUTRAL_SMILEY]          = u8"😐",       /* actually called: NEUTRAL FACE */
-                        [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = u8"🙁",       /* actually called: SLIGHTLY FROWNING FACE */
-                        [SPECIAL_GLYPH_UNHAPPY_SMILEY]          = u8"😨",       /* actually called: FEARFUL FACE */
-                        [SPECIAL_GLYPH_DEPRESSED_SMILEY]        = u8"🤢",       /* actually called: NAUSEATED FACE */
+                        [GLYPH_ECSTATIC_SMILEY]         = UTF8("😇"),       /* actually called: SMILING FACE WITH HALO */
+                        [GLYPH_HAPPY_SMILEY]            = UTF8("😀"),       /* actually called: GRINNING FACE */
+                        [GLYPH_SLIGHTLY_HAPPY_SMILEY]   = UTF8("🙂"),       /* actually called: SLIGHTLY SMILING FACE */
+                        [GLYPH_NEUTRAL_SMILEY]          = UTF8("😐"),       /* actually called: NEUTRAL FACE */
+                        [GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = UTF8("🙁"),       /* actually called: SLIGHTLY FROWNING FACE */
+                        [GLYPH_UNHAPPY_SMILEY]          = UTF8("😨"),       /* actually called: FEARFUL FACE */
+                        [GLYPH_DEPRESSED_SMILEY]        = UTF8("🤢"),       /* actually called: NAUSEATED FACE */
 
                         /* This emoji is a single character cell glyph in Unicode, and three in ASCII */
-                        [SPECIAL_GLYPH_LOCK_AND_KEY]            = u8"🔐",       /* actually called: CLOSED LOCK WITH KEY */
+                        [GLYPH_LOCK_AND_KEY]            = UTF8("🔐"),       /* actually called: CLOSED LOCK WITH KEY */
 
                         /* This emoji is a single character cell glyph in Unicode, and two in ASCII */
-                        [SPECIAL_GLYPH_TOUCH]                   = u8"👆",       /* actually called: BACKHAND INDEX POINTING UP */
+                        [GLYPH_TOUCH]                   = UTF8("👆"),       /* actually called: BACKHAND INDEX POINTING UP */
 
                         /* These four emojis are single character cell glyphs in Unicode and also in ASCII. */
-                        [SPECIAL_GLYPH_RECYCLING]               = u8"♻️",        /* actually called: UNIVERSAL RECYCLNG SYMBOL */
-                        [SPECIAL_GLYPH_DOWNLOAD]                = u8"⤵️",        /* actually called: RIGHT ARROW CURVING DOWN */
-                        [SPECIAL_GLYPH_SPARKLES]                = u8"✨",
-                        [SPECIAL_GLYPH_LOW_BATTERY]             = u8"🪫",
-                        [SPECIAL_GLYPH_WARNING_SIGN]            = u8"⚠️",
-                        [SPECIAL_GLYPH_COMPUTER_DISK]           = u8"💽",
-                        [SPECIAL_GLYPH_WORLD]                   = u8"🌍",
-
-                        [SPECIAL_GLYPH_RED_CIRCLE]              = u8"🔴",
-                        [SPECIAL_GLYPH_YELLOW_CIRCLE]           = u8"🟡",
-                        [SPECIAL_GLYPH_BLUE_CIRCLE]             = u8"🔵",
-                        [SPECIAL_GLYPH_GREEN_CIRCLE]            = u8"🟢",
-                        [SPECIAL_GLYPH_SUPERHERO]               = u8"🦸",
-                        [SPECIAL_GLYPH_IDCARD]                  = u8"🪪",
+                        [GLYPH_RECYCLING]               = UTF8("♻️"),        /* actually called: UNIVERSAL RECYCLNG SYMBOL */
+                        [GLYPH_DOWNLOAD]                = UTF8("⤵️"),        /* actually called: RIGHT ARROW CURVING DOWN */
+                        [GLYPH_SPARKLES]                = UTF8("✨"),
+                        [GLYPH_LOW_BATTERY]             = UTF8("🪫"),
+                        [GLYPH_WARNING_SIGN]            = UTF8("⚠️"),
+                        [GLYPH_COMPUTER_DISK]           = UTF8("💽"),
+                        [GLYPH_WORLD]                   = UTF8("🌍"),
+
+                        [GLYPH_RED_CIRCLE]              = UTF8("🔴"),
+                        [GLYPH_YELLOW_CIRCLE]           = UTF8("🟡"),
+                        [GLYPH_BLUE_CIRCLE]             = UTF8("🔵"),
+                        [GLYPH_GREEN_CIRCLE]            = UTF8("🟢"),
+                        [GLYPH_SUPERHERO]               = UTF8("🦸"),
+                        [GLYPH_IDCARD]                  = UTF8("🪪"),
+                        [GLYPH_HOME]                    = UTF8("🏠"),
                 },
         };
 
         if (code < 0)
                 return NULL;
 
-        assert(code < _SPECIAL_GLYPH_MAX);
-        return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
+        assert(code < _GLYPH_MAX);
+        return draw_table[force_utf || (code >= _GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
 }
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.h b/src/libnm-systemd-shared/src/basic/glyph-util.h
index ca4d4eda..730f2695 100644
--- a/src/libnm-systemd-shared/src/basic/glyph-util.h
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.h
@@ -6,73 +6,79 @@
 
 #include "macro.h"
 
-typedef enum SpecialGlyph {
-        SPECIAL_GLYPH_TREE_VERTICAL,
-        SPECIAL_GLYPH_TREE_BRANCH,
-        SPECIAL_GLYPH_TREE_RIGHT,
-        SPECIAL_GLYPH_TREE_SPACE,
-        SPECIAL_GLYPH_TREE_TOP,
-        SPECIAL_GLYPH_VERTICAL_DOTTED,
-        SPECIAL_GLYPH_HORIZONTAL_DOTTED,
-        SPECIAL_GLYPH_HORIZONTAL_FAT,
-        SPECIAL_GLYPH_TRIANGULAR_BULLET,
-        SPECIAL_GLYPH_BLACK_CIRCLE,
-        SPECIAL_GLYPH_WHITE_CIRCLE,
-        SPECIAL_GLYPH_MULTIPLICATION_SIGN,
-        SPECIAL_GLYPH_CIRCLE_ARROW,
-        SPECIAL_GLYPH_BULLET,
-        SPECIAL_GLYPH_MU,
-        SPECIAL_GLYPH_CHECK_MARK,
-        SPECIAL_GLYPH_CROSS_MARK,
-        SPECIAL_GLYPH_LIGHT_SHADE,
-        SPECIAL_GLYPH_DARK_SHADE,
-        SPECIAL_GLYPH_FULL_BLOCK,
-        SPECIAL_GLYPH_SIGMA,
-        SPECIAL_GLYPH_ARROW_UP,
-        SPECIAL_GLYPH_ARROW_DOWN,
-        SPECIAL_GLYPH_ARROW_LEFT,
-        SPECIAL_GLYPH_ARROW_RIGHT,
-        SPECIAL_GLYPH_ELLIPSIS,
-        SPECIAL_GLYPH_EXTERNAL_LINK,
-        _SPECIAL_GLYPH_FIRST_EMOJI,
-        SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
-        SPECIAL_GLYPH_HAPPY_SMILEY,
-        SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY,
-        SPECIAL_GLYPH_NEUTRAL_SMILEY,
-        SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
-        SPECIAL_GLYPH_UNHAPPY_SMILEY,
-        SPECIAL_GLYPH_DEPRESSED_SMILEY,
-        SPECIAL_GLYPH_LOCK_AND_KEY,
-        SPECIAL_GLYPH_TOUCH,
-        SPECIAL_GLYPH_RECYCLING,
-        SPECIAL_GLYPH_DOWNLOAD,
-        SPECIAL_GLYPH_SPARKLES,
-        SPECIAL_GLYPH_LOW_BATTERY,
-        SPECIAL_GLYPH_WARNING_SIGN,
-        SPECIAL_GLYPH_COMPUTER_DISK,
-        SPECIAL_GLYPH_WORLD,
-        SPECIAL_GLYPH_RED_CIRCLE,
-        SPECIAL_GLYPH_YELLOW_CIRCLE,
-        SPECIAL_GLYPH_BLUE_CIRCLE,
-        SPECIAL_GLYPH_GREEN_CIRCLE,
-        SPECIAL_GLYPH_SUPERHERO,
-        SPECIAL_GLYPH_IDCARD,
-        _SPECIAL_GLYPH_MAX,
-        _SPECIAL_GLYPH_INVALID = -EINVAL,
-} SpecialGlyph;
+typedef enum Glyph {
+        GLYPH_SPACE,
+        GLYPH_TREE_VERTICAL,
+        GLYPH_TREE_BRANCH,
+        GLYPH_TREE_RIGHT,
+        GLYPH_TREE_SPACE,
+        GLYPH_TREE_TOP,
+        GLYPH_VERTICAL_DOTTED,
+        GLYPH_HORIZONTAL_DOTTED,
+        GLYPH_HORIZONTAL_FAT,
+        GLYPH_TRIANGULAR_BULLET,
+        GLYPH_BLACK_CIRCLE,
+        GLYPH_WHITE_CIRCLE,
+        GLYPH_MULTIPLICATION_SIGN,
+        GLYPH_CIRCLE_ARROW,
+        GLYPH_BULLET,
+        GLYPH_MU,
+        GLYPH_CHECK_MARK,
+        GLYPH_CROSS_MARK,
+        GLYPH_LIGHT_SHADE,
+        GLYPH_DARK_SHADE,
+        GLYPH_FULL_BLOCK,
+        GLYPH_SIGMA,
+        GLYPH_ARROW_UP,
+        GLYPH_ARROW_DOWN,
+        GLYPH_ARROW_LEFT,
+        GLYPH_ARROW_RIGHT,
+        GLYPH_ELLIPSIS,
+        GLYPH_EXTERNAL_LINK,
+        _GLYPH_FIRST_EMOJI,
+        GLYPH_ECSTATIC_SMILEY = _GLYPH_FIRST_EMOJI,
+        GLYPH_HAPPY_SMILEY,
+        GLYPH_SLIGHTLY_HAPPY_SMILEY,
+        GLYPH_NEUTRAL_SMILEY,
+        GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
+        GLYPH_UNHAPPY_SMILEY,
+        GLYPH_DEPRESSED_SMILEY,
+        GLYPH_LOCK_AND_KEY,
+        GLYPH_TOUCH,
+        GLYPH_RECYCLING,
+        GLYPH_DOWNLOAD,
+        GLYPH_SPARKLES,
+        GLYPH_LOW_BATTERY,
+        GLYPH_WARNING_SIGN,
+        GLYPH_COMPUTER_DISK,
+        GLYPH_WORLD,
+        GLYPH_RED_CIRCLE,
+        GLYPH_YELLOW_CIRCLE,
+        GLYPH_BLUE_CIRCLE,
+        GLYPH_GREEN_CIRCLE,
+        GLYPH_SUPERHERO,
+        GLYPH_IDCARD,
+        GLYPH_HOME,
+        _GLYPH_MAX,
+        _GLYPH_INVALID = -EINVAL,
+} Glyph;
 
 bool emoji_enabled(void);
 
-const char* special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
+const char* glyph_full(Glyph code, bool force_utf) _const_;
 
-static inline const char* special_glyph(SpecialGlyph code) {
-        return special_glyph_full(code, false);
+static inline const char* glyph(Glyph code) {
+        return glyph_full(code, false);
 }
 
-static inline const char* special_glyph_check_mark(bool b) {
-        return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
+static inline const char* optional_glyph(Glyph code) {
+        return emoji_enabled() ? glyph(code) : "";
 }
 
-static inline const char* special_glyph_check_mark_space(bool b) {
-        return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " ";
+static inline const char* glyph_check_mark(bool b) {
+        return b ? glyph(GLYPH_CHECK_MARK) : glyph(GLYPH_CROSS_MARK);
+}
+
+static inline const char* glyph_check_mark_space(bool b) {
+        return b ? glyph(GLYPH_CHECK_MARK) : " ";
 }
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.c b/src/libnm-systemd-shared/src/basic/hash-funcs.c
index d20ca3c3..b1975cfe 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.c
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c
@@ -12,15 +12,23 @@ void string_hash_func(const char *p, struct siphash *state) {
         siphash24_compress(p, strlen(p) + 1, state);
 }
 
-DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
-                                    char, string_hash_func, string_compare_func, free);
-DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
-                     char, string_hash_func, string_compare_func, free,
-                     void, free);
-DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free,
-                     char, string_hash_func, string_compare_func, free,
-                     char*, strv_free);
+DEFINE_HASH_OPS(string_hash_ops,
+                char, string_hash_func, string_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                string_hash_ops_free,
+                char, string_hash_func, string_compare_func, free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                string_hash_ops_value_free,
+                char, string_hash_func, string_compare_func,
+                void, free);
+DEFINE_HASH_OPS_FULL(
+                string_hash_ops_free_free,
+                char, string_hash_func, string_compare_func, free,
+                void, free);
+DEFINE_HASH_OPS_FULL(
+                string_hash_ops_free_strv_free,
+                char, string_hash_func, string_compare_func, free,
+                char*, strv_free);
 
 void path_hash_func(const char *q, struct siphash *state) {
         bool add_slash = false;
@@ -61,12 +69,15 @@ void path_hash_func(const char *q, struct siphash *state) {
         }
 }
 
-DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare);
-DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
-                                    char, path_hash_func, path_compare, free);
-DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
-                     char, path_hash_func, path_compare, free,
-                     void, free);
+DEFINE_HASH_OPS(path_hash_ops,
+                char, path_hash_func, path_compare);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                path_hash_ops_free,
+                char, path_hash_func, path_compare, free);
+DEFINE_HASH_OPS_FULL(
+                path_hash_ops_free_free,
+                char, path_hash_func, path_compare, free,
+                void, free);
 
 void trivial_hash_func(const void *p, struct siphash *state) {
         siphash24_compress_typesafe(p, state);
@@ -76,23 +87,19 @@ int trivial_compare_func(const void *a, const void *b) {
         return CMP(a, b);
 }
 
-const struct hash_ops trivial_hash_ops = {
-        .hash = trivial_hash_func,
-        .compare = trivial_compare_func,
-};
-
-const struct hash_ops trivial_hash_ops_free = {
-        .hash = trivial_hash_func,
-        .compare = trivial_compare_func,
-        .free_key = free,
-};
-
-const struct hash_ops trivial_hash_ops_free_free = {
-        .hash = trivial_hash_func,
-        .compare = trivial_compare_func,
-        .free_key = free,
-        .free_value = free,
-};
+DEFINE_HASH_OPS(trivial_hash_ops,
+                void, trivial_hash_func, trivial_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                trivial_hash_ops_free,
+                void, trivial_hash_func, trivial_compare_func, free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                trivial_hash_ops_value_free,
+                void, trivial_hash_func, trivial_compare_func,
+                void, free);
+DEFINE_HASH_OPS_FULL(
+                trivial_hash_ops_free_free,
+                void, trivial_hash_func, trivial_compare_func, free,
+                void, free);
 
 void uint64_hash_func(const uint64_t *p, struct siphash *state) {
         siphash24_compress_typesafe(*p, state);
@@ -102,7 +109,12 @@ int uint64_compare_func(const uint64_t *a, const uint64_t *b) {
         return CMP(*a, *b);
 }
 
-DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func);
+DEFINE_HASH_OPS(uint64_hash_ops,
+                uint64_t, uint64_hash_func, uint64_compare_func);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                uint64_hash_ops_value_free,
+                uint64_t, uint64_hash_func, uint64_compare_func,
+                void, free);
 
 #if 0 /* NM_IGNORED */
 #if SIZEOF_DEV_T != 8
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h
index 3804e94d..d0736807 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.h
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h
@@ -77,6 +77,7 @@ void string_hash_func(const char *p, struct siphash *state);
 #define string_compare_func strcmp
 extern const struct hash_ops string_hash_ops;
 extern const struct hash_ops string_hash_ops_free;
+extern const struct hash_ops string_hash_ops_value_free;
 extern const struct hash_ops string_hash_ops_free_free;
 extern const struct hash_ops string_hash_ops_free_strv_free;
 
@@ -91,6 +92,7 @@ void trivial_hash_func(const void *p, struct siphash *state);
 int trivial_compare_func(const void *a, const void *b) _const_;
 extern const struct hash_ops trivial_hash_ops;
 extern const struct hash_ops trivial_hash_ops_free;
+extern const struct hash_ops trivial_hash_ops_value_free;
 extern const struct hash_ops trivial_hash_ops_free_free;
 
 /* 32-bit values we can always just embed in the pointer itself, but in order to support 32-bit archs we need store 64-bit
@@ -98,6 +100,7 @@ extern const struct hash_ops trivial_hash_ops_free_free;
 void uint64_hash_func(const uint64_t *p, struct siphash *state);
 int uint64_compare_func(const uint64_t *a, const uint64_t *b) _pure_;
 extern const struct hash_ops uint64_hash_ops;
+extern const struct hash_ops uint64_hash_ops_value_free;
 
 /* On some archs dev_t is 32-bit, and on others 64-bit. And sometimes it's 64-bit on 32-bit archs, and sometimes 32-bit on
  * 64-bit archs. Yuck! */
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c
index 6b0247c3..e58375c9 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.c
+++ b/src/libnm-systemd-shared/src/basic/hashmap.c
@@ -14,6 +14,7 @@
 #include "alloc-util.h"
 #include "fileio.h"
 #include "hashmap.h"
+#include "log.h"
 #include "logarithm.h"
 #include "macro.h"
 #include "memory-util.h"
@@ -916,24 +917,20 @@ static void hashmap_free_no_clear(HashmapBase *h) {
                 free(h);
 }
 
-HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
+HashmapBase* _hashmap_free(HashmapBase *h) {
         if (h) {
-                _hashmap_clear(h, default_free_key, default_free_value);
+                _hashmap_clear(h);
                 hashmap_free_no_clear(h);
         }
 
         return NULL;
 }
 
-void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
-        free_func_t free_key, free_value;
+void _hashmap_clear(HashmapBase *h) {
         if (!h)
                 return;
 
-        free_key = h->hash_ops->free_key ?: default_free_key;
-        free_value = h->hash_ops->free_value ?: default_free_value;
-
-        if (free_key || free_value) {
+        if (h->hash_ops->free_key || h->hash_ops->free_value) {
 
                 /* If destructor calls are defined, let's destroy things defensively: let's take the item out of the
                  * hash table, and only then call the destructor functions. If these destructors then try to unregister
@@ -945,11 +942,11 @@ void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t de
 
                         v = _hashmap_first_key_and_value(h, true, &k);
 
-                        if (free_key)
-                                free_key(k);
+                        if (h->hash_ops->free_key)
+                                h->hash_ops->free_key(k);
 
-                        if (free_value)
-                                free_value(v);
+                        if (h->hash_ops->free_value)
+                                h->hash_ops->free_value(v);
                 }
         }
 
@@ -1784,7 +1781,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h  HASHMAP_DEBUG_PARAMS) {
         }
 
         if (r < 0)
-                return _hashmap_free(copy, NULL, NULL);
+                return _hashmap_free(copy);
 
         return copy;
 }
@@ -1809,6 +1806,23 @@ char** _hashmap_get_strv(HashmapBase *h) {
         return sv;
 }
 
+char** set_to_strv(Set **s) {
+        assert(s);
+
+        /* This is similar to set_get_strv(), but invalidates the set on success. */
+
+        char **v = new(char*, set_size(*s) + 1);
+        if (!v)
+                return NULL;
+
+        for (char **p = v; (*p = set_steal_first(*s)); p++)
+                ;
+
+        assert(set_isempty(*s));
+        *s = set_free(*s);
+        return v;
+}
+
 void* ordered_hashmap_next(OrderedHashmap *h, const void *key) {
         struct ordered_hashmap_entry *e;
         unsigned hash, idx;
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index 01a4fb32..8ced7e6a 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -88,36 +88,17 @@ OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DE
 #define hashmap_new(ops) _hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
 #define ordered_hashmap_new(ops) _ordered_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
 
-#define hashmap_free_and_replace(a, b)          \
+#define hashmap_free_and_replace(a, b)                          \
         free_and_replace_full(a, b, hashmap_free)
+#define ordered_hashmap_free_and_replace(a, b)                  \
+        free_and_replace_full(a, b, ordered_hashmap_free)
 
-HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+HashmapBase* _hashmap_free(HashmapBase *h);
 static inline Hashmap* hashmap_free(Hashmap *h) {
-        return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
+        return (void*) _hashmap_free(HASHMAP_BASE(h));
 }
 static inline OrderedHashmap* ordered_hashmap_free(OrderedHashmap *h) {
-        return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
-}
-
-static inline Hashmap* hashmap_free_free(Hashmap *h) {
-        return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
-}
-static inline OrderedHashmap* ordered_hashmap_free_free(OrderedHashmap *h) {
-        return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
-}
-
-static inline Hashmap* hashmap_free_free_key(Hashmap *h) {
-        return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
-}
-static inline OrderedHashmap* ordered_hashmap_free_free_key(OrderedHashmap *h) {
-        return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
-}
-
-static inline Hashmap* hashmap_free_free_free(Hashmap *h) {
-        return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
-}
-static inline OrderedHashmap* ordered_hashmap_free_free_free(OrderedHashmap *h) {
-        return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
+        return (void*) _hashmap_free(HASHMAP_BASE(h));
 }
 
 IteratedCache* iterated_cache_free(IteratedCache *cache);
@@ -285,33 +266,12 @@ static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void
         return _hashmap_iterate(HASHMAP_BASE(h), i, value, key);
 }
 
-void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+void _hashmap_clear(HashmapBase *h);
 static inline void hashmap_clear(Hashmap *h) {
-        _hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
+        _hashmap_clear(HASHMAP_BASE(h));
 }
 static inline void ordered_hashmap_clear(OrderedHashmap *h) {
-        _hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
-}
-
-static inline void hashmap_clear_free(Hashmap *h) {
-        _hashmap_clear(HASHMAP_BASE(h), NULL, free);
-}
-static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
-        _hashmap_clear(HASHMAP_BASE(h), NULL, free);
-}
-
-static inline void hashmap_clear_free_key(Hashmap *h) {
-        _hashmap_clear(HASHMAP_BASE(h), free, NULL);
-}
-static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
-        _hashmap_clear(HASHMAP_BASE(h), free, NULL);
-}
-
-static inline void hashmap_clear_free_free(Hashmap *h) {
-        _hashmap_clear(HASHMAP_BASE(h), free, free);
-}
-static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
-        _hashmap_clear(HASHMAP_BASE(h), free, free);
+        _hashmap_clear(HASHMAP_BASE(h));
 }
 
 /*
@@ -371,27 +331,6 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
         return _hashmap_first_key(HASHMAP_BASE(h), false);
 }
 
-#define hashmap_clear_with_destructor(h, f)                     \
-        ({                                                      \
-                Hashmap *_h = (h);                              \
-                void *_item;                                    \
-                while ((_item = hashmap_steal_first(_h)))       \
-                        f(_item);                               \
-                _h;                                             \
-        })
-#define hashmap_free_with_destructor(h, f)                      \
-        hashmap_free(hashmap_clear_with_destructor(h, f))
-#define ordered_hashmap_clear_with_destructor(h, f)                     \
-        ({                                                              \
-                OrderedHashmap *_h = (h);                               \
-                void *_item;                                            \
-                while ((_item = ordered_hashmap_steal_first(_h)))       \
-                        f(_item);                                       \
-                _h;                                                     \
-        })
-#define ordered_hashmap_free_with_destructor(h, f)                      \
-        ordered_hashmap_free(ordered_hashmap_clear_with_destructor(h, f))
-
 /* no hashmap_next */
 void* ordered_hashmap_next(OrderedHashmap *h, const void *key);
 
@@ -459,20 +398,10 @@ static inline int ordered_hashmap_dump_keys_sorted(OrderedHashmap *h, void ***re
         _ORDERED_HASHMAP_FOREACH_KEY(e, k, h, UNIQ_T(i, UNIQ))
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_key);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_key);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
 
 #define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
-#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep)
-#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep)
 #define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep)
-#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep)
-#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep)
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(IteratedCache*, iterated_cache_free);
 
diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.h b/src/libnm-systemd-shared/src/basic/hexdecoct.h
index d160ca28..b456200e 100644
--- a/src/libnm-systemd-shared/src/basic/hexdecoct.h
+++ b/src/libnm-systemd-shared/src/basic/hexdecoct.h
@@ -17,8 +17,8 @@ int undecchar(char c) _const_;
 char hexchar(int x) _const_;
 int unhexchar(char c) _const_;
 
-char* hexmem(const void *p, size_t l);
-int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+char* hexmem(const void *p, size_t l) _nonnull_if_nonzero_(1, 2);
+int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size) _nonnull_if_nonzero_(1, 2);
 static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) {
         return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size);
 }
@@ -30,10 +30,10 @@ char base64char(int x) _const_;
 char urlsafe_base64char(int x) _const_;
 int unbase64char(char c) _const_;
 
-char* base32hexmem(const void *p, size_t l, bool padding);
-int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
+char* base32hexmem(const void *p, size_t l, bool padding) _nonnull_if_nonzero_(1, 2);
+int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len) _nonnull_if_nonzero_(1, 2);
 
-ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret);
+ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret) _nonnull_if_nonzero_(1, 2);
 static inline ssize_t base64mem(const void *p, size_t l, char **ret) {
         return base64mem_full(p, l, SIZE_MAX, ret);
 }
@@ -45,9 +45,9 @@ ssize_t base64_append(
                 size_t l,
                 size_t margin,
                 size_t width);
-int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size);
+int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size) _nonnull_if_nonzero_(1, 2);
 static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) {
         return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size);
 }
 
-void hexdump(FILE *f, const void *p, size_t s);
+void hexdump(FILE *f, const void *p, size_t s) _nonnull_if_nonzero_(2, 3);
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c
index 26e196f3..eed3bf0a 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.c
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.c
@@ -12,18 +12,22 @@
 #include "alloc-util.h"
 #include "env-file.h"
 #include "hostname-util.h"
+#include "log.h"
 #include "os-util.h"
 #include "string-util.h"
 #include "strv.h"
 
 #if 0 /* NM_IGNORED */
-char* get_default_hostname(void) {
+char* get_default_hostname_raw(void) {
         int r;
 
+        /* Returns the default hostname, and leaves any ??? in place. */
+
         const char *e = secure_getenv("SYSTEMD_DEFAULT_HOSTNAME");
         if (e) {
-                if (hostname_is_valid(e, 0))
+                if (hostname_is_valid(e, VALID_HOSTNAME_QUESTION_MARK))
                         return strdup(e);
+
                 log_debug("Invalid hostname in $SYSTEMD_DEFAULT_HOSTNAME, ignoring: %s", e);
         }
 
@@ -32,48 +36,14 @@ char* get_default_hostname(void) {
         if (r < 0)
                 log_debug_errno(r, "Failed to parse os-release, ignoring: %m");
         else if (f) {
-                if (hostname_is_valid(f, 0))
+                if (hostname_is_valid(f, VALID_HOSTNAME_QUESTION_MARK))
                         return TAKE_PTR(f);
+
                 log_debug("Invalid hostname in os-release, ignoring: %s", f);
         }
 
         return strdup(FALLBACK_HOSTNAME);
 }
-
-int gethostname_full(GetHostnameFlags flags, char **ret) {
-        _cleanup_free_ char *buf = NULL, *fallback = NULL;
-        struct utsname u;
-        const char *s;
-
-        assert(ret);
-
-        assert_se(uname(&u) >= 0);
-
-        s = u.nodename;
-        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))
-                        return -ENXIO;
-
-                s = fallback = get_default_hostname();
-                if (!s)
-                        return -ENOMEM;
-
-                if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')
-                        return -ENXIO;
-        }
-
-        if (FLAGS_SET(flags, GET_HOSTNAME_SHORT))
-                buf = strdupcspn(s, ".");
-        else
-                buf = strdup(s);
-        if (!buf)
-                return -ENOMEM;
-
-        *ret = TAKE_PTR(buf);
-        return 0;
-}
 #endif /* NM_IGNORED */
 
 bool valid_ldh_char(char c) {
@@ -120,7 +90,7 @@ bool hostname_is_valid(const char *s, ValidHostnameFlags flags) {
                         hyphen = true;
 
                 } else {
-                        if (!valid_ldh_char(*p))
+                        if (!valid_ldh_char(*p) && (*p != '?' || !FLAGS_SET(flags, VALID_HOSTNAME_QUESTION_MARK)))
                                 return false;
 
                         dot = false;
@@ -163,7 +133,7 @@ char* hostname_cleanup(char *s) {
                         dot = false;
                         hyphen = true;
 
-                } else if (valid_ldh_char(*p)) {
+                } else if (valid_ldh_char(*p) || *p == '?') {
                         *(d++) = *p;
                         dot = false;
                         hyphen = false;
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h
index bcac3d9f..4c5abe76 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.h
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.h
@@ -7,42 +7,14 @@
 #include "macro.h"
 #include "strv.h"
 
-typedef enum GetHostnameFlags {
-        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);
-static inline int gethostname_strict(char **ret) {
-        return gethostname_full(0, ret);
-}
-
-static inline char* gethostname_malloc(void) {
-        char *s;
-
-        if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0)
-                return NULL;
-
-        return s;
-}
-
-static inline char* gethostname_short_malloc(void) {
-        char *s;
-
-        if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0)
-                return NULL;
-
-        return s;
-}
-
-char* get_default_hostname(void);
+char* get_default_hostname_raw(void);
 
 bool valid_ldh_char(char c) _const_;
 
 typedef enum ValidHostnameFlags {
-        VALID_HOSTNAME_TRAILING_DOT = 1 << 0,   /* Accept trailing dot on multi-label names */
-        VALID_HOSTNAME_DOT_HOST     = 1 << 1,   /* Accept ".host" as valid hostname */
+        VALID_HOSTNAME_TRAILING_DOT  = 1 << 0,   /* Accept trailing dot on multi-label names */
+        VALID_HOSTNAME_DOT_HOST      = 1 << 1,   /* Accept ".host" as valid hostname */
+        VALID_HOSTNAME_QUESTION_MARK = 1 << 2,   /* Accept "?" as place holder for hashed machine ID value */
 } ValidHostnameFlags;
 
 bool hostname_is_valid(const char *s, ValidHostnameFlags flags) _pure_;
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 44445f1e..b7a64b18 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -15,6 +15,7 @@
 #include "in-addr-util.h"
 #include "logarithm.h"
 #include "macro.h"
+#include "memory-util.h"
 #include "parse-util.h"
 #include "random-util.h"
 #include "stdio-util.h"
@@ -30,7 +31,7 @@ bool in4_addr_is_null(const struct in_addr *a) {
 bool in6_addr_is_null(const struct in6_addr *a) {
         assert(a);
 
-        return IN6_IS_ADDR_UNSPECIFIED(a);
+        return eqzero(a->s6_addr32);
 }
 
 int in_addr_is_null(int family, const union in_addr_union *u) {
@@ -68,7 +69,7 @@ bool in4_addr_is_link_local_dynamic(const struct in_addr *a) {
 bool in6_addr_is_link_local(const struct in6_addr *a) {
         assert(a);
 
-        return IN6_IS_ADDR_LINKLOCAL(a);
+        return (a->s6_addr32[0] & htobe32(0xffc00000)) == htobe32(0xfe800000);
 }
 
 int in_addr_is_link_local(int family, const union in_addr_union *u) {
@@ -102,7 +103,7 @@ bool in4_addr_is_multicast(const struct in_addr *a) {
 bool in6_addr_is_multicast(const struct in6_addr *a) {
         assert(a);
 
-        return IN6_IS_ADDR_MULTICAST(a);
+        return a->s6_addr[0] == 0xff;
 }
 
 int in_addr_is_multicast(int family, const union in_addr_union *u) {
@@ -138,6 +139,10 @@ bool in4_addr_is_non_local(const struct in_addr *a) {
                !in4_addr_is_localhost(a);
 }
 
+static bool in6_addr_is_loopback(const struct in6_addr *a) {
+        return memcmp(a, &(struct in6_addr) IN6ADDR_LOOPBACK_INIT, sizeof(struct in6_addr)) == 0;
+}
+
 int in_addr_is_localhost(int family, const union in_addr_union *u) {
         assert(u);
 
@@ -145,7 +150,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
                 return in4_addr_is_localhost(&u->in);
 
         if (family == AF_INET6)
-                return IN6_IS_ADDR_LOOPBACK(&u->in6);
+                return in6_addr_is_loopback(&u->in6);
 
         return -EAFNOSUPPORT;
 }
@@ -158,7 +163,7 @@ int in_addr_is_localhost_one(int family, const union in_addr_union *u) {
                 return be32toh(u->in.s_addr) == UINT32_C(0x7F000001);
 
         if (family == AF_INET6)
-                return IN6_IS_ADDR_LOOPBACK(&u->in6);
+                return in6_addr_is_loopback(&u->in6);
 
         return -EAFNOSUPPORT;
 }
@@ -180,7 +185,7 @@ bool in6_addr_equal(const struct in6_addr *a, const struct in6_addr *b) {
         assert(a);
         assert(b);
 
-        return IN6_ARE_ADDR_EQUAL(a, b);
+        return memcmp(a, b, sizeof(struct in6_addr)) == 0;
 }
 
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
@@ -951,7 +956,6 @@ int in_addr_prefix_from_string_auto_full(
                 *ret_prefixlen = k;
 
         return 0;
-
 }
 
 void in_addr_hash_func(const union in_addr_union *u, int family, struct siphash *state) {
diff --git a/src/libnm-systemd-shared/src/basic/include/net/if.h b/src/libnm-systemd-shared/src/basic/include/net/if.h
new file mode 100644
index 00000000..7d5b61ba
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/include/net/if.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <linux/if.h>
+
+#define IF_NAMESIZE       16
+
+extern unsigned int if_nametoindex(const char *__ifname) __THROW;
+extern char *if_indextoname(unsigned int __ifindex, char __ifname[IF_NAMESIZE]) __THROW;
diff --git a/src/libnm-systemd-shared/src/basic/include/netinet/in.h b/src/libnm-systemd-shared/src/basic/include/netinet/in.h
new file mode 100644
index 00000000..97475ac8
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/include/netinet/in.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ipv6.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/socket.h>
+
+#define INET_ADDRSTRLEN 16
+#define INET6_ADDRSTRLEN 46
+
+extern const struct in6_addr in6addr_any;        /* :: */
+extern const struct in6_addr in6addr_loopback;   /* ::1 */
+#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
+#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
+
+typedef uint32_t in_addr_t;
diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c
index abe61ed5..706535a1 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.c
+++ b/src/libnm-systemd-shared/src/basic/io-util.c
@@ -90,6 +90,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
                         return n;
 
                 assert((size_t) k <= nbytes);
+                assert(k <= SSIZE_MAX - n);
 
                 p += k;
                 nbytes -= k;
@@ -194,7 +195,7 @@ int pipe_eof(int fd) {
 }
 #endif /* NM_IGNORED */
 
-int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
+int ppoll_usec_full(struct pollfd *fds, size_t nfds, usec_t timeout, const sigset_t *ss) {
         int r;
 
         assert(fds || nfds == 0);
@@ -214,10 +215,10 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
          *  to handle signals, such as signalfd() or signal handlers. ⚠️ ⚠️ ⚠️
          */
 
-        if (nfds == 0)
+        if (nfds == 0 && timeout == 0)
                 return 0;
 
-        r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL);
+        r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), ss);
         if (r < 0)
                 return -errno;
         if (r == 0)
diff --git a/src/libnm-systemd-shared/src/basic/io-util.h b/src/libnm-systemd-shared/src/basic/io-util.h
index e027c1a8..208e1683 100644
--- a/src/libnm-systemd-shared/src/basic/io-util.h
+++ b/src/libnm-systemd-shared/src/basic/io-util.h
@@ -15,14 +15,18 @@ int flush_fd(int fd);
 ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
 int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll);
 
-int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout);
+int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) _nonnull_if_nonzero_(2, 3);
 static inline int loop_write(int fd, const void *buf, size_t nbytes) {
         return loop_write_full(fd, buf, nbytes, 0);
 }
 
 int pipe_eof(int fd);
 
-int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout);
+int ppoll_usec_full(struct pollfd *fds, size_t nfds, usec_t timeout, const sigset_t *ss) _nonnull_if_nonzero_(1, 2);
+_nonnull_if_nonzero_(1, 2) static inline int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
+        return ppoll_usec_full(fds, nfds, timeout, NULL);
+}
+
 int fd_wait_for_event(int fd, int event, usec_t timeout);
 
 ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length);
@@ -42,5 +46,4 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
                 return true;
 
         return FILE_SIZE_VALID(l);
-
 }
diff --git a/src/libnm-systemd-shared/src/basic/iovec-util.h b/src/libnm-systemd-shared/src/basic/iovec-util.h
index 86845404..ace20098 100644
--- a/src/libnm-systemd-shared/src/basic/iovec-util.h
+++ b/src/libnm-systemd-shared/src/basic/iovec-util.h
@@ -12,9 +12,9 @@
 extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */
 extern const struct iovec iovec_empty;    /* Points to an empty, but valid (i.e. non-NULL) pointer */
 
-size_t iovec_total_size(const struct iovec *iovec, size_t n);
+size_t iovec_total_size(const struct iovec *iovec, size_t n) _nonnull_if_nonzero_(1, 2);
 
-bool iovec_increment(struct iovec *iovec, size_t n, size_t k);
+bool iovec_increment(struct iovec *iovec, size_t n, size_t k) _nonnull_if_nonzero_(1, 2);
 
 static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) {
         assert(iovec);
@@ -42,7 +42,7 @@ static inline void iovec_done_erase(struct iovec *iovec) {
 char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
 char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
 
-void iovec_array_free(struct iovec *iovec, size_t n_iovec);
+void iovec_array_free(struct iovec *iovec, size_t n_iovec) _nonnull_if_nonzero_(1, 2);
 
 static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
 
@@ -55,7 +55,7 @@ static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) {
                          b ? b->iov_len : 0);
 }
 
-static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) {
+static inline struct iovec* iovec_memdup(const struct iovec *source, struct iovec *ret) {
         assert(ret);
 
         if (!iovec_is_set(source))
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index 10e69541..090bdc7b 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include "macro.h"
+
 /* The head of the linked list. Use this in the structure that shall
  * contain the head of the linked list */
 #define LIST_HEAD(t,name)                                               \
@@ -203,7 +205,3 @@
                         free_func(elem);                \
                 head;                                   \
         })
-
-/* Now include "macro.h", because we want our definition of assert() which the macros above use. We include
- * it down here instead of up top, since macro.h pulls in log.h which in turn needs our own definitions. */
-#include "macro.h"
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.c b/src/libnm-systemd-shared/src/basic/locale-util.c
index 0654fde8..d491d81f 100644
--- a/src/libnm-systemd-shared/src/basic/locale-util.c
+++ b/src/libnm-systemd-shared/src/basic/locale-util.c
@@ -19,8 +19,9 @@
 #include "fileio.h"
 #include "hashmap.h"
 #include "locale-util.h"
-#include "missing_syscall.h"
+#include "log.h"
 #include "path-util.h"
+#include "process-util.h"
 #include "set.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -28,7 +29,7 @@
 #include "utf8.h"
 
 #if 0 /* NM_IGNORED */
-static char *normalize_locale(const char *name) {
+static char* normalize_locale(const char *name) {
         const char *e;
 
         /* Locale names are weird: glibc has some magic rules when looking for the charset name on disk: it
@@ -96,18 +97,15 @@ static int add_locales_from_archive(Set *locales) {
                 uint32_t locrec_offset;
         };
 
-        const struct locarhead *h;
-        const struct namehashent *e;
-        const void *p = MAP_FAILED;
-        _cleanup_close_ int fd = -EBADF;
-        size_t sz = 0;
-        struct stat st;
         int r;
 
-        fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
+        assert(locales);
+
+        _cleanup_close_ int fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
         if (fd < 0)
                 return errno == ENOENT ? 0 : -errno;
 
+        struct stat st;
         if (fstat(fd, &st) < 0)
                 return -errno;
 
@@ -120,11 +118,12 @@ static int add_locales_from_archive(Set *locales) {
         if (file_offset_beyond_memory_size(st.st_size))
                 return -EFBIG;
 
-        p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+        void *p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
         if (p == MAP_FAILED)
                 return -errno;
 
-        h = (const struct locarhead *) p;
+        const struct namehashent *e;
+        const struct locarhead *h = p;
         if (h->magic != 0xde020109 ||
             h->namehash_offset + h->namehash_size > st.st_size ||
             h->string_offset + h->string_size > st.st_size ||
@@ -157,9 +156,9 @@ static int add_locales_from_archive(Set *locales) {
 
         r = 0;
 
- finish:
+finish:
         if (p != MAP_FAILED)
-                munmap((void*) p, sz);
+                munmap((void*) p, st.st_size);
 
         return r;
 }
@@ -168,6 +167,8 @@ static int add_locales_from_libdir(Set *locales) {
         _cleanup_closedir_ DIR *dir = NULL;
         int r;
 
+        assert(locales);
+
         dir = opendir("/usr/lib/locale");
         if (!dir)
                 return errno == ENOENT ? 0 : -errno;
@@ -183,7 +184,7 @@ static int add_locales_from_libdir(Set *locales) {
                         return -ENOMEM;
 
                 r = set_consume(locales, z);
-                if (r < 0 && r != -EEXIST)
+                if (r < 0)
                         return r;
         }
 
@@ -191,11 +192,10 @@ static int add_locales_from_libdir(Set *locales) {
 }
 
 int get_locales(char ***ret) {
-        _cleanup_set_free_free_ Set *locales = NULL;
-        _cleanup_strv_free_ char **l = NULL;
+        _cleanup_set_free_ Set *locales = NULL;
         int r;
 
-        locales = set_new(&string_hash_ops);
+        locales = set_new(&string_hash_ops_free);
         if (!locales)
                 return -ENOMEM;
 
@@ -216,31 +216,25 @@ int get_locales(char ***ret) {
                         free(set_remove(locales, locale));
         }
 
-        l = set_get_strv(locales);
+        _cleanup_strv_free_ char **l = set_to_strv(&locales);
         if (!l)
                 return -ENOMEM;
 
-        /* Now, all elements are owned by strv 'l'. Hence, do not call set_free_free(). */
-        locales = set_free(locales);
-
         r = getenv_bool("SYSTEMD_LIST_NON_UTF8_LOCALES");
-        if (IN_SET(r, -ENXIO, 0)) {
-                char **a, **b;
+        if (r <= 0) {
+                if (!IN_SET(r, -ENXIO, 0))
+                        log_debug_errno(r, "Failed to parse $SYSTEMD_LIST_NON_UTF8_LOCALES as boolean, ignoring: %m");
 
                 /* Filter out non-UTF-8 locales, because it's 2019, by default */
-                for (a = b = l; *a; a++) {
-
-                        if (endswith(*a, "UTF-8") ||
-                            strstr(*a, ".UTF-8@"))
+                char **b = l;
+                STRV_FOREACH(a, l)
+                        if (endswith(*a, "UTF-8") || strstr(*a, ".UTF-8@"))
                                 *(b++) = *a;
                         else
                                 free(*a);
-                }
 
                 *b = NULL;
-
-        } else if (r < 0)
-                log_debug_errno(r, "Failed to parse $SYSTEMD_LIST_NON_UTF8_LOCALES as boolean");
+        }
 
         strv_sort(l);
 
@@ -287,64 +281,48 @@ int locale_is_installed(const char *name) {
 }
 #endif /* NM_IGNORED */
 
-bool is_locale_utf8(void) {
-        static int cached_answer = -1;
+static bool is_locale_utf8_impl(void) {
         const char *set;
         int r;
 
-        /* Note that we default to 'true' here, since today UTF8 is
-         * pretty much supported everywhere. */
-
-        if (cached_answer >= 0)
-                goto out;
+        /* Note that we default to 'true' here, since today UTF8 is pretty much supported everywhere. */
 
         r = secure_getenv_bool("SYSTEMD_UTF8");
-        if (r >= 0) {
-                cached_answer = r;
-                goto out;
-        } else if (r != -ENXIO)
+        if (r >= 0)
+                return r;
+        if (r != -ENXIO)
                 log_debug_errno(r, "Failed to parse $SYSTEMD_UTF8, ignoring: %m");
 
         /* This function may be called from libsystemd, and setlocale() is not thread safe. Assuming yes. */
-        if (gettid() != raw_getpid()) {
-                cached_answer = true;
-                goto out;
-        }
+        if (!is_main_thread())
+                return true;
 
-        if (!setlocale(LC_ALL, "")) {
-                cached_answer = true;
-                goto out;
-        }
+        if (!setlocale(LC_ALL, ""))
+                return true;
 
         set = nl_langinfo(CODESET);
-        if (!set) {
-                cached_answer = true;
-                goto out;
-        }
-
-        if (streq(set, "UTF-8")) {
-                cached_answer = true;
-                goto out;
-        }
+        if (!set || streq(set, "UTF-8"))
+                return true;
 
-        /* For LC_CTYPE=="C" return true, because CTYPE is effectively
-         * unset and everything can do to UTF-8 nowadays. */
         set = setlocale(LC_CTYPE, NULL);
-        if (!set) {
-                cached_answer = true;
-                goto out;
-        }
+        if (!set)
+                return true;
 
-        /* Check result, but ignore the result if C was set
-         * explicitly. */
-        cached_answer =
-                STR_IN_SET(set, "C", "POSIX") &&
+        /* Unless LC_CTYPE is explicitly overridden, return true. Because here CTYPE is effectively unset
+         * and everything can do to UTF-8 nowadays. */
+        return STR_IN_SET(set, "C", "POSIX") &&
                 !getenv("LC_ALL") &&
                 !getenv("LC_CTYPE") &&
                 !getenv("LANG");
+}
+
+bool is_locale_utf8(void) {
+        static int cached = -1;
+
+        if (cached < 0)
+                cached = is_locale_utf8_impl();
 
-out:
-        return (bool) cached_answer;
+        return cached;
 }
 
 #if 0 /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/lock-util.h b/src/libnm-systemd-shared/src/basic/lock-util.h
index a67d8b2c..b327b8e5 100644
--- a/src/libnm-systemd-shared/src/basic/lock-util.h
+++ b/src/libnm-systemd-shared/src/basic/lock-util.h
@@ -5,6 +5,8 @@
 /* Include here so consumers have LOCK_{EX,SH,NB} available. */
 #include <sys/file.h>
 
+#include "time-util.h"
+
 typedef struct LockFile {
         int dir_fd;
         char *path;
diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h
index 6477bff4..4741f580 100644
--- a/src/libnm-systemd-shared/src/basic/log.h
+++ b/src/libnm-systemd-shared/src/basic/log.h
@@ -7,10 +7,7 @@
 #include <string.h>
 #include <syslog.h>
 
-#include "list.h"
 #include "macro.h"
-#include "ratelimit.h"
-#include "stdio-util.h"
 
 /* Some structures we reference but don't want to pull in headers for */
 struct iovec;
@@ -90,13 +87,6 @@ int log_show_tid_from_string(const char *e);
  * environment should not be called from library code — this is always a job
  * for the application itself. */
 
-#if 0 /* NM_IGNORED */
-assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
-#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
-#else /* NM_IGNORED */
-#define PROJECT_FILE __FILE__
-#endif /* NM_IGNORED */
-
 bool stderr_is_journal(void);
 int log_open(void);
 void log_close(void);
@@ -224,7 +214,7 @@ int log_struct_internal(
                 const char *file,
                 int line,
                 const char *func,
-                const char *format, ...) _printf_(6,0) _sentinel_;
+                const char *format, ...) _sentinel_;
 #endif /* NM_IGNORED */
 
 #if 0 /* NM_IGNORED */
@@ -294,53 +284,6 @@ _noreturn_ void log_assert_failed(
 #endif /* NM_IGNORED */
 
 #if 0 /* NM_IGNORED */
-_noreturn_ void log_assert_failed_unreachable(
-                const char *file,
-                int line,
-                const char *func);
-#else /* NM_IGNORED */
-#define log_assert_failed_unreachable(file, line, func)                               \
-    G_STMT_START                                                                      \
-    {                                                                                 \
-        log_internal(LOG_CRIT,                                                        \
-                     0,                                                               \
-                     file,                                                            \
-                     line,                                                            \
-                     func,                                                            \
-                     "Code should not be reached at %s:%u, function %s(). Aborting.", \
-                     file,                                                            \
-                     line,                                                            \
-                     func);                                                           \
-        g_assert_not_reached();                                                       \
-    }                                                                                 \
-    G_STMT_END
-#endif /* NM_IGNORED */
-
-#if 0 /* NM_IGNORED */
-void log_assert_failed_return(
-                const char *text,
-                const char *file,
-                int line,
-                const char *func);
-#else /* NM_IGNORED */
-#define log_assert_failed_return(text, file, line, func)                         \
-    ({                                                                           \
-        log_internal(LOG_DEBUG,                                                  \
-                     0,                                                          \
-                     file,                                                       \
-                     line,                                                       \
-                     func,                                                       \
-                     "Assertion '%s' failed at %s:%u, function %s(). Ignoring.", \
-                     text,                                                       \
-                     file,                                                       \
-                     line,                                                       \
-                     func);                                                      \
-        g_return_if_fail_warning(G_LOG_DOMAIN, G_STRFUNC, text);                 \
-        (void) 0;                                                                \
-    })
-#endif /* NM_IGNORED */
-
-#if 0 /* NM_IGNORED */
 #define log_dispatch(level, error, buffer)                              \
         log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer)
 #endif /* NM_IGNORED */
@@ -450,11 +393,15 @@ bool log_on_console(void) _pure_;
 /* Do a fake formatting of the message string to let the scanner verify the arguments against the format
  * message. The variable will never be set to true, but we don't tell the compiler that :) */
 extern bool _log_message_dummy;
-#  define LOG_MESSAGE(fmt, ...) "MESSAGE=%.0d" fmt, (_log_message_dummy && printf(fmt, ##__VA_ARGS__)), ##__VA_ARGS__
+#  define LOG_ITEM(fmt, ...) "%.0d" fmt, (_log_message_dummy && printf(fmt, ##__VA_ARGS__)), ##__VA_ARGS__
+#  define LOG_MESSAGE(fmt, ...) LOG_ITEM("MESSAGE=" fmt, ##__VA_ARGS__)
 #else
+#  define LOG_ITEM(fmt, ...) fmt, ##__VA_ARGS__
 #  define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
 #endif
 
+#define LOG_MESSAGE_ID(id) LOG_ITEM("MESSAGE_ID=" id)
+
 void log_received_signal(int level, const struct signalfd_siginfo *si);
 
 /* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */
@@ -471,9 +418,6 @@ void log_set_open_when_needed(bool b);
  * stderr, the console or kmsg */
 void log_set_prohibit_ipc(bool b);
 
-void log_set_assert_return_is_critical(bool b);
-bool log_get_assert_return_is_critical(void) _pure_;
-
 int log_dup_console(void);
 
 #if 0 /* NM_IGNORED */
@@ -540,58 +484,6 @@ int log_syntax_parse_error_internal(
 
 void log_setup(void);
 
-typedef struct LogRateLimit {
-        int error;
-        int level;
-        RateLimit ratelimit;
-} LogRateLimit;
-
-#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...)        \
-({                                                                              \
-        int _log_ratelimit_error = (_error);                                    \
-        int _log_ratelimit_level = (_level);                                    \
-        static LogRateLimit _log_ratelimit = {                                  \
-                .ratelimit = (_ratelimit),                                      \
-        };                                                                      \
-        unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \
-        if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \
-                ratelimit_reset(&_log_ratelimit.ratelimit);                     \
-                _log_ratelimit.error = _log_ratelimit_error;                    \
-                _log_ratelimit.level = _log_ratelimit_level;                    \
-        }                                                                       \
-        if (log_get_max_level() == LOG_DEBUG || ratelimit_below(&_log_ratelimit.ratelimit)) \
-                _log_ratelimit_error = _num_dropped_errors > 0                  \
-                ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", ##__VA_ARGS__, _num_dropped_errors) \
-                : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, ##__VA_ARGS__); \
-        _log_ratelimit_error;                                                   \
-})
-
-#define log_ratelimit_full_errno(level, error, _ratelimit, format, ...)             \
-        ({                                                              \
-                int _level = (level), _e = (error);                     \
-                _e = (log_get_max_level() >= LOG_PRI(_level))           \
-                        ? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
-                        : -ERRNO_VALUE(_e);                             \
-                _e < 0 ? _e : -ESTRPIPE;                                \
-        })
-
-#define log_ratelimit_full(level, _ratelimit, format, ...)                          \
-        log_ratelimit_full_errno(level, 0, _ratelimit, format, ##__VA_ARGS__)
-
-/* Normal logging */
-#define log_ratelimit_info(...)      log_ratelimit_full(LOG_INFO,    __VA_ARGS__)
-#define log_ratelimit_notice(...)    log_ratelimit_full(LOG_NOTICE,  __VA_ARGS__)
-#define log_ratelimit_warning(...)   log_ratelimit_full(LOG_WARNING, __VA_ARGS__)
-#define log_ratelimit_error(...)     log_ratelimit_full(LOG_ERR,     __VA_ARGS__)
-#define log_ratelimit_emergency(...) log_ratelimit_full(log_emergency_level(), __VA_ARGS__)
-
-/* Logging triggered by an errno-like error */
-#define log_ratelimit_info_errno(error, ...)      log_ratelimit_full_errno(LOG_INFO,    error, __VA_ARGS__)
-#define log_ratelimit_notice_errno(error, ...)    log_ratelimit_full_errno(LOG_NOTICE,  error, __VA_ARGS__)
-#define log_ratelimit_warning_errno(error, ...)   log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__)
-#define log_ratelimit_error_errno(error, ...)     log_ratelimit_full_errno(LOG_ERR,     error, __VA_ARGS__)
-#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
-
 const char* _log_set_prefix(const char *prefix, bool force);
 static inline const char* _log_unset_prefixp(const char **p) {
         assert(p);
@@ -601,112 +493,3 @@ static inline const char* _log_unset_prefixp(const char **p) {
 
 #define LOG_SET_PREFIX(prefix) \
         _cleanup_(_log_unset_prefixp) _unused_ const char *CONCATENATE(_cleanup_log_unset_prefix_, UNIQ) = _log_set_prefix(prefix, false);
-
-/*
- * The log context allows attaching extra metadata to log messages written to the journal via log.h. We keep
- * track of a thread local log context onto which we can push extra metadata fields that should be logged.
- *
- * LOG_CONTEXT_PUSH() will add the provided field to the log context and will remove it again when the
- * current block ends. LOG_CONTEXT_PUSH_STRV() will do the same but for all fields in the given strv.
- * LOG_CONTEXT_PUSHF() is like LOG_CONTEXT_PUSH() but takes a format string and arguments.
- *
- * Using the macros is as simple as putting them anywhere inside a block to add a field to all following log
- * messages logged from inside that block.
- *
- * void myfunction(...) {
- *         ...
- *
- *         LOG_CONTEXT_PUSHF("MYMETADATA=%s", "abc");
- *
- *         // Every journal message logged will now have the MYMETADATA=abc
- *         // field included.
- * }
- *
- * One special case to note is async code, where we use callbacks that are invoked to continue processing
- * when some event occurs. For async code, there's usually an associated "userdata" struct containing all the
- * information associated with the async operation. In this "userdata" struct, we can store a log context
- * allocated with log_context_new() and freed with log_context_free(). We can then add and remove fields to
- * the `fields` member of the log context object and all those fields will be logged along with each log
- * message.
- */
-
-typedef struct LogContext LogContext;
-
-bool log_context_enabled(void);
-
-LogContext* log_context_new(const char *key, const char *value);
-LogContext* log_context_new_strv(char **fields, bool owned);
-LogContext* log_context_new_iov(struct iovec *input_iovec, size_t n_input_iovec, bool owned);
-
-/* Same as log_context_new(), but frees the given fields strv/iovec on failure. */
-LogContext* log_context_new_strv_consume(char **fields);
-LogContext* log_context_new_iov_consume(struct iovec *input_iovec, size_t n_input_iovec);
-
-LogContext *log_context_ref(LogContext *c);
-LogContext *log_context_unref(LogContext *c);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_unref);
-
-/* Returns the number of attached log context objects. */
-size_t log_context_num_contexts(void);
-/* Returns the number of fields in all attached log contexts. */
-size_t log_context_num_fields(void);
-
-static inline void _reset_log_level(int *saved_log_level) {
-        assert(saved_log_level);
-
-        log_set_max_level(*saved_log_level);
-}
-
-#define LOG_CONTEXT_SET_LOG_LEVEL(level) \
-        _cleanup_(_reset_log_level) _unused_ int _saved_log_level_ = log_set_max_level(level);
-
-#define LOG_CONTEXT_PUSH(...) \
-        LOG_CONTEXT_PUSH_STRV(STRV_MAKE(__VA_ARGS__))
-
-#define LOG_CONTEXT_PUSHF(...) \
-        LOG_CONTEXT_PUSH(snprintf_ok((char[LINE_MAX]) {}, LINE_MAX, __VA_ARGS__))
-
-#define _LOG_CONTEXT_PUSH_KEY_VALUE(key, value, c) \
-        _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new(key, value);
-
-#define LOG_CONTEXT_PUSH_KEY_VALUE(key, value) \
-        _LOG_CONTEXT_PUSH_KEY_VALUE(key, value, UNIQ_T(c, UNIQ))
-
-#define _LOG_CONTEXT_PUSH_STRV(strv, c) \
-        _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv(strv, /*owned=*/ false);
-
-#define LOG_CONTEXT_PUSH_STRV(strv) \
-        _LOG_CONTEXT_PUSH_STRV(strv, UNIQ_T(c, UNIQ))
-
-#define _LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, c) \
-        _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_iov(input_iovec, n_input_iovec, /*owned=*/ false);
-
-#define LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec) \
-        _LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ))
-
-/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV()/LOG_CONTEXT_CONSUME_IOV() are identical to
- * LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV()/LOG_CONTEXT_PUSH_IOV() except they take ownership of the
- * given str/strv argument.
- */
-
-#define _LOG_CONTEXT_CONSUME_STR(s, c, strv) \
-        _unused_ _cleanup_strv_free_ strv = strv_new(s);                                                \
-        if (!strv)                                                                                      \
-                free(s);                                                                                \
-        _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv_consume(TAKE_PTR(strv))
-
-#define LOG_CONTEXT_CONSUME_STR(s) \
-        _LOG_CONTEXT_CONSUME_STR(s, UNIQ_T(c, UNIQ), UNIQ_T(sv, UNIQ))
-
-#define _LOG_CONTEXT_CONSUME_STRV(strv, c) \
-        _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_strv_consume(strv);
-
-#define LOG_CONTEXT_CONSUME_STRV(strv) \
-        _LOG_CONTEXT_CONSUME_STRV(strv, UNIQ_T(c, UNIQ))
-
-#define _LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, c) \
-        _unused_ _cleanup_(log_context_unrefp) LogContext *c = log_context_new_iov_consume(input_iovec, n_input_iovec);
-
-#define LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec) \
-        _LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ))
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index 026ec136..4ee1ad4d 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -103,76 +103,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
                 (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type, member) ); \
         })
 
-#ifdef __COVERITY__
-
-/* Use special definitions of assertion macros in order to prevent
- * false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer
- * for uses of assert_se() and assert_return().
- *
- * These definitions make expression go through a (trivial) function
- * call to ensure they are not discarded. Also use ! or !! to ensure
- * the boolean expressions are seen as such.
- *
- * This technique has been described and recommended in:
- * https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects
- */
-
-extern void __coverity_panic__(void);
-
-static inline void __coverity_check__(int condition) {
-        if (!condition)
-                __coverity_panic__();
-}
-
-static inline int __coverity_check_and_return__(int condition) {
-        return condition;
-}
-
-#define assert_message_se(expr, message) __coverity_check__(!!(expr))
-
-#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
-
-#else  /* ! __COVERITY__ */
-
-#define assert_message_se(expr, message)                                \
-        do {                                                            \
-                if (_unlikely_(!(expr)))                                \
-                        log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
-        } while (false)
-
-#define assert_log(expr, message) ((_likely_(expr))                     \
-        ? (true)                                                        \
-        : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
-
-#endif  /* __COVERITY__ */
-
-#define assert_se(expr) assert_message_se(expr, #expr)
-
-/* We override the glibc assert() here. */
-#undef assert
-#ifdef NDEBUG
-#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
-#else
-#define assert(expr) assert_message_se(expr, #expr)
-#endif
-
-#define assert_not_reached()                                            \
-        log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
-
-#define assert_return(expr, r)                                          \
-        do {                                                            \
-                if (!assert_log(expr, #expr))                           \
-                        return (r);                                     \
-        } while (false)
-
-#define assert_return_errno(expr, r, err)                               \
-        do {                                                            \
-                if (!assert_log(expr, #expr)) {                         \
-                        errno = err;                                    \
-                        return (r);                                     \
-                }                                                       \
-        } while (false)
-
 #define return_with_errno(r, err)                     \
         do {                                          \
                 errno = abs(err);                     \
@@ -251,59 +181,6 @@ static inline int __coverity_check_and_return__(int condition) {
 /* Pointers range from NULL to POINTER_MAX */
 #define POINTER_MAX ((void*) UINTPTR_MAX)
 
-#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope)             \
-        scope type *name##_ref(type *p) {                       \
-                if (!p)                                         \
-                        return NULL;                            \
-                                                                \
-                /* For type check. */                           \
-                unsigned *q = &p->n_ref;                        \
-                assert(*q > 0);                                 \
-                assert_se(*q < UINT_MAX);                       \
-                                                                \
-                (*q)++;                                         \
-                return p;                                       \
-        }
-
-#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \
-        scope type *name##_unref(type *p) {                      \
-                if (!p)                                          \
-                        return NULL;                             \
-                                                                 \
-                assert(p->n_ref > 0);                            \
-                p->n_ref--;                                      \
-                if (p->n_ref > 0)                                \
-                        return NULL;                             \
-                                                                 \
-                return free_func(p);                             \
-        }
-
-#define DEFINE_TRIVIAL_REF_FUNC(type, name)     \
-        _DEFINE_TRIVIAL_REF_FUNC(type, name,)
-#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name)     \
-        _DEFINE_TRIVIAL_REF_FUNC(type, name, static)
-#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name)      \
-        _DEFINE_TRIVIAL_REF_FUNC(type, name, _public_)
-
-#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func)        \
-        _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,)
-#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func)        \
-        _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static)
-#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func)         \
-        _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_)
-
-#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func)    \
-        DEFINE_TRIVIAL_REF_FUNC(type, name);                    \
-        DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func);
-
-#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func)    \
-        DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name);                    \
-        DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func);
-
-#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func)    \
-        DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name);                    \
-        DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
-
 /* A macro to force copying of a variable from memory. This is useful whenever we want to read something from
  * memory and want to make sure the compiler won't optimize away the destination variable for us. It's not
  * supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not
@@ -328,12 +205,6 @@ static inline size_t size_add(size_t x, size_t y) {
         return saturate_add(x, y, SIZE_MAX);
 }
 
-typedef struct {
-        int _empty[0];
-} dummy_t;
-
-assert_cc(sizeof(dummy_t) == 0);
-
 /* A little helper for subtracting 1 off a pointer in a safe UB-free way. This is intended to be used for
  * loops that count down from a high pointer until some base. A naive loop would implement this like this:
  *
@@ -358,5 +229,3 @@ assert_cc(sizeof(dummy_t) == 0);
         for (typeof(entry) _va_sentinel_[1] = {}, _entries_[] = { __VA_ARGS__ __VA_OPT__(,) _va_sentinel_[0] }, *_current_ = _entries_; \
              ((long)(_current_ - _entries_) < (long)(ELEMENTSOF(_entries_) - 1)) && ({ entry = *_current_; true; }); \
              _current_++)
-
-#include "log.h"
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.c b/src/libnm-systemd-shared/src/basic/memory-util.c
index 789e96a9..3e9a3683 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.c
+++ b/src/libnm-systemd-shared/src/basic/memory-util.c
@@ -2,10 +2,11 @@
 
 #include "nm-sd-adapt-shared.h"
 
+#include <threads.h>
 #include <unistd.h>
 
+#include "alloc-util.h"
 #include "memory-util.h"
-#include "missing_threads.h"
 
 size_t page_size(void) {
         static thread_local size_t pgsz = 0;
@@ -42,7 +43,7 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
         return memcmp(data, p + 16, length) == 0;
 }
 
-void *memdup_reverse(const void *mem, size_t size) {
+void* memdup_reverse(const void *mem, size_t size) {
         assert(mem);
         assert(size != 0);
 
@@ -57,3 +58,14 @@ void *memdup_reverse(const void *mem, size_t size) {
 
         return p;
 }
+
+void* erase_and_free(void *p) {
+        size_t l;
+
+        if (!p)
+                return NULL;
+
+        l = MALLOC_SIZEOF_SAFE(p);
+        explicit_bzero_safe(p, l);
+        return mfree(p);
+}
diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h
index 1f604cc4..443fc3a8 100644
--- a/src/libnm-systemd-shared/src/basic/memory-util.h
+++ b/src/libnm-systemd-shared/src/basic/memory-util.h
@@ -7,7 +7,6 @@
 #include <string.h>
 #include <sys/types.h>
 
-#include "alloc-util.h"
 #include "macro.h"
 #include "memory-util-fundamental.h"
 
@@ -35,13 +34,16 @@ static inline void* mempcpy_safe(void *dst, const void *src, size_t n) {
         return mempcpy(dst, src, n);
 }
 
-#define mempcpy_typesafe(dst, src, n)                                   \
+#define _mempcpy_typesafe(dst, src, n, sz)                              \
         ({                                                              \
-                size_t _sz_;                                            \
-                assert_se(MUL_SAFE(&_sz_, sizeof((dst)[0]), n));        \
-                (typeof((dst)[0])*) mempcpy_safe(dst, src, _sz_);       \
+                size_t sz;                                              \
+                assert_se(MUL_SAFE(&sz, sizeof((dst)[0]), n));          \
+                (typeof((dst)[0])*) mempcpy_safe(dst, src, sz);         \
         })
 
+#define mempcpy_typesafe(dst, src, n)                                   \
+        _mempcpy_typesafe(dst, src, n, UNIQ_T(sz, UNIQ))
+
 /* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */
 static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
         if (n == 0)
@@ -59,19 +61,19 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
 
 #define zero(x) (memzero(&(x), sizeof(x)))
 
-bool memeqbyte(uint8_t byte, const void *data, size_t length);
+bool memeqbyte(uint8_t byte, const void *data, size_t length) _nonnull_if_nonzero_(2, 3);
 
 #define memeqzero(data, length) memeqbyte(0x00, data, length)
 
 #define eqzero(x) memeqzero(x, sizeof(x))
 
-static inline void *mempset(void *s, int c, size_t n) {
+static inline void* mempset(void *s, int c, size_t n) {
         memset(s, c, n);
-        return (uint8_t*)s + n;
+        return (uint8_t*) s + n;
 }
 
 /* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
-static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+static inline void* memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
 
         if (needlelen <= 0)
                 return (void*) haystack;
@@ -85,7 +87,7 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
         return memmem(haystack, haystacklen, needle, needlelen);
 }
 
-static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+static inline void* mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
         const uint8_t *p;
 
         p = memmem_safe(haystack, haystacklen, needle, needlelen);
@@ -95,16 +97,7 @@ static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const
         return (uint8_t*) p + needlelen;
 }
 
-static inline void* erase_and_free(void *p) {
-        size_t l;
-
-        if (!p)
-                return NULL;
-
-        l = MALLOC_SIZEOF_SAFE(p);
-        explicit_bzero_safe(p, l);
-        return mfree(p);
-}
+void* erase_and_free(void *p);
 
 static inline void erase_and_freep(void *p) {
         erase_and_free(*(void**) p);
@@ -116,4 +109,57 @@ static inline void erase_char(char *p) {
 }
 
 /* Makes a copy of the buffer with reversed order of bytes */
-void *memdup_reverse(const void *mem, size_t size);
+void* memdup_reverse(const void *mem, size_t size);
+
+#define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope)             \
+        scope type *name##_ref(type *p) {                       \
+                if (!p)                                         \
+                        return NULL;                            \
+                                                                \
+                /* For type check. */                           \
+                unsigned *q = &p->n_ref;                        \
+                assert(*q > 0);                                 \
+                assert_se(*q < UINT_MAX);                       \
+                                                                \
+                (*q)++;                                         \
+                return p;                                       \
+        }
+
+#define _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, scope) \
+        scope type *name##_unref(type *p) {                      \
+                if (!p)                                          \
+                        return NULL;                             \
+                                                                 \
+                assert(p->n_ref > 0);                            \
+                p->n_ref--;                                      \
+                if (p->n_ref > 0)                                \
+                        return NULL;                             \
+                                                                 \
+                return free_func(p);                             \
+        }
+
+#define DEFINE_TRIVIAL_REF_FUNC(type, name)     \
+        _DEFINE_TRIVIAL_REF_FUNC(type, name,)
+#define DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name)     \
+        _DEFINE_TRIVIAL_REF_FUNC(type, name, static)
+#define DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name)      \
+        _DEFINE_TRIVIAL_REF_FUNC(type, name, _public_)
+
+#define DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func)        \
+        _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func,)
+#define DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func)        \
+        _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, static)
+#define DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func)         \
+        _DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func, _public_)
+
+#define DEFINE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func)    \
+        DEFINE_TRIVIAL_REF_FUNC(type, name);                    \
+        DEFINE_TRIVIAL_UNREF_FUNC(type, name, free_func);
+
+#define DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(type, name, free_func)    \
+        DEFINE_PRIVATE_TRIVIAL_REF_FUNC(type, name);                    \
+        DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(type, name, free_func);
+
+#define DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(type, name, free_func)    \
+        DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name);                    \
+        DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
diff --git a/src/libnm-systemd-shared/src/basic/mempool.c b/src/libnm-systemd-shared/src/basic/mempool.c
index e467d706..60a89a6b 100644
--- a/src/libnm-systemd-shared/src/basic/mempool.c
+++ b/src/libnm-systemd-shared/src/basic/mempool.c
@@ -6,6 +6,7 @@
 #include <stdlib.h>
 
 #include "format-util.h"
+#include "log.h"
 #include "macro.h"
 #include "memory-util.h"
 #include "mempool.h"
diff --git a/src/libnm-systemd-shared/src/basic/missing_fcntl.h b/src/libnm-systemd-shared/src/basic/missing_fcntl.h
index a6188879..280e233b 100644
--- a/src/libnm-systemd-shared/src/basic/missing_fcntl.h
+++ b/src/libnm-systemd-shared/src/basic/missing_fcntl.h
@@ -3,74 +3,16 @@
 
 #include <fcntl.h>
 
-#ifndef F_LINUX_SPECIFIC_BASE
-#define F_LINUX_SPECIFIC_BASE 1024
-#endif
-
+/* This is defined since glibc-2.41. */
 #ifndef F_DUPFD_QUERY
-#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
-#endif
-
-#ifndef F_SETPIPE_SZ
-#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
-#endif
-
-#ifndef F_GETPIPE_SZ
-#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
-#endif
-
-#ifndef F_ADD_SEALS
-#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
-#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
-
-#define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
-#define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
-#define F_SEAL_GROW     0x0004  /* prevent file from growing */
-#define F_SEAL_WRITE    0x0008  /* prevent writes */
-#endif
-
-#ifndef F_SEAL_FUTURE_WRITE
-#define F_SEAL_FUTURE_WRITE 0x0010 /* prevent future writes while mapped */
+#define F_DUPFD_QUERY 1027
 #endif
 
+/* This is defined since glibc-2.39. */
 #ifndef F_SEAL_EXEC
 #define F_SEAL_EXEC     0x0020  /* prevent chmod modifying exec bits */
 #endif
 
-#ifndef F_OFD_GETLK
-#define F_OFD_GETLK     36
-#define F_OFD_SETLK     37
-#define F_OFD_SETLKW    38
-#endif
-
-#ifndef MAX_HANDLE_SZ
-#define MAX_HANDLE_SZ 128
-#endif
-
-/* The precise definition of __O_TMPFILE is arch specific; use the
- * values defined by the kernel (note: some are hexa, some are octal,
- * duplicated as-is from the kernel definitions):
- * - alpha, parisc, sparc: each has a specific value;
- * - others: they use the "generic" value.
- */
-
-#ifndef __O_TMPFILE
-#if defined(__alpha__)
-#define __O_TMPFILE     0100000000
-#elif defined(__parisc__) || defined(__hppa__)
-#define __O_TMPFILE     0400000000
-#elif defined(__sparc__) || defined(__sparc64__)
-#define __O_TMPFILE     0x2000000
-#else
-#define __O_TMPFILE     020000000
-#endif
-#endif
-
-/* a horrid kludge trying to make sure that this will fail on old kernels */
-#ifndef O_TMPFILE
-#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
-#endif
-
 /* So O_LARGEFILE is generally implied by glibc, and defined to zero hence, because we only build in LFS
  * mode. However, when invoking fcntl(F_GETFL) the flag is ORed into the result anyway — glibc does not mask
  * it away. Which sucks. Let's define the actual value here, so that we can mask it ourselves.
@@ -79,6 +21,7 @@
  * are hexa and others are octal; duplicated as-is from the kernel definitions):
  * - alpha, arm, arm64, m68k, mips, parisc, powerpc, sparc: each has a specific value;
  * - others: they use the "generic" value (defined in include/uapi/asm-generic/fcntl.h) */
+#if 0  /* NM_IGNORED */
 #if O_LARGEFILE != 0
 #define RAW_O_LARGEFILE O_LARGEFILE
 #else
@@ -96,7 +39,15 @@
 #define RAW_O_LARGEFILE 00100000
 #endif
 #endif
+#endif  /* NM_IGNORED */
 
+/* This is defined since glibc-2.39. */
 #ifndef AT_HANDLE_FID
 #define AT_HANDLE_FID AT_REMOVEDIR
 #endif
+
+/* On musl, O_ACCMODE is defined as (03|O_SEARCH), unlike glibc which defines it as
+ * (O_RDONLY|O_WRONLY|O_RDWR). Additionally, O_SEARCH is simply defined as O_PATH. This changes the behaviour
+ * of O_ACCMODE in certain situations, which we don't want. This definition is copied from glibc and works
+ * around the problems with musl's definition. */
+#define O_ACCMODE_STRICT (O_RDONLY|O_WRONLY|O_RDWR)
diff --git a/src/libnm-systemd-shared/src/basic/missing_fs.h b/src/libnm-systemd-shared/src/basic/missing_fs.h
new file mode 100644
index 00000000..1e2f07e5
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/missing_fs.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <linux/fs.h>
+
+/* Not exposed yet. Defined at fs/ext4/ext4.h */
+#ifndef EXT4_IOC_RESIZE_FS
+#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64)
+#endif
+
+/* linux/exportfs.h (33c5ac9175195c36a0b7005aaf503a2e81f117a1, 5.5) */
+#ifndef FILEID_KERNFS
+#define FILEID_KERNFS 0xfe
+#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_random.h b/src/libnm-systemd-shared/src/basic/missing_random.h
index 5f40c4e5..69002196 100644
--- a/src/libnm-systemd-shared/src/basic/missing_random.h
+++ b/src/libnm-systemd-shared/src/basic/missing_random.h
@@ -1,26 +1,11 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#include "macro.h"
-
-#if HAVE_GETRANDOM
-#  include <sys/random.h>
-#else
-#  include <linux/random.h>
-#endif
-
-#ifndef GRND_NONBLOCK
-#  define GRND_NONBLOCK 0x0001
-#else
-assert_cc(GRND_NONBLOCK == 0x0001);
-#endif
+#include <sys/random.h>
 
-#ifndef GRND_RANDOM
-#  define GRND_RANDOM 0x0002
-#else
-assert_cc(GRND_RANDOM == 0x0002);
-#endif
+#include "macro.h"
 
+/* Defined since glibc-2.32. */
 #ifndef GRND_INSECURE
 #  define GRND_INSECURE 0x0004
 #else
diff --git a/src/libnm-systemd-shared/src/basic/missing_socket.h b/src/libnm-systemd-shared/src/basic/missing_socket.h
index 8460ce13..874e93f3 100644
--- a/src/libnm-systemd-shared/src/basic/missing_socket.h
+++ b/src/libnm-systemd-shared/src/basic/missing_socket.h
@@ -3,75 +3,36 @@
 
 #include <sys/socket.h>
 
-#ifndef AF_VSOCK
-#define AF_VSOCK 40
-#endif
-
-#ifndef SO_REUSEPORT
-#define SO_REUSEPORT 15
-#endif
-
-#ifndef SO_PEERGROUPS
-#define SO_PEERGROUPS 59
-#endif
-
+/* Supported since kernel v6.5 (5e2ff6704a275be009be8979af17c52361b79b89) */
 #ifndef SO_PASSPIDFD
 #define SO_PASSPIDFD 76
 #endif
 
+/* Supported since kernel v6.5 (7b26952a91cf65ff1cc867a2382a8964d8c0ee7d) */
 #ifndef SO_PEERPIDFD
 #define SO_PEERPIDFD 77
 #endif
 
-#ifndef SO_BINDTOIFINDEX
-#define SO_BINDTOIFINDEX 62
-#endif
-
-#ifndef SOL_NETLINK
-#define SOL_NETLINK 270
-#endif
-
-#ifndef SOL_ALG
-#define SOL_ALG 279
-#endif
-
 /* Not exposed yet. Defined in include/linux/socket.h. */
 #ifndef SOL_SCTP
 #define SOL_SCTP 132
 #endif
 
+/* Supported since kernel v2.6.17 (2c7946a7bf45ae86736ab3b43d0085e43947945c).
+ * Defined since glibc-2.39 */
 #ifndef SCM_SECURITY
 #define SCM_SECURITY 0x03
 #endif
 
+/* Supported since kernel v6.5 (5e2ff6704a275be009be8979af17c52361b79b89).
+ * Defined since glibc-2.39 */
 #ifndef SCM_PIDFD
 #define SCM_PIDFD 0x04
 #endif
 
-/* netinet/in.h */
-#ifndef IP_FREEBIND
-#define IP_FREEBIND 15
-#endif
-
-#ifndef IP_TRANSPARENT
-#define IP_TRANSPARENT 19
-#endif
-
-#ifndef IPV6_FREEBIND
-#define IPV6_FREEBIND 78
-#endif
-
-#ifndef IP_RECVFRAGSIZE
-#define IP_RECVFRAGSIZE 25
-#endif
-
-#ifndef IPV6_RECVFRAGSIZE
-#define IPV6_RECVFRAGSIZE 77
-#endif
-
-/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant, but very much
- * useful for userspace too. It's documented in unix(7) these days, hence should be fairly reliable to define
- * here. */
+/* The maximum number of fds that SCM_RIGHTS accepts. This is an internal kernel constant defined in
+ * include/net/scm.h, but very much useful for userspace too. It's documented in unix(7) these days, hence
+ * should be fairly reliable to define here. */
 #ifndef SCM_MAX_FD
 #define SCM_MAX_FD 253U
 #endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_stat.h b/src/libnm-systemd-shared/src/basic/missing_stat.h
deleted file mode 100644
index 18a15ab0..00000000
--- a/src/libnm-systemd-shared/src/basic/missing_stat.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <linux/types.h>
-#include <sys/stat.h>
-
-#if 0 /* NM_IGNORED */
-#if WANT_LINUX_STAT_H
-#include <linux/stat.h>
-#endif
-
-/* The newest definition we are aware of (fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60; 5.8) */
-#define STATX_DEFINITION {                      \
-        __u32 stx_mask;                         \
-        __u32 stx_blksize;                      \
-        __u64 stx_attributes;                   \
-        __u32 stx_nlink;                        \
-        __u32 stx_uid;                          \
-        __u32 stx_gid;                          \
-        __u16 stx_mode;                         \
-        __u16 __spare0[1];                      \
-        __u64 stx_ino;                          \
-        __u64 stx_size;                         \
-        __u64 stx_blocks;                       \
-        __u64 stx_attributes_mask;              \
-        struct statx_timestamp stx_atime;       \
-        struct statx_timestamp stx_btime;       \
-        struct statx_timestamp stx_ctime;       \
-        struct statx_timestamp stx_mtime;       \
-        __u32 stx_rdev_major;                   \
-        __u32 stx_rdev_minor;                   \
-        __u32 stx_dev_major;                    \
-        __u32 stx_dev_minor;                    \
-        __u64 stx_mnt_id;                       \
-        __u64 __spare2;                         \
-        __u64 __spare3[12];                     \
-}
-
-#if !HAVE_STRUCT_STATX
-struct statx_timestamp {
-        __s64 tv_sec;
-        __u32 tv_nsec;
-        __s32 __reserved;
-};
-
-struct statx STATX_DEFINITION;
-#endif
-
-/* Always define the newest version we are aware of as a distinct type, so that we can use it even if glibc
- * defines an older definition */
-struct new_statx STATX_DEFINITION;
-#else /* NM_IGNORED */
-struct new_statx;
-#endif /* NM_IGNORED */
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef AT_STATX_SYNC_AS_STAT
-#define AT_STATX_SYNC_AS_STAT 0x0000
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef AT_STATX_FORCE_SYNC
-#define AT_STATX_FORCE_SYNC 0x2000
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef AT_STATX_DONT_SYNC
-#define AT_STATX_DONT_SYNC 0x4000
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_TYPE
-#define STATX_TYPE 0x00000001U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_MODE
-#define STATX_MODE 0x00000002U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_NLINK
-#define STATX_NLINK 0x00000004U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_UID
-#define STATX_UID 0x00000008U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_GID
-#define STATX_GID 0x00000010U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_ATIME
-#define STATX_ATIME 0x00000020U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_MTIME
-#define STATX_MTIME 0x00000040U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_CTIME
-#define STATX_CTIME 0x00000080U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_INO
-#define STATX_INO 0x00000100U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_SIZE
-#define STATX_SIZE 0x00000200U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_BLOCKS
-#define STATX_BLOCKS 0x00000400U
-#endif
-
-/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
-#ifndef STATX_BTIME
-#define STATX_BTIME 0x00000800U
-#endif
-
-/* fa2fcf4f1df1559a0a4ee0f46915b496cc2ebf60 (5.8) */
-#ifndef STATX_MNT_ID
-#define STATX_MNT_ID 0x00001000U
-#endif
-
-/* 80340fe3605c0e78cfe496c3b3878be828cfdbfe (5.8) */
-#ifndef STATX_ATTR_MOUNT_ROOT
-#define STATX_ATTR_MOUNT_ROOT 0x00002000 /* Root of a mount */
-#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h
index da6f9827..55475f7e 100644
--- a/src/libnm-systemd-shared/src/basic/missing_syscall.h
+++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h
@@ -4,12 +4,7 @@
 /* Missing glibc definitions to access certain kernel APIs */
 
 #include <errno.h>
-#include <fcntl.h>
-#if HAVE_LINUX_TIME_TYPES_H
-/* This header defines __kernel_timespec for us, but is only available since Linux 5.1, hence conditionally
- * include this. */
 #include <linux/time_types.h>
-#endif
 #include <signal.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -23,26 +18,15 @@
 #include "macro.h"
 #include "missing_keyctl.h"
 #include "missing_sched.h"
-#include "missing_stat.h"
 #include "missing_syscall_def.h"
 
 #if 0 /* NM_IGNORED */
-
-/* linux/kcmp.h */
-#ifndef KCMP_FILE /* 3f4994cfc15f38a3159c6e3a4b3ab2e1481a6b02 (3.19) */
-#define KCMP_FILE 0
-#endif
-
 /* ======================================================================= */
 
 #if !HAVE_FCHMODAT2
+/* since kernel v6.6 (78252deb023cf0879256fcfbafe37022c390762b) */
 static inline int missing_fchmodat2(int dirfd, const char *path, mode_t mode, int flags) {
-#  ifdef __NR_fchmodat2
         return syscall(__NR_fchmodat2, dirfd, path, mode, flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
 }
 
 #  define fchmodat2 missing_fchmodat2
@@ -80,95 +64,6 @@ static inline int missing_ioprio_set(int which, int who, int ioprio) {
 
 /* ======================================================================= */
 
-#if !HAVE_MEMFD_CREATE
-static inline int missing_memfd_create(const char *name, unsigned int flags) {
-        return syscall(__NR_memfd_create, name, flags);
-}
-
-#  define memfd_create missing_memfd_create
-#endif
-#endif /* NM_IGNORED */
-
-/* ======================================================================= */
-
-#if !HAVE_GETRANDOM
-/* glibc says getrandom() returns ssize_t */
-static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned flags) {
-        return syscall(__NR_getrandom, buffer, count, flags);
-}
-
-#  define getrandom missing_getrandom
-#endif
-
-/* ======================================================================= */
-
-#if 0 /* NM_IGNORED */
-/* The syscall has been defined since forever, but the glibc wrapper was missing. */
-#if !HAVE_GETTID
-static inline pid_t missing_gettid(void) {
-#  if defined __NR_gettid && __NR_gettid >= 0
-        return (pid_t) syscall(__NR_gettid);
-#  else
-#    error "__NR_gettid not defined"
-#  endif
-}
-
-#  define gettid missing_gettid
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_NAME_TO_HANDLE_AT
-struct file_handle {
-        unsigned int handle_bytes;
-        int handle_type;
-        unsigned char f_handle[0];
-};
-
-static inline int missing_name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) {
-#  ifdef __NR_name_to_handle_at
-        return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-
-#  define name_to_handle_at missing_name_to_handle_at
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_SETNS
-static inline int missing_setns(int fd, int nstype) {
-        return syscall(__NR_setns, fd, nstype);
-}
-
-#  define setns missing_setns
-#endif
-
-/* ======================================================================= */
-
-static inline pid_t raw_getpid(void) {
-#if defined(__alpha__)
-        return (pid_t) syscall(__NR_getxpid);
-#else
-        return (pid_t) syscall(__NR_getpid);
-#endif
-}
-
-/* ======================================================================= */
-
-#if !HAVE_RENAMEAT2
-static inline int missing_renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
-        return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
-}
-
-#  define renameat2 missing_renameat2
-#endif
-
-/* ======================================================================= */
-
 #if !HAVE_KCMP
 static inline int missing_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) {
         return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2);
@@ -186,12 +81,16 @@ static inline long missing_keyctl(int cmd, unsigned long arg2, unsigned long arg
 #  define keyctl missing_keyctl
 }
 
+/* ======================================================================= */
+
 static inline key_serial_t missing_add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
         return syscall(__NR_add_key, type, description, payload, plen, ringid);
 
 #  define add_key missing_add_key
 }
 
+/* ======================================================================= */
+
 static inline key_serial_t missing_request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) {
         return syscall(__NR_request_key, type, description, callout_info, destringid);
 
@@ -201,34 +100,11 @@ static inline key_serial_t missing_request_key(const char *type, const char *des
 
 /* ======================================================================= */
 
-#if !HAVE_COPY_FILE_RANGE
-static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in,
-                                              int fd_out, loff_t *off_out,
-                                              size_t len,
-                                              unsigned int flags) {
-#  ifdef __NR_copy_file_range
-        return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-
-#  define copy_file_range missing_copy_file_range
-#endif
-
-/* ======================================================================= */
-
 #if !HAVE_BPF
 union bpf_attr;
 
 static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
-#ifdef __NR_bpf
         return (int) syscall(__NR_bpf, cmd, attr, size);
-#else
-        errno = ENOSYS;
-        return -1;
-#endif
 }
 
 #  define bpf missing_bpf
@@ -236,28 +112,6 @@ static inline int missing_bpf(int cmd, union bpf_attr *attr, size_t size) {
 
 /* ======================================================================= */
 
-#if !HAVE_STATX
-struct statx;
-
-static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flags, unsigned int mask, struct statx *buffer) {
-#  ifdef __NR_statx
-        return syscall(__NR_statx, dfd, filename, flags, mask, buffer);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-#endif
-
-/* This typedef is supposed to be always defined. */
-typedef struct statx struct_statx;
-
-#if !HAVE_STATX
-#  define statx(dfd, filename, flags, mask, buffer) missing_statx(dfd, filename, flags, mask, buffer)
-#endif
-
-/* ======================================================================= */
-
 #if !HAVE_SET_MEMPOLICY
 enum {
         MPOL_DEFAULT,
@@ -269,14 +123,7 @@ enum {
 
 static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask,
                            unsigned long maxnode) {
-        long i;
-#  if defined __NR_set_mempolicy && __NR_set_mempolicy >= 0
-        i = syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
-#  else
-        errno = ENOSYS;
-        i = -1;
-#  endif
-        return i;
+        return syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
 }
 
 #  define set_mempolicy missing_set_mempolicy
@@ -286,14 +133,7 @@ static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask
 static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
                            unsigned long maxnode, void *addr,
                            unsigned long flags) {
-        long i;
-#  if defined __NR_get_mempolicy && __NR_get_mempolicy >= 0
-        i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
-#  else
-        errno = ENOSYS;
-        i = -1;
-#  endif
-        return i;
+        return syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
 }
 
 #  define get_mempolicy missing_get_mempolicy
@@ -304,6 +144,7 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
 /* ======================================================================= */
 
 #if !HAVE_PIDFD_SEND_SIGNAL
+/* since kernel v5.1 (3eb39f47934f9d5a3027fe00d906a45fe3a15fad) */
 static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
         return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
 }
@@ -311,7 +152,10 @@ static inline int missing_pidfd_send_signal(int fd, int sig, siginfo_t *info, un
 #  define pidfd_send_signal missing_pidfd_send_signal
 #endif
 
+/* ======================================================================= */
+
 #if !HAVE_PIDFD_OPEN
+/* since kernel v5.3 (7615d9e1780e26e0178c93c55b73309a5dc093d7) */
 static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
         return syscall(__NR_pidfd_open, pid, flags);
 }
@@ -321,28 +165,10 @@ static inline int missing_pidfd_open(pid_t pid, unsigned flags) {
 
 /* ======================================================================= */
 
-#if !HAVE_RT_SIGQUEUEINFO
-static inline int missing_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) {
-#  if defined __NR_rt_sigqueueinfo && __NR_rt_sigqueueinfo >= 0
-        return syscall(__NR_rt_sigqueueinfo, tgid, sig, info);
-#  else
-#    error "__NR_rt_sigqueueinfo not defined"
-#  endif
-}
-
-#  define rt_sigqueueinfo missing_rt_sigqueueinfo
-#endif
-
-/* ======================================================================= */
-
 #if 0 /* NM_IGNORED */
 #if !HAVE_RT_TGSIGQUEUEINFO
 static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *info) {
-#  if defined __NR_rt_tgsigqueueinfo && __NR_rt_tgsigqueueinfo >= 0
         return syscall(__NR_rt_tgsigqueueinfo, tgid, tid, sig, info);
-#  else
-#    error "__NR_rt_tgsigqueueinfo not defined"
-#  endif
 }
 
 #  define rt_tgsigqueueinfo missing_rt_tgsigqueueinfo
@@ -351,27 +177,21 @@ static inline int missing_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, sigi
 /* ======================================================================= */
 
 #if !HAVE_EXECVEAT
+/* since kernel v3.19 (51f39a1f0cea1cacf8c787f652f26dfee9611874) */
 static inline int missing_execveat(int dirfd, const char *pathname,
                                    char *const argv[], char *const envp[],
                                    int flags) {
-#  if defined __NR_execveat && __NR_execveat >= 0
         return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
 }
 
-#  undef AT_EMPTY_PATH
-#  define AT_EMPTY_PATH 0x1000
 #  define execveat missing_execveat
 #endif
 
 /* ======================================================================= */
 
 #if !HAVE_CLOSE_RANGE
+/* since kernel v5.9 (9b4feb630e8e9801603f3cab3a36369e3c1cf88d) */
 static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsigned flags) {
-#  ifdef __NR_close_range
         /* Kernel-side the syscall expects fds as unsigned integers (just like close() actually), while
          * userspace exclusively uses signed integers for fds. glibc chose to expose it 1:1 however, hence we
          * do so here too, even if we end up passing signed fds to it most of the time. */
@@ -379,10 +199,6 @@ static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsign
                        first_fd,
                        end_fd,
                        flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
 }
 
 #  define close_range missing_close_range
@@ -390,243 +206,8 @@ static inline int missing_close_range(unsigned first_fd, unsigned end_fd, unsign
 
 /* ======================================================================= */
 
-#if !HAVE_MOUNT_SETATTR
-
-#if !HAVE_STRUCT_MOUNT_ATTR
-struct mount_attr {
-        uint64_t attr_set;
-        uint64_t attr_clr;
-        uint64_t propagation;
-        uint64_t userns_fd;
-};
-#else
-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 /* 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
-#define AT_RECURSIVE 0x8000
-#endif
-
-static inline int missing_mount_setattr(
-                int dfd,
-                const char *path,
-                unsigned flags,
-                struct mount_attr *attr,
-                size_t size) {
-
-#  if defined __NR_mount_setattr && __NR_mount_setattr >= 0
-        return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-
-#  define mount_setattr missing_mount_setattr
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_OPEN_TREE
-
-#ifndef OPEN_TREE_CLONE
-#define OPEN_TREE_CLONE 1
-#endif
-
-#ifndef OPEN_TREE_CLOEXEC
-#define OPEN_TREE_CLOEXEC O_CLOEXEC
-#endif
-
-static inline int missing_open_tree(
-                int dfd,
-                const char *filename,
-                unsigned flags) {
-
-#  if defined __NR_open_tree && __NR_open_tree >= 0
-        return syscall(__NR_open_tree, dfd, filename, flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-
-#  define open_tree missing_open_tree
-#endif
-
-/* ======================================================================= */
-
-#ifndef MOVE_MOUNT_BENEATH
-#define MOVE_MOUNT_BENEATH 0x00000200
-#endif
-
-#if !HAVE_MOVE_MOUNT
-
-#ifndef MOVE_MOUNT_F_EMPTY_PATH
-#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
-#endif
-
-#ifndef MOVE_MOUNT_T_EMPTY_PATH
-#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
-#endif
-
-static inline int missing_move_mount(
-                int from_dfd,
-                const char *from_pathname,
-                int to_dfd,
-                const char *to_pathname,
-                unsigned flags) {
-
-#  if defined __NR_move_mount && __NR_move_mount >= 0
-        return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-
-#  define move_mount missing_move_mount
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_FSOPEN
-
-#ifndef FSOPEN_CLOEXEC
-#define FSOPEN_CLOEXEC 0x00000001
-#endif
-
-static inline int missing_fsopen(const char *fsname, unsigned flags) {
-#  if defined __NR_fsopen && __NR_fsopen >= 0
-        return syscall(__NR_fsopen, fsname, flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-
-#  define fsopen missing_fsopen
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_FSCONFIG
-
-#ifndef FSCONFIG_SET_FLAG
-#define FSCONFIG_SET_FLAG 0 /* Set parameter, supplying no value */
-#endif
-
-#ifndef FSCONFIG_SET_STRING
-#define FSCONFIG_SET_STRING 1 /* Set parameter, supplying a string value */
-#endif
-
-#ifndef FSCONFIG_SET_FD
-#define FSCONFIG_SET_FD 5 /* Set parameter, supplying an object by fd */
-#endif
-
-#ifndef FSCONFIG_CMD_CREATE
-#define FSCONFIG_CMD_CREATE 6 /* Invoke superblock creation */
-#endif
-
-static inline int missing_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux) {
-#  if defined __NR_fsconfig && __NR_fsconfig >= 0
-        return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-
-#  define fsconfig missing_fsconfig
-#endif
-
-/* ======================================================================= */
-
-#if !HAVE_FSMOUNT
-
-#ifndef FSMOUNT_CLOEXEC
-#define FSMOUNT_CLOEXEC 0x00000001
-#endif
-
-static inline int missing_fsmount(int fd, unsigned flags, unsigned ms_flags) {
-#  if defined __NR_fsmount && __NR_fsmount >= 0
-        return syscall(__NR_fsmount, fd, flags, ms_flags);
-#  else
-        errno = ENOSYS;
-        return -1;
-#  endif
-}
-
-#  define fsmount missing_fsmount
-#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
-
-/* ======================================================================= */
-
 #if !HAVE_SCHED_SETATTR
-
+/* since kernel 3.14 (e6cfc0295c7d51b008999a8b13a44fb43f8685ea) */
 static inline ssize_t missing_sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags) {
         return syscall(__NR_sched_setattr, pid, attr, flags);
 }
@@ -653,16 +234,39 @@ int __clone2(int (*fn)(void *), void *stack_base, size_t stack_size, int flags,
 
 #if 0 /* NM_IGNORED */
 #if !HAVE_QUOTACTL_FD
-
+/* since kernel v5.14 (64c2c2c62f92339b176ea24403d8db16db36f9e6) */
 static inline int missing_quotactl_fd(int fd, int cmd, int id, void *addr) {
-#if defined __NR_quotactl_fd
         return syscall(__NR_quotactl_fd, fd, cmd, id, addr);
-#else
-        errno = ENOSYS;
-        return -1;
-#endif
 }
 
 #  define quotactl_fd missing_quotactl_fd
 #endif
+
+/* ======================================================================= */
+
+#if !HAVE_SETXATTRAT
+/* since kernel v6.13 (6140be90ec70c39fa844741ca3cc807dd0866394) */
+struct xattr_args {
+        _align_(8) uint64_t value;
+        uint32_t size;
+        uint32_t flags;
+};
+
+static inline int missing_setxattrat(int fd, const char *path, int at_flags, const char *name, const struct xattr_args *args, size_t size) {
+        return syscall(__NR_setxattrat, fd, path, at_flags, name, args, size);
+}
+
+#  define setxattrat missing_setxattrat
+#endif
+
+/* ======================================================================= */
+
+#if !HAVE_REMOVEXATTRAT
+/* since kernel v6.13 (6140be90ec70c39fa844741ca3cc807dd0866394) */
+static inline int missing_removexattrat(int fd, const char *path, int at_flags, const char *name) {
+        return syscall(__NR_removexattrat, fd, path, at_flags, name);
+}
+
+#  define removexattrat missing_removexattrat
+#endif
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/missing_threads.h b/src/libnm-systemd-shared/src/basic/missing_threads.h
deleted file mode 100644
index c7da1dbd..00000000
--- a/src/libnm-systemd-shared/src/basic/missing_threads.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/* If threads.h doesn't exist, then define our own thread_local to match C11's thread_local. */
-#if HAVE_THREADS_H
-#  include <threads.h>
-#elif !(defined(thread_local))
-#  ifndef __STDC_NO_THREADS__
-#    define thread_local _Thread_local
-#  else
-#    define thread_local __thread
-#  endif
-#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_type.h b/src/libnm-systemd-shared/src/basic/missing_type.h
deleted file mode 100644
index 1d17705c..00000000
--- a/src/libnm-systemd-shared/src/basic/missing_type.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <uchar.h>
-
-#if !HAVE_CHAR32_T
-#  define char32_t uint32_t
-#endif
-
-#if !HAVE_CHAR16_T
-#  define char16_t uint16_t
-#endif
diff --git a/src/libnm-systemd-shared/src/basic/missing_wait.h b/src/libnm-systemd-shared/src/basic/missing_wait.h
index 3965b5bd..05648779 100644
--- a/src/libnm-systemd-shared/src/basic/missing_wait.h
+++ b/src/libnm-systemd-shared/src/basic/missing_wait.h
@@ -5,6 +5,7 @@
 
 #include "macro.h"
 
+/* since glibc-2.36 */
 #ifndef P_PIDFD
 #  define P_PIDFD 3
 #else
diff --git a/src/libnm-systemd-shared/src/basic/mountpoint-util.c b/src/libnm-systemd-shared/src/basic/mountpoint-util.c
new file mode 100644
index 00000000..7d7ef5eb
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/mountpoint-util.c
@@ -0,0 +1,823 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+
+#include "alloc-util.h"
+#include "chase.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "filesystems.h"
+#include "fs-util.h"
+#include "log.h"
+#include "missing_fcntl.h"
+#include "missing_fs.h"
+#include "missing_syscall.h"
+#include "mkdir.h"
+#include "mountpoint-util.h"
+#include "nulstr-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "user-util.h"
+
+/* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
+ * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
+ * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
+ * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
+ * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
+ * with large file handles anyway. */
+#define ORIGINAL_MAX_HANDLE_SZ 128
+
+bool is_name_to_handle_at_fatal_error(int err) {
+        /* name_to_handle_at() can return "acceptable" errors that are due to the context. For example
+         * the file system does not support name_to_handle_at() (EOPNOTSUPP), or the syscall was blocked
+         * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container), or
+         * the mount point is not triggered yet (EOVERFLOW, think autofs+nfs4), or some general name_to_handle_at()
+         * flakiness (EINVAL). However other errors are not supposed to happen and therefore are considered
+         * fatal ones. */
+
+        assert(err < 0);
+
+        if (ERRNO_IS_NEG_NOT_SUPPORTED(err))
+                return false;
+        if (ERRNO_IS_NEG_PRIVILEGE(err))
+                return false;
+
+        return !IN_SET(err, -EOVERFLOW, -EINVAL);
+}
+
+#if 0  /* NM_IGNORED */
+int name_to_handle_at_loop(
+                int fd,
+                const char *path,
+                struct file_handle **ret_handle,
+                int *ret_mnt_id,
+                int flags) {
+
+        size_t n = ORIGINAL_MAX_HANDLE_SZ;
+
+        assert(fd >= 0 || fd == AT_FDCWD);
+        assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH|AT_HANDLE_FID)) == 0);
+
+        /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
+         * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
+         * start value, it is not an upper bound on the buffer size required.
+         *
+         * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
+         * as NULL if there's no interest in either. */
+
+        for (;;) {
+                _cleanup_free_ struct file_handle *h = NULL;
+                int mnt_id = -1;
+
+                h = malloc0(offsetof(struct file_handle, f_handle) + n);
+                if (!h)
+                        return -ENOMEM;
+
+                h->handle_bytes = n;
+
+                if (name_to_handle_at(fd, strempty(path), h, &mnt_id, flags) >= 0) {
+
+                        if (ret_handle)
+                                *ret_handle = TAKE_PTR(h);
+
+                        if (ret_mnt_id)
+                                *ret_mnt_id = mnt_id;
+
+                        return 0;
+                }
+                if (errno != EOVERFLOW)
+                        return -errno;
+
+                if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
+
+                        /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
+                         * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
+                         * be filled in, and the caller was interested in only the mount ID an nothing else. */
+
+                        *ret_mnt_id = mnt_id;
+                        return 0;
+                }
+
+                /* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by
+                 * something else (apparently EOVERFLOW is returned for untriggered nfs4 autofs mounts
+                 * sometimes), not by the too small buffer. In that case propagate EOVERFLOW */
+                if (h->handle_bytes <= n)
+                        return -EOVERFLOW;
+
+                /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
+                n = h->handle_bytes;
+
+                /* paranoia: check for overflow (note that .handle_bytes is unsigned only) */
+                if (n > UINT_MAX - offsetof(struct file_handle, f_handle))
+                        return -EOVERFLOW;
+        }
+}
+
+int name_to_handle_at_try_fid(
+                int fd,
+                const char *path,
+                struct file_handle **ret_handle,
+                int *ret_mnt_id,
+                int flags) {
+
+        int r;
+
+        assert(fd >= 0 || fd == AT_FDCWD);
+
+        /* First issues name_to_handle_at() with AT_HANDLE_FID. If this fails and this is not a fatal error
+         * we'll try without the flag, in order to support older kernels that didn't have AT_HANDLE_FID
+         * (i.e. older than Linux 6.5). */
+
+        r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags | AT_HANDLE_FID);
+        if (r >= 0 || is_name_to_handle_at_fatal_error(r))
+                return r;
+
+        return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags & ~AT_HANDLE_FID);
+}
+
+static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mnt_id) {
+        char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
+        _cleanup_close_ int subfd = -EBADF;
+        int r;
+
+        assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
+        assert(ret_mnt_id);
+
+        if ((flags & AT_EMPTY_PATH) && isempty(filename))
+                xsprintf(path, "/proc/self/fdinfo/%i", fd);
+        else {
+                subfd = openat(fd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_FOLLOW ? 0 : O_NOFOLLOW));
+                if (subfd < 0)
+                        return -errno;
+
+                xsprintf(path, "/proc/self/fdinfo/%i", subfd);
+        }
+
+        _cleanup_free_ char *p = NULL;
+        r = get_proc_field(path, "mnt_id", &p);
+        if (r == -ENOENT)
+                return -EBADF;
+        if (r < 0)
+                return r;
+
+        return safe_atoi(p, ret_mnt_id);
+}
+
+static bool filename_possibly_with_slash_suffix(const char *s) {
+        const char *slash, *copied;
+
+        /* Checks whether the specified string is either file name, or a filename with a suffix of
+         * slashes. But nothing else.
+         *
+         * this is OK: foo, bar, foo/, bar/, foo//, bar///
+         * this is not OK: "", "/", "/foo", "foo/bar", ".", ".." … */
+
+        slash = strchr(s, '/');
+        if (!slash)
+                return filename_is_valid(s);
+
+        if (slash - s > PATH_MAX) /* We want to allocate on the stack below, hence do a size check first */
+                return false;
+
+        if (slash[strspn(slash, "/")] != 0) /* Check that the suffix consist only of one or more slashes */
+                return false;
+
+        copied = strndupa_safe(s, slash - s);
+        return filename_is_valid(copied);
+}
+
+bool file_handle_equal(const struct file_handle *a, const struct file_handle *b) {
+        if (a == b)
+                return true;
+        if (!a != !b)
+                return false;
+        if (a->handle_type != b->handle_type)
+                return false;
+
+        return memcmp_nn(a->f_handle, a->handle_bytes, b->f_handle, b->handle_bytes) == 0;
+}
+
+int is_mount_point_at(int fd, const char *filename, int flags) {
+        bool fd_is_self;
+        int r;
+
+        assert(fd >= 0 || fd == AT_FDCWD);
+        assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
+
+        if (isempty(filename)) {
+                if (fd == AT_FDCWD)
+                        filename = ".";
+                else {
+                        /* If the file name is empty we'll see if the specified 'fd' is a mount point.
+                         * That's only supported by statx(), or if the inode specified via 'fd' refers to a
+                         * directory. Otherwise, we'll have to fail (ENOTDIR), because we have no kernel API
+                         * to query the information we need. */
+                        flags |= AT_EMPTY_PATH;
+                        filename = "";
+                }
+
+                fd_is_self = true;
+        } else if (STR_IN_SET(filename, ".", "./"))
+                fd_is_self = true;
+        else {
+                /* Insist that the specified filename is actually a filename, and not a path, i.e. some inode
+                 * further up or down the tree then immediately below the specified directory fd. */
+                if (!filename_possibly_with_slash_suffix(filename))
+                        return -EINVAL;
+
+                fd_is_self = false;
+        }
+
+        /* First we will try statx()' STATX_ATTR_MOUNT_ROOT attribute, which is our ideal API, available
+         * since kernel 5.8.
+         *
+         * If that fails, our second try is the name_to_handle_at() syscall, which tells us the mount id and
+         * an opaque file "handle". It is not supported everywhere though (kernel compile-time option, not
+         * all file systems are hooked up). If it works the mount id is usually good enough to tell us
+         * whether something is a mount point.
+         *
+         * If that didn't work we will try to read the mount id from /proc/self/fdinfo/<fd>. This is almost
+         * as good as name_to_handle_at(), however, does not return the opaque file handle. The opaque file
+         * handle is pretty useful to detect the root directory, which we should always consider a mount
+         * point. Hence we use this only as fallback.
+         *
+         * Note that traditionally the check is done via fstat()-based st_dev comparisons. However, various
+         * file systems don't guarantee same st_dev across single fs anymore, e.g. unionfs exposes file systems
+         * with a variety of st_dev reported. Also, btrfs subvolumes have different st_dev, even though
+         * they aren't real mounts of their own. */
+
+        struct statx sx = {}; /* explicitly initialize the struct to make msan silent. */
+        if (statx(fd, filename,
+                  at_flags_normalize_nofollow(flags) |
+                  AT_NO_AUTOMOUNT |            /* don't trigger automounts – mounts are a local concept, hence no need to trigger automounts to determine STATX_ATTR_MOUNT_ROOT */
+                  AT_STATX_DONT_SYNC,          /* don't go to the network for this – for similar reasons */
+                  STATX_TYPE,
+                  &sx) < 0)
+                return -errno;
+
+        if (FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT)) /* yay! */
+                return FLAGS_SET(sx.stx_attributes, STATX_ATTR_MOUNT_ROOT);
+
+        _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
+        int mount_id = -1, mount_id_parent = -1;
+        bool nosupp = false;
+
+        r = name_to_handle_at_try_fid(fd, filename, &h, &mount_id, flags);
+        if (r < 0) {
+                if (is_name_to_handle_at_fatal_error(r))
+                        return r;
+                if (!ERRNO_IS_NOT_SUPPORTED(r))
+                        goto fallback_fdinfo;
+
+                /* This file system does not support name_to_handle_at(), hence let's see if the upper fs
+                 * supports it (in which case it is a mount point), otherwise fall back to the fdinfo logic. */
+                nosupp = true;
+        }
+
+        if (fd_is_self)
+                r = name_to_handle_at_try_fid(fd, "..", &h_parent, &mount_id_parent, 0); /* can't work for non-directories 😢 */
+        else
+                r = name_to_handle_at_try_fid(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
+        if (r < 0) {
+                if (is_name_to_handle_at_fatal_error(r))
+                        return r;
+                if (!ERRNO_IS_NOT_SUPPORTED(r))
+                        goto fallback_fdinfo;
+                if (nosupp)
+                        /* Both the parent and the directory can't do name_to_handle_at() */
+                        goto fallback_fdinfo;
+
+                /* The parent can't do name_to_handle_at() but the directory we are
+                 * interested in can?  If so, it must be a mount point. */
+                return 1;
+        }
+
+        /* The parent can do name_to_handle_at() but the directory we are interested in can't? If
+         * so, it must be a mount point. */
+        if (nosupp)
+                return 1;
+
+        /* If the file handle for the directory we are interested in and its parent are identical,
+         * we assume this is the root directory, which is a mount point. */
+        if (file_handle_equal(h_parent, h))
+                return 1;
+
+        return mount_id != mount_id_parent;
+
+fallback_fdinfo:
+        r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
+        if (r < 0)
+                return r;
+
+        if (fd_is_self)
+                r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent); /* can't work for non-directories 😢 */
+        else
+                r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
+        if (r < 0)
+                return r;
+
+        if (mount_id != mount_id_parent)
+                return 1;
+
+        /* Hmm, so, the mount ids are the same. This leaves one special case though for the root file
+         * system. For that, let's see if the parent directory has the same inode as we are interested
+         * in. */
+
+        struct stat a, b;
+
+        /* yay for fstatat() taking a different set of flags than the other _at() above */
+        if (fstatat(fd, filename, &a, at_flags_normalize_nofollow(flags)) < 0)
+                return -errno;
+
+        if (fd_is_self)
+                r = fstatat(fd, "..", &b, 0);
+        else
+                r = fstatat(fd, "", &b, AT_EMPTY_PATH);
+        if (r < 0)
+                return -errno;
+
+        /* A directory with same device and inode as its parent must be the root directory. Otherwise
+         * not a mount point.
+         *
+         * NB: we avoid inode_same_at() here because it internally attempts name_to_handle_at_try_fid() first,
+         * which is redundant. */
+        return stat_inode_same(&a, &b);
+}
+
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point_full(const char *path, const char *root, int flags) {
+        _cleanup_close_ int dfd = -EBADF;
+        _cleanup_free_ char *fn = NULL;
+
+        assert(path);
+        assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
+
+        if (path_equal(path, "/"))
+                return 1;
+
+        /* we need to resolve symlinks manually, we can't just rely on is_mount_point_at() to do that for us;
+         * if we have a structure like /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
+         * look at needs to be /usr, not /. */
+        dfd = chase_and_open_parent(path, root,
+                                    CHASE_TRAIL_SLASH|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : CHASE_NOFOLLOW),
+                                    &fn);
+        if (dfd < 0)
+                return dfd;
+
+        return is_mount_point_at(dfd, fn, flags);
+}
+
+int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
+        int r;
+
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+        assert(ret);
+
+        r = name_to_handle_at_loop(dir_fd, path, NULL, ret, isempty(path) ? AT_EMPTY_PATH : 0);
+        if (r >= 0 || is_name_to_handle_at_fatal_error(r))
+                return r;
+
+        return fd_fdinfo_mnt_id(dir_fd, path, isempty(path) ? AT_EMPTY_PATH : 0, ret);
+}
+
+int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
+        struct statx sx;
+
+        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+        assert(ret);
+
+        if (statx(dir_fd,
+                  strempty(path),
+                  (isempty(path) ? AT_EMPTY_PATH : AT_SYMLINK_NOFOLLOW) |
+                  AT_NO_AUTOMOUNT |    /* don't trigger automounts, mnt_id is a local concept */
+                  AT_STATX_DONT_SYNC,  /* don't go to the network, mnt_id is a local concept */
+                  STATX_MNT_ID,
+                  &sx) < 0)
+                return -errno;
+
+        if (FLAGS_SET(sx.stx_mask, STATX_MNT_ID)) {
+                *ret = sx.stx_mnt_id;
+                return 0;
+        }
+
+        return path_get_mnt_id_at_fallback(dir_fd, path, ret);
+}
+
+bool fstype_is_network(const char *fstype) {
+        const char *x;
+
+        x = startswith(fstype, "fuse.");
+        if (x)
+                fstype = x;
+
+        if (nulstr_contains(filesystem_sets[FILESYSTEM_SET_NETWORK].value, fstype))
+                return true;
+
+        /* Filesystems not present in the internal database */
+        return STR_IN_SET(fstype,
+                          "davfs",
+                          "glusterfs",
+                          "lustre",
+                          "sshfs");
+}
+
+bool fstype_needs_quota(const char *fstype) {
+       /* 1. quotacheck needs to be run for some filesystems after they are mounted
+        *    if the filesystem was not unmounted cleanly.
+        * 2. You may need to run quotaon to enable quota usage tracking and/or
+        *    enforcement.
+        * ext2     - needs 1) and 2)
+        * ext3     - needs 2) if configured using usrjquota/grpjquota mount options
+        * ext4     - needs 1) if created without journal, needs 2) if created without QUOTA
+        *            filesystem feature
+        * reiserfs - needs 2).
+        * jfs      - needs 2)
+        * f2fs     - needs 2) if configured using usrjquota/grpjquota/prjjquota mount options
+        * xfs      - nothing needed
+        * gfs2     - nothing needed
+        * ocfs2    - nothing needed
+        * btrfs    - nothing needed
+        * for reference see filesystem and quota manpages */
+        return STR_IN_SET(fstype,
+                          "ext2",
+                          "ext3",
+                          "ext4",
+                          "reiserfs",
+                          "jfs",
+                          "f2fs");
+}
+
+bool fstype_is_api_vfs(const char *fstype) {
+        assert(fstype);
+
+        const FilesystemSet *fs;
+        FOREACH_ARGUMENT(fs,
+                         filesystem_sets + FILESYSTEM_SET_BASIC_API,
+                         filesystem_sets + FILESYSTEM_SET_AUXILIARY_API,
+                         filesystem_sets + FILESYSTEM_SET_PRIVILEGED_API,
+                         filesystem_sets + FILESYSTEM_SET_TEMPORARY)
+                if (nulstr_contains(fs->value, fstype))
+                    return true;
+
+        /* Filesystems not present in the internal database */
+        return STR_IN_SET(fstype,
+                          "autofs",
+                          "cpuset",
+                          "devtmpfs");
+}
+
+bool fstype_is_blockdev_backed(const char *fstype) {
+        const char *x;
+
+        x = startswith(fstype, "fuse.");
+        if (x)
+                fstype = x;
+
+        return !streq(fstype, "9p") && !fstype_is_network(fstype) && !fstype_is_api_vfs(fstype);
+}
+
+bool fstype_is_ro(const char *fstype) {
+        /* All Linux file systems that are necessarily read-only */
+        return STR_IN_SET(fstype,
+                          "DM_verity_hash",
+                          "cramfs",
+                          "erofs",
+                          "iso9660",
+                          "squashfs");
+}
+
+bool fstype_can_discard(const char *fstype) {
+        assert(fstype);
+
+        /* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
+         * not be allowed in our MAC context. */
+        if (STR_IN_SET(fstype, "btrfs", "f2fs", "ext4", "vfat", "xfs"))
+                return true;
+
+        /* On new kernels we can just ask the kernel */
+        return mount_option_supported(fstype, "discard", NULL) > 0;
+}
+
+const char* fstype_norecovery_option(const char *fstype) {
+        int r;
+
+        assert(fstype);
+
+        /* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
+         * not be allowed in our MAC context. */
+        if (STR_IN_SET(fstype, "ext3", "ext4", "xfs"))
+                return "norecovery";
+
+        /* btrfs dropped support for the "norecovery" option in 6.8
+         * (https://github.com/torvalds/linux/commit/a1912f712188291f9d7d434fba155461f1ebef66) and replaced
+         * it with rescue=nologreplay, so we check for the new name first and fall back to checking for the
+         * old name if the new name doesn't work. */
+        if (streq(fstype, "btrfs")) {
+                r = mount_option_supported(fstype, "rescue=nologreplay", NULL);
+                if (r == -EAGAIN) {
+                        log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming old kernel with 'norecovery': %m");
+                        return "norecovery";
+                }
+                if (r < 0)
+                        log_debug_errno(r, "Failed to check for btrfs 'rescue=nologreplay' option, assuming it is not supported: %m");
+                if (r > 0)
+                        return "rescue=nologreplay";
+        }
+
+        /* On new kernels we can just ask the kernel */
+        return mount_option_supported(fstype, "norecovery", NULL) > 0 ? "norecovery" : NULL;
+}
+
+bool fstype_can_fmask_dmask(const char *fstype) {
+        assert(fstype);
+
+        /* Use a curated list as first check, to avoid calling fsopen() which might load kmods, which might
+         * not be allowed in our MAC context. If we don't know ourselves, on new kernels we can just ask the
+         * kernel. */
+        return streq(fstype, "vfat") || (mount_option_supported(fstype, "fmask", "0177") > 0 && mount_option_supported(fstype, "dmask", "0077") > 0);
+}
+
+bool fstype_can_uid_gid(const char *fstype) {
+        /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and
+         * directories, current and future. Note that this does *not* ask the kernel via
+         * mount_option_supported() here because the uid=/gid= setting of various file systems mean different
+         * things: some apply it only to the root dir inode, others to all inodes in the file system. Thus we
+         * maintain the curated list below. 😢 */
+
+        return STR_IN_SET(fstype,
+                          "adfs",
+                          "exfat",
+                          "fat",
+                          "hfs",
+                          "hpfs",
+                          "iso9660",
+                          "msdos",
+                          "ntfs",
+                          "vfat");
+}
+
+int dev_is_devtmpfs(void) {
+        _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+        int mount_id, r;
+        char *e;
+
+        r = path_get_mnt_id("/dev", &mount_id);
+        if (r < 0)
+                return r;
+
+        r = fopen_unlocked("/proc/self/mountinfo", "re", &proc_self_mountinfo);
+        if (r == -ENOENT)
+                return proc_mounted() > 0 ? -ENOENT : -ENOSYS;
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                _cleanup_free_ char *line = NULL;
+                int mid;
+
+                r = read_line(proc_self_mountinfo, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (sscanf(line, "%i", &mid) != 1)
+                        continue;
+
+                if (mid != mount_id)
+                        continue;
+
+                e = strstrafter(line, " - ");
+                if (!e)
+                        continue;
+
+                /* accept any name that starts with the currently expected type */
+                if (startswith(e, "devtmpfs"))
+                        return true;
+        }
+
+        return false;
+}
+
+static int mount_fd(
+                const char *source,
+                int target_fd,
+                const char *filesystemtype,
+                unsigned long mountflags,
+                const void *data) {
+
+        assert(target_fd >= 0);
+
+        if (mount(source, FORMAT_PROC_FD_PATH(target_fd), filesystemtype, mountflags, data) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                /* ENOENT can mean two things: either that the source is missing, or that /proc/ isn't
+                 * mounted. Check for the latter to generate better error messages. */
+                if (proc_mounted() == 0)
+                        return -ENOSYS;
+
+                return -ENOENT;
+        }
+
+        return 0;
+}
+
+int mount_nofollow(
+                const char *source,
+                const char *target,
+                const char *filesystemtype,
+                unsigned long mountflags,
+                const void *data) {
+
+        _cleanup_close_ int fd = -EBADF;
+
+        assert(target);
+
+        /* In almost all cases we want to manipulate the mount table without following symlinks, hence
+         * mount_nofollow() is usually the way to go. The only exceptions are environments where /proc/ is
+         * not available yet, since we need /proc/self/fd/ for this logic to work. i.e. during the early
+         * initialization of namespacing/container stuff where /proc is not yet mounted (and maybe even the
+         * fs to mount) we can only use traditional mount() directly.
+         *
+         * Note that this disables following only for the final component of the target, i.e symlinks within
+         * the path of the target are honoured, as are symlinks in the source path everywhere. */
+
+        fd = open(target, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+        if (fd < 0)
+                return -errno;
+
+        return mount_fd(source, fd, filesystemtype, mountflags, data);
+}
+
+const char* mount_propagation_flag_to_string(unsigned long flags) {
+
+        switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
+        case 0:
+                return "";
+        case MS_SHARED:
+                return "shared";
+        case MS_SLAVE:
+                return "slave";
+        case MS_PRIVATE:
+                return "private";
+        }
+
+        return NULL;
+}
+
+int mount_propagation_flag_from_string(const char *name, unsigned long *ret) {
+
+        if (isempty(name))
+                *ret = 0;
+        else if (streq(name, "shared"))
+                *ret = MS_SHARED;
+        else if (streq(name, "slave"))
+                *ret = MS_SLAVE;
+        else if (streq(name, "private"))
+                *ret = MS_PRIVATE;
+        else
+                return -EINVAL;
+        return 0;
+}
+
+bool mount_propagation_flag_is_valid(unsigned long flag) {
+        return IN_SET(flag, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE);
+}
+
+bool mount_new_api_supported(void) {
+        static int cache = -1;
+        int r;
+
+        if (cache >= 0)
+                return cache;
+
+        /* This is the newer API among the ones we use, so use it as boundary */
+        r = RET_NERRNO(mount_setattr(-EBADF, NULL, 0, NULL, 0));
+        if (r == 0 || ERRNO_IS_NOT_SUPPORTED(r)) /* This should return an error if it is working properly */
+                return (cache = false);
+
+        return (cache = true);
+}
+
+unsigned long ms_nosymfollow_supported(void) {
+        _cleanup_close_ int fsfd = -EBADF, mntfd = -EBADF;
+        static int cache = -1;
+
+        /* Returns MS_NOSYMFOLLOW if it is supported, zero otherwise. */
+
+        if (cache >= 0)
+                return cache ? MS_NOSYMFOLLOW : 0;
+
+        if (!mount_new_api_supported())
+                goto not_supported;
+
+        /* Checks if MS_NOSYMFOLLOW is supported (which was added in 5.10). We use the new mount API's
+         * mount_setattr() call for that, which was added in 5.12, which is close enough. */
+
+        fsfd = fsopen("tmpfs", FSOPEN_CLOEXEC);
+        if (fsfd < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(errno))
+                        goto not_supported;
+
+                log_debug_errno(errno, "Failed to open superblock context for tmpfs: %m");
+                return 0;
+        }
+
+        if (fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(errno))
+                        goto not_supported;
+
+                log_debug_errno(errno, "Failed to create tmpfs superblock: %m");
+                return 0;
+        }
+
+        mntfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
+        if (mntfd < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(errno))
+                        goto not_supported;
+
+                log_debug_errno(errno, "Failed to turn superblock fd into mount fd: %m");
+                return 0;
+        }
+
+        if (mount_setattr(mntfd, "", AT_EMPTY_PATH|AT_RECURSIVE,
+                          &(struct mount_attr) {
+                                  .attr_set = MOUNT_ATTR_NOSYMFOLLOW,
+                          }, sizeof(struct mount_attr)) < 0) {
+                if (ERRNO_IS_NOT_SUPPORTED(errno))
+                        goto not_supported;
+
+                log_debug_errno(errno, "Failed to set MOUNT_ATTR_NOSYMFOLLOW mount attribute: %m");
+                return 0;
+        }
+
+        cache = true;
+        return MS_NOSYMFOLLOW;
+
+not_supported:
+        cache = false;
+        return 0;
+}
+
+int mount_option_supported(const char *fstype, const char *key, const char *value) {
+        _cleanup_close_ int fd = -EBADF;
+        int r;
+
+        /* Checks if the specified file system supports a mount option. Returns > 0 if it supports it, == 0 if
+         * it does not. Return -EAGAIN if we can't determine it. And any other error otherwise. */
+
+        assert(fstype);
+        assert(key);
+
+        fd = fsopen(fstype, FSOPEN_CLOEXEC);
+        if (fd < 0)
+                return log_debug_errno(errno, "Failed to open superblock context for '%s': %m", fstype);
+
+        /* Various file systems support fs context only in recent kernels (e.g. btrfs). For older kernels
+         * fsconfig() with FSCONFIG_SET_STRING/FSCONFIG_SET_FLAG never fail. Which sucks, because we want to
+         * use it for testing support, after all. Let's hence do a check if the file system got converted yet
+         * first. */
+        if (fsconfig(fd, FSCONFIG_SET_FD, "adefinitelynotexistingmountoption", NULL, fd) < 0) {
+                /* If FSCONFIG_SET_FD is not supported for the fs, then the file system was not converted to
+                 * the new mount API yet. If it returns EINVAL the mount option doesn't exist, but the fstype
+                 * is converted. */
+                if (errno == EOPNOTSUPP)
+                        return -EAGAIN; /* fs not converted to new mount API → don't know */
+                if (errno != EINVAL)
+                        return log_debug_errno(errno, "Failed to check if file system '%s' has been converted to new mount API: %m", fstype);
+
+                /* So FSCONFIG_SET_FD worked, but the option didn't exist (we got EINVAL), this means the fs
+                 * is converted. Let's now ask the actual question we wonder about. */
+        } else
+                return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "FSCONFIG_SET_FD worked unexpectedly for '%s', whoa!", fstype);
+
+        if (value)
+                r = fsconfig(fd, FSCONFIG_SET_STRING, key, value, 0);
+        else
+                r = fsconfig(fd, FSCONFIG_SET_FLAG, key, NULL, 0);
+        if (r < 0) {
+                if (errno == EINVAL)
+                        return false; /* EINVAL means option not supported. */
+
+                return log_debug_errno(errno, "Failed to set '%s%s%s' on '%s' superblock context: %m",
+                                       key, value ? "=" : "", strempty(value), fstype);
+        }
+
+        return true; /* works! */
+}
+
+bool path_below_api_vfs(const char *p) {
+        assert(p);
+
+        /* API VFS are either directly mounted on any of these three paths, or below it. */
+        return PATH_STARTSWITH_SET(p, "/dev", "/sys", "/proc");
+}
+#endif  /* NM_IGNORED */
\ No newline at end of file
diff --git a/src/libnm-systemd-shared/src/basic/mountpoint-util.h b/src/libnm-systemd-shared/src/basic/mountpoint-util.h
new file mode 100644
index 00000000..43e47581
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/mountpoint-util.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+/* The limit used for /dev itself. 4MB should be enough since device nodes and symlinks don't
+ * consume any space and udev isn't supposed to create regular file either. There's no limit on the
+ * max number of inodes since such limit is hard to guess especially on large storage array
+ * systems. */
+#define TMPFS_LIMITS_DEV             ",size=4m"
+
+/* The limit used for /dev in private namespaces. 4MB for contents of regular files. The number of
+ * inodes should be relatively low in private namespaces but for now use a 64k limit. */
+#define TMPFS_LIMITS_PRIVATE_DEV     ",size=4m,nr_inodes=64k"
+
+/* Very little, if any use expected */
+#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k"
+#define TMPFS_LIMITS_SYS             TMPFS_LIMITS_EMPTY_OR_ALMOST
+#define TMPFS_LIMITS_SYS_FS_CGROUP   TMPFS_LIMITS_EMPTY_OR_ALMOST
+
+/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of
+ * PID1 because 16MB of free space is required. */
+#define TMPFS_LIMITS_RUN             ",size=20%,nr_inodes=800k"
+
+/* The limit used for various nested tmpfs mounts, in particular for guests started by systemd-nspawn.
+ * 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25%
+ * translates to 1M inodes.
+ * (On the host, /tmp is configured through a .mount unit file.) */
+#define NESTED_TMPFS_LIMITS          ",size=10%,nr_inodes=400k"
+
+/* More space for volatile root and /var */
+#define TMPFS_LIMITS_VAR             ",size=25%,nr_inodes=1m"
+#define TMPFS_LIMITS_ROOTFS          TMPFS_LIMITS_VAR
+#define TMPFS_LIMITS_VOLATILE_STATE  TMPFS_LIMITS_VAR
+
+bool is_name_to_handle_at_fatal_error(int err);
+
+int name_to_handle_at_loop(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
+int name_to_handle_at_try_fid(int fd, const char *path, struct file_handle **ret_handle, int *ret_mnt_id, int flags);
+
+bool file_handle_equal(const struct file_handle *a, const struct file_handle *b);
+
+int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret);
+int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
+static inline int path_get_mnt_id(const char *path, int *ret) {
+        return path_get_mnt_id_at(AT_FDCWD, path, ret);
+}
+
+int is_mount_point_at(int fd, const char *filename, int flags);
+int path_is_mount_point_full(const char *path, const char *root, int flags);
+static inline int path_is_mount_point(const char *path) {
+        return path_is_mount_point_full(path, NULL, 0);
+}
+
+bool fstype_is_network(const char *fstype);
+bool fstype_needs_quota(const char *fstype);
+bool fstype_is_api_vfs(const char *fstype);
+bool fstype_is_blockdev_backed(const char *fstype);
+bool fstype_is_ro(const char *fsype);
+bool fstype_can_discard(const char *fstype);
+bool fstype_can_uid_gid(const char *fstype);
+bool fstype_can_fmask_dmask(const char *fstype);
+
+const char* fstype_norecovery_option(const char *fstype);
+
+int dev_is_devtmpfs(void);
+
+int mount_nofollow(
+                const char *source,
+                const char *target,
+                const char *filesystemtype,
+                unsigned long mountflags,
+                const void *data);
+
+const char* mount_propagation_flag_to_string(unsigned long flags);
+int mount_propagation_flag_from_string(const char *name, unsigned long *ret);
+bool mount_propagation_flag_is_valid(unsigned long flag);
+
+bool mount_new_api_supported(void);
+unsigned long ms_nosymfollow_supported(void);
+
+int mount_option_supported(const char *fstype, const char *key, const char *value);
+
+bool path_below_api_vfs(const char *p);
diff --git a/src/libnm-systemd-shared/src/basic/namespace-util.h b/src/libnm-systemd-shared/src/basic/namespace-util.h
index 3d40a515..d7ac8156 100644
--- a/src/libnm-systemd-shared/src/basic/namespace-util.h
+++ b/src/libnm-systemd-shared/src/basic/namespace-util.h
@@ -28,6 +28,8 @@ extern const struct namespace_info {
 
 NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag);
 
+bool namespace_type_supported(NamespaceType type);
+
 int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type);
 int namespace_open_by_type(NamespaceType type);
 
@@ -86,7 +88,8 @@ static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
 int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
 
 int userns_acquire_empty(void);
-int userns_acquire(const char *uid_map, const char *gid_map);
+int userns_acquire(const char *uid_map, const char *gid_map, bool setgroups_deny);
+int userns_acquire_self_root(void);
 int userns_enter_and_pin(int userns_fd, pid_t *ret_pid);
 
 int userns_get_base_uid(int userns_fd, uid_t *ret_uid, gid_t *ret_gid);
diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.h b/src/libnm-systemd-shared/src/basic/ordered-set.h
index e73da205..b7113ad9 100644
--- a/src/libnm-systemd-shared/src/basic/ordered-set.h
+++ b/src/libnm-systemd-shared/src/basic/ordered-set.h
@@ -22,18 +22,10 @@ static inline void ordered_set_clear(OrderedSet *s) {
         return ordered_hashmap_clear((OrderedHashmap*) s);
 }
 
-static inline void ordered_set_clear_free(OrderedSet *s) {
-        return ordered_hashmap_clear_free((OrderedHashmap*) s);
-}
-
 static inline OrderedSet* ordered_set_free(OrderedSet *s) {
         return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
 }
 
-static inline OrderedSet* ordered_set_free_free(OrderedSet *s) {
-        return (OrderedSet*) ordered_hashmap_free_free((OrderedHashmap*) s);
-}
-
 static inline int ordered_set_contains(OrderedSet *s, const void *p) {
         return ordered_hashmap_contains((OrderedHashmap*) s, p);
 }
@@ -91,19 +83,6 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s);
 #define ORDERED_SET_FOREACH(e, s) \
         _ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ))
 
-#define ordered_set_clear_with_destructor(s, f)                 \
-        ({                                                      \
-                OrderedSet *_s = (s);                           \
-                void *_item;                                    \
-                while ((_item = ordered_set_steal_first(_s)))   \
-                        f(_item);                               \
-                _s;                                             \
-        })
-#define ordered_set_free_with_destructor(s, f)                  \
-        ordered_set_free(ordered_set_clear_with_destructor(s, f))
-
 DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
 
 #define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
-#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep)
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index bff09b74..e524d9d5 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -5,6 +5,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <linux/ipv6.h>
+#include <linux/netfilter/nf_tables.h>
 #include <net/if.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -14,6 +15,7 @@
 #include "errno-list.h"
 #include "extract-word.h"
 #include "locale-util.h"
+#include "log.h"
 #include "macro.h"
 #include "missing_network.h"
 #include "parse-util.h"
@@ -533,7 +535,7 @@ int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu)
         return 0;
 }
 
-int safe_atolli(const char *s, long long int *ret_lli) {
+int safe_atolli(const char *s, long long *ret_lli) {
         unsigned base = 0;
         char *x = NULL;
         long long l;
@@ -784,19 +786,15 @@ int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
 /* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
  * https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
 bool nft_identifier_valid(const char *id) {
-        if (!id)
+        if (isempty(id))
                 return false;
 
-        size_t len = strlen(id);
-        if (len == 0 || len > 31)
+        if (strlen(id) >= NFT_NAME_MAXLEN)
                 return false;
 
         if (!ascii_isalpha(id[0]))
                 return false;
 
-        for (size_t i = 1; i < len; i++)
-                if (!ascii_isalpha(id[i]) && !ascii_isdigit(id[i]) && !IN_SET(id[i], '/', '\\', '_', '.'))
-                        return false;
-        return true;
+        return in_charset(id + 1, ALPHANUMERICAL "/\\_.");
 }
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h
index a47c8c79..0af407b3 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.h
+++ b/src/libnm-systemd-shared/src/basic/parse-util.h
@@ -41,7 +41,7 @@ static inline int safe_atou(const char *s, unsigned *ret_u) {
 int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret);
 
 int safe_atoi(const char *s, int *ret_i);
-int safe_atolli(const char *s, long long int *ret_i);
+int safe_atolli(const char *s, long long *ret_i);
 
 int safe_atou8_full(const char *s, unsigned base, uint8_t *ret);
 
@@ -87,8 +87,8 @@ static inline int safe_atou64(const char *s, uint64_t *ret_u) {
 }
 
 static inline int safe_atoi64(const char *s, int64_t *ret_i) {
-        assert_cc(sizeof(int64_t) == sizeof(long long int));
-        return safe_atolli(s, (long long int*) ret_i);
+        assert_cc(sizeof(int64_t) == sizeof(long long));
+        return safe_atolli(s, (long long*) ret_i);
 }
 
 static inline int safe_atoux64(const char *s, uint64_t *ret) {
@@ -101,8 +101,8 @@ static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *r
         assert_cc(sizeof(unsigned long) == sizeof(unsigned));
         return safe_atou_full(s, base, (unsigned*) ret_u);
 }
-static inline int safe_atoli(const char *s, long int *ret_u) {
-        assert_cc(sizeof(long int) == sizeof(int));
+static inline int safe_atoli(const char *s, long *ret_u) {
+        assert_cc(sizeof(long) == sizeof(int));
         return safe_atoi(s, (int*) ret_u);
 }
 #else
@@ -110,9 +110,9 @@ static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *r
         assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
         return safe_atollu_full(s, base, (unsigned long long*) ret_u);
 }
-static inline int safe_atoli(const char *s, long int *ret_u) {
-        assert_cc(sizeof(long int) == sizeof(long long int));
-        return safe_atolli(s, (long long int*) ret_u);
+static inline int safe_atoli(const char *s, long *ret_u) {
+        assert_cc(sizeof(long) == sizeof(long long));
+        return safe_atolli(s, (long long*) ret_u);
 }
 #endif
 
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index 1de055d5..74f256a6 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -624,37 +624,9 @@ char* path_extend_internal(char **x, ...) {
 }
 
 #if 0 /* NM_IGNORED */
-static int check_x_access(const char *path, int *ret_fd) {
+int open_and_check_executable(const char *name, const char *root, char **ret_path, int *ret_fd) {
         _cleanup_close_ int fd = -EBADF;
-        int r;
-
-        /* We need to use O_PATH because there may be executables for which we have only exec
-         * permissions, but not read (usually suid executables). */
-        fd = open(path, O_PATH|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        r = fd_verify_regular(fd);
-        if (r < 0)
-                return r;
-
-        r = access_fd(fd, X_OK);
-        if (r == -ENOSYS) {
-                /* /proc is not mounted. Fallback to access(). */
-                if (access(path, X_OK) < 0)
-                        return -errno;
-        } else if (r < 0)
-                return r;
-
-        if (ret_fd)
-                *ret_fd = TAKE_FD(fd);
-
-        return 0;
-}
-
-static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) {
-        _cleanup_close_ int fd = -EBADF;
-        _cleanup_free_ char *path_name = NULL;
+        _cleanup_free_ char *resolved = NULL;
         int r;
 
         assert(name);
@@ -665,23 +637,40 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
          * needed to avoid unforeseen regression or other complicated changes. */
         if (root) {
                 /* prefix root to name in case full paths are not specified */
-                r = chase(name, root, CHASE_PREFIX_ROOT, &path_name, /* ret_fd= */ NULL);
+                r = chase(name, root, CHASE_PREFIX_ROOT, &resolved, &fd);
                 if (r < 0)
                         return r;
 
-                name = path_name;
+                name = resolved;
+        } else {
+                /* We need to use O_PATH because there may be executables for which we have only exec permissions,
+                 * but not read (usually suid executables). */
+                fd = open(name, O_PATH|O_CLOEXEC);
+                if (fd < 0)
+                        return -errno;
         }
 
-        r = check_x_access(name, ret_fd ? &fd : NULL);
+        r = fd_verify_regular(fd);
         if (r < 0)
                 return r;
 
-        if (ret_filename) {
-                r = path_make_absolute_cwd(name, ret_filename);
-                if (r < 0)
-                        return r;
+        r = access_fd(fd, X_OK);
+        if (r == -ENOSYS)
+                /* /proc/ is not mounted. Fall back to access(). */
+                r = RET_NERRNO(access(name, X_OK));
+        if (r < 0)
+                return r;
 
-                path_simplify(*ret_filename);
+        if (ret_path) {
+                if (resolved)
+                        *ret_path = TAKE_PTR(resolved);
+                else {
+                        r = path_make_absolute_cwd(name, ret_path);
+                        if (r < 0)
+                                return r;
+
+                        path_simplify(*ret_path);
+                }
         }
 
         if (ret_fd)
@@ -703,7 +692,7 @@ int find_executable_full(
         assert(name);
 
         if (is_path(name))
-                return find_executable_impl(name, root, ret_filename, ret_fd);
+                return open_and_check_executable(name, root, ret_filename, ret_fd);
 
         if (exec_search_path) {
                 STRV_FOREACH(element, exec_search_path) {
@@ -718,7 +707,7 @@ int find_executable_full(
                         if (!full_path)
                                 return -ENOMEM;
 
-                        r = find_executable_impl(full_path, root, ret_filename, ret_fd);
+                        r = open_and_check_executable(full_path, root, ret_filename, ret_fd);
                         if (r >= 0)
                                 return 0;
                         if (r != -EACCES)
@@ -754,7 +743,7 @@ int find_executable_full(
                 if (!path_extend(&element, name))
                         return -ENOMEM;
 
-                r = find_executable_impl(element, root, ret_filename, ret_fd);
+                r = open_and_check_executable(element, root, ret_filename, ret_fd);
                 if (r >= 0) /* Found it! */
                         return 0;
                 /* PATH entries which we don't have access to are ignored, as per tradition. */
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index 9b13d958..5bfa8fc7 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -117,6 +117,7 @@ int path_strv_make_absolute_cwd(char **l);
 char** path_strv_resolve(char **l, const char *root);
 char** path_strv_resolve_uniq(char **l, const char *root);
 
+int open_and_check_executable(const char *name, const char *root, char **ret_path, int *ret_fd);
 int find_executable_full(
                 const char *name,
                 const char *root,
diff --git a/src/libnm-systemd-shared/src/basic/pidfd-util.c b/src/libnm-systemd-shared/src/basic/pidfd-util.c
index d6bd5517..91638c77 100644
--- a/src/libnm-systemd-shared/src/basic/pidfd-util.c
+++ b/src/libnm-systemd-shared/src/basic/pidfd-util.c
@@ -3,15 +3,18 @@
 #include "nm-sd-adapt-shared.h"
 
 #include <sys/ioctl.h>
+#include <threads.h>
 #include <unistd.h>
 
 #include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "log.h"
 #include "macro.h"
 #include "memory-util.h"
+#include "missing_fs.h"
 #include "missing_magic.h"
-#include "missing_threads.h"
+#include "mountpoint-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "pidfd-util.h"
@@ -89,26 +92,21 @@ static int pidfd_get_info(int fd, struct pidfd_info *info) {
 
 static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
         char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
-        _cleanup_free_ char *fdinfo = NULL;
+        _cleanup_free_ char *p = NULL;
         int r;
 
         assert(fd >= 0);
 
         xsprintf(path, "/proc/self/fdinfo/%i", fd);
 
-        r = read_full_virtual_file(path, &fdinfo, NULL);
+        r = get_proc_field(path, "Pid", &p);
         if (r == -ENOENT)
-                return proc_fd_enoent_errno();
+                return -EBADF;
+        if (r == -ENODATA) /* not a pidfd? */
+                return -ENOTTY;
         if (r < 0)
                 return r;
 
-        char *p = find_line_startswith(fdinfo, "Pid:");
-        if (!p)
-                return -ENOTTY; /* not a pidfd? */
-
-        p = skip_leading_chars(p, /* bad = */ NULL);
-        p[strcspn(p, WHITESPACE)] = 0;
-
         if (streq(p, "0"))
                 return -EREMOTE; /* PID is in foreign PID namespace? */
         if (streq(p, "-1"))
@@ -230,6 +228,7 @@ int pidfd_get_cgroupid(int fd, uint64_t *ret) {
 #endif /* NM_IGNORED */
 
 int pidfd_get_inode_id(int fd, uint64_t *ret) {
+        static bool file_handle_supported = true;
         int r;
 
         assert(fd >= 0);
@@ -240,6 +239,30 @@ int pidfd_get_inode_id(int fd, uint64_t *ret) {
         if (r == 0)
                 return -EOPNOTSUPP;
 
+        if (file_handle_supported) {
+                union {
+                        struct file_handle file_handle;
+                        uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
+                } fh = {
+                        .file_handle.handle_bytes = sizeof(uint64_t),
+                        .file_handle.handle_type = FILEID_KERNFS,
+                };
+                int mnt_id;
+
+                r = RET_NERRNO(name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH));
+                if (r >= 0) {
+                        if (ret)
+                                *ret = *CAST_ALIGN_PTR(uint64_t, fh.file_handle.f_handle);
+                        return 0;
+                }
+                assert(r != -EOVERFLOW);
+                if (is_name_to_handle_at_fatal_error(r))
+                        return r;
+
+                file_handle_supported = false;
+        }
+
+#if SIZEOF_INO_T == 8
         struct stat st;
         if (fstat(fd, &st) < 0)
                 return -errno;
@@ -247,6 +270,16 @@ int pidfd_get_inode_id(int fd, uint64_t *ret) {
         if (ret)
                 *ret = (uint64_t) st.st_ino;
         return 0;
+
+#elif SIZEOF_INO_T == 4
+        /* On 32-bit systems (where sizeof(ino_t) == 4), the inode id returned by fstat() cannot be used to
+         * reliably identify the process, nor can we communicate the origin of the id with the clients.
+         * Hence let's just refuse to acquire pidfdid through fstat() here. All clients shall also insist on
+         * the 64-bit id from name_to_handle_at(). */
+        return -EOPNOTSUPP;
+#else
+#  error Unsupported ino_t size
+#endif
 }
 
 int pidfd_get_inode_id_self_cached(uint64_t *ret) {
diff --git a/src/libnm-systemd-shared/src/basic/pidref.h b/src/libnm-systemd-shared/src/basic/pidref.h
index 9e8a39ec..064b9d85 100644
--- a/src/libnm-systemd-shared/src/basic/pidref.h
+++ b/src/libnm-systemd-shared/src/basic/pidref.h
@@ -1,10 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-typedef struct PidRef PidRef;
-
 #include "macro.h"
-#include "process-util.h"
+#include "memory-util.h"
 
 /* An embeddable structure carrying a reference to a process. Supposed to be used when tracking processes
  * continuously. This combines a PID, a modern Linux pidfd and the 64bit inode number of the pidfd into one
@@ -29,22 +27,22 @@ typedef struct PidRef PidRef;
  *    process. Moreover, most operations will fail with -EREMOTE. Only PidRef structures that are not marked
  *    *unset* can be marked *remote*.
  */
-struct PidRef {
-        pid_t pid;      /* > 0 if the PidRef is set, otherwise set to PID_AUTOMATIC if automatic mode is
-                         * desired, or 0 otherwise. */
+typedef struct PidRef {
+        pid_t pid;      /* > 0 if the PidRef is set, otherwise set to INT_MIN (PID_AUTOMATIC) if automatic
+                         * mode is desired, or 0 otherwise. */
         int fd;         /* only valid if pidfd are available in the kernel, and we manage to get an fd. If we
                          * know that the PID is not from the local machine we set this to -EREMOTE, otherwise
                          * we use -EBADF as indicator the fd is invalid. */
         uint64_t fd_id; /* the inode number of pidfd. only useful in kernel 6.9+ where pidfds live in
                            their own pidfs and each process comes with a unique inode number */
-};
+} PidRef;
 
 #define PIDREF_NULL (PidRef) { .fd = -EBADF }
 
 /* A special pidref value that we are using when a PID shall be automatically acquired from some surrounding
  * context, for example connection peer. Much like PIDREF_NULL it will be considered unset by
- * pidref_is_set().*/
-#define PIDREF_AUTOMATIC (const PidRef) { .pid = PID_AUTOMATIC, .fd = -EBADF }
+ * pidref_is_set(). */
+#define PIDREF_AUTOMATIC (const PidRef) { .pid = (pid_t) INT_MIN, .fd = -EBADF }
 
 /* Turns a pid_t into a PidRef structure on-the-fly *without* acquiring a pidfd for it. (As opposed to
  * pidref_set_pid() which does so *with* acquiring one, see below) */
@@ -70,7 +68,7 @@ bool pidref_equal(PidRef *a, PidRef *b);
 int pidref_set_pid(PidRef *pidref, pid_t pid);
 int pidref_set_pidstr(PidRef *pidref, const char *pid);
 int pidref_set_pidfd(PidRef *pidref, int fd);
-int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success*/
+int pidref_set_pidfd_take(PidRef *pidref, int fd); /* takes ownership of the passed pidfd on success */
 int pidref_set_pidfd_consume(PidRef *pidref, int fd); /* takes ownership of the passed pidfd in both success and failure */
 int pidref_set_parent(PidRef *ret);
 static inline int pidref_set_self(PidRef *pidref) {
@@ -108,5 +106,9 @@ int pidref_verify(const PidRef *pidref);
 
 #define TAKE_PIDREF(p) TAKE_GENERIC((p), PidRef, PIDREF_NULL)
 
+struct siphash;
+void pidref_hash_func(const PidRef *pidref, struct siphash *state);
+int pidref_compare_func(const PidRef *a, const PidRef *b);
+
 extern const struct hash_ops pidref_hash_ops;
 extern const struct hash_ops pidref_hash_ops_free; /* Has destructor call for pidref_free(), i.e. expects heap allocated PidRef as keys */
diff --git a/src/libnm-systemd-shared/src/basic/prioq.c b/src/libnm-systemd-shared/src/basic/prioq.c
index c36d0ddd..0a007a29 100644
--- a/src/libnm-systemd-shared/src/basic/prioq.c
+++ b/src/libnm-systemd-shared/src/basic/prioq.c
@@ -48,6 +48,11 @@ Prioq* prioq_free(Prioq *q) {
         if (!q)
                 return NULL;
 
+        /* Invalidate the index fields of any remaining objects */
+        FOREACH_ARRAY(item, q->items, q->n_items)
+                if (item->idx)
+                        *(item->idx) = PRIOQ_IDX_NULL;
+
         free(q->items);
         return mfree(q);
 }
@@ -180,6 +185,11 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
         assert(q);
         assert(i);
 
+        /* Let's invalidate the index pointer stored in the user's object to indicate the item is now removed
+         * from the priority queue */
+        if (i->idx)
+                *(i->idx) = PRIOQ_IDX_NULL;
+
         l = q->items + q->n_items - 1;
 
         if (i == l)
@@ -191,6 +201,7 @@ static void remove_item(Prioq *q, struct prioq_item *i) {
                 /* Not last entry, let's replace the last entry with
                  * this one, and reshuffle */
 
+                assert(i >= q->items);
                 k = i - q->items;
 
                 i->data = l->data;
@@ -254,6 +265,7 @@ void prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
         if (!i)
                 return;
 
+        assert(i >= q->items);
         k = i - q->items;
         k = shuffle_down(q, k);
         shuffle_up(q, k);
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 85187562..517990b5 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -17,6 +17,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <syslog.h>
+#include <threads.h>
 #include <unistd.h>
 #if 0 /* NM_IGNORED */
 #if HAVE_VALGRIND_VALGRIND_H
@@ -38,13 +39,13 @@
 #include "fs-util.h"
 #include "hostname-util.h"
 #include "io-util.h"
+#include "iovec-util.h"
 #include "locale-util.h"
 #include "log.h"
 #include "macro.h"
 #include "memory-util.h"
 #include "missing_sched.h"
 #include "missing_syscall.h"
-#include "missing_threads.h"
 #include "mountpoint-util.h"
 #include "namespace-util.h"
 #include "nulstr-util.h"
@@ -55,6 +56,7 @@
 #include "raw-clone.h"
 #include "rlimit-util.h"
 #include "signal-util.h"
+#include "socket-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-table.h"
@@ -509,56 +511,25 @@ int get_process_exe(pid_t pid, char **ret) {
         return 0;
 }
 
-static int get_process_id(pid_t pid, const char *field, uid_t *ret) {
-        _cleanup_fclose_ FILE *f = NULL;
-        const char *p;
+int pid_get_uid(pid_t pid, uid_t *ret) {
         int r;
 
-        assert(field);
+        assert(pid >= 0);
         assert(ret);
 
-        if (pid < 0)
-                return -EINVAL;
+        if (pid == 0 || pid == getpid_cached()) {
+                *ret = getuid();
+                return 0;
+        }
 
-        p = procfs_file_alloca(pid, "status");
-        r = fopen_unlocked(p, "re", &f);
+        _cleanup_free_ char *v = NULL;
+        r = procfs_file_get_field(pid, "status", "Uid", &v);
         if (r == -ENOENT)
                 return -ESRCH;
         if (r < 0)
                 return r;
 
-        for (;;) {
-                _cleanup_free_ char *line = NULL;
-                char *l;
-
-                r = read_stripped_line(f, LONG_LINE_MAX, &line);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                l = startswith(line, field);
-                if (l) {
-                        l += strspn(l, WHITESPACE);
-
-                        l[strcspn(l, WHITESPACE)] = 0;
-
-                        return parse_uid(l, ret);
-                }
-        }
-
-        return -EIO;
-}
-
-int pid_get_uid(pid_t pid, uid_t *ret) {
-        assert(ret);
-
-        if (pid == 0 || pid == getpid_cached()) {
-                *ret = getuid();
-                return 0;
-        }
-
-        return get_process_id(pid, "Uid:", ret);
+        return parse_uid(v, ret);
 }
 
 int pidref_get_uid(const PidRef *pid, uid_t *ret) {
@@ -591,14 +562,24 @@ int pidref_get_uid(const PidRef *pid, uid_t *ret) {
 }
 
 int get_process_gid(pid_t pid, gid_t *ret) {
+        int r;
+
+        assert(pid >= 0);
+        assert(ret);
 
         if (pid == 0 || pid == getpid_cached()) {
                 *ret = getgid();
                 return 0;
         }
 
-        assert_cc(sizeof(uid_t) == sizeof(gid_t));
-        return get_process_id(pid, "Gid:", ret);
+        _cleanup_free_ char *v = NULL;
+        r = procfs_file_get_field(pid, "status", "Gid", &v);
+        if (r == -ENOENT)
+                return -ESRCH;
+        if (r < 0)
+                return r;
+
+        return parse_gid(v, ret);
 }
 
 int get_process_cwd(pid_t pid, char **ret) {
@@ -864,15 +845,12 @@ int pidref_get_start_time(const PidRef *pid, usec_t *ret) {
 
 int get_process_umask(pid_t pid, mode_t *ret) {
         _cleanup_free_ char *m = NULL;
-        const char *p;
         int r;
 
         assert(pid >= 0);
         assert(ret);
 
-        p = procfs_file_alloca(pid, "status");
-
-        r = get_proc_field(p, "Umask", WHITESPACE, &m);
+        r = procfs_file_get_field(pid, "status", "Umask", &m);
         if (r == -ENOENT)
                 return -ESRCH;
         if (r < 0)
@@ -881,27 +859,8 @@ int get_process_umask(pid_t pid, mode_t *ret) {
         return parse_mode(m, ret);
 }
 
-int wait_for_terminate(pid_t pid, siginfo_t *status) {
-        siginfo_t dummy;
-
-        assert(pid >= 1);
-
-        if (!status)
-                status = &dummy;
-
-        for (;;) {
-                zero(*status);
-
-                if (waitid(P_PID, pid, status, WEXITED) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        return negative_errno();
-                }
-
-                return 0;
-        }
+int wait_for_terminate(pid_t pid, siginfo_t *ret) {
+        return pidref_wait_for_terminate(&PIDREF_MAKE_FROM_PID(pid), ret);
 }
 
 /*
@@ -918,24 +877,29 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
  * A warning is emitted if the process terminates abnormally,
  * and also if it returns non-zero unless check_exit_code is true.
  */
-int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
-        _cleanup_free_ char *buffer = NULL;
-        siginfo_t status;
-        int r, prio;
+int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFlags flags) {
+        int r;
 
-        assert(pid > 1);
+        if (!pidref_is_set(pidref))
+                return -ESRCH;
+        if (pidref_is_remote(pidref))
+                return -EREMOTE;
+        if (pidref->pid == 1 || pidref_is_self(pidref))
+                return -ECHILD;
 
+        _cleanup_free_ char *buffer = NULL;
         if (!name) {
-                r = pid_get_comm(pid, &buffer);
+                r = pidref_get_comm(pidref, &buffer);
                 if (r < 0)
-                        log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
+                        log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pidref->pid);
                 else
                         name = buffer;
         }
 
-        prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
+        int prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
 
-        r = wait_for_terminate(pid, &status);
+        siginfo_t status;
+        r = pidref_wait_for_terminate(pidref, &status);
         if (r < 0)
                 return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
 
@@ -958,6 +922,10 @@ int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
         return -EPROTO;
 }
 
+int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
+        return pidref_wait_for_terminate_and_check(name, &PIDREF_MAKE_FROM_PID(pid), flags);
+}
+
 /*
  * Return values:
  *
@@ -1385,8 +1353,8 @@ void valgrind_summary_hack(void) {
                 if (pid < 0)
                         log_struct_errno(
                                 LOG_EMERG, errno,
-                                "MESSAGE_ID=" SD_MESSAGE_VALGRIND_HELPER_FORK_STR,
-                                LOG_MESSAGE( "Failed to fork off valgrind helper: %m"));
+                                LOG_MESSAGE_ID(SD_MESSAGE_VALGRIND_HELPER_FORK_STR),
+                                LOG_MESSAGE("Failed to fork off valgrind helper: %m"));
                 else if (pid == 0)
                         exit(EXIT_SUCCESS);
                 else {
@@ -1446,7 +1414,7 @@ pid_t getpid_cached(void) {
         case CACHED_PID_UNSET: { /* Not initialized yet, then do so now */
                 pid_t new_pid;
 
-                new_pid = raw_getpid();
+                new_pid = getpid();
 
                 if (!installed) {
                         /* __register_atfork() either returns 0 or -ENOMEM, in its glibc implementation. Since it's
@@ -1467,7 +1435,7 @@ pid_t getpid_cached(void) {
         }
 
         case CACHED_PID_BUSY: /* Somebody else is currently initializing */
-                return raw_getpid();
+                return getpid();
 
         default: /* Properly initialized */
                 return current_value;
@@ -1533,25 +1501,28 @@ static int fork_flags_to_signal(ForkFlags flags) {
                                                  SIGKILL;
 }
 
-int safe_fork_full(
+int pidref_safe_fork_full(
                 const char *name,
                 const int stdio_fds[3],
                 int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
-                pid_t *ret_pid) {
+                PidRef *ret_pid) {
 
         pid_t original_pid, pid;
         sigset_t saved_ss, ss;
         _unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
         bool block_signals = false, block_all = false, intermediary = false;
+        _cleanup_close_pair_ int pidref_transport_fds[2] = EBADF_PAIR;
         int prio, r;
 
+        assert(!FLAGS_SET(flags, FORK_WAIT|FORK_FREEZE));
         assert(!FLAGS_SET(flags, FORK_DETACH) ||
-               (!ret_pid && (flags & (FORK_WAIT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) == 0));
+               (flags & (FORK_WAIT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) == 0);
 
-        /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
-         * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
+        /* A wrapper around fork(), that does a couple of important initializations in addition to mere
+         * forking. If provided, ret_pid is initialized in both the parent and the child process, both times
+         * referencing the child process. Returns == 0 in the child and > 0 in the parent. */
 
         prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
 
@@ -1594,14 +1565,43 @@ int safe_fork_full(
                 if (!r) {
                         /* Not a reaper process, hence do a double fork() so we are reparented to one */
 
+                        if (ret_pid && socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pidref_transport_fds) < 0)
+                                return log_full_errno(prio, errno, "Failed to allocate pidref socket: %m");
+
                         pid = fork();
                         if (pid < 0)
                                 return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name));
                         if (pid > 0) {
                                 log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid);
+
+                                pidref_transport_fds[1] = safe_close(pidref_transport_fds[1]);
+
+                                if (pidref_transport_fds[0] >= 0) {
+                                        /* Wait for the intermediary child to exit so the caller can be certain the actual child
+                                         * process has been reparented by the time this function returns. */
+                                        r = wait_for_terminate_and_check(name, pid, FLAGS_SET(flags, FORK_LOG) ? WAIT_LOG : 0);
+                                        if (r < 0)
+                                                return log_full_errno(prio, r, "Failed to wait for intermediary process: %m");
+                                        if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
+                                                return -EPROTO;
+
+                                        int pidfd;
+                                        ssize_t n = receive_one_fd_iov(
+                                                        pidref_transport_fds[0],
+                                                        &IOVEC_MAKE(&pid, sizeof(pid)),
+                                                        /* iovlen= */ 1,
+                                                        /* flags= */ 0,
+                                                        &pidfd);
+                                        if (n < 0)
+                                                return log_full_errno(prio, n, "Failed to receive child pidref: %m");
+
+                                        *ret_pid = (PidRef) { .pid = pid, .fd = pidfd };
+                                }
+
                                 return 1; /* return in the parent */
                         }
 
+                        pidref_transport_fds[0] = safe_close(pidref_transport_fds[0]);
                         intermediary = true;
                 }
         }
@@ -1619,8 +1619,30 @@ int safe_fork_full(
         if (pid > 0) {
 
                 /* If we are in the intermediary process, exit now */
-                if (intermediary)
+                if (intermediary) {
+                        if (pidref_transport_fds[1] >= 0) {
+                                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+
+                                r = pidref_set_pid(&pidref, pid);
+                                if (r < 0) {
+                                        log_full_errno(prio, r, "Failed to open reference to PID "PID_FMT": %m", pid);
+                                        _exit(EXIT_FAILURE);
+                                }
+
+                                r = send_one_fd_iov(
+                                                pidref_transport_fds[1],
+                                                pidref.fd,
+                                                &IOVEC_MAKE(&pidref.pid, sizeof(pidref.pid)),
+                                                /* iovlen= */ 1,
+                                                /* flags= */ 0);
+                                if (r < 0) {
+                                        log_full_errno(prio, r, "Failed to send child pidref: %m");
+                                        _exit(EXIT_FAILURE);
+                                }
+                        }
+
                         _exit(EXIT_SUCCESS);
+                }
 
                 /* We are in the parent process */
                 log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
@@ -1638,16 +1660,31 @@ int safe_fork_full(
                                 return r;
                         if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
                                 return -EPROTO;
+
+                        /* If we are in the parent and successfully waited, then the process doesn't exist anymore. */
+                        if (ret_pid)
+                                *ret_pid = PIDREF_NULL;
+
+                        return 1;
                 }
 
-                if (ret_pid)
-                        *ret_pid = pid;
+                if (ret_pid) {
+                        if (FLAGS_SET(flags, FORK_PID_ONLY))
+                                *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+                        else {
+                                r = pidref_set_pid(ret_pid, pid);
+                                if (r < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
+                                        *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+                        }
+                }
 
                 return 1;
         }
 
         /* We are in the child process */
 
+        pidref_transport_fds[1] = safe_close(pidref_transport_fds[1]);
+
         /* Restore signal mask manually */
         saved_ssp = NULL;
 
@@ -1809,36 +1846,41 @@ int safe_fork_full(
         if (FLAGS_SET(flags, FORK_FREEZE))
                 freeze();
 
-        if (ret_pid)
-                *ret_pid = getpid_cached();
+        if (ret_pid) {
+                if (FLAGS_SET(flags, FORK_PID_ONLY))
+                        *ret_pid = PIDREF_MAKE_FROM_PID(getpid_cached());
+                else {
+                        r = pidref_set_self(ret_pid);
+                        if (r < 0) {
+                                log_full_errno(prio, r, "Failed to acquire PID reference on ourselves: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+                }
+        }
 
         return 0;
 }
 
-int pidref_safe_fork_full(
+int safe_fork_full(
                 const char *name,
                 const int stdio_fds[3],
                 int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
-                PidRef *ret_pid) {
+                pid_t *ret_pid) {
 
-        pid_t pid;
-        int r, q;
+        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+        int r;
 
-        r = safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags, &pid);
-        if (r < 0 || !ret_pid)
-                return r;
+        /* Getting the detached child process pid without pidfd is racy, so don't allow it if not returning
+         * a pidref to the caller. */
+        assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid);
 
-        if (r > 0 && FLAGS_SET(flags, FORK_WAIT)) {
-                /* If we are in the parent and successfully waited, then the process doesn't exist anymore */
-                *ret_pid = PIDREF_NULL;
+        r = pidref_safe_fork_full(name, stdio_fds, except_fds, n_except_fds, flags|FORK_PID_ONLY, ret_pid ? &pidref : NULL);
+        if (r < 0 || !ret_pid)
                 return r;
-        }
 
-        q = pidref_set_pid(ret_pid, pid);
-        if (q < 0) /* Let's not fail for this, no matter what, the process exists after all, and that's key */
-                *ret_pid = PIDREF_MAKE_FROM_PID(pid);
+        *ret_pid = pidref.pid;
 
         return r;
 }
@@ -2013,17 +2055,14 @@ _noreturn_ void freeze(void) {
 
 int get_process_threads(pid_t pid) {
         _cleanup_free_ char *t = NULL;
-        const char *p;
         int n, r;
 
         if (pid < 0)
                 return -EINVAL;
 
-        p = procfs_file_alloca(pid, "status");
-
-        r = get_proc_field(p, "Threads", WHITESPACE, &t);
+        r = procfs_file_get_field(pid, "status", "Threads", &t);
         if (r == -ENOENT)
-                return proc_mounted() == 0 ? -ENOSYS : -ESRCH;
+                return -ESRCH;
         if (r < 0)
                 return r;
 
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index 19ac55dd..395df64a 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -12,6 +12,8 @@
 #include <sys/types.h>
 
 #include "alloc-util.h"
+#include "assert-util.h"
+#include "fileio.h"
 #include "format-util.h"
 #include "macro.h"
 #include "pidref.h"
@@ -22,16 +24,20 @@
                 pid_t _pid_ = (pid);                                    \
                 const char *_field_ = (field);                          \
                 char *_r_;                                              \
-                if (_pid_ == 0) {                                       \
-                        _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \
-                        strcpy(stpcpy(_r_, "/proc/self/"), _field_);    \
-                } else {                                                \
+                if (_pid_ == 0)                                         \
+                        _r_ = strjoina("/proc/self/", _field_);         \
+                else {                                                  \
+                        assert(_pid_ > 0);                              \
                         _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \
                         sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \
                 }                                                       \
                 (const char*) _r_;                                      \
         })
 
+static inline int procfs_file_get_field(pid_t pid, const char *name, const char *key, char **ret) {
+        return get_proc_field(procfs_file_alloca(pid, name), key, ret);
+}
+
 typedef enum ProcessCmdlineFlags {
         PROCESS_CMDLINE_COMM_FALLBACK = 1 << 0,
         PROCESS_CMDLINE_USE_LOCALE    = 1 << 1,
@@ -61,7 +67,11 @@ int get_process_umask(pid_t pid, mode_t *ret);
 
 int container_get_leader(const char *machine, pid_t *pid);
 
-int wait_for_terminate(pid_t pid, siginfo_t *status);
+static inline bool SIGINFO_CODE_IS_DEAD(int code) {
+        return IN_SET(code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *ret);
 
 typedef enum WaitFlags {
         WAIT_LOG_ABNORMAL             = 1 << 0,
@@ -71,7 +81,9 @@ typedef enum WaitFlags {
         WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
 } WaitFlags;
 
+int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFlags flags);
 int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
+
 int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
 
 void sigkill_wait(pid_t pid);
@@ -195,30 +207,31 @@ typedef enum ForkFlags {
         FORK_NEW_NETNS          = 1 << 20, /* Run child in its own network namespace                             💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
         FORK_NEW_PIDNS          = 1 << 21, /* Run child in its own PID namespace                                 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */
         FORK_FREEZE             = 1 << 22, /* Don't return in child, just call freeze() instead */
+        FORK_PID_ONLY           = 1 << 23, /* Don't open a pidfd referencing the child process */
 } ForkFlags;
 
-int safe_fork_full(
+int pidref_safe_fork_full(
                 const char *name,
                 const int stdio_fds[3],
                 int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
-                pid_t *ret_pid);
+                PidRef *ret_pid);
 
-static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
-        return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
+static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) {
+        return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
 }
 
-int pidref_safe_fork_full(
+int safe_fork_full(
                 const char *name,
                 const int stdio_fds[3],
                 int except_fds[],
                 size_t n_except_fds,
                 ForkFlags flags,
-                PidRef *ret_pid);
+                pid_t *ret_pid);
 
-static inline int pidref_safe_fork(const char *name, ForkFlags flags, PidRef *ret_pid) {
-        return pidref_safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
+static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
+        return safe_fork_full(name, NULL, NULL, 0, flags, ret_pid);
 }
 
 int namespace_fork(
diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c
index 1e1a24bd..dab4334a 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.c
+++ b/src/libnm-systemd-shared/src/basic/random-util.c
@@ -13,6 +13,7 @@
 #include <sys/auxv.h>
 #include <sys/ioctl.h>
 #include <sys/time.h>
+#include <threads.h>
 
 #include "alloc-util.h"
 #include "env-util.h"
@@ -21,9 +22,9 @@
 #include "fileio.h"
 #include "io-util.h"
 #include "iovec-util.h"
+#include "log.h"
 #include "missing_random.h"
 #include "missing_syscall.h"
-#include "missing_threads.h"
 #include "parse-util.h"
 #include "pidfd-util.h"
 #include "process-util.h"
diff --git a/src/libnm-systemd-shared/src/basic/random-util.h b/src/libnm-systemd-shared/src/basic/random-util.h
index 0b5ba771..587ca1c2 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.h
+++ b/src/libnm-systemd-shared/src/basic/random-util.h
@@ -6,8 +6,10 @@
 #include <stdint.h>
 #include <sys/uio.h>
 
-void random_bytes(void *p, size_t n); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */
-int crypto_random_bytes(void *p, size_t n); /* Returns secure random bytes after waiting for the RNG to initialize. */
+#include "macro.h"
+
+void random_bytes(void *p, size_t n) _nonnull_if_nonzero_(1, 2); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */
+int crypto_random_bytes(void *p, size_t n) _nonnull_if_nonzero_(1, 2); /* Returns secure random bytes after waiting for the RNG to initialize. */
 int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret);
 
 static inline uint64_t random_u64(void) {
@@ -29,6 +31,6 @@ static inline uint32_t random_u32(void) {
 
 size_t random_pool_size(void);
 
-int random_write_entropy(int fd, const void *seed, size_t size, bool credit);
+int random_write_entropy(int fd, const void *seed, size_t size, bool credit) _nonnull_if_nonzero_(2, 3);
 
 uint64_t random_u64_range(uint64_t max);
diff --git a/src/libnm-systemd-shared/src/basic/ratelimit.h b/src/libnm-systemd-shared/src/basic/ratelimit.h
index 7801ef42..fd834263 100644
--- a/src/libnm-systemd-shared/src/basic/ratelimit.h
+++ b/src/libnm-systemd-shared/src/basic/ratelimit.h
@@ -28,3 +28,55 @@ unsigned ratelimit_num_dropped(const RateLimit *rl);
 
 usec_t ratelimit_end(const RateLimit *rl);
 usec_t ratelimit_left(const RateLimit *rl);
+
+typedef struct LogRateLimit {
+        int error;
+        int level;
+        RateLimit ratelimit;
+} LogRateLimit;
+
+#define log_ratelimit_internal(_level, _error, _ratelimit, _file, _line, _func, _format, ...)        \
+({                                                                              \
+        int _log_ratelimit_error = (_error);                                    \
+        int _log_ratelimit_level = (_level);                                    \
+        static LogRateLimit _log_ratelimit = {                                  \
+                .ratelimit = (_ratelimit),                                      \
+        };                                                                      \
+        unsigned _num_dropped_errors = ratelimit_num_dropped(&_log_ratelimit.ratelimit); \
+        if (_log_ratelimit_error != _log_ratelimit.error || _log_ratelimit_level != _log_ratelimit.level) { \
+                ratelimit_reset(&_log_ratelimit.ratelimit);                     \
+                _log_ratelimit.error = _log_ratelimit_error;                    \
+                _log_ratelimit.level = _log_ratelimit_level;                    \
+        }                                                                       \
+        if (log_get_max_level() == LOG_DEBUG || ratelimit_below(&_log_ratelimit.ratelimit)) \
+                _log_ratelimit_error = _num_dropped_errors > 0                  \
+                ? log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format " (Dropped %u similar message(s))", ##__VA_ARGS__, _num_dropped_errors) \
+                : log_internal(_log_ratelimit_level, _log_ratelimit_error, _file, _line, _func, _format, ##__VA_ARGS__); \
+        _log_ratelimit_error;                                                   \
+})
+
+#define log_ratelimit_full_errno(level, error, _ratelimit, format, ...)             \
+        ({                                                              \
+                int _level = (level), _e = (error);                     \
+                _e = (log_get_max_level() >= LOG_PRI(_level))           \
+                        ? log_ratelimit_internal(_level, _e, _ratelimit, PROJECT_FILE, __LINE__, __func__, format, ##__VA_ARGS__) \
+                        : -ERRNO_VALUE(_e);                             \
+                _e < 0 ? _e : -ESTRPIPE;                                \
+        })
+
+#define log_ratelimit_full(level, _ratelimit, format, ...)                          \
+        log_ratelimit_full_errno(level, 0, _ratelimit, format, ##__VA_ARGS__)
+
+/* Normal logging */
+#define log_ratelimit_info(...)      log_ratelimit_full(LOG_INFO,    __VA_ARGS__)
+#define log_ratelimit_notice(...)    log_ratelimit_full(LOG_NOTICE,  __VA_ARGS__)
+#define log_ratelimit_warning(...)   log_ratelimit_full(LOG_WARNING, __VA_ARGS__)
+#define log_ratelimit_error(...)     log_ratelimit_full(LOG_ERR,     __VA_ARGS__)
+#define log_ratelimit_emergency(...) log_ratelimit_full(log_emergency_level(), __VA_ARGS__)
+
+/* Logging triggered by an errno-like error */
+#define log_ratelimit_info_errno(error, ...)      log_ratelimit_full_errno(LOG_INFO,    error, __VA_ARGS__)
+#define log_ratelimit_notice_errno(error, ...)    log_ratelimit_full_errno(LOG_NOTICE,  error, __VA_ARGS__)
+#define log_ratelimit_warning_errno(error, ...)   log_ratelimit_full_errno(LOG_WARNING, error, __VA_ARGS__)
+#define log_ratelimit_error_errno(error, ...)     log_ratelimit_full_errno(LOG_ERR,     error, __VA_ARGS__)
+#define log_ratelimit_emergency_errno(error, ...) log_ratelimit_full_errno(log_emergency_level(), error, __VA_ARGS__)
diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h
index 618e7297..c7b84e0a 100644
--- a/src/libnm-systemd-shared/src/basic/set.h
+++ b/src/libnm-systemd-shared/src/basic/set.h
@@ -12,15 +12,9 @@ Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
 #define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
 
 static inline Set* set_free(Set *s) {
-        return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL);
+        return (Set*) _hashmap_free(HASHMAP_BASE(s));
 }
 
-static inline Set* set_free_free(Set *s) {
-        return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL);
-}
-
-/* no set_free_free_free */
-
 #define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s)  HASHMAP_DEBUG_SRC_ARGS))
 
 int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
@@ -77,30 +71,13 @@ static inline bool set_iterate(const Set *s, Iterator *i, void **value) {
 }
 
 static inline void set_clear(Set *s) {
-        _hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
-}
-
-static inline void set_clear_free(Set *s) {
-        _hashmap_clear(HASHMAP_BASE(s), free, NULL);
+        _hashmap_clear(HASHMAP_BASE(s));
 }
 
-/* no set_clear_free_free */
-
 static inline void *set_steal_first(Set *s) {
         return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
 }
 
-#define set_clear_with_destructor(s, f)                 \
-        ({                                              \
-                Set *_s = (s);                          \
-                void *_item;                            \
-                while ((_item = set_steal_first(_s)))   \
-                        f(_item);                       \
-                _s;                                     \
-        })
-#define set_free_with_destructor(s, f)                  \
-        set_free(set_clear_with_destructor(s, f))
-
 /* no set_steal_first_key */
 /* no set_first_key */
 
@@ -114,6 +91,8 @@ static inline char **set_get_strv(Set *s) {
         return _hashmap_get_strv(HASHMAP_BASE(s));
 }
 
+char** set_to_strv(Set **s);
+
 int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key  HASHMAP_DEBUG_PARAMS);
 #define set_ensure_put(s, hash_ops, key) _set_ensure_put(s, hash_ops, key  HASHMAP_DEBUG_SRC_ARGS)
 
@@ -143,10 +122,8 @@ int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags
         for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); )
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
 
 #define _cleanup_set_free_ _cleanup_(set_freep)
-#define _cleanup_set_free_free_ _cleanup_(set_free_freep)
 
 int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret);
 
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c
index 05b2b50d..6722e4bc 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.c
+++ b/src/libnm-systemd-shared/src/basic/signal-util.c
@@ -4,11 +4,11 @@
 
 #include <errno.h>
 #include <stdarg.h>
+#include <threads.h>
 
 #include "errno-util.h"
 #include "macro.h"
 #include "missing_syscall.h"
-#include "missing_threads.h"
 #include "parse-util.h"
 #include "signal-util.h"
 #include "stdio-util.h"
@@ -283,13 +283,13 @@ void propagate_signal(int sig, siginfo_t *siginfo) {
 
         /* To be called from a signal handler. Will raise the same signal again, in our process + in our threads.
          *
-         * Note that we use raw_getpid() instead of getpid_cached(). We might have forked with raw_clone()
+         * Note that we use getpid() instead of getpid_cached(). We might have forked with raw_clone()
          * earlier (see PID 1), and hence let's go to the raw syscall here. In particular as this is not
          * performance sensitive code.
          *
          * Note that we use kill() rather than raise() as fallback, for similar reasons. */
 
-        p = raw_getpid();
+        p = getpid();
 
         if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0)
                 assert_se(kill(p, sig) >= 0);
diff --git a/src/libnm-systemd-shared/src/basic/signal-util.h b/src/libnm-systemd-shared/src/basic/signal-util.h
index dc2b9de1..d64d5031 100644
--- a/src/libnm-systemd-shared/src/basic/signal-util.h
+++ b/src/libnm-systemd-shared/src/basic/signal-util.h
@@ -3,6 +3,7 @@
 
 #include <signal.h>
 
+#include "assert-util.h"
 #include "macro.h"
 
 int reset_all_signal_handlers(void);
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index 9cb0d7f4..7c83551c 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -2,11 +2,12 @@
 
 #include "nm-sd-adapt-shared.h"
 
-/* Make sure the net/if.h header is included before any linux/ one */
-#include <net/if.h>
 #include <arpa/inet.h>
 #include <errno.h>
 #include <limits.h>
+#include <net/if.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
 #include <netdb.h>
 #include <netinet/ip.h>
 #include <poll.h>
@@ -16,9 +17,6 @@
 #include <stdlib.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
-#if 0 /* NM_IGNORED */
-#include <linux/if.h>
-#endif /* NM_IGNORED */
 
 #include "alloc-util.h"
 #include "errno-util.h"
@@ -32,6 +30,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "random-util.h"
 #include "socket-util.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -1361,6 +1360,53 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
 }
 
 #if 0 /* NM_IGNORED */
+size_t sockaddr_ll_len(const struct sockaddr_ll *sa) {
+        /* Certain hardware address types (e.g Infiniband) do not fit into sll_addr
+         * (8 bytes) and run over the structure. This function returns the correct size that
+         * must be passed to kernel. */
+
+        assert(sa->sll_family == AF_PACKET);
+
+        size_t mac_len = sizeof(sa->sll_addr);
+
+        if (be16toh(sa->sll_hatype) == ARPHRD_ETHER)
+                mac_len = MAX(mac_len, (size_t) ETH_ALEN);
+        if (be16toh(sa->sll_hatype) == ARPHRD_INFINIBAND)
+                mac_len = MAX(mac_len, (size_t) INFINIBAND_ALEN);
+
+        return offsetof(struct sockaddr_ll, sll_addr) + mac_len;
+}
+
+size_t sockaddr_un_len(const struct sockaddr_un *sa) {
+        /* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
+
+        assert(sa->sun_family == AF_UNIX);
+
+        return offsetof(struct sockaddr_un, sun_path) +
+                (sa->sun_path[0] == 0 ?
+                        1 + strnlen(sa->sun_path+1, sizeof(sa->sun_path)-1) :
+                        strnlen(sa->sun_path, sizeof(sa->sun_path))+1);
+}
+
+size_t sockaddr_len(const union sockaddr_union *sa) {
+        switch (sa->sa.sa_family) {
+        case AF_INET:
+                return sizeof(struct sockaddr_in);
+        case AF_INET6:
+                return sizeof(struct sockaddr_in6);
+        case AF_UNIX:
+                return sockaddr_un_len(&sa->un);
+        case AF_PACKET:
+                return sockaddr_ll_len(&sa->ll);
+        case AF_NETLINK:
+                return sizeof(struct sockaddr_nl);
+        case AF_VSOCK:
+                return sizeof(struct sockaddr_vm);
+        default:
+                assert_not_reached();
+        }
+}
+
 int socket_ioctl_fd(void) {
         int fd;
 
@@ -1457,25 +1503,43 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
 }
 
 int socket_bind_to_ifindex(int fd, int ifindex) {
-        char ifname[IF_NAMESIZE];
-        int r;
-
         assert(fd >= 0);
 
         if (ifindex <= 0)
                 /* Drop binding */
                 return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
 
-        r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
-        if (r != -ENOPROTOOPT)
-                return r;
+        return setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
+}
+
+int socket_autobind(int fd, char **ret_name) {
+        _cleanup_free_ char *name = NULL;
+        uint64_t random;
+        int r;
+
+        /* Generate a random abstract socket name and bind fd to it. This is modeled after the kernel
+         * "autobind" feature, but uses 64-bit random number internally. */
 
-        /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
-        r = format_ifname(ifindex, ifname);
+        assert(fd >= 0);
+        assert(ret_name);
+
+        random = random_u64();
+
+        if (asprintf(&name, "@%" PRIu64, random) < 0)
+                return -ENOMEM;
+
+        union sockaddr_union sa;
+        assert_cc(DECIMAL_STR_MAX(uint64_t) < sizeof(sa.un.sun_path));
+
+        r = sockaddr_un_set_path(&sa.un, name);
         if (r < 0)
                 return r;
 
-        return socket_bind_to_ifname(fd, ifname);
+        if (bind(fd, &sa.sa, r) < 0)
+                return -errno;
+
+        *ret_name = TAKE_PTR(name);
+        return 0;
 }
 
 ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
@@ -1815,8 +1879,6 @@ int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_
 
         assert(fd >= 0);
 
-        /* This returns ENOPROTOOPT if the kernel is older than 4.2. */
-
         if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len) < 0)
                 return -errno;
 
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index b6c38725..e5eda855 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -6,12 +6,12 @@
 #include <linux/if_infiniband.h>
 #include <linux/if_packet.h>
 #include <linux/netlink.h>
-#include <sys/socket.h> /* linux/vms_sockets.h requires 'struct sockaddr' */
 #include <linux/vm_sockets.h>
 #include <netinet/in.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <string.h>
+#include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
 
@@ -240,62 +240,11 @@ void* cmsg_find_and_copy_data(struct msghdr *mh, int level, int type, void *buf,
                                     (size) == CMSG_ALIGN(size) ? 1 : -1]; \
         }
 
-/*
- * Certain hardware address types (e.g Infiniband) do not fit into sll_addr
- * (8 bytes) and run over the structure. This macro returns the correct size that
- * must be passed to kernel.
- */
-#define SOCKADDR_LL_LEN(sa)                                             \
-        ({                                                              \
-                const struct sockaddr_ll *_sa = &(sa);                  \
-                size_t _mac_len = sizeof(_sa->sll_addr);                \
-                assert(_sa->sll_family == AF_PACKET);                   \
-                if (be16toh(_sa->sll_hatype) == ARPHRD_ETHER)           \
-                        _mac_len = MAX(_mac_len, (size_t) ETH_ALEN);    \
-                if (be16toh(_sa->sll_hatype) == ARPHRD_INFINIBAND)      \
-                        _mac_len = MAX(_mac_len, (size_t) INFINIBAND_ALEN); \
-                offsetof(struct sockaddr_ll, sll_addr) + _mac_len;      \
-        })
+size_t sockaddr_ll_len(const struct sockaddr_ll *sa);
 
-/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
-#define SOCKADDR_UN_LEN(sa)                                             \
-        ({                                                              \
-                const struct sockaddr_un *_sa = &(sa);                  \
-                assert(_sa->sun_family == AF_UNIX);                     \
-                offsetof(struct sockaddr_un, sun_path) +                \
-                        (_sa->sun_path[0] == 0 ?                        \
-                         1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
-                         strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
-        })
+size_t sockaddr_un_len(const struct sockaddr_un *sa);
 
-#define SOCKADDR_LEN(saddr)                                             \
-        ({                                                              \
-                const union sockaddr_union *__sa = &(saddr);            \
-                size_t _len;                                            \
-                switch (__sa->sa.sa_family) {                           \
-                case AF_INET:                                           \
-                        _len = sizeof(struct sockaddr_in);              \
-                        break;                                          \
-                case AF_INET6:                                          \
-                        _len = sizeof(struct sockaddr_in6);             \
-                        break;                                          \
-                case AF_UNIX:                                           \
-                        _len = SOCKADDR_UN_LEN(__sa->un);               \
-                        break;                                          \
-                case AF_PACKET:                                         \
-                        _len = SOCKADDR_LL_LEN(__sa->ll);               \
-                        break;                                          \
-                case AF_NETLINK:                                        \
-                        _len = sizeof(struct sockaddr_nl);              \
-                        break;                                          \
-                case AF_VSOCK:                                          \
-                        _len = sizeof(struct sockaddr_vm);              \
-                        break;                                          \
-                default:                                                \
-                        assert_not_reached();                           \
-                }                                                       \
-                _len;                                                   \
-        })
+size_t sockaddr_len(const union sockaddr_union *sa);
 
 int socket_ioctl_fd(void);
 
@@ -324,6 +273,8 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) {
 int socket_bind_to_ifname(int fd, const char *ifname);
 int socket_bind_to_ifindex(int fd, int ifindex);
 
+int socket_autobind(int fd, char **ret_name);
+
 /* Define a 64-bit version of timeval/timespec in any case, even on 32-bit userspace. */
 struct timeval_large {
         uint64_t tvl_sec, tvl_usec;
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index ef813e19..1b352290 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -18,10 +18,10 @@
 #include "filesystems.h"
 #include "fs-util.h"
 #include "hash-funcs.h"
+#include "log.h"
 #include "macro.h"
 #include "missing_fs.h"
 #include "missing_magic.h"
-#include "missing_syscall.h"
 #include "mountpoint-util.h"
 #include "nulstr-util.h"
 #include "parse-util.h"
@@ -175,7 +175,7 @@ int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup)
                 struct dirent *de;
                 ssize_t n;
 
-                n = getdents64(fd, buf, m);
+                n = posix_getdents(fd, buf, m, /* flags = */ 0);
                 if (n < 0)
                         return -errno;
                 if (n == 0)
@@ -301,7 +301,7 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl
                         flags |= AT_EMPTY_PATH;
                 }
 
-                int ntha_flags = (flags & AT_EMPTY_PATH) | (FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? 0 : AT_SYMLINK_FOLLOW);
+                int ntha_flags = at_flags_normalize_follow(flags) & (AT_EMPTY_PATH|AT_SYMLINK_FOLLOW);
                 _cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
                 int mntida = -1, mntidb = -1;
 
@@ -478,8 +478,8 @@ bool statx_inode_same(const struct statx *a, const struct statx *b) {
                 a->stx_ino == b->stx_ino;
 }
 
-bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
-        if (!new_statx_is_set(a) || !new_statx_is_set(b))
+bool statx_mount_same(const struct statx *a, const struct statx *b) {
+        if (!statx_is_set(a) || !statx_is_set(b))
                 return false;
 
         /* if we have the mount ID, that's all we need */
@@ -490,76 +490,6 @@ bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
         return a->stx_dev_major == b->stx_dev_major &&
                 a->stx_dev_minor == b->stx_dev_minor;
 }
-
-static bool is_statx_fatal_error(int err, int flags) {
-        assert(err < 0);
-
-        /* If statx() is not supported or if we see EPERM (which might indicate seccomp filtering or so),
-         * let's do a fallback. Note that on EACCES we'll not fall back, since that is likely an indication of
-         * fs access issues, which we should propagate. */
-        if (ERRNO_IS_NOT_SUPPORTED(err) || err == -EPERM)
-                return false;
-
-        /* When unsupported flags are specified, glibc's fallback function returns -EINVAL.
-         * See statx_generic() in glibc. */
-        if (err != -EINVAL)
-                return true;
-
-        if ((flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT)) != 0)
-                return false; /* Unsupported flags are specified. Let's try to use our implementation. */
-
-        return true;
-}
-
-int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
-        static bool avoid_statx = false;
-        struct stat st;
-        int r;
-
-        if (!avoid_statx) {
-                r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
-                if (r >= 0 || is_statx_fatal_error(r, flags))
-                        return r;
-
-                avoid_statx = true;
-        }
-
-        /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
-         * OK to ignore */
-        if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW|
-                      AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0)
-                return -EOPNOTSUPP;
-
-        if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0)
-                return -errno;
-
-        *sx = (struct statx) {
-                .stx_mask = STATX_TYPE|STATX_MODE|
-                STATX_NLINK|STATX_UID|STATX_GID|
-                STATX_ATIME|STATX_MTIME|STATX_CTIME|
-                STATX_INO|STATX_SIZE|STATX_BLOCKS,
-                .stx_blksize = st.st_blksize,
-                .stx_nlink = st.st_nlink,
-                .stx_uid = st.st_uid,
-                .stx_gid = st.st_gid,
-                .stx_mode = st.st_mode,
-                .stx_ino = st.st_ino,
-                .stx_size = st.st_size,
-                .stx_blocks = st.st_blocks,
-                .stx_rdev_major = major(st.st_rdev),
-                .stx_rdev_minor = minor(st.st_rdev),
-                .stx_dev_major = major(st.st_dev),
-                .stx_dev_minor = minor(st.st_dev),
-                .stx_atime.tv_sec = st.st_atim.tv_sec,
-                .stx_atime.tv_nsec = st.st_atim.tv_nsec,
-                .stx_mtime.tv_sec = st.st_mtim.tv_sec,
-                .stx_mtime.tv_nsec = st.st_mtim.tv_nsec,
-                .stx_ctime.tv_sec = st.st_ctim.tv_sec,
-                .stx_ctime.tv_nsec = st.st_ctim.tv_nsec,
-        };
-
-        return 0;
-}
 #endif /* NM_IGNORED */
 
 int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
@@ -568,11 +498,14 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
         assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
         assert(ret);
 
-        fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return fd;
+        if (!isempty(path)) {
+                fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
+                if (fd < 0)
+                        return fd;
+                dir_fd = fd;
+        }
 
-        return RET_NERRNO(fstatfs(fd, ret));
+        return RET_NERRNO(fstatfs(dir_fd, ret));
 }
 
 #if 0 /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h
index 17fb520f..fcde7784 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.h
+++ b/src/libnm-systemd-shared/src/basic/stat-util.h
@@ -11,7 +11,6 @@
 
 #include "fs-util.h"
 #include "macro.h"
-#include "missing_stat.h"
 #include "siphash24.h"
 #include "time-util.h"
 
@@ -52,10 +51,11 @@ int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int fl
 static inline int inode_same(const char *filea, const char *fileb, int flags) {
         return inode_same_at(AT_FDCWD, filea, AT_FDCWD, fileb, flags);
 }
+#if 0  /* NM_IGNORED */
 static inline int fd_inode_same(int fda, int fdb) {
         return inode_same_at(fda, NULL, fdb, NULL, AT_EMPTY_PATH);
 }
-
+#endif  /* NM_IGNORED */
 /* The .f_type field of struct statfs is really weird defined on
  * different archs. Let's give its type a name. */
 typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t;
@@ -90,31 +90,10 @@ bool stat_inode_same(const struct stat *a, const struct stat *b);
 bool stat_inode_unmodified(const struct stat *a, const struct stat *b);
 
 bool statx_inode_same(const struct statx *a, const struct statx *b);
-bool statx_mount_same(const struct new_statx *a, const struct new_statx *b);
-
-int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx);
+bool statx_mount_same(const struct statx *a, const struct statx *b);
 
 int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
 
-#if HAS_FEATURE_MEMORY_SANITIZER
-#  warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
-#  define STRUCT_STATX_DEFINE(var)              \
-        struct statx var = {}
-#  define STRUCT_NEW_STATX_DEFINE(var)          \
-        union {                                 \
-                struct statx sx;                \
-                struct new_statx nsx;           \
-        } var = {}
-#else
-#  define STRUCT_STATX_DEFINE(var)              \
-        struct statx var
-#  define STRUCT_NEW_STATX_DEFINE(var)          \
-        union {                                 \
-                struct statx sx;                \
-                struct new_statx nsx;           \
-        } var
-#endif
-
 #if 0 /* NM_IGNORED */
 static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
         return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
@@ -143,7 +122,4 @@ static inline bool stat_is_set(const struct stat *st) {
 static inline bool statx_is_set(const struct statx *sx) {
         return sx && sx->stx_mask != 0;
 }
-static inline bool new_statx_is_set(const struct new_statx *sx) {
-        return sx && sx->stx_mask != 0;
-}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/stdio-util.h b/src/libnm-systemd-shared/src/basic/stdio-util.h
index c35e6655..6914d998 100644
--- a/src/libnm-systemd-shared/src/basic/stdio-util.h
+++ b/src/libnm-systemd-shared/src/basic/stdio-util.h
@@ -8,6 +8,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 
+#include "assert-util.h"
 #include "macro.h"
 
 _printf_(3, 4)
diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c
index 6a06594a..5efd11ea 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.c
+++ b/src/libnm-systemd-shared/src/basic/string-util.c
@@ -16,6 +16,7 @@
 #include "glyph-util.h"
 #include "gunicode.h"
 #include "locale-util.h"
+#include "log.h"
 #include "macro.h"
 #include "memory-util.h"
 #include "memstream-util.h"
@@ -48,35 +49,42 @@ char* first_word(const char *s, const char *word) {
         return (char*) nw;
 }
 
-char* strnappend(const char *s, const char *suffix, size_t b) {
-        size_t a;
-        char *r;
+char* strprepend(char **x, const char *s) {
+        assert(x);
 
-        if (!s && !suffix)
-                return strdup("");
+        if (isempty(s) && *x)
+                return *x;
 
-        if (!s)
-                return strndup(suffix, b);
+        char *p = strjoin(strempty(s), *x);
+        if (!p)
+                return NULL;
 
-        if (!suffix)
-                return strdup(s);
+        free_and_replace(*x, p);
+        return *x;
+}
 
-        assert(s);
-        assert(suffix);
+char* strextendn(char **x, const char *s, size_t l) {
+        assert(x);
+        assert(s || l == 0);
 
-        a = strlen(s);
-        if (b > SIZE_MAX - a)
-                return NULL;
+        if (l > 0)
+                l = strnlen(s, l); /* ignore trailing noise */
 
-        r = new(char, a+b+1);
-        if (!r)
-                return NULL;
+        if (l > 0 || !*x) {
+                size_t q;
+                char *m;
+
+                q = strlen_ptr(*x);
+                m = realloc(*x, q + l + 1);
+                if (!m)
+                        return NULL;
 
-        memcpy(r, s, a);
-        memcpy(r+a, suffix, b);
-        r[a+b] = 0;
+                *mempcpy_typesafe(m + q, s, l) = 0;
 
-        return r;
+                *x = m;
+        }
+
+        return *x;
 }
 
 #if 0 /* NM_IGNORED */
@@ -245,7 +253,7 @@ bool string_has_cc(const char *p, const char *ok) {
 
 #if 0 /* NM_IGNORED */
 static int write_ellipsis(char *buf, bool unicode) {
-        const char *s = special_glyph_full(SPECIAL_GLYPH_ELLIPSIS, unicode);
+        const char *s = glyph_full(GLYPH_ELLIPSIS, unicode);
         assert(strlen(s) == 3);
         memcpy(buf, s, 3);
         return 3;
@@ -333,10 +341,6 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
         case 2:
                 if (!is_locale_utf8())
                         return strdup("..");
-
-                break;
-
-        default:
                 break;
         }
 
@@ -983,33 +987,6 @@ oom:
         return -ENOMEM;
 }
 
-char* strextendn(char **x, const char *s, size_t l) {
-        assert(x);
-        assert(s || l == 0);
-
-        if (l == SIZE_MAX)
-                l = strlen_ptr(s);
-        else if (l > 0)
-                l = strnlen(s, l); /* ignore trailing noise */
-
-        if (l > 0 || !*x) {
-                size_t q;
-                char *m;
-
-                q = strlen_ptr(*x);
-                m = realloc(*x, q + l + 1);
-                if (!m)
-                        return NULL;
-
-                memcpy_safe(m + q, s, l);
-                m[q + l] = 0;
-
-                *x = m;
-        }
-
-        return *x;
-}
-
 char* strrep(const char *s, unsigned n) {
         char *r, *p;
         size_t l;
@@ -1074,6 +1051,15 @@ int free_and_strdup(char **p, const char *s) {
         return 1;
 }
 
+int free_and_strdup_warn(char **p, const char *s) {
+        int r;
+
+        r = free_and_strdup(p, s);
+        if (r < 0)
+                return log_oom();
+        return r;
+}
+
 int free_and_strndup(char **p, const char *s, size_t l) {
         char *t;
 
@@ -1502,3 +1488,19 @@ char* strrstr(const char *haystack, const char *needle) {
         }
         return NULL;
 }
+
+size_t str_common_prefix(const char *a, const char *b) {
+        assert(a);
+        assert(b);
+
+        /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
+         * strings are fully identical. */
+
+        for (size_t n = 0;; n++) {
+                char c = a[n];
+                if (c != b[n])
+                        return n;
+                if (c == 0)
+                        return SIZE_MAX;
+        }
+}
diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h
index 1bcb1c40..91e63c9b 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.h
+++ b/src/libnm-systemd-shared/src/basic/string-util.h
@@ -106,7 +106,8 @@ static inline const char* empty_or_dash_to_null(const char *p) {
 
 char* first_word(const char *s, const char *word) _pure_;
 
-char* strnappend(const char *s, const char *suffix, size_t length);
+char* strprepend(char **x, const char *s);
+char* strextendn(char **x, const char *s, size_t l) _nonnull_if_nonzero_(2, 3);
 
 #define strjoin(a, ...) strextend_with_separator_internal(NULL, NULL, a, __VA_ARGS__, NULL)
 
@@ -193,8 +194,6 @@ char* strextend_with_separator_internal(char **x, const char *separator, ...) _s
 #define strextend_with_separator(x, separator, ...) strextend_with_separator_internal(x, separator, __VA_ARGS__, NULL)
 #define strextend(x, ...) strextend_with_separator_internal(x, NULL, __VA_ARGS__, NULL)
 
-char* strextendn(char **x, const char *s, size_t l);
-
 int strextendf_with_separator(char **x, const char *separator, const char *format, ...) _printf_(3,4);
 #define strextendf(x, ...) strextendf_with_separator(x, NULL, __VA_ARGS__)
 
@@ -216,15 +215,8 @@ char* strrep(const char *s, unsigned n);
 int split_pair(const char *s, const char *sep, char **ret_first, char **ret_second);
 
 int free_and_strdup(char **p, const char *s);
-static inline int free_and_strdup_warn(char **p, const char *s) {
-        int r;
-
-        r = free_and_strdup(p, s);
-        if (r < 0)
-                return log_oom();
-        return r;
-}
-int free_and_strndup(char **p, const char *s, size_t l);
+int free_and_strdup_warn(char **p, const char *s);
+int free_and_strndup(char **p, const char *s, size_t l) _nonnull_if_nonzero_(2, 3);
 
 int strdup_to_full(char **ret, const char *src);
 static inline int strdup_to(char **ret, const char *src) {
@@ -309,3 +301,5 @@ bool version_is_valid_versionspec(const char *s);
 ssize_t strlevenshtein(const char *x, const char *y);
 
 char* strrstr(const char *haystack, const char *needle);
+
+size_t str_common_prefix(const char *a, const char *b);
diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c
index 38466696..f433afc7 100644
--- a/src/libnm-systemd-shared/src/basic/strv.c
+++ b/src/libnm-systemd-shared/src/basic/strv.c
@@ -14,6 +14,7 @@
 #include "extract-word.h"
 #include "fileio.h"
 #include "gunicode.h"
+#include "log.h"
 #include "memory-util.h"
 #include "nulstr-util.h"
 #include "sort-util.h"
@@ -637,7 +638,7 @@ int strv_insert(char ***l, size_t position, char *value) {
         n = strv_length(*l);
         position = MIN(position, n);
 
-        /* check for overflow and increase*/
+        /* check for overflow and increase */
         if (n > SIZE_MAX - 2)
                 return -ENOMEM;
         m = n + 2;
@@ -868,11 +869,11 @@ int strv_compare(char * const *a, char * const *b) {
         return 0;
 }
 
-bool strv_equal_ignore_order(char **a, char **b) {
+bool strv_equal_ignore_order(char * const *a, char * const *b) {
 
         /* Just like strv_equal(), but doesn't care about the order of elements or about redundant entries
          * (i.e. it's even ok if the number of entries in the array differ, as long as the difference just
-         * consists of repititions) */
+         * consists of repetitions). */
 
         if (a == b)
                 return true;
@@ -892,6 +893,7 @@ void strv_print_full(char * const *l, const char *prefix) {
         STRV_FOREACH(s, l)
                 printf("%s%s\n", strempty(prefix), *s);
 }
+#endif /* NM_IGNORED */
 
 int strv_extendf(char ***l, const char *format, ...) {
         va_list ap;
@@ -907,7 +909,7 @@ int strv_extendf(char ***l, const char *format, ...) {
 
         return strv_consume(l, x);
 }
-#endif /* NM_IGNORED */
+
 
 char* startswith_strv(const char *s, char * const *l) {
         STRV_FOREACH(i, l) {
@@ -986,14 +988,16 @@ bool strv_fnmatch_full(
 }
 
 char** strv_skip(char **l, size_t n) {
-
         while (n > 0) {
                 if (strv_isempty(l))
-                        return l;
+                        return NULL;
 
                 l++, n--;
         }
 
+        /* To simplify callers, always return NULL instead of a zero-item array. */
+        if (strv_isempty(l))
+                return NULL;
         return l;
 }
 
@@ -1074,7 +1078,22 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
 }
 
 #if 0 /* NM_IGNORED */
-DEFINE_PRIVATE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
+void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value) {
+        assert(key);
+
+        if (value) {
+                char **l = hashmap_get(h, key);
+                if (!l)
+                        return;
+
+                strv_remove(l, value);
+                if (!strv_isempty(l))
+                        return;
+        }
+
+        _unused_ _cleanup_free_ char *key_free = NULL;
+        strv_free(hashmap_remove2(h, key, (void**) &key_free));
+}
 
 static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
         char **l;
@@ -1126,7 +1145,7 @@ int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HA
         assert(key);
         assert(value);
 
-        r = _hashmap_ensure_allocated(h, &string_strv_hash_ops  HASHMAP_DEBUG_PASS_ARGS);
+        r = _hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free  HASHMAP_DEBUG_PASS_ARGS);
         if (r < 0)
                 return r;
 
@@ -1140,7 +1159,7 @@ int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const
         assert(key);
         assert(value);
 
-        r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops  HASHMAP_DEBUG_PASS_ARGS);
+        r = _ordered_hashmap_ensure_allocated(h, &string_hash_ops_free_strv_free  HASHMAP_DEBUG_PASS_ARGS);
         if (r < 0)
                 return r;
 
@@ -1234,4 +1253,25 @@ int strv_rebreak_lines(char **l, size_t width, char ***ret) {
         *ret = TAKE_PTR(broken);
         return 0;
 }
+
+char** strv_filter_prefix(char * const *l, const char *prefix) {
+
+        /* Allocates a copy of 'l', but only copies over entries starting with 'prefix' */
+
+        if (isempty(prefix))
+                return strv_copy(l);
+
+        _cleanup_strv_free_ char **f = NULL;
+        size_t sz = 0;
+
+        STRV_FOREACH(i, l) {
+                if (!startswith(*i, prefix))
+                        continue;
+
+                if (strv_extend_with_size(&f, &sz, *i) < 0)
+                        return NULL;
+        }
+
+        return TAKE_PTR(f);
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index 86ba06f8..0ca90087 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -33,7 +33,7 @@ char** strv_free_erase(char **l);
 DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
 #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
 
-void strv_free_many(char ***strvs, size_t n);
+void strv_free_many(char ***strvs, size_t n) _nonnull_if_nonzero_(1, 2);
 
 char** strv_copy_n(char * const *l, size_t n);
 static inline char** strv_copy(char * const *l) {
@@ -96,7 +96,7 @@ static inline bool strv_equal(char * const *a, char * const *b) {
         return strv_compare(a, b) == 0;
 }
 
-bool strv_equal_ignore_order(char **a, char **b);
+bool strv_equal_ignore_order(char * const *a, char * const *b);
 
 char** strv_new_internal(const char *x, ...) _sentinel_;
 char** strv_new_ap(const char *x, va_list ap);
@@ -258,9 +258,15 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
 #define strv_free_and_replace(a, b)             \
         free_and_replace_full(a, b, strv_free)
 
+void string_strv_hashmap_remove(Hashmap *h, const char *key, const char *value);
+static inline void string_strv_ordered_hashmap_remove(OrderedHashmap *h, const char *key, const char *value) {
+        string_strv_hashmap_remove(PLAIN_HASHMAP(h), key, value);
+}
 int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS);
 int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS);
 #define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
 #define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
 
 int strv_rebreak_lines(char **l, size_t width, char ***ret);
+
+char** strv_filter_prefix(char * const *l, const char *prefix);
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 594d5315..06e27cfb 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -10,6 +10,7 @@
 #include <sys/time.h>
 #include <sys/timerfd.h>
 #include <sys/types.h>
+#include <threads.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -19,8 +20,6 @@
 #include "io-util.h"
 #include "log.h"
 #include "macro.h"
-#include "missing_threads.h"
-#include "missing_timerfd.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
@@ -1643,7 +1642,7 @@ int mktime_or_timegm_usec(
 
         assert(tm);
 
-        if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!)*/
+        if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!) */
                 return -ERANGE;
         if ((usec_t) tm->tm_year > CONST_MIN(USEC_INFINITY / USEC_PER_YEAR, (usec_t) TIME_T_MAX / (365U * 24U * 60U * 60U)) - 1900) /* early check for possible overrun of usec_t or time_t */
                 return -ERANGE;
diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h
index 14d660ee..3f6e3c9f 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.h
+++ b/src/libnm-systemd-shared/src/basic/time-util.h
@@ -222,8 +222,8 @@ static inline int usleep_safe(usec_t usec) {
         if (usec == 0)
                 return 0;
 
-        // FIXME: use RET_NERRNO() macro here. Currently, this header cannot include errno-util.h.
-        return clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL) < 0 ? -errno : 0;
+        /* `clock_nanosleep()` does not use `errno`, but returns positive error codes. */
+        return -clock_nanosleep(CLOCK_MONOTONIC, 0, TIMESPEC_STORE(usec), NULL);
 }
 
 /* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit
diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c
index 3d1b8d04..4e1fe711 100644
--- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c
+++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c
@@ -9,6 +9,7 @@
 #include "fileio.h"
 #include "fs-util.h"
 #include "hexdecoct.h"
+#include "log.h"
 #include "macro.h"
 #include "memfd-util.h"
 #include "missing_fcntl.h"
diff --git a/src/libnm-systemd-shared/src/basic/user-util.c b/src/libnm-systemd-shared/src/basic/user-util.c
new file mode 100644
index 00000000..9b80750a
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/user-util.c
@@ -0,0 +1,1243 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utmpx.h>
+
+#include "sd-messages.h"
+
+#include "alloc-util.h"
+#include "chase.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "format-util.h"
+#include "lock-util.h"
+#include "log.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "random-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "user-util.h"
+#include "utf8.h"
+
+bool uid_is_valid(uid_t uid) {
+
+        /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
+
+        /* Some libc APIs use UID_INVALID as special placeholder */
+        if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
+                return false;
+
+        /* A long time ago UIDs where 16 bit, hence explicitly avoid the 16-bit -1 too */
+        if (uid == (uid_t) UINT32_C(0xFFFF))
+                return false;
+
+        return true;
+}
+
+int parse_uid(const char *s, uid_t *ret) {
+        uint32_t uid = 0;
+        int r;
+
+        assert(s);
+
+        assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+
+        /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and
+         * whitespace. We do this, since this call is often used in a context where we parse things as UID
+         * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs
+         * are parsed as UIDs only if they really really look like UIDs. */
+        r = safe_atou32_full(s, 10
+                             | SAFE_ATO_REFUSE_PLUS_MINUS
+                             | SAFE_ATO_REFUSE_LEADING_ZERO
+                             | SAFE_ATO_REFUSE_LEADING_WHITESPACE, &uid);
+        if (r < 0)
+                return r;
+
+        if (!uid_is_valid(uid))
+                return -ENXIO; /* we return ENXIO instead of EINVAL
+                                * here, to make it easy to distinguish
+                                * invalid numeric uids from invalid
+                                * strings. */
+
+        if (ret)
+                *ret = uid;
+
+        return 0;
+}
+
+#if 0  /* NM_IGNORED */
+int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper) {
+        _cleanup_free_ char *word = NULL;
+        uid_t l, u;
+        int r;
+
+        assert(s);
+        assert(ret_lower);
+        assert(ret_upper);
+
+        r = extract_first_word(&s, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
+
+        r = parse_uid(word, &l);
+        if (r < 0)
+                return r;
+
+        /* Check for the upper bound and extract it if needed */
+        if (!s)
+                /* Single number with no dash. */
+                u = l;
+        else if (!*s)
+                /* Trailing dash is an error. */
+                return -EINVAL;
+        else {
+                r = parse_uid(s, &u);
+                if (r < 0)
+                        return r;
+
+                if (l > u)
+                        return -EINVAL;
+        }
+
+        *ret_lower = l;
+        *ret_upper = u;
+        return 0;
+}
+
+char* getlogname_malloc(void) {
+        uid_t uid;
+        struct stat st;
+
+        if (isatty_safe(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+                uid = st.st_uid;
+        else
+                uid = getuid();
+
+        return uid_to_name(uid);
+}
+
+char* getusername_malloc(void) {
+        const char *e;
+
+        e = secure_getenv("USER");
+        if (e)
+                return strdup(e);
+
+        return uid_to_name(getuid());
+}
+
+bool is_nologin_shell(const char *shell) {
+        return PATH_IN_SET(shell,
+                           /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
+                            * message and exits. Different distributions place the binary at different places though,
+                            * hence let's list them all. */
+                           "/bin/nologin",
+                           "/sbin/nologin",
+                           "/usr/bin/nologin",
+                           "/usr/sbin/nologin",
+                           /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
+                            * any message printing. Different distributions place the binary at various places but at
+                            * least not in the 'sbin' directory. */
+                           "/bin/false",
+                           "/usr/bin/false",
+                           "/bin/true",
+                           "/usr/bin/true");
+}
+
+const char* default_root_shell_at(int rfd) {
+        /* We want to use the preferred shell, i.e. DEFAULT_USER_SHELL, which usually
+         * will be /bin/bash. Fall back to /bin/sh if DEFAULT_USER_SHELL is not found,
+         * or any access errors. */
+
+        assert(rfd >= 0 || rfd == AT_FDCWD);
+
+        int r = chaseat(rfd, DEFAULT_USER_SHELL, CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL);
+        if (r < 0 && r != -ENOENT)
+                log_debug_errno(r, "Failed to look up shell '%s': %m", DEFAULT_USER_SHELL);
+        if (r > 0)
+                return DEFAULT_USER_SHELL;
+
+        return "/bin/sh";
+}
+
+const char* default_root_shell(const char *root) {
+        _cleanup_close_ int rfd = -EBADF;
+
+        rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH);
+        if (rfd < 0)
+                return "/bin/sh";
+
+        return default_root_shell_at(rfd);
+}
+
+static int synthesize_user_creds(
+                const char **username,
+                uid_t *ret_uid, gid_t *ret_gid,
+                const char **ret_home,
+                const char **ret_shell,
+                UserCredsFlags flags) {
+
+        assert(username);
+        assert(*username);
+
+        /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
+         * their user record data. */
+
+        if (STR_IN_SET(*username, "root", "0")) {
+                *username = "root";
+
+                if (ret_uid)
+                        *ret_uid = 0;
+                if (ret_gid)
+                        *ret_gid = 0;
+                if (ret_home)
+                        *ret_home = "/root";
+                if (ret_shell)
+                        *ret_shell = default_root_shell(NULL);
+
+                return 0;
+        }
+
+        if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
+            synthesize_nobody()) {
+                *username = NOBODY_USER_NAME;
+
+                if (ret_uid)
+                        *ret_uid = UID_NOBODY;
+                if (ret_gid)
+                        *ret_gid = GID_NOBODY;
+                if (ret_home)
+                        *ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
+                if (ret_shell)
+                        *ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
+
+                return 0;
+        }
+
+        return -ENOMEDIUM;
+}
+
+int get_user_creds(
+                const char **username,
+                uid_t *ret_uid, gid_t *ret_gid,
+                const char **ret_home,
+                const char **ret_shell,
+                UserCredsFlags flags) {
+
+        bool patch_username = false;
+        uid_t u = UID_INVALID;
+        struct passwd *p;
+        int r;
+
+        assert(username);
+        assert(*username);
+        assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
+
+        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
+            (!ret_home && !ret_shell)) {
+
+                /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
+                 * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
+                 * user database will override the synthetic records instead — except if the user is only interested in
+                 * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
+                 * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why?
+                 * Simply because there are valid usecase where the user might change the home directory or the shell
+                 * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
+                 * support. */
+
+                r = synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags);
+                if (r >= 0)
+                        return 0;
+                if (r != -ENOMEDIUM) /* not a username we can synthesize */
+                        return r;
+        }
+
+        if (parse_uid(*username, &u) >= 0) {
+                errno = 0;
+                p = getpwuid(u);
+
+                /* If there are multiple users with the same id, make sure to leave $USER to the configured value
+                 * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
+                 * then let's pick the real username from /etc/passwd. */
+                if (p)
+                        patch_username = true;
+                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !ret_gid && !ret_home && !ret_shell) {
+
+                        /* If the specified user is a numeric UID and it isn't in the user database, and the caller
+                         * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that
+                         * and don't complain. */
+
+                        if (ret_uid)
+                                *ret_uid = u;
+
+                        return 0;
+                }
+        } else {
+                errno = 0;
+                p = getpwnam(*username);
+        }
+        if (!p) {
+                /* getpwnam() may fail with ENOENT if /etc/passwd is missing.
+                 * For us that is equivalent to the name not being defined. */
+                r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
+
+                /* If the user requested that we only synthesize as fallback, do so now */
+                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
+                        if (synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags) >= 0)
+                                return 0;
+
+                return r;
+        }
+
+        if (ret_uid && !uid_is_valid(p->pw_uid))
+                return -EBADMSG;
+
+        if (ret_gid && !gid_is_valid(p->pw_gid))
+                return -EBADMSG;
+
+        if (ret_uid)
+                *ret_uid = p->pw_uid;
+
+        if (ret_gid)
+                *ret_gid = p->pw_gid;
+
+        if (ret_home)
+                /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
+                *ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
+                            (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
+                            ? NULL : p->pw_dir;
+
+        if (ret_shell)
+                *ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
+                             (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
+                             ? NULL : p->pw_shell;
+
+        if (patch_username)
+                *username = p->pw_name;
+
+        return 0;
+}
+
+static int synthesize_group_creds(
+                const char **groupname,
+                gid_t *ret_gid) {
+
+        assert(groupname);
+        assert(*groupname);
+
+        if (STR_IN_SET(*groupname, "root", "0")) {
+                *groupname = "root";
+
+                if (ret_gid)
+                        *ret_gid = 0;
+
+                return 0;
+        }
+
+        if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
+            synthesize_nobody()) {
+                *groupname = NOBODY_GROUP_NAME;
+
+                if (ret_gid)
+                        *ret_gid = GID_NOBODY;
+
+                return 0;
+        }
+
+        return -ENOMEDIUM;
+}
+
+int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags) {
+        bool patch_groupname = false;
+        struct group *g;
+        gid_t id;
+        int r;
+
+        assert(groupname);
+        assert(*groupname);
+
+        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
+                r = synthesize_group_creds(groupname, ret_gid);
+                if (r >= 0)
+                        return 0;
+                if (r != -ENOMEDIUM) /* not a groupname we can synthesize */
+                        return r;
+        }
+
+        if (parse_gid(*groupname, &id) >= 0) {
+                errno = 0;
+                g = getgrgid(id);
+
+                if (g)
+                        patch_groupname = true;
+                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
+                        if (ret_gid)
+                                *ret_gid = id;
+
+                        return 0;
+                }
+        } else {
+                errno = 0;
+                g = getgrnam(*groupname);
+        }
+
+        if (!g) {
+                /* getgrnam() may fail with ENOENT if /etc/group is missing.
+                 * For us that is equivalent to the name not being defined. */
+                r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
+
+                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
+                        if (synthesize_group_creds(groupname, ret_gid) >= 0)
+                                return 0;
+
+                return r;
+        }
+
+        if (ret_gid) {
+                if (!gid_is_valid(g->gr_gid))
+                        return -EBADMSG;
+
+                *ret_gid = g->gr_gid;
+        }
+
+        if (patch_groupname)
+                *groupname = g->gr_name;
+
+        return 0;
+}
+
+char* uid_to_name(uid_t uid) {
+        char *ret;
+        int r;
+
+        /* Shortcut things to avoid NSS lookups */
+        if (uid == 0)
+                return strdup("root");
+        if (uid == UID_NOBODY && synthesize_nobody())
+                return strdup(NOBODY_USER_NAME);
+
+        if (uid_is_valid(uid)) {
+                _cleanup_free_ struct passwd *pw = NULL;
+
+                r = getpwuid_malloc(uid, &pw);
+                if (r >= 0)
+                        return strdup(pw->pw_name);
+        }
+
+        if (asprintf(&ret, UID_FMT, uid) < 0)
+                return NULL;
+
+        return ret;
+}
+
+char* gid_to_name(gid_t gid) {
+        char *ret;
+        int r;
+
+        if (gid == 0)
+                return strdup("root");
+        if (gid == GID_NOBODY && synthesize_nobody())
+                return strdup(NOBODY_GROUP_NAME);
+
+        if (gid_is_valid(gid)) {
+                _cleanup_free_ struct group *gr = NULL;
+
+                r = getgrgid_malloc(gid, &gr);
+                if (r >= 0)
+                        return strdup(gr->gr_name);
+        }
+
+        if (asprintf(&ret, GID_FMT, gid) < 0)
+                return NULL;
+
+        return ret;
+}
+
+static bool gid_list_has(const gid_t *list, size_t size, gid_t val) {
+        assert(list || size == 0);
+
+        FOREACH_ARRAY(i, list, size)
+                if (*i == val)
+                        return true;
+
+        return false;
+}
+
+int in_gid(gid_t gid) {
+        _cleanup_free_ gid_t *gids = NULL;
+        int ngroups;
+
+        if (getgid() == gid)
+                return 1;
+
+        if (getegid() == gid)
+                return 1;
+
+        if (!gid_is_valid(gid))
+                return -EINVAL;
+
+        ngroups = getgroups_alloc(&gids);
+        if (ngroups < 0)
+                return ngroups;
+
+        return gid_list_has(gids, ngroups, gid);
+}
+
+int in_group(const char *name) {
+        int r;
+        gid_t gid;
+
+        r = get_group_creds(&name, &gid, 0);
+        if (r < 0)
+                return r;
+
+        return in_gid(gid);
+}
+
+int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **ret) {
+        size_t nresult = 0;
+        assert(ret);
+
+        if (size2 > INT_MAX - size1)
+                return -ENOBUFS;
+
+        gid_t *buf = new(gid_t, size1 + size2);
+        if (!buf)
+                return -ENOMEM;
+
+        /* Duplicates need to be skipped on merging, otherwise they'll be passed on and stored in the kernel. */
+        for (size_t i = 0; i < size1; i++)
+                if (!gid_list_has(buf, nresult, list1[i]))
+                        buf[nresult++] = list1[i];
+        for (size_t i = 0; i < size2; i++)
+                if (!gid_list_has(buf, nresult, list2[i]))
+                        buf[nresult++] = list2[i];
+        *ret = buf;
+        return (int)nresult;
+}
+
+int getgroups_alloc(gid_t **ret) {
+        int ngroups = 8;
+
+        assert(ret);
+
+        for (unsigned attempt = 0;;) {
+                _cleanup_free_ gid_t *p = NULL;
+
+                p = new(gid_t, ngroups);
+                if (!p)
+                        return -ENOMEM;
+
+                ngroups = getgroups(ngroups, p);
+                if (ngroups > 0) {
+                        *ret = TAKE_PTR(p);
+                        return ngroups;
+                }
+                if (ngroups == 0)
+                        break;
+                if (errno != EINVAL)
+                        return -errno;
+
+                /* Give up eventually */
+                if (attempt++ > 10)
+                        return -EINVAL;
+
+                /* Get actual size needed, and size the array explicitly. Note that this is potentially racy
+                 * to use (in multi-threaded programs), hence let's call this in a loop. */
+                ngroups = getgroups(0, NULL);
+                if (ngroups < 0)
+                        return -errno;
+                if (ngroups == 0)
+                        break;
+        }
+
+        *ret = NULL;
+        return 0;
+}
+
+int get_home_dir(char **ret) {
+        _cleanup_free_ struct passwd *p = NULL;
+        const char *e;
+        uid_t u;
+        int r;
+
+        assert(ret);
+
+        /* Take the user specified one */
+        e = secure_getenv("HOME");
+        if (e && path_is_valid(e) && path_is_absolute(e))
+                goto found;
+
+        /* Hardcode home directory for root and nobody to avoid NSS */
+        u = getuid();
+        if (u == 0) {
+                e = "/root";
+                goto found;
+        }
+        if (u == UID_NOBODY && synthesize_nobody()) {
+                e = "/";
+                goto found;
+        }
+
+        /* Check the database... */
+        r = getpwuid_malloc(u, &p);
+        if (r < 0)
+                return r;
+
+        e = p->pw_dir;
+        if (!path_is_valid(e) || !path_is_absolute(e))
+                return -EINVAL;
+
+ found:
+        return path_simplify_alloc(e, ret);
+}
+
+int get_shell(char **ret) {
+        _cleanup_free_ struct passwd *p = NULL;
+        const char *e;
+        uid_t u;
+        int r;
+
+        assert(ret);
+
+        /* Take the user specified one */
+        e = secure_getenv("SHELL");
+        if (e && path_is_valid(e) && path_is_absolute(e))
+                goto found;
+
+        /* Hardcode shell for root and nobody to avoid NSS */
+        u = getuid();
+        if (u == 0) {
+                e = default_root_shell(NULL);
+                goto found;
+        }
+        if (u == UID_NOBODY && synthesize_nobody()) {
+                e = NOLOGIN;
+                goto found;
+        }
+
+        /* Check the database... */
+        r = getpwuid_malloc(u, &p);
+        if (r < 0)
+                return r;
+
+        e = p->pw_shell;
+        if (!path_is_valid(e) || !path_is_absolute(e))
+                return -EINVAL;
+
+ found:
+        return path_simplify_alloc(e, ret);
+}
+
+int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids) {
+        int r;
+
+        assert(supplementary_gids || n_supplementary_gids == 0);
+
+        /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */
+
+        r = maybe_setgroups(n_supplementary_gids, supplementary_gids);
+        if (r < 0)
+                return r;
+
+        if (gid_is_valid(gid))
+                if (setresgid(gid, gid, gid) < 0)
+                        return -errno;
+
+        if (uid_is_valid(uid))
+                if (setresuid(uid, uid, uid) < 0)
+                        return -errno;
+
+        return 0;
+}
+
+int take_etc_passwd_lock(const char *root) {
+        int r;
+
+        /* This is roughly the same as lckpwdf(), but not as awful. We don't want to use alarm() and signals,
+         * hence we implement our own trivial version of this.
+         *
+         * Note that shadow-utils also takes per-database locks in addition to lckpwdf(). However, we don't,
+         * given that they are redundant: they invoke lckpwdf() first and keep it during everything they do.
+         * The per-database locks are awfully racy, and thus we just won't do them. */
+
+        _cleanup_free_ char *path = path_join(root, ETC_PASSWD_LOCK_PATH);
+        if (!path)
+                return log_oom_debug();
+
+        (void) mkdir_parents(path, 0755);
+
+        _cleanup_close_ int fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
+        if (fd < 0)
+                return log_debug_errno(errno, "Cannot open %s: %m", path);
+
+        r = unposix_lock(fd, LOCK_EX);
+        if (r < 0)
+                return log_debug_errno(r, "Locking %s failed: %m", path);
+
+        return TAKE_FD(fd);
+}
+
+bool valid_user_group_name(const char *u, ValidUserFlags flags) {
+        const char *i;
+
+        /* Checks if the specified name is a valid user/group name. There are two flavours of this call:
+         * strict mode is the default which is POSIX plus some extra rules; and relaxed mode where we accept
+         * pretty much everything except the really worst offending names.
+         *
+         * Whenever we synthesize users ourselves we should use the strict mode. But when we process users
+         * created by other stuff, let's be more liberal. */
+
+        if (isempty(u)) /* An empty user name is never valid */
+                return false;
+
+        if (parse_uid(u, NULL) >= 0) /* Something that parses as numeric UID string is valid exactly when the
+                                      * flag for it is set */
+                return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC);
+
+        if (FLAGS_SET(flags, VALID_USER_RELAX)) {
+
+                /* In relaxed mode we just check very superficially. Apparently SSSD and other stuff is
+                 * extremely liberal (way too liberal if you ask me, even inserting "@" in user names, which
+                 * is bound to cause problems for example when used with an MTA), hence only filter the most
+                 * obvious cases, or where things would result in an invalid entry if such a user name would
+                 * show up in /etc/passwd (or equivalent getent output).
+                 *
+                 * Note that we stepped far out of POSIX territory here. It's not our fault though, but
+                 * SSSD's, Samba's and everybody else who ignored POSIX on this. (I mean, I am happy to step
+                 * outside of POSIX' bounds any day, but I must say in this case I probably wouldn't
+                 * have...) */
+
+                if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed
+                                                             * at front and back (accept in the middle, since
+                                                             * that's apparently a thing on Windows). Note
+                                                             * that this also blocks usernames consisting of
+                                                             * whitespace only. */
+                        return false;
+
+                if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */
+                        return false;
+
+                if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
+                                             * record separator in /etc/passwd), so we can't allow that. */
+                        return false;
+
+                if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow
+                                       * that. Slashes are special to file systems paths and user names
+                                       * typically show up in the file system as home directories, hence
+                                       * don't allow slashes. */
+                        return false;
+
+                if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused
+                                                  * with UIDs (note that this test is more broad than
+                                                  * the parse_uid() test above, as it will cover more than
+                                                  * the 32-bit range, and it will detect 65535 (which is in
+                                                  * invalid UID, even though in the unsigned 32 bit range) */
+                        return false;
+
+                if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric
+                                                                     * strings either. After all some people
+                                                                     * write 65535 as -1 (even though that's
+                                                                     * not even true on 32-bit uid_t
+                                                                     * anyway) */
+                        return false;
+
+                if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are
+                                        * special in that context, don't allow that. */
+                        return false;
+
+                /* Compare with strict result and warn if result doesn't match */
+                if (FLAGS_SET(flags, VALID_USER_WARN) && !valid_user_group_name(u, 0))
+                        log_struct(LOG_NOTICE,
+                                   LOG_MESSAGE("Accepting user/group name '%s', which does not match strict user/group name rules.", u),
+                                   LOG_ITEM("USER_GROUP_NAME=%s", u),
+                                   LOG_MESSAGE_ID(SD_MESSAGE_UNSAFE_USER_NAME_STR));
+
+                /* Note that we make no restrictions on the length in relaxed mode! */
+        } else {
+                long sz;
+                size_t l;
+
+                /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here
+                 * however. Specifically we deviate from POSIX rules:
+                 *
+                 * - We don't allow empty user names (see above)
+                 * - We require that names fit into the appropriate utmp field
+                 * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
+                 * - We don't allow dashes or digit as the first character
+                 *
+                 * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
+                 */
+
+                if (!ascii_isalpha(u[0]) &&
+                    u[0] != '_')
+                        return false;
+
+                for (i = u+1; *i; i++)
+                        if (!ascii_isalpha(*i) &&
+                            !ascii_isdigit(*i) &&
+                            !IN_SET(*i, '_', '-'))
+                                return false;
+
+                l = i - u;
+
+                sz = sysconf(_SC_LOGIN_NAME_MAX);
+                assert_se(sz > 0);
+
+                if (l > (size_t) sz) /* glibc: 256 */
+                        return false;
+                if (l > NAME_MAX) /* must fit in a filename: 255 */
+                        return false;
+                if (l > sizeof_field(struct utmpx, ut_user) - 1) /* must fit in utmp: 31 */
+                        return false;
+        }
+
+        return true;
+}
+
+bool valid_gecos(const char *d) {
+
+        if (!d)
+                return false;
+
+        if (!utf8_is_valid(d))
+                return false;
+
+        if (string_has_cc(d, NULL))
+                return false;
+
+        /* Colons are used as field separators, and hence not OK */
+        if (strchr(d, ':'))
+                return false;
+
+        return true;
+}
+
+char* mangle_gecos(const char *d) {
+        char *mangled;
+
+        /* Makes sure the provided string becomes valid as a GEGOS field, by dropping bad chars. glibc's
+         * putwent() only changes \n and : to spaces. We do more: replace all CC too, and remove invalid
+         * UTF-8 */
+
+        mangled = strdup(d);
+        if (!mangled)
+                return NULL;
+
+        for (char *i = mangled; *i; i++) {
+                int len;
+
+                if ((uint8_t) *i < (uint8_t) ' ' || *i == ':') {
+                        *i = ' ';
+                        continue;
+                }
+
+                len = utf8_encoded_valid_unichar(i, SIZE_MAX);
+                if (len < 0) {
+                        *i = ' ';
+                        continue;
+                }
+
+                i += len - 1;
+        }
+
+        return mangled;
+}
+
+bool valid_home(const char *p) {
+        /* Note that this function is also called by valid_shell(), any
+         * changes must account for that. */
+
+        if (isempty(p))
+                return false;
+
+        if (!utf8_is_valid(p))
+                return false;
+
+        if (string_has_cc(p, NULL))
+                return false;
+
+        if (!path_is_absolute(p))
+                return false;
+
+        if (!path_is_normalized(p))
+                return false;
+
+        /* Colons are used as field separators, and hence not OK */
+        if (strchr(p, ':'))
+                return false;
+
+        return true;
+}
+
+bool valid_shell(const char *p) {
+        /* We have the same requirements, so just piggy-back on the home check.
+         *
+         * Let's ignore /etc/shells because this is only applicable to real and not system users. It is also
+         * incompatible with the idea of empty /etc/. */
+        if (!valid_home(p))
+                return false;
+
+        return !endswith(p, "/"); /* one additional restriction: shells may not be dirs */
+}
+
+int maybe_setgroups(size_t size, const gid_t *list) {
+        int r;
+
+        /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
+        if (size == 0) { /* Dropping all aux groups? */
+                _cleanup_free_ char *setgroups_content = NULL;
+                bool can_setgroups;
+
+                r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
+                if (r == -ENOENT)
+                        /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
+                        can_setgroups = true;
+                else if (r < 0)
+                        return r;
+                else
+                        can_setgroups = streq(setgroups_content, "allow");
+
+                if (!can_setgroups) {
+                        log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
+                        return 0;
+                }
+        }
+
+        return RET_NERRNO(setgroups(size, list));
+}
+
+bool synthesize_nobody(void) {
+        /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by
+         * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems
+         * that used the "nobody" user name and group name for other UIDs/GIDs than 65534.
+         *
+         * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is
+         * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that
+         * shouldn't matter as each initialization should come to the same result. */
+        static int cache = -1;
+
+        if (cache < 0)
+                cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
+
+        return cache;
+}
+
+int putpwent_sane(const struct passwd *pw, FILE *stream) {
+        assert(pw);
+        assert(stream);
+
+        errno = 0;
+        if (putpwent(pw, stream) != 0)
+                return errno_or_else(EIO);
+
+        return 0;
+}
+
+int putspent_sane(const struct spwd *sp, FILE *stream) {
+        assert(sp);
+        assert(stream);
+
+        errno = 0;
+        if (putspent(sp, stream) != 0)
+                return errno_or_else(EIO);
+
+        return 0;
+}
+
+int putgrent_sane(const struct group *gr, FILE *stream) {
+        assert(gr);
+        assert(stream);
+
+        errno = 0;
+        if (putgrent(gr, stream) != 0)
+                return errno_or_else(EIO);
+
+        return 0;
+}
+
+#if ENABLE_GSHADOW
+int putsgent_sane(const struct sgrp *sg, FILE *stream) {
+        assert(sg);
+        assert(stream);
+
+        errno = 0;
+        if (putsgent(sg, stream) != 0)
+                return errno_or_else(EIO);
+
+        return 0;
+}
+#endif
+
+int fgetpwent_sane(FILE *stream, struct passwd **pw) {
+        assert(stream);
+        assert(pw);
+
+        errno = 0;
+        struct passwd *p = fgetpwent(stream);
+        if (!p && !IN_SET(errno, 0, ENOENT))
+                return -errno;
+
+        *pw = p;
+        return !!p;
+}
+
+int fgetspent_sane(FILE *stream, struct spwd **sp) {
+        assert(stream);
+        assert(sp);
+
+        errno = 0;
+        struct spwd *s = fgetspent(stream);
+        if (!s && !IN_SET(errno, 0, ENOENT))
+                return -errno;
+
+        *sp = s;
+        return !!s;
+}
+
+int fgetgrent_sane(FILE *stream, struct group **gr) {
+        assert(stream);
+        assert(gr);
+
+        errno = 0;
+        struct group *g = fgetgrent(stream);
+        if (!g && !IN_SET(errno, 0, ENOENT))
+                return -errno;
+
+        *gr = g;
+        return !!g;
+}
+
+#if ENABLE_GSHADOW
+int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
+        assert(stream);
+        assert(sg);
+
+        errno = 0;
+        struct sgrp *s = fgetsgent(stream);
+        if (!s && !IN_SET(errno, 0, ENOENT))
+                return -errno;
+
+        *sg = s;
+        return !!s;
+}
+#endif
+
+int is_this_me(const char *username) {
+        uid_t uid;
+        int r;
+
+        /* Checks if the specified username is our current one. Passed string might be a UID or a user name. */
+
+        r = get_user_creds(&username, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
+        if (r < 0)
+                return r;
+
+        return uid == getuid();
+}
+
+const char* get_home_root(void) {
+        const char *e;
+
+        /* For debug purposes allow overriding where we look for home dirs */
+        e = secure_getenv("SYSTEMD_HOME_ROOT");
+        if (e && path_is_absolute(e) && path_is_normalized(e))
+                return e;
+
+        return "/home";
+}
+
+static size_t getpw_buffer_size(void) {
+        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+        return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+static bool errno_is_user_doesnt_exist(int error) {
+        /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
+         * not found. */
+        return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM);
+}
+
+int getpwnam_malloc(const char *name, struct passwd **ret) {
+        size_t bufsize = getpw_buffer_size();
+        int r;
+
+        /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must
+         * free() the returned structures! */
+
+        if (isempty(name))
+                return -EINVAL;
+
+        for (;;) {
+                _cleanup_free_ void *buf = NULL;
+
+                buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+                if (!buf)
+                        return -ENOMEM;
+
+                struct passwd *pw = NULL;
+                r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+                if (r == 0) {
+                        if (pw) {
+                                if (ret)
+                                        *ret = TAKE_PTR(buf);
+                                return 0;
+                        }
+
+                        return -ESRCH;
+                }
+
+                assert(r > 0);
+
+                /* getpwnam() may fail with ENOENT if /etc/passwd is missing.  For us that is equivalent to
+                 * the name not being defined. */
+                if (errno_is_user_doesnt_exist(r))
+                        return -ESRCH;
+                if (r != ERANGE)
+                        return -r;
+
+                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+                        return -ENOMEM;
+                bufsize *= 2;
+        }
+}
+
+int getpwuid_malloc(uid_t uid, struct passwd **ret) {
+        size_t bufsize = getpw_buffer_size();
+        int r;
+
+        if (!uid_is_valid(uid))
+                return -EINVAL;
+
+        for (;;) {
+                _cleanup_free_ void *buf = NULL;
+
+                buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
+                if (!buf)
+                        return -ENOMEM;
+
+                struct passwd *pw = NULL;
+                r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
+                if (r == 0) {
+                        if (pw) {
+                                if (ret)
+                                        *ret = TAKE_PTR(buf);
+                                return 0;
+                        }
+
+                        return -ESRCH;
+                }
+
+                assert(r > 0);
+
+                if (errno_is_user_doesnt_exist(r))
+                        return -ESRCH;
+                if (r != ERANGE)
+                        return -r;
+
+                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
+                        return -ENOMEM;
+                bufsize *= 2;
+        }
+}
+
+static size_t getgr_buffer_size(void) {
+        long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+        return bufsize <= 0 ? 4096U : (size_t) bufsize;
+}
+
+int getgrnam_malloc(const char *name, struct group **ret) {
+        size_t bufsize = getgr_buffer_size();
+        int r;
+
+        if (isempty(name))
+                return -EINVAL;
+
+        for (;;) {
+                _cleanup_free_ void *buf = NULL;
+
+                buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+                if (!buf)
+                        return -ENOMEM;
+
+                struct group *gr = NULL;
+                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+                if (r == 0) {
+                        if (gr) {
+                                if (ret)
+                                        *ret = TAKE_PTR(buf);
+                                return 0;
+                        }
+
+                        return -ESRCH;
+                }
+
+                assert(r > 0);
+
+                if (errno_is_user_doesnt_exist(r))
+                        return -ESRCH;
+                if (r != ERANGE)
+                        return -r;
+
+                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+                        return -ENOMEM;
+                bufsize *= 2;
+        }
+}
+
+int getgrgid_malloc(gid_t gid, struct group **ret) {
+        size_t bufsize = getgr_buffer_size();
+        int r;
+
+        if (!gid_is_valid(gid))
+                return -EINVAL;
+
+        for (;;) {
+                _cleanup_free_ void *buf = NULL;
+
+                buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
+                if (!buf)
+                        return -ENOMEM;
+
+                struct group *gr = NULL;
+                r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
+                if (r == 0) {
+                        if (gr) {
+                                if (ret)
+                                        *ret = TAKE_PTR(buf);
+                                return 0;
+                        }
+
+                        return -ESRCH;
+                }
+
+                assert(r > 0);
+
+                if (errno_is_user_doesnt_exist(r))
+                        return -ESRCH;
+                if (r != ERANGE)
+                        return -r;
+
+                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
+                        return -ENOMEM;
+                bufsize *= 2;
+        }
+}
+#endif  /* NM_IGNORED */
\ No newline at end of file
diff --git a/src/libnm-systemd-shared/src/basic/utf8.c b/src/libnm-systemd-shared/src/basic/utf8.c
index 33fdd96e..74d60f56 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.c
+++ b/src/libnm-systemd-shared/src/basic/utf8.c
@@ -616,4 +616,29 @@ size_t utf8_console_width(const char *str) {
 
         return n;
 }
+
+size_t utf8_last_length(const char *s, size_t n) {
+        int r;
+
+        assert(s);
+
+        if (n == SIZE_MAX)
+                n = strlen(s);
+
+        /* Determines length in bytes of last UTF-8 codepoint in string. If the string is empty, returns
+         * zero. Treats invalid UTF-8 codepoints as 1 sized ones. */
+
+        for (size_t last = 0;;) {
+                if (n == 0)
+                        return last;
+
+                r = utf8_encoded_valid_unichar(s, n);
+                if (r <= 0)
+                        r = 1; /* treat invalid UTF-8 as byte-wide */
+
+                s += r;
+                n -= r;
+                last = r;
+        }
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/utf8.h b/src/libnm-systemd-shared/src/basic/utf8.h
index 221bc46a..3b1a468c 100644
--- a/src/libnm-systemd-shared/src/basic/utf8.h
+++ b/src/libnm-systemd-shared/src/basic/utf8.h
@@ -7,7 +7,6 @@
 #include <uchar.h>
 
 #include "macro.h"
-#include "missing_type.h"
 
 #define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd"
 #define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf"
@@ -62,3 +61,5 @@ static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t t
 size_t utf8_n_codepoints(const char *str);
 int utf8_char_console_width(const char *str);
 size_t utf8_console_width(const char *str);
+
+size_t utf8_last_length(const char *s, size_t n);
diff --git a/src/libnm-systemd-shared/src/fundamental/assert-fundamental.h b/src/libnm-systemd-shared/src/fundamental/assert-fundamental.h
new file mode 100644
index 00000000..e207958b
--- /dev/null
+++ b/src/libnm-systemd-shared/src/fundamental/assert-fundamental.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#if !SD_BOOT
+#  include <assert.h>
+#endif
+
+#include "macro-fundamental.h"
+#include "log.h"
+
+#if SD_BOOT
+        _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
+
+        #ifdef NDEBUG
+                #define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
+                #define assert_not_reached() __builtin_unreachable()
+        #else
+                #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
+                #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
+        #endif
+        #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
+#else
+
+#if 0 /* NM_IGNORED */
+_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func);
+#else /* NM_IGNORED */
+#define log_assert_failed(text, file, line, func)                                \
+    G_STMT_START                                                                 \
+    {                                                                            \
+        log_internal(LOG_CRIT,                                                   \
+                     0,                                                          \
+                     file,                                                       \
+                     line,                                                       \
+                     func,                                                       \
+                     "Assertion '%s' failed at %s:%u, function %s(). Aborting.", \
+                     text,                                                       \
+                     file,                                                       \
+                     line,                                                       \
+                     func);                                                      \
+        g_assert_not_reached();                                                  \
+    }                                                                            \
+    G_STMT_END
+#endif /* NM_IGNORED */
+
+#if 0 /* NM_IGNORED */
+_noreturn_ void log_assert_failed_unreachable(const char *file, int line, const char *func);
+#else /* NM_IGNORED */
+#define log_assert_failed_unreachable(file, line, func)                               \
+    G_STMT_START                                                                      \
+    {                                                                                 \
+        log_internal(LOG_CRIT,                                                        \
+                     0,                                                               \
+                     file,                                                            \
+                     line,                                                            \
+                     func,                                                            \
+                     "Code should not be reached at %s:%u, function %s(). Aborting.", \
+                     file,                                                            \
+                     line,                                                            \
+                     func);                                                           \
+        g_assert_not_reached();                                                       \
+    }                                                                                 \
+    G_STMT_END
+#endif /* NM_IGNORED */
+
+#ifdef __COVERITY__
+
+/* Use special definitions of assertion macros in order to prevent
+ * false positives of ASSERT_SIDE_EFFECT on Coverity static analyzer
+ * for uses of assert_se() and assert_return().
+ *
+ * These definitions make expression go through a (trivial) function
+ * call to ensure they are not discarded. Also use ! or !! to ensure
+ * the boolean expressions are seen as such.
+ *
+ * This technique has been described and recommended in:
+ * https://community.synopsys.com/s/question/0D534000046Yuzb/suppressing-assertsideeffect-for-functions-that-allow-for-sideeffects
+ */
+
+extern void __coverity_panic__(void);
+
+static inline void __coverity_check__(int condition) {
+        if (!condition)
+                __coverity_panic__();
+}
+
+static inline int __coverity_check_and_return__(int condition) {
+        return condition;
+}
+
+#define assert_message_se(expr, message) __coverity_check__(!!(expr))
+
+#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
+
+#else  /* ! __COVERITY__ */
+
+#define assert_message_se(expr, message)                                \
+        do {                                                            \
+                if (_unlikely_(!(expr)))                                \
+                        log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
+        } while (false)
+
+#endif  /* __COVERITY__ */
+
+#define assert_se(expr) assert_message_se(expr, #expr)
+
+/* We override the glibc assert() here. */
+#undef assert
+#ifdef NDEBUG
+#define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
+#else
+#define assert(expr) assert_message_se(expr, #expr)
+#endif
+
+#define assert_not_reached()                                            \
+        log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
+
+#endif
+
+/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
+#define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert)
+#define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se)
+#define _ASSERT_PTR(expr, var, check)      \
+        ({                                 \
+                typeof(expr) var = (expr); \
+                check(var);                \
+                var;                       \
+        })
+
+#define ASSERT_NONNEG(expr)                              \
+        ({                                               \
+                typeof(expr) _expr_ = (expr), _zero = 0; \
+                assert(_expr_ >= _zero);                 \
+                _expr_;                                  \
+        })
+
+#define ASSERT_SE_NONNEG(expr)                           \
+        ({                                               \
+                typeof(expr) _expr_ = (expr), _zero = 0; \
+                assert_se(_expr_ >= _zero);              \
+                _expr_;                                  \
+        })
diff --git a/src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h
index 68d5bf4e..4c86197e 100644
--- a/src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/iovec-util-fundamental.h
@@ -1,6 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#if !SD_BOOT
+#include <stdlib.h>
+#include <sys/uio.h>
+#endif
+
+#include "assert-fundamental.h"
+#include "macro-fundamental.h"
+
 #if SD_BOOT
 /* struct iovec is a POSIX userspace construct. Let's introduce it also in EFI mode, it's just so useful */
 struct iovec {
@@ -8,7 +16,9 @@ struct iovec {
         size_t iov_len;
 };
 
-static inline void free(void *p);
+DISABLE_WARNING_REDUNDANT_DECLS;
+void free(void *p);
+REENABLE_WARNING;
 #endif
 
 /* This accepts both const and non-const pointers */
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 418ea55f..abdfb9b6 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -1,16 +1,19 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
-#if !SD_BOOT
-#  include <assert.h>
-#endif
-
 #include <limits.h>
 #include <stdalign.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 
+/* This header unconditionally defines MAX() so include it here already so
+ * it won't override our own definition of MAX() that we define later in this
+ * file. */
+#if !SD_BOOT
+#include <sys/param.h>
+#endif
+
 /* Temporarily disable some warnings */
 #define DISABLE_WARNING_DEPRECATED_DECLARATIONS                         \
         _Pragma("GCC diagnostic push");                                 \
@@ -53,6 +56,9 @@
         _Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"")
 
 #if 0 /* NM_IGNORED */
+#define DISABLE_WARNING_REDUNDANT_DECLS                                 \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wredundant-decls\"")
 
 #if HAVE_WARNING_ZERO_LENGTH_BOUNDS
 #  define DISABLE_WARNING_ZERO_LENGTH_BOUNDS                            \
@@ -123,9 +129,19 @@
 #  define _fallthrough_ __attribute__((__fallthrough__))
 #endif
 
+#if __GNUC__ >= 15
+#  define _nonnull_if_nonzero_(p, n) __attribute__((nonnull_if_nonzero(p, n)))
+#else
+#  define _nonnull_if_nonzero_(p, n)
+#endif
+
 #define XSTRINGIFY(x) #x
 #define STRINGIFY(x) XSTRINGIFY(x)
 
+/* C23 changed char8_t from char to unsigned char, hence we cannot pass u8 literals to e.g. fputs() without
+ * casting. Let's introduce our own way to declare UTF-8 literals, which casts u8 literals to const char*. */
+#define UTF8(s) ((const char*) (u8"" s))
+
 #ifndef __COVERITY__
 #  define VOID_0 ((void)0)
 #else
@@ -141,45 +157,7 @@
 #define XCONCATENATE(x, y) x ## y
 #define CONCATENATE(x, y) XCONCATENATE(x, y)
 
-#if SD_BOOT
-        _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
-
-        #ifdef NDEBUG
-                #define assert(expr) ({ if (!(expr)) __builtin_unreachable(); })
-                #define assert_not_reached() __builtin_unreachable()
-        #else
-                #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
-                #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
-        #endif
-        #define static_assert _Static_assert
-        #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
-#endif
-
-/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
-#define ASSERT_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert)
-#define ASSERT_SE_PTR(expr) _ASSERT_PTR(expr, UNIQ_T(_expr_, UNIQ), assert_se)
-#define _ASSERT_PTR(expr, var, check)      \
-        ({                                 \
-                typeof(expr) var = (expr); \
-                check(var);                \
-                var;                       \
-        })
-
-#define ASSERT_NONNEG(expr)                              \
-        ({                                               \
-                typeof(expr) _expr_ = (expr), _zero = 0; \
-                assert(_expr_ >= _zero);                 \
-                _expr_;                                  \
-        })
-
-#define ASSERT_SE_NONNEG(expr)                           \
-        ({                                               \
-                typeof(expr) _expr_ = (expr), _zero = 0; \
-                assert_se(_expr_ >= _zero);              \
-                _expr_;                                  \
-        })
-
-#define assert_cc(expr) static_assert(expr, #expr)
+#define assert_cc(expr) _Static_assert(expr, #expr)
 
 #define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
 #define UNIQ __COUNTER__
@@ -434,7 +412,7 @@
                         _found = true;                                  \
                         break;                                          \
                 default:                                                \
-                        break;                                          \
+                        ;                                               \
                 }                                                       \
                 _found;                                                 \
         })
@@ -467,82 +445,6 @@
                 (typeof(memory)) NULL;          \
         })
 
-static inline size_t ALIGN_TO(size_t l, size_t ali) {
-        assert(ISPOWEROF2(ali));
-
-        if (l > SIZE_MAX - (ali - 1))
-                return SIZE_MAX; /* indicate overflow */
-
-        return ((l + (ali - 1)) & ~(ali - 1));
-}
-
-static inline uint64_t ALIGN_TO_U64(uint64_t l, uint64_t ali) {
-        assert(ISPOWEROF2(ali));
-
-        if (l > UINT64_MAX - (ali - 1))
-                return UINT64_MAX; /* indicate overflow */
-
-        return ((l + (ali - 1)) & ~(ali - 1));
-}
-
-static inline size_t ALIGN_DOWN(size_t l, size_t ali) {
-        assert(ISPOWEROF2(ali));
-
-        return l & ~(ali - 1);
-}
-
-static inline uint64_t ALIGN_DOWN_U64(uint64_t l, uint64_t ali) {
-        assert(ISPOWEROF2(ali));
-
-        return l & ~(ali - 1);
-}
-
-static inline size_t ALIGN_OFFSET(size_t l, size_t ali) {
-        assert(ISPOWEROF2(ali));
-
-        return l & (ali - 1);
-}
-
-static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
-        assert(ISPOWEROF2(ali));
-
-        return l & (ali - 1);
-}
-
-#define ALIGN2(l) ALIGN_TO(l, 2)
-#define ALIGN4(l) ALIGN_TO(l, 4)
-#define ALIGN8(l) ALIGN_TO(l, 8)
-#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
-#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
-#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
-#define ALIGN(l)  ALIGN_TO(l, sizeof(void*))
-#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
-
-/* Checks if the specified pointer is aligned as appropriate for the specific type */
-#define IS_ALIGNED16(p) (((uintptr_t) p) % alignof(uint16_t) == 0)
-#define IS_ALIGNED32(p) (((uintptr_t) p) % alignof(uint32_t) == 0)
-#define IS_ALIGNED64(p) (((uintptr_t) p) % alignof(uint64_t) == 0)
-
-/* 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) &&                           \
-                CONST_ISPOWEROF2(ali) &&                               \
-                (l <= SIZE_MAX - (ali - 1)),      /* overflow? */      \
-                ((l) + (ali) - 1) & ~((ali) - 1),                      \
-                VOID_0)
-
-/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable
- * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a
- * warning or if you want to assert that the cast gives a pointer of suitable alignment. */
-#define CAST_ALIGN_PTR(t, p)                                    \
-        ({                                                      \
-                const void *_p = (p);                           \
-                assert(((uintptr_t) _p) % alignof(t) == 0); \
-                (t *) _p;                                       \
-        })
-
 #define UPDATE_FLAG(orig, flag, b)                      \
         ((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
 #define SET_FLAG(v, flag, b) \
@@ -550,41 +452,11 @@ static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
 #define FLAGS_SET(v, flags) \
         ((~(v) & (flags)) == 0)
 
-/* A wrapper for 'func' to return void.
- * Only useful when a void-returning function is required by some API. */
-#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func)             \
-        static inline void name(type *p) {                      \
-                func(p);                                        \
-        }
-
-/* When func() returns the void value (NULL, -1, …) of the appropriate type */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                 \
-        static inline void func##p(type *p) {                   \
-                if (*p)                                         \
-                        *p = func(*p);                          \
-        }
-
-/* When func() doesn't return the appropriate type, set variable to empty afterwards.
- * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty)     \
-        static inline void func##p(type *p) {                   \
-                if (*p != (empty)) {                            \
-                        DISABLE_WARNING_ADDRESS;                \
-                        assert(func);                           \
-                        REENABLE_WARNING;                       \
-                        func(*p);                               \
-                        *p = (empty);                           \
-                }                                               \
-        }
+typedef struct {
+        int _empty[0];
+} dummy_t;
 
-/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */
-#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty)       \
-        static inline void func##p(type *p) {                           \
-                if (*p != (empty)) {                                    \
-                        func(*p);                                       \
-                        *p = (empty);                                   \
-                }                                                       \
-        }
+assert_cc(sizeof(dummy_t) == 0);
 
 /* Restriction/bug (see below) was fixed in GCC 15 and clang 19. */
 #if __GNUC__ >= 15 || (defined(__clang__) && __clang_major__ >= 19)
@@ -633,3 +505,10 @@ static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
 
 #define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p)))
 #define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#if 0 /* NM_IGNORED */
+assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1);
+#define PROJECT_FILE (&__FILE__[STRLEN(RELATIVE_SOURCE_PATH) + 1])
+#else /* NM_IGNORED */
+#define PROJECT_FILE __FILE__
+#endif /* NM_IGNORED */
\ No newline at end of file
diff --git a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
index 6870f54f..4b50714f 100644
--- a/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/memory-util-fundamental.h
@@ -9,6 +9,7 @@
 #  include <string.h>
 #endif
 
+#include "assert-fundamental.h"
 #include "macro-fundamental.h"
 
 #define memzero(x, l)                                           \
@@ -17,7 +18,7 @@
                 _l_ > 0 ? memset((x), 0, _l_) : (x);            \
         })
 
-#if !SD_BOOT && HAVE_EXPLICIT_BZERO
+#if !SD_BOOT
 static inline void *explicit_bzero_safe(void *p, size_t l) {
         if (p && l > 0)
                 explicit_bzero(p, l);
@@ -106,3 +107,115 @@ static inline void array_cleanup(const ArrayCleanup *c) {
                                 _f;                                     \
                         }),                                             \
         }
+
+/* A wrapper for 'func' to return void.
+ * Only useful when a void-returning function is required by some API. */
+#define DEFINE_TRIVIAL_DESTRUCTOR(name, type, func)             \
+        static inline void name(type *p) {                      \
+                func(p);                                        \
+        }
+
+/* When func() returns the void value (NULL, -1, …) of the appropriate type */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                 \
+        static inline void func##p(type *p) {                   \
+                if (*p)                                         \
+                        *p = func(*p);                          \
+        }
+
+/* When func() doesn't return the appropriate type, set variable to empty afterwards.
+ * The func() may be provided by a dynamically loaded shared library, hence add an assertion. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(type, func, empty)     \
+        static inline void func##p(type *p) {                   \
+                if (*p != (empty)) {                            \
+                        DISABLE_WARNING_ADDRESS;                \
+                        assert(func);                           \
+                        REENABLE_WARNING;                       \
+                        func(*p);                               \
+                        *p = (empty);                           \
+                }                                               \
+        }
+
+/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */
+#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty)       \
+        static inline void func##p(type *p) {                           \
+                if (*p != (empty)) {                                    \
+                        func(*p);                                       \
+                        *p = (empty);                                   \
+                }                                                       \
+        }
+
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        if (l > SIZE_MAX - (ali - 1))
+                return SIZE_MAX; /* indicate overflow */
+
+        return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline uint64_t ALIGN_TO_U64(uint64_t l, uint64_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        if (l > UINT64_MAX - (ali - 1))
+                return UINT64_MAX; /* indicate overflow */
+
+        return ((l + (ali - 1)) & ~(ali - 1));
+}
+
+static inline size_t ALIGN_DOWN(size_t l, size_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        return l & ~(ali - 1);
+}
+
+static inline uint64_t ALIGN_DOWN_U64(uint64_t l, uint64_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        return l & ~(ali - 1);
+}
+
+static inline size_t ALIGN_OFFSET(size_t l, size_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        return l & (ali - 1);
+}
+
+static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
+        assert(ISPOWEROF2(ali));
+
+        return l & (ali - 1);
+}
+
+#define ALIGN2(l) ALIGN_TO(l, 2)
+#define ALIGN4(l) ALIGN_TO(l, 4)
+#define ALIGN8(l) ALIGN_TO(l, 8)
+#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
+#define ALIGN(l)  ALIGN_TO(l, sizeof(void*))
+#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
+
+/* Checks if the specified pointer is aligned as appropriate for the specific type */
+#define IS_ALIGNED16(p) (((uintptr_t) p) % alignof(uint16_t) == 0)
+#define IS_ALIGNED32(p) (((uintptr_t) p) % alignof(uint32_t) == 0)
+#define IS_ALIGNED64(p) (((uintptr_t) p) % alignof(uint64_t) == 0)
+
+/* 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) &&                           \
+                CONST_ISPOWEROF2(ali) &&                               \
+                (l <= SIZE_MAX - (ali - 1)),      /* overflow? */      \
+                ((l) + (ali) - 1) & ~((ali) - 1),                      \
+                VOID_0)
+
+/* Similar to ((t *) (void *) (p)) to cast a pointer. The macro asserts that the pointer has a suitable
+ * alignment for type "t". This exists for places where otherwise "-Wcast-align=strict" would issue a
+ * warning or if you want to assert that the cast gives a pointer of suitable alignment. */
+#define CAST_ALIGN_PTR(t, p)                                    \
+        ({                                                      \
+                const void *_p = (p);                           \
+                assert(((uintptr_t) _p) % alignof(t) == 0); \
+                (t *) _p;                                       \
+        })
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c b/src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c
index 6682bc87..8e842535 100644
--- a/src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/sha256-fundamental.c
@@ -23,6 +23,13 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#if SD_BOOT
+#  include "efi-string.h"
+#else
+#  include <string.h>
+#endif
+
+#include "assert-fundamental.h"
 #include "macro-fundamental.h"
 #include "memory-util-fundamental.h"
 #include "sha256-fundamental.h"
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 da810cb7..0a62dbb2 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
@@ -85,7 +85,7 @@ static bool is_valid_version_char(sd_char a) {
 int strverscmp_improved(const sd_char *a, const sd_char *b) {
         /* This function is similar to strverscmp(3), but it treats '-' and '.' as separators.
          *
-         * The logic is based on rpm's rpmvercmp(), but unlike rpmvercmp(), it distiguishes e.g.
+         * The logic is based on rpm's rpmvercmp(), but unlike rpmvercmp(), it distinguishes e.g.
          * '123a' and '123.a', with '123a' being newer.
          *
          * It allows direct comparison of strings which contain both a version and a release; e.g.
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 419f1cc3..a9638b4d 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
@@ -8,6 +8,7 @@
 #  include <string.h>
 #endif
 
+#include "assert-fundamental.h"
 #include "macro-fundamental.h"
 
 #if SD_BOOT
@@ -16,6 +17,8 @@
 #  define strncmp strncmp16
 #  define strcasecmp strcasecmp16
 #  define strncasecmp strncasecmp16
+#  define strspn strspn16
+#  define strcspn strcspn16
 #  define STR_C(str)       (L ## str)
 typedef char16_t sd_char;
 #else
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c
index 42efd04a..eaca0715 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.c
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.c
@@ -15,6 +15,7 @@
 #include "hostname-util.h"
 #include "idn-util.h"
 #include "in-addr-util.h"
+#include "log.h"
 #include "macro.h"
 #include "parse-util.h"
 #include "string-util.h"
@@ -1395,7 +1396,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
                 r = sym_idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
                                        IDN2_NFC_INPUT | IDN2_TRANSITIONAL);
 
-        log_debug("idn2_lookup_u8: %s %s %s", name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), t);
+        log_debug("idn2_lookup_u8: %s %s %s", name, glyph(GLYPH_ARROW_RIGHT), t);
         if (r == IDN2_OK) {
                 if (!startswith(name, "xn--")) {
                         _cleanup_free_ char *s = NULL;
@@ -1410,8 +1411,8 @@ int dns_name_apply_idna(const char *name, char **ret) {
 
                         if (!streq_ptr(name, s)) {
                                 log_debug("idn2 roundtrip failed: \"%s\" %s \"%s\" %s \"%s\", ignoring.",
-                                          name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), t,
-                                          special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), s);
+                                          name, glyph(GLYPH_ARROW_RIGHT), t,
+                                          glyph(GLYPH_ARROW_RIGHT), s);
                                 *ret = NULL;
                                 return 0;
                         }