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.build3
-rw-r--r--src/libnm-systemd-shared/nm-sd-utils-shared.c60
-rw-r--r--src/libnm-systemd-shared/nm-sd-utils-shared.h17
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/locale-util.h3
-rw-r--r--src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.h38
-rw-r--r--src/libnm-systemd-shared/src/basic/env-util.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c9
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/fileio.c31
-rw-r--r--src/libnm-systemd-shared/src/basic/fs-util.c21
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.c139
-rw-r--r--src/libnm-systemd-shared/src/basic/glyph-util.h60
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.c14
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.c45
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/hostname-util.c6
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c54
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.h56
-rw-r--r--src/libnm-systemd-shared/src/basic/list.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.c376
-rw-r--r--src/libnm-systemd-shared/src/basic/locale-util.h56
-rw-r--r--src/libnm-systemd-shared/src/basic/log.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h60
-rw-r--r--src/libnm-systemd-shared/src/basic/mempool.c17
-rw-r--r--src/libnm-systemd-shared/src/basic/mempool.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/missing_syscall.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/ordered-set.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.c32
-rw-r--r--src/libnm-systemd-shared/src/basic/parse-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c124
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h22
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c38
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.c278
-rw-r--r--src/libnm-systemd-shared/src/basic/random-util.h11
-rw-r--r--src/libnm-systemd-shared/src/basic/set.h18
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.c99
-rw-r--r--src/libnm-systemd-shared/src/basic/socket-util.h8
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c153
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.h52
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.c27
-rw-r--r--src/libnm-systemd-shared/src/basic/string-util.h38
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.c40
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h43
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c46
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h138
-rw-r--r--src/libnm-systemd-shared/src/fundamental/sha256.c293
-rw-r--r--src/libnm-systemd-shared/src/fundamental/sha256.h29
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c78
-rw-r--r--src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h70
-rw-r--r--src/libnm-systemd-shared/src/fundamental/types-fundamental.h39
-rw-r--r--src/libnm-systemd-shared/src/shared/dns-domain.c108
-rw-r--r--src/libnm-systemd-shared/src/shared/dns-domain.h6
56 files changed, 1818 insertions, 1089 deletions
diff --git a/src/libnm-systemd-shared/meson.build b/src/libnm-systemd-shared/meson.build
index 05c81faf..d8e7a31a 100644
--- a/src/libnm-systemd-shared/meson.build
+++ b/src/libnm-systemd-shared/meson.build
@@ -14,6 +14,7 @@ libnm_systemd_shared = static_library(
     'src/basic/fileio.c',
     'src/basic/format-util.c',
     'src/basic/fs-util.c',
+    'src/basic/glyph-util.c',
     'src/basic/hash-funcs.c',
     'src/basic/hashmap.c',
     'src/basic/hexdecoct.c',
@@ -21,6 +22,7 @@ libnm_systemd_shared = static_library(
     'src/basic/in-addr-util.c',
     'src/basic/inotify-util.c',
     'src/basic/io-util.c',
+    'src/basic/locale-util.c',
     'src/basic/memory-util.c',
     'src/basic/mempool.c',
     'src/basic/ordered-set.c',
@@ -41,6 +43,7 @@ libnm_systemd_shared = static_library(
     'src/basic/tmpfile-util.c',
     'src/basic/utf8.c',
     'src/basic/util.c',
+    'src/fundamental/sha256.c',
     'src/fundamental/string-util-fundamental.c',
     'src/shared/dns-domain.c',
     'src/shared/web-util.c',
diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.c b/src/libnm-systemd-shared/nm-sd-utils-shared.c
index 0a35016a..dad21596 100644
--- a/src/libnm-systemd-shared/nm-sd-utils-shared.c
+++ b/src/libnm-systemd-shared/nm-sd-utils-shared.c
@@ -21,58 +21,6 @@ const bool mempool_use_allowed = true;
 
 /*****************************************************************************/
 
-gboolean
-nm_sd_utils_path_equal(const char *a, const char *b)
-{
-    return path_equal(a, b);
-}
-
-char *
-nm_sd_utils_path_simplify(char *path)
-{
-    return path_simplify(path);
-}
-
-const char *
-nm_sd_utils_path_startswith(const char *path, const char *prefix)
-{
-    return path_startswith(path, prefix);
-}
-
-/*****************************************************************************/
-
-int
-nm_sd_utils_unbase64char(char ch, gboolean accept_padding_equal)
-{
-    if (ch == '=' && accept_padding_equal)
-        return G_MAXINT;
-    return unbase64char(ch);
-}
-
-/**
- * nm_sd_utils_unbase64mem:
- * @p: a valid base64 string. Whitespace is ignored, but invalid encodings
- *   will cause the function to fail.
- * @l: the length of @p. @p is not treated as NUL terminated string but
- *   merely as a buffer of ascii characters.
- * @secure: whether the temporary memory will be cleared to avoid leaving
- *   secrets in memory (see also nm_explicit_bzero()).
- * @mem: (transfer full): the decoded buffer on success.
- * @len: the length of @mem on success.
- *
- * glib provides g_base64_decode(), but that does not report any errors
- * from invalid encodings. Expose systemd's implementation which does
- * reject invalid inputs.
- *
- * Returns: a non-negative code on success. Invalid encoding let the
- *   function fail.
- */
-int
-nm_sd_utils_unbase64mem(const char *p, size_t l, gboolean secure, guint8 **mem, size_t *len)
-{
-    return unbase64mem_full(p, l, secure, (void **) mem, len);
-}
-
 int
 nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical)
 {
@@ -85,14 +33,6 @@ nm_sd_dns_name_is_valid(const char *s)
     return dns_name_is_valid(s);
 }
 
-gboolean
-nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot)
-{
-    return hostname_is_valid(s,
-                             allow_trailing_dot ? VALID_HOSTNAME_TRAILING_DOT
-                                                : (ValidHostnameFlags) 0);
-}
-
 char *
 nm_sd_dns_name_normalize(const char *s)
 {
diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.h b/src/libnm-systemd-shared/nm-sd-utils-shared.h
index 82a50678..fefb8a1c 100644
--- a/src/libnm-systemd-shared/nm-sd-utils-shared.h
+++ b/src/libnm-systemd-shared/nm-sd-utils-shared.h
@@ -8,25 +8,10 @@
 
 /*****************************************************************************/
 
-gboolean nm_sd_utils_path_equal(const char *a, const char *b);
-
-char *nm_sd_utils_path_simplify(char *path);
-
-const char *nm_sd_utils_path_startswith(const char *path, const char *prefix);
-
-/*****************************************************************************/
-
-int nm_sd_utils_unbase64char(char ch, gboolean accept_padding_equal);
-
-int nm_sd_utils_unbase64mem(const char *p, size_t l, gboolean secure, guint8 **mem, size_t *len);
-
-/*****************************************************************************/
-
 int
 nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gboolean canonical);
 
-int      nm_sd_dns_name_is_valid(const char *s);
-gboolean nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot);
+int nm_sd_dns_name_is_valid(const char *s);
 
 char *nm_sd_dns_name_normalize(const char *s);
 
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/locale-util.h b/src/libnm-systemd-shared/sd-adapt-shared/locale-util.h
deleted file mode 100644
index 637892c2..00000000
--- a/src/libnm-systemd-shared/sd-adapt-shared/locale-util.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#pragma once
-
-/* dummy header */
diff --git a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h
index 586c3a7e..ee5a0a24 100644
--- a/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h
+++ b/src/libnm-systemd-shared/sd-adapt-shared/nm-sd-adapt-shared.h
@@ -51,8 +51,12 @@
 
 #define LOG_TRACE 0
 
+#define WANT_LINUX_FS_H 0
+
 #define BUILD_MODE_DEVELOPER (NM_MORE_ASSERTS > 0)
 
+#define LOG_MESSAGE_VERIFICATION (NM_MORE_ASSERTS > 0)
+
 /*****************************************************************************/
 
 /* systemd cannot be compiled with "-Wdeclaration-after-statement". In particular
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index 9dde770d..b38db7d4 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -50,16 +50,30 @@ typedef void* (*mfree_func_t)(void *p);
 
 #define malloc0(n) (calloc(1, (n) ?: 1))
 
-#define free_and_replace(a, b)                  \
+#define free_and_replace_full(a, b, free_func)  \
         ({                                      \
                 typeof(a)* _a = &(a);           \
                 typeof(b)* _b = &(b);           \
-                free(*_a);                      \
-                (*_a) = (*_b);                  \
-                (*_b) = NULL;                   \
+                free_func(*_a);                 \
+                *_a = *_b;                      \
+                *_b = NULL;                     \
                 0;                              \
         })
 
+#define free_and_replace(a, b)                  \
+        free_and_replace_full(a, b, free)
+
+/* This is similar to free_and_replace_full(), but NULL is not assigned to 'b', and its reference counter is
+ * increased. */
+#define unref_and_replace_full(a, b, ref_func, unref_func)      \
+        ({                                       \
+                typeof(a)* _a = &(a);            \
+                typeof(b) _b = ref_func(b);      \
+                unref_func(*_a);                 \
+                *_a = _b;                        \
+                0;                               \
+        })
+
 void* memdup(const void *p, size_t l) _alloc_(2);
 void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, since we return a buffer one byte larger than the specified size */
 
@@ -174,23 +188,13 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
  * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the
  * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of
  * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by
- * both the compiler's and libc's standards. Note that _FORTIFY_SOURCES=3 handles also dynamically allocated
- * objects and thus it's safer using __builtin_dynamic_object_size if _FORTIFY_SOURCES=3 is used (#22801).
- * Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and
+ * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the
+ * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory,
+ * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and
  * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner
  * case. */
-
-#if defined __has_builtin
-#  if __has_builtin(__builtin_dynamic_object_size)
-#    define MALLOC_SIZEOF_SAFE(x) \
-        MIN(malloc_usable_size(x), __builtin_dynamic_object_size(x, 0))
-#  endif
-#endif
-
-#ifndef MALLOC_SIZEOF_SAFE
 #define MALLOC_SIZEOF_SAFE(x) \
         MIN(malloc_usable_size(x), __builtin_object_size(x, 0))
-#endif
 
 /* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items
  * that fit into the specified memory block */
diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c
index b4ac6811..e869fad1 100644
--- a/src/libnm-systemd-shared/src/basic/env-util.c
+++ b/src/libnm-systemd-shared/src/basic/env-util.c
@@ -35,7 +35,7 @@ static bool env_name_is_valid_n(const char *e, size_t n) {
         if (n <= 0)
                 return false;
 
-        if (e[0] >= '0' && e[0] <= '9')
+        if (ascii_isdigit(e[0]))
                 return false;
 
         /* POSIX says the overall size of the environment block cannot
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 4504b6be..7984ddf4 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -35,6 +35,15 @@ char *hw_addr_to_string_full(
         return buffer;
 }
 
+struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length) {
+        assert(addr);
+        assert(length <= HW_ADDR_MAX_SIZE);
+
+        addr->length = length;
+        memcpy_safe(addr->bytes, bytes, length);
+        return addr;
+}
+
 int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) {
         int r;
 
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 32f45fe8..83ed77d6 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -52,6 +52,8 @@ static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buff
 
 #define HW_ADDR_NULL ((const struct hw_addr_data){})
 
+struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length);
+
 void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state);
 int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
 static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index 53efeb40..80295445 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -5,7 +5,9 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/btrfs.h>
+#if WANT_LINUX_FS_H
 #include <linux/fs.h>
+#endif
 #include <linux/magic.h>
 #include <sys/ioctl.h>
 #include <sys/resource.h>
diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c
index 0ad8ae03..d996bc02 100644
--- a/src/libnm-systemd-shared/src/basic/fileio.c
+++ b/src/libnm-systemd-shared/src/basic/fileio.c
@@ -767,8 +767,7 @@ int read_full_file_full(
 
         r = xfopenat(dir_fd, filename, "re", 0, &f);
         if (r < 0) {
-                _cleanup_close_ int dfd = -1, sk = -1;
-                union sockaddr_union sa;
+                _cleanup_close_ int sk = -1;
 
                 /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */
                 if (r != -ENXIO)
@@ -780,23 +779,7 @@ int read_full_file_full(
 
                 /* Seeking is not supported on AF_UNIX sockets */
                 if (offset != UINT64_MAX)
-                        return -ESPIPE;
-
-                if (dir_fd == AT_FDCWD)
-                        r = sockaddr_un_set_path(&sa.un, filename);
-                else {
-                        /* If we shall operate relative to some directory, then let's use O_PATH first to
-                         * open the socket inode, and then connect to it via /proc/self/fd/. We have to do
-                         * this since there's not connectat() that takes a directory fd as first arg. */
-
-                        dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC);
-                        if (dfd < 0)
-                                return -errno;
-
-                        r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd));
-                }
-                if (r < 0)
-                        return r;
+                        return -ENXIO;
 
                 sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
                 if (sk < 0)
@@ -813,12 +796,14 @@ int read_full_file_full(
                                 return r;
 
                         if (bind(sk, &bsa.sa, r) < 0)
-                                return r;
+                                return -errno;
                 }
 
-                if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
-                        return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is
-                                                                     * not a socket after all */
+                r = connect_unix_path(sk, dir_fd, filename);
+                if (IN_SET(r, -ENOTSOCK, -EINVAL)) /* propagate original error if this is not a socket after all */
+                        return -ENXIO;
+                if (r < 0)
+                        return r;
 
                 if (shutdown(sk, SHUT_WR) < 0)
                         return -errno;
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c
index dccfb27b..1a951bd5 100644
--- a/src/libnm-systemd-shared/src/basic/fs-util.c
+++ b/src/libnm-systemd-shared/src/basic/fs-util.c
@@ -160,24 +160,23 @@ int readlink_malloc(const char *p, char **ret) {
 
 #if 0 /* NM_IGNORED */
 int readlink_value(const char *p, char **ret) {
-        _cleanup_free_ char *link = NULL;
-        char *value;
+        _cleanup_free_ char *link = NULL, *name = NULL;
         int r;
 
+        assert(p);
+        assert(ret);
+
         r = readlink_malloc(p, &link);
         if (r < 0)
                 return r;
 
-        value = basename(link);
-        if (!value)
-                return -ENOENT;
-
-        value = strdup(value);
-        if (!value)
-                return -ENOMEM;
-
-        *ret = value;
+        r = path_extract_filename(link, &name);
+        if (r < 0)
+                return r;
+        if (r == O_DIRECTORY)
+                return -EINVAL;
 
+        *ret = TAKE_PTR(name);
         return 0;
 }
 
diff --git a/src/libnm-systemd-shared/src/basic/glyph-util.c b/src/libnm-systemd-shared/src/basic/glyph-util.c
new file mode 100644
index 00000000..ec851e8b
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.c
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include "env-util.h"
+#include "glyph-util.h"
+#include "locale-util.h"
+#include "strv.h"
+
+bool emoji_enabled(void) {
+        static int cached_emoji_enabled = -1;
+
+        if (cached_emoji_enabled < 0) {
+                int val;
+
+                val = getenv_bool("SYSTEMD_EMOJI");
+                if (val < 0)
+                        cached_emoji_enabled =
+                                is_locale_utf8() &&
+                                !STRPTR_IN_SET(getenv("TERM"), "dumb", "linux");
+                else
+                        cached_emoji_enabled = val;
+        }
+
+        return cached_emoji_enabled;
+}
+
+const char *special_glyph(SpecialGlyph code) {
+
+        /* 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
+         * works reasonably well on the Linux console. For details see:
+         *
+         * 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] = {
+                /* 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_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_SIGMA]                   = "S",
+                        [SPECIAL_GLYPH_ARROW_RIGHT]             = "->",
+                        [SPECIAL_GLYPH_ARROW_UP]                = "^",
+                        [SPECIAL_GLYPH_ARROW_DOWN]              = "v",
+                        [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]                = "*",
+                },
+
+                /* UTF-8 */
+                [true] = {
+                        /* 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"┌─",
+
+                        /* Single glyphs in both cases */
+                        [SPECIAL_GLYPH_VERTICAL_DOTTED]         = 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_SIGMA]                   = u8"Σ",
+                        [SPECIAL_GLYPH_ARROW_UP]                = u8"↑",       /* actually called: UPWARDS ARROW */
+                        [SPECIAL_GLYPH_ARROW_DOWN]              = u8"↓",       /* actually called: DOWNWARDS ARROW */
+
+                        /* Single glyph in Unicode, two in ASCII */
+                        [SPECIAL_GLYPH_ARROW_RIGHT]             = u8"→",       /* actually called: RIGHTWARDS ARROW */
+
+                        /* Single glyph in Unicode, three in ASCII */
+                        [SPECIAL_GLYPH_ELLIPSIS]                = u8"…",       /* 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 [] */
+
+                        /* 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 */
+
+                        /* 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 */
+
+                        /* This emoji is a single character cell glyph in Unicode, and two in ASCII */
+                        [SPECIAL_GLYPH_TOUCH]                   = u8"👆",       /* actually called: BACKHAND INDEX POINTING UP */
+
+                        /* These three 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"✨",
+                },
+        };
+
+        if (code < 0)
+                return NULL;
+
+        assert(code < _SPECIAL_GLYPH_MAX);
+        return draw_table[code >= _SPECIAL_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
new file mode 100644
index 00000000..065dde8a
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/glyph-util.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <errno.h>
+#include <stdbool.h>
+
+#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_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_ARROW_RIGHT,
+        SPECIAL_GLYPH_ARROW_UP,
+        SPECIAL_GLYPH_ARROW_DOWN,
+        SPECIAL_GLYPH_ELLIPSIS,
+        SPECIAL_GLYPH_LIGHT_SHADE,
+        SPECIAL_GLYPH_DARK_SHADE,
+        SPECIAL_GLYPH_SIGMA,
+        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_MAX,
+        _SPECIAL_GLYPH_INVALID = -EINVAL,
+} SpecialGlyph;
+
+const char *special_glyph(SpecialGlyph code) _const_;
+
+bool emoji_enabled(void);
+
+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 *special_glyph_check_mark_space(bool b) {
+        return b ? special_glyph(SPECIAL_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 c1d28ff6..eed30f09 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.c
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c
@@ -6,6 +6,7 @@
 
 #include "hash-funcs.h"
 #include "path-util.h"
+#include "strv.h"
 
 void string_hash_func(const char *p, struct siphash *state) {
         siphash24_compress(p, strlen(p) + 1, state);
@@ -17,6 +18,9 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_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);
 
 #if 0 /* NM_IGNORED */
 void path_hash_func(const char *q, struct siphash *state) {
@@ -107,11 +111,17 @@ DEFINE_HASH_OPS(uint64_hash_ops, uint64_t, uint64_hash_func, uint64_compare_func
 void devt_hash_func(const dev_t *p, struct siphash *state) {
         siphash24_compress(p, sizeof(dev_t), state);
 }
+#endif
 
 int devt_compare_func(const dev_t *a, const dev_t *b) {
-        return CMP(*a, *b);
+        int r;
+
+        r = CMP(major(*a), major(*b));
+        if (r != 0)
+                return r;
+
+        return CMP(minor(*a), minor(*b));
 }
 
 DEFINE_HASH_OPS(devt_hash_ops, dev_t, devt_hash_func, devt_compare_func);
-#endif
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h
index 023cfdf5..c14302ec 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.h
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h
@@ -78,6 +78,7 @@ void string_hash_func(const char *p, struct siphash *state);
 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_free_free;
+extern const struct hash_ops string_hash_ops_free_strv_free;
 
 void path_hash_func(const char *p, struct siphash *state);
 extern const struct hash_ops path_hash_ops;
@@ -102,10 +103,9 @@ extern const struct hash_ops uint64_hash_ops;
  * 64bit archs. Yuck! */
 #if SIZEOF_DEV_T != 8
 void devt_hash_func(const dev_t *p, struct siphash *state) _pure_;
-int devt_compare_func(const dev_t *a, const dev_t *b) _pure_;
-extern const struct hash_ops devt_hash_ops;
 #else
 #define devt_hash_func uint64_hash_func
-#define devt_compare_func uint64_compare_func
-#define devt_hash_ops uint64_hash_ops
 #endif
+
+int devt_compare_func(const dev_t *a, const dev_t *b) _pure_;
+extern const struct hash_ops devt_hash_ops;
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c
index e7dedc6d..5d0ae6aa 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.c
+++ b/src/libnm-systemd-shared/src/basic/hashmap.c
@@ -3,6 +3,7 @@
 #include "nm-sd-adapt-shared.h"
 
 #include <errno.h>
+#include <fnmatch.h>
 #include <pthread.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -772,16 +773,15 @@ static void shared_hash_key_initialize(void) {
 static struct HashmapBase* hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type  HASHMAP_DEBUG_PARAMS) {
         HashmapBase *h;
         const struct hashmap_type_info *hi = &hashmap_type_info[type];
-        bool up;
 
-        up = mempool_enabled();
+        bool use_pool = mempool_enabled && mempool_enabled();
 
-        h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
+        h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size);
         if (!h)
                 return NULL;
 
         h->type = type;
-        h->from_pool = up;
+        h->from_pool = use_pool;
         h->hash_ops = hash_ops ?: &trivial_hash_ops;
 
         if (type == HASHMAP_TYPE_ORDERED) {
@@ -1845,7 +1845,7 @@ int _hashmap_put_strdup_full(Hashmap **h, const struct hash_ops *hash_ops, const
 }
 #endif /* NM_IGNORED */
 
-int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p  HASHMAP_DEBUG_PARAMS) {
+int _set_put_strndup_full(Set **s, const struct hash_ops *hash_ops, const char *p, size_t n  HASHMAP_DEBUG_PARAMS) {
         char *c;
         int r;
 
@@ -1856,10 +1856,13 @@ int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p
         if (r < 0)
                 return r;
 
-        if (set_contains(*s, (char*) p))
-                return 0;
+        if (n == SIZE_MAX) {
+                if (set_contains(*s, (char*) p))
+                        return 0;
 
-        c = strdup(p);
+                c = strdup(p);
+        } else
+                c = strndup(p, n);
         if (!c)
                 return -ENOMEM;
 
@@ -1872,7 +1875,7 @@ int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l  HA
         assert(s);
 
         STRV_FOREACH(i, l) {
-                r = _set_put_strdup_full(s, hash_ops, *i  HASHMAP_DEBUG_PASS_ARGS);
+                r = _set_put_strndup_full(s, hash_ops, *i, SIZE_MAX  HASHMAP_DEBUG_PASS_ARGS);
                 if (r < 0)
                         return r;
 
@@ -2074,3 +2077,27 @@ bool set_equal(Set *a, Set *b) {
 
         return true;
 }
+
+static bool set_fnmatch_one(Set *patterns, const char *needle) {
+        const char *p;
+
+        assert(needle);
+
+        SET_FOREACH(p, patterns)
+                if (fnmatch(p, needle, 0) == 0)
+                        return true;
+
+        return false;
+}
+
+bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needle) {
+        assert(needle);
+
+        if (set_fnmatch_one(exclude_patterns, needle))
+                return false;
+
+        if (set_isempty(include_patterns))
+                return true;
+
+        return set_fnmatch_one(include_patterns, needle);
+}
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index eafc08f6..91b3fe86 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -90,12 +90,7 @@ OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DE
 #define ordered_hashmap_new(ops) _ordered_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
 
 #define hashmap_free_and_replace(a, b)          \
-        ({                                      \
-                hashmap_free(a);                \
-                (a) = (b);                      \
-                (b) = NULL;                     \
-                0;                              \
-        })
+        free_and_replace_full(a, b, hashmap_free)
 
 HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
 static inline Hashmap* hashmap_free(Hashmap *h) {
diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c
index 692733ac..1e94ce13 100644
--- a/src/libnm-systemd-shared/src/basic/hostname-util.c
+++ b/src/libnm-systemd-shared/src/basic/hostname-util.c
@@ -79,10 +79,8 @@ int gethostname_full(GetHostnameFlags flags, char **ret) {
 bool valid_ldh_char(char c) {
         /* "LDH" → "Letters, digits, hyphens", as per RFC 5890, Section 2.3.1 */
 
-        return
-                (c >= 'a' && c <= 'z') ||
-                (c >= 'A' && c <= 'Z') ||
-                (c >= '0' && c <= '9') ||
+        return ascii_isalpha(c) ||
+                ascii_isdigit(c) ||
                 c == '-';
 }
 
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 51a6b165..dc4446ae 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -16,6 +16,7 @@
 #include "macro.h"
 #include "parse-util.h"
 #include "random-util.h"
+#include "stdio-util.h"
 #include "string-util.h"
 #include "strxcpyx.h"
 #include "util.h"
@@ -50,6 +51,20 @@ bool in4_addr_is_link_local(const struct in_addr *a) {
         return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
 }
 
+bool in4_addr_is_link_local_dynamic(const struct in_addr *a) {
+        assert(a);
+
+        if (!in4_addr_is_link_local(a))
+                return false;
+
+        /* 169.254.0.0/24 and 169.254.255.0/24 must not be used for the dynamic IPv4LL assignment.
+         * See RFC 3927 Section 2.1:
+         * The IPv4 prefix 169.254/16 is registered with the IANA for this purpose. The first 256 and last
+         * 256 addresses in the 169.254/16 prefix are reserved for future use and MUST NOT be selected by a
+         * host using this dynamic configuration mechanism. */
+        return !IN_SET(be32toh(a->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
+}
+
 bool in6_addr_is_link_local(const struct in6_addr *a) {
         assert(a);
 
@@ -450,7 +465,7 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
                 return -ENOMEM;
 
         errno = 0;
-        if (!inet_ntop(family, u, x, l))
+        if (!typesafe_inet_ntop(family, u, x, l))
                 return errno_or_else(EINVAL);
 
         *ret = TAKE_PTR(x);
@@ -458,37 +473,26 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
 }
 
 #if 0 /* NM_IGNORED */
-int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret) {
-        _cleanup_free_ char *x = NULL;
-        char *p;
-        size_t l;
+int in_addr_prefix_to_string(
+                int family,
+                const union in_addr_union *u,
+                unsigned prefixlen,
+                char *buf,
+                size_t buf_len) {
 
         assert(u);
-        assert(ret);
+        assert(buf);
 
-        if (family == AF_INET)
-                l = INET_ADDRSTRLEN + 3;
-        else if (family == AF_INET6)
-                l = INET6_ADDRSTRLEN + 4;
-        else
+        if (!IN_SET(family, AF_INET, AF_INET6))
                 return -EAFNOSUPPORT;
 
-        if (prefixlen > FAMILY_ADDRESS_SIZE(family) * 8)
-                return -EINVAL;
-
-        x = new(char, l);
-        if (!x)
-                return -ENOMEM;
-
         errno = 0;
-        if (!inet_ntop(family, u, x, l))
-                return errno_or_else(EINVAL);
+        if (!typesafe_inet_ntop(family, u, buf, buf_len))
+                return errno_or_else(ENOSPC);
 
-        p = x + strlen(x);
-        l -= strlen(x);
-        (void) strpcpyf(&p, l, "/%u", prefixlen);
-
-        *ret = TAKE_PTR(x);
+        size_t l = strlen(buf);
+        if (!snprintf_ok(buf + l, buf_len - l, "/%u", prefixlen))
+                return -ENOSPC;
         return 0;
 }
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h
index 5de87a95..fbc60436 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include <arpa/inet.h>
 #include <netinet/in.h>
 #include <stddef.h>
 #include <sys/socket.h>
@@ -43,6 +44,7 @@ static inline bool in_addr_data_is_set(const struct in_addr_data *a) {
 int in_addr_is_multicast(int family, const union in_addr_union *u);
 
 bool in4_addr_is_link_local(const struct in_addr *a);
+bool in4_addr_is_link_local_dynamic(const struct in_addr *a);
 bool in6_addr_is_link_local(const struct in6_addr *a);
 int in_addr_is_link_local(int family, const union in_addr_union *u);
 bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
@@ -68,14 +70,62 @@ int in_addr_prefix_range(
                 unsigned prefixlen,
                 union in_addr_union *ret_start,
                 union in_addr_union *ret_end);
+
 int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
 static inline int in6_addr_to_string(const struct in6_addr *u, char **ret) {
         return in_addr_to_string(AF_INET6, (const union in_addr_union*) u, ret);
 }
-int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
-static inline int in6_addr_prefix_to_string(const struct in6_addr *u, unsigned prefixlen, char **ret) {
-        return in_addr_prefix_to_string(AF_INET6, (const union in_addr_union*) u, prefixlen, ret);
+
+static inline const char* typesafe_inet_ntop(int family, const union in_addr_union *a, char *buf, size_t len) {
+        return inet_ntop(family, a, buf, len);
+}
+static inline const char* typesafe_inet_ntop4(const struct in_addr *a, char *buf, size_t len) {
+        return inet_ntop(AF_INET, a, buf, len);
+}
+static inline const char* typesafe_inet_ntop6(const struct in6_addr *a, char *buf, size_t len) {
+        return inet_ntop(AF_INET6, a, buf, len);
+}
+
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
+ * see C11 §6.5.2.5, and
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks */
+#define IN_ADDR_MAX CONST_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)
+#define IN_ADDR_TO_STRING(family, addr) typesafe_inet_ntop(family, addr, (char[IN_ADDR_MAX]){}, IN_ADDR_MAX)
+#define IN4_ADDR_TO_STRING(addr) typesafe_inet_ntop4(addr, (char[INET_ADDRSTRLEN]){}, INET_ADDRSTRLEN)
+#define IN6_ADDR_TO_STRING(addr) typesafe_inet_ntop6(addr, (char[INET6_ADDRSTRLEN]){}, INET6_ADDRSTRLEN)
+
+int in_addr_prefix_to_string(
+                int family,
+                const union in_addr_union *u,
+                unsigned prefixlen,
+                char *buf,
+                size_t buf_len);
+
+static inline const char* _in_addr_prefix_to_string(
+                int family,
+                const union in_addr_union *u,
+                unsigned prefixlen,
+                char *buf,
+                size_t buf_len) {
+        /* We assume that this is called with an appropriately sized buffer and can never fail. */
+        assert_se(in_addr_prefix_to_string(family, u, prefixlen, buf, buf_len) == 0);
+        return buf;
+}
+static inline const char* _in4_addr_prefix_to_string(const struct in_addr *a, unsigned prefixlen, char *buf, size_t buf_len) {
+        return _in_addr_prefix_to_string(AF_INET, (const union in_addr_union *) a, prefixlen, buf, buf_len);
+}
+static inline const char* _in6_addr_prefix_to_string(const struct in6_addr *a, unsigned prefixlen, char *buf, size_t buf_len) {
+        return _in_addr_prefix_to_string(AF_INET6, (const union in_addr_union *) a, prefixlen, buf, buf_len);
 }
+
+#define PREFIX_SUFFIX_MAX (1 + DECIMAL_STR_MAX(unsigned))
+#define IN_ADDR_PREFIX_TO_STRING(family, addr, prefixlen) \
+        _in_addr_prefix_to_string(family, addr, prefixlen, (char[IN_ADDR_MAX + PREFIX_SUFFIX_MAX]){}, IN_ADDR_MAX + PREFIX_SUFFIX_MAX)
+#define IN4_ADDR_PREFIX_TO_STRING(addr, prefixlen) \
+        _in4_addr_prefix_to_string(addr, prefixlen, (char[INET_ADDRSTRLEN + PREFIX_SUFFIX_MAX]){}, INET_ADDRSTRLEN + PREFIX_SUFFIX_MAX)
+#define IN6_ADDR_PREFIX_TO_STRING(addr, prefixlen) \
+        _in6_addr_prefix_to_string(addr, prefixlen, (char[INET6_ADDRSTRLEN + PREFIX_SUFFIX_MAX]){}, INET6_ADDRSTRLEN + PREFIX_SUFFIX_MAX)
+
 int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);
 static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
         return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret);
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index 58e83a6c..ca300396 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -170,9 +170,6 @@
              i != (p);                                                  \
              i = i->name##_next ? i->name##_next : (head))
 
-#define LIST_IS_EMPTY(head)                                             \
-        (!(head))
-
 /* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */
 #define LIST_JOIN(name,a,b)                                             \
         do {                                                            \
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.c b/src/libnm-systemd-shared/src/basic/locale-util.c
new file mode 100644
index 00000000..ae567345
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/locale-util.c
@@ -0,0 +1,376 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <langinfo.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "def.h"
+#include "dirent-util.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "locale-util.h"
+#include "path-util.h"
+#include "set.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "utf8.h"
+
+#if 0 /* NM_IGNORED */
+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
+         * lowercases everything, and removes most special chars. This means the official .UTF-8 suffix
+         * becomes .utf8 when looking things up on disk. When enumerating locales, let's do the reverse
+         * operation, and go back to ".UTF-8" which appears to be the more commonly accepted name. We only do
+         * that for UTF-8 however, since it's kinda the only charset that matters. */
+
+        e = endswith(name, ".utf8");
+        if (e) {
+                _cleanup_free_ char *prefix = NULL;
+
+                prefix = strndup(name, e - name);
+                if (!prefix)
+                        return NULL;
+
+                return strjoin(prefix, ".UTF-8");
+        }
+
+        e = strstr(name, ".utf8@");
+        if (e) {
+                _cleanup_free_ char *prefix = NULL;
+
+                prefix = strndup(name, e - name);
+                if (!prefix)
+                        return NULL;
+
+                return strjoin(prefix, ".UTF-8@", e + 6);
+        }
+
+        return strdup(name);
+}
+
+static int add_locales_from_archive(Set *locales) {
+        /* Stolen from glibc... */
+
+        struct locarhead {
+                uint32_t magic;
+                /* Serial number.  */
+                uint32_t serial;
+                /* Name hash table.  */
+                uint32_t namehash_offset;
+                uint32_t namehash_used;
+                uint32_t namehash_size;
+                /* String table.  */
+                uint32_t string_offset;
+                uint32_t string_used;
+                uint32_t string_size;
+                /* Table with locale records.  */
+                uint32_t locrectab_offset;
+                uint32_t locrectab_used;
+                uint32_t locrectab_size;
+                /* MD5 sum hash table.  */
+                uint32_t sumhash_offset;
+                uint32_t sumhash_used;
+                uint32_t sumhash_size;
+        };
+
+        struct namehashent {
+                /* Hash value of the name.  */
+                uint32_t hashval;
+                /* Offset of the name in the string table.  */
+                uint32_t name_offset;
+                /* Offset of the locale record.  */
+                uint32_t locrec_offset;
+        };
+
+        const struct locarhead *h;
+        const struct namehashent *e;
+        const void *p = MAP_FAILED;
+        _cleanup_close_ int fd = -1;
+        size_t sz = 0;
+        struct stat st;
+        int r;
+
+        fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return errno == ENOENT ? 0 : -errno;
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if (!S_ISREG(st.st_mode))
+                return -EBADMSG;
+
+        if (st.st_size < (off_t) sizeof(struct locarhead))
+                return -EBADMSG;
+
+        if (file_offset_beyond_memory_size(st.st_size))
+                return -EFBIG;
+
+        p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+        if (p == MAP_FAILED)
+                return -errno;
+
+        h = (const struct locarhead *) p;
+        if (h->magic != 0xde020109 ||
+            h->namehash_offset + h->namehash_size > st.st_size ||
+            h->string_offset + h->string_size > st.st_size ||
+            h->locrectab_offset + h->locrectab_size > st.st_size ||
+            h->sumhash_offset + h->sumhash_size > st.st_size) {
+                r = -EBADMSG;
+                goto finish;
+        }
+
+        e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
+        for (size_t i = 0; i < h->namehash_size; i++) {
+                char *z;
+
+                if (e[i].locrec_offset == 0)
+                        continue;
+
+                if (!utf8_is_valid((char*) p + e[i].name_offset))
+                        continue;
+
+                z = normalize_locale((char*) p + e[i].name_offset);
+                if (!z) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                r = set_consume(locales, z);
+                if (r < 0)
+                        goto finish;
+        }
+
+        r = 0;
+
+ finish:
+        if (p != MAP_FAILED)
+                munmap((void*) p, sz);
+
+        return r;
+}
+
+static int add_locales_from_libdir(Set *locales) {
+        _cleanup_closedir_ DIR *dir = NULL;
+        int r;
+
+        dir = opendir("/usr/lib/locale");
+        if (!dir)
+                return errno == ENOENT ? 0 : -errno;
+
+        FOREACH_DIRENT(de, dir, return -errno) {
+                char *z;
+
+                if (de->d_type != DT_DIR)
+                        continue;
+
+                z = normalize_locale(de->d_name);
+                if (!z)
+                        return -ENOMEM;
+
+                r = set_consume(locales, z);
+                if (r < 0 && r != -EEXIST)
+                        return r;
+        }
+
+        return 0;
+}
+
+int get_locales(char ***ret) {
+        _cleanup_set_free_free_ Set *locales = NULL;
+        _cleanup_strv_free_ char **l = NULL;
+        int r;
+
+        locales = set_new(&string_hash_ops);
+        if (!locales)
+                return -ENOMEM;
+
+        r = add_locales_from_archive(locales);
+        if (r < 0 && r != -ENOENT)
+                return r;
+
+        r = add_locales_from_libdir(locales);
+        if (r < 0)
+                return r;
+
+        char *locale;
+        SET_FOREACH(locale, locales) {
+                r = locale_is_installed(locale);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        free(set_remove(locales, locale));
+        }
+
+        l = set_get_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 (r == -ENXIO || r == 0) {
+                char **a, **b;
+
+                /* 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@"))
+                                *(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);
+
+        *ret = TAKE_PTR(l);
+
+        return 0;
+}
+
+bool locale_is_valid(const char *name) {
+
+        if (isempty(name))
+                return false;
+
+        if (strlen(name) >= 128)
+                return false;
+
+        if (!utf8_is_valid(name))
+                return false;
+
+        if (!filename_is_valid(name))
+                return false;
+
+        if (!string_is_safe(name))
+                return false;
+
+        return true;
+}
+
+int locale_is_installed(const char *name) {
+        if (!locale_is_valid(name))
+                return false;
+
+        if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */
+                return true;
+
+        _cleanup_(freelocalep) locale_t loc =
+                newlocale(LC_ALL_MASK, name, 0);
+        if (loc == (locale_t) 0)
+                return errno == ENOMEM ? -ENOMEM : false;
+
+        return true;
+}
+
+void init_gettext(void) {
+        setlocale(LC_ALL, "");
+        textdomain(GETTEXT_PACKAGE);
+}
+#endif /* NM_IGNORED */
+
+bool is_locale_utf8(void) {
+        const char *set;
+        static int cached_answer = -1;
+
+        /* Note that we default to 'true' here, since today UTF8 is
+         * pretty much supported everywhere. */
+
+        if (cached_answer >= 0)
+                goto out;
+
+        if (!setlocale(LC_ALL, "")) {
+                cached_answer = true;
+                goto out;
+        }
+
+        set = nl_langinfo(CODESET);
+        if (!set) {
+                cached_answer = true;
+                goto out;
+        }
+
+        if (streq(set, "UTF-8")) {
+                cached_answer = true;
+                goto out;
+        }
+
+        /* 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;
+        }
+
+        /* Check result, but ignore the result if C was set
+         * explicitly. */
+        cached_answer =
+                STR_IN_SET(set, "C", "POSIX") &&
+                !getenv("LC_ALL") &&
+                !getenv("LC_CTYPE") &&
+                !getenv("LANG");
+
+out:
+        return (bool) cached_answer;
+}
+
+#if 0 /* NM_IGNORED */
+void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
+        if (!l)
+                return;
+
+        for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++)
+                l[i] = mfree(l[i]);
+}
+
+void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]) {
+        assert(l);
+
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
+                if (p == VARIABLE_LANG)
+                        continue;
+                if (isempty(l[p]) || streq_ptr(l[VARIABLE_LANG], l[p]))
+                        l[p] = mfree(l[p]);
+        }
+}
+
+static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
+        [VARIABLE_LANG]              = "LANG",
+        [VARIABLE_LANGUAGE]          = "LANGUAGE",
+        [VARIABLE_LC_CTYPE]          = "LC_CTYPE",
+        [VARIABLE_LC_NUMERIC]        = "LC_NUMERIC",
+        [VARIABLE_LC_TIME]           = "LC_TIME",
+        [VARIABLE_LC_COLLATE]        = "LC_COLLATE",
+        [VARIABLE_LC_MONETARY]       = "LC_MONETARY",
+        [VARIABLE_LC_MESSAGES]       = "LC_MESSAGES",
+        [VARIABLE_LC_PAPER]          = "LC_PAPER",
+        [VARIABLE_LC_NAME]           = "LC_NAME",
+        [VARIABLE_LC_ADDRESS]        = "LC_ADDRESS",
+        [VARIABLE_LC_TELEPHONE]      = "LC_TELEPHONE",
+        [VARIABLE_LC_MEASUREMENT]    = "LC_MEASUREMENT",
+        [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/locale-util.h b/src/libnm-systemd-shared/src/basic/locale-util.h
new file mode 100644
index 00000000..8990cb6a
--- /dev/null
+++ b/src/libnm-systemd-shared/src/basic/locale-util.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+
+#include "macro.h"
+
+typedef enum LocaleVariable {
+        /* We don't list LC_ALL here on purpose. People should be
+         * using LANG instead. */
+
+        VARIABLE_LANG,
+        VARIABLE_LANGUAGE,
+        VARIABLE_LC_CTYPE,
+        VARIABLE_LC_NUMERIC,
+        VARIABLE_LC_TIME,
+        VARIABLE_LC_COLLATE,
+        VARIABLE_LC_MONETARY,
+        VARIABLE_LC_MESSAGES,
+        VARIABLE_LC_PAPER,
+        VARIABLE_LC_NAME,
+        VARIABLE_LC_ADDRESS,
+        VARIABLE_LC_TELEPHONE,
+        VARIABLE_LC_MEASUREMENT,
+        VARIABLE_LC_IDENTIFICATION,
+        _VARIABLE_LC_MAX,
+        _VARIABLE_LC_INVALID = -EINVAL,
+} LocaleVariable;
+
+int get_locales(char ***l);
+bool locale_is_valid(const char *name);
+int locale_is_installed(const char *name);
+
+#define _(String) gettext(String)
+#define N_(String) String
+void init_gettext(void);
+
+bool is_locale_utf8(void);
+
+const char* locale_variable_to_string(LocaleVariable i) _const_;
+LocaleVariable locale_variable_from_string(const char *s) _pure_;
+
+static inline void freelocalep(locale_t *p) {
+        if (*p == (locale_t) 0)
+                return;
+
+        freelocale(*p);
+}
+
+void locale_variables_free(char* l[_VARIABLE_LC_MAX]);
+static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) {
+        locale_variables_free(*l);
+}
+void locale_variables_simplify(char *l[_VARIABLE_LC_MAX]);
diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h
index 6361eb0f..1bc84e46 100644
--- a/src/libnm-systemd-shared/src/basic/log.h
+++ b/src/libnm-systemd-shared/src/basic/log.h
@@ -434,8 +434,16 @@ int log_emergency_level(void);
 
 bool log_on_console(void) _pure_;
 
-/* Helper to prepare various field for structured logging */
-#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
+/* Helper to wrap the main message in structured logging. The macro doesn't do much,
+ * except to provide visual grouping of the format string and its arguments. */
+#if LOG_MESSAGE_VERIFICATION || defined(__COVERITY__)
+/* 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__
+#else
+#  define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
+#endif
 
 void log_received_signal(int level, const struct signalfd_siginfo *si);
 
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index 41d83b3e..7380da10 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -11,24 +11,6 @@
 
 #include "macro-fundamental.h"
 
-#define _printf_(a, b) __attribute__((__format__(printf, a, b)))
-#ifdef __clang__
-#  define _alloc_(...)
-#else
-#  define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
-#endif
-#define _sentinel_ __attribute__((__sentinel__))
-#define _destructor_ __attribute__((__destructor__))
-#define _deprecated_ __attribute__((__deprecated__))
-#define _malloc_ __attribute__((__malloc__))
-#define _weak_ __attribute__((__weak__))
-#define _public_ __attribute__((__visibility__("default")))
-#define _hidden_ __attribute__((__visibility__("hidden")))
-#define _weakref_(x) __attribute__((__weakref__(#x)))
-#define _alignas_(x) __attribute__((__aligned__(__alignof__(x))))
-#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
-#define _warn_unused_result_ __attribute__((__warn_unused_result__))
-
 #if !defined(HAS_FEATURE_MEMORY_SANITIZER)
 #  if defined(__has_feature)
 #    if __has_feature(memory_sanitizer)
@@ -106,10 +88,6 @@
         _Pragma("GCC diagnostic push")
 #endif
 
-#define DISABLE_WARNING_FLOAT_EQUAL \
-        _Pragma("GCC diagnostic push");                                 \
-        _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
-
 #define DISABLE_WARNING_TYPE_LIMITS \
         _Pragma("GCC diagnostic push");                                 \
         _Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
@@ -137,25 +115,6 @@
 #error "neither int nor long are four bytes long?!?"
 #endif
 
-/* Rounds up */
-
-#define ALIGN4(l) (((l) + 3) & ~3)
-#define ALIGN8(l) (((l) + 7) & ~7)
-
-#if __SIZEOF_POINTER__ == 8
-#define ALIGN(l) ALIGN8(l)
-#elif __SIZEOF_POINTER__ == 4
-#define ALIGN(l) ALIGN4(l)
-#else
-#error "Wut? Pointers are neither 4 nor 8 bytes long?"
-#endif
-
-#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p)))
-#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p)))
-#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p)))
-
-#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali)))
-
 /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */
 static inline unsigned long ALIGN_POWER2(unsigned long u) {
 
@@ -370,7 +329,7 @@ static inline int __coverity_check_and_return__(int condition) {
 #ifndef thread_local
 /*
  * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
- * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
+ * see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
  */
 #if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
 #define thread_local _Thread_local
@@ -405,8 +364,12 @@ static inline int __coverity_check_and_return__(int condition) {
                 if (!p)                                         \
                         return NULL;                            \
                                                                 \
-                assert(p->n_ref > 0);                           \
-                p->n_ref++;                                     \
+                /* For type check. */                           \
+                unsigned *q = &p->n_ref;                        \
+                assert(*q > 0);                                 \
+                assert_se(*q < UINT_MAX);                       \
+                                                                \
+                (*q)++;                                         \
                 return p;                                       \
         }
 
@@ -462,8 +425,15 @@ static inline int __coverity_check_and_return__(int condition) {
                 _copy;                                                  \
         })
 
+#define saturate_add(x, y, limit)                                       \
+        ({                                                              \
+                typeof(limit) _x = (x);                                 \
+                typeof(limit) _y = (y);                                 \
+                _x > (limit) || _y >= (limit) - _x ? (limit) : _x + _y; \
+        })
+
 static inline size_t size_add(size_t x, size_t y) {
-        return y >= SIZE_MAX - x ? SIZE_MAX : x + y;
+        return saturate_add(x, y, SIZE_MAX);
 }
 
 typedef struct {
diff --git a/src/libnm-systemd-shared/src/basic/mempool.c b/src/libnm-systemd-shared/src/basic/mempool.c
index 46c44914..53a719e0 100644
--- a/src/libnm-systemd-shared/src/basic/mempool.c
+++ b/src/libnm-systemd-shared/src/basic/mempool.c
@@ -5,12 +5,9 @@
 #include <stdint.h>
 #include <stdlib.h>
 
-#include "env-util.h"
 #include "macro.h"
 #include "memory-util.h"
 #include "mempool.h"
-#include "process-util.h"
-#include "util.h"
 
 struct pool {
         struct pool *next;
@@ -75,20 +72,6 @@ void mempool_free_tile(struct mempool *mp, void *p) {
         mp->freelist = p;
 }
 
-bool mempool_enabled(void) {
-        static int b = -1;
-
-        if (!is_main_thread())
-                return false;
-
-        if (!mempool_use_allowed)
-                b = false;
-        if (b < 0)
-                b = getenv_bool("SYSTEMD_MEMPOOL") != 0;
-
-        return b;
-}
-
 #if VALGRIND
 void mempool_drop(struct mempool *mp) {
         struct pool *p = mp->first_pool;
diff --git a/src/libnm-systemd-shared/src/basic/mempool.h b/src/libnm-systemd-shared/src/basic/mempool.h
index 0fe2f278..539ccbdf 100644
--- a/src/libnm-systemd-shared/src/basic/mempool.h
+++ b/src/libnm-systemd-shared/src/basic/mempool.h
@@ -23,8 +23,7 @@ static struct mempool pool_name = { \
         .at_least = alloc_at_least, \
 }
 
-extern const bool mempool_use_allowed;
-bool mempool_enabled(void);
+__attribute__((weak)) bool mempool_enabled(void);
 
 #if VALGRIND
 void mempool_drop(struct mempool *mp);
diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h
index e5bdc0e1..107f4b01 100644
--- a/src/libnm-systemd-shared/src/basic/missing_syscall.h
+++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h
@@ -76,6 +76,7 @@ static inline int missing_memfd_create(const char *name, unsigned int flags) {
 
 #  define memfd_create missing_memfd_create
 #endif
+#endif /* NM_IGNORED */
 
 /* ======================================================================= */
 
@@ -95,6 +96,7 @@ static inline ssize_t missing_getrandom(void *buffer, size_t count, unsigned fla
 
 /* ======================================================================= */
 
+#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) {
diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.h b/src/libnm-systemd-shared/src/basic/ordered-set.h
index c0650e01..e73da205 100644
--- a/src/libnm-systemd-shared/src/basic/ordered-set.h
+++ b/src/libnm-systemd-shared/src/basic/ordered-set.h
@@ -74,6 +74,10 @@ static inline char** ordered_set_get_strv(OrderedSet *s) {
         return _hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s));
 }
 
+static inline int ordered_set_reserve(OrderedSet *s, unsigned entries_add) {
+        return ordered_hashmap_reserve((OrderedHashmap*) s, entries_add);
+}
+
 int ordered_set_consume(OrderedSet *s, void *p);
 int _ordered_set_put_strdup(OrderedSet **s, const char *p  HASHMAP_DEBUG_PARAMS);
 #define ordered_set_put_strdup(s, p) _ordered_set_put_strdup(s, p  HASHMAP_DEBUG_SRC_ARGS)
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c
index 0a623c51..d5dcaae8 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.c
+++ b/src/libnm-systemd-shared/src/basic/parse-util.c
@@ -215,7 +215,7 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
                         e++;
 
                         /* strtoull() itself would accept space/+/- */
-                        if (*e >= '0' && *e <= '9') {
+                        if (ascii_isdigit(*e)) {
                                 unsigned long long l2;
                                 char *e2;
 
@@ -606,7 +606,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
 
         /* accept any number of digits, strtoull is limited to 19 */
         for (size_t i = 0; i < digits; i++,s++) {
-                if (*s < '0' || *s > '9') {
+                if (!ascii_isdigit(*s)) {
                         if (i == 0)
                                 return -EINVAL;
 
@@ -699,34 +699,6 @@ int parse_ip_prefix_length(const char *s, int *ret) {
         return 0;
 }
 
-int parse_dev(const char *s, dev_t *ret) {
-        const char *major;
-        unsigned x, y;
-        size_t n;
-        int r;
-
-        n = strspn(s, DIGITS);
-        if (n == 0)
-                return -EINVAL;
-        if (s[n] != ':')
-                return -EINVAL;
-
-        major = strndupa_safe(s, n);
-        r = safe_atou(major, &x);
-        if (r < 0)
-                return r;
-
-        r = safe_atou(s + n + 1, &y);
-        if (r < 0)
-                return r;
-
-        if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
-                return -ERANGE;
-
-        *ret = makedev(x, y);
-        return 0;
-}
-
 int parse_oom_score_adjust(const char *s, int *ret) {
         int r, v;
 
diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h
index 82731246..f2222dcf 100644
--- a/src/libnm-systemd-shared/src/basic/parse-util.h
+++ b/src/libnm-systemd-shared/src/basic/parse-util.h
@@ -12,7 +12,6 @@
 typedef unsigned long loadavg_t;
 
 int parse_boolean(const char *v) _pure_;
-int parse_dev(const char *s, dev_t *ret);
 int parse_pid(const char *s, pid_t* ret_pid);
 int parse_mode(const char *s, mode_t *ret);
 int parse_ifindex(const char *s);
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index f6088458..3ba3510f 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -19,17 +19,13 @@
 #include "extract-word.h"
 #include "fd-util.h"
 #include "fs-util.h"
-#include "glob-util.h"
 #include "log.h"
 #include "macro.h"
-#include "nulstr-util.h"
-#include "parse-util.h"
 #include "path-util.h"
 #include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
-#include "utf8.h"
 
 #if 0 /* NM_IGNORED */
 int path_split_and_make_absolute(const char *p, char ***ret) {
@@ -380,54 +376,6 @@ char *path_simplify(char *path) {
         return path;
 }
 
-#if 0 /* NM_IGNORED */
-int path_simplify_and_warn(
-                char *path,
-                unsigned flag,
-                const char *unit,
-                const char *filename,
-                unsigned line,
-                const char *lvalue) {
-
-        bool fatal = flag & PATH_CHECK_FATAL;
-
-        assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE));
-
-        if (!utf8_is_valid(path))
-                return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path);
-
-        if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) {
-                bool absolute;
-
-                absolute = path_is_absolute(path);
-
-                if (!absolute && (flag & PATH_CHECK_ABSOLUTE))
-                        return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
-                                          "%s= path is not absolute%s: %s",
-                                          lvalue, fatal ? "" : ", ignoring", path);
-
-                if (absolute && (flag & PATH_CHECK_RELATIVE))
-                        return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
-                                          "%s= path is absolute%s: %s",
-                                          lvalue, fatal ? "" : ", ignoring", path);
-        }
-
-        path_simplify(path);
-
-        if (!path_is_valid(path))
-                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
-                                  "%s= path has invalid length (%zu bytes)%s.",
-                                  lvalue, strlen(path), fatal ? "" : ", ignoring");
-
-        if (!path_is_normalized(path))
-                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
-                                  "%s= path is not normalized%s: %s",
-                                  lvalue, fatal ? "" : ", ignoring", path);
-
-        return 0;
-}
-#endif /* NM_IGNORED */
-
 char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
         assert(path);
         assert(prefix);
@@ -1113,7 +1061,7 @@ int path_extract_filename(const char *path, char **ret) {
                 return -ENOMEM;
 
         *ret = TAKE_PTR(a);
-        return strlen(c) > (size_t)r ? O_DIRECTORY : 0;
+        return strlen(c) > (size_t) r ? O_DIRECTORY : 0;
 }
 
 int path_extract_directory(const char *path, char **ret) {
@@ -1167,7 +1115,7 @@ bool filename_is_valid(const char *p) {
         if (isempty(p))
                 return false;
 
-        if (dot_or_dot_dot(p))
+        if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
                 return false;
 
         e = strchrnul(p, '/');
@@ -1322,74 +1270,6 @@ bool valid_device_allow_pattern(const char *path) {
 
         return valid_device_node_path(path);
 }
-
-int systemd_installation_has_version(const char *root, unsigned minimal_version) {
-        const char *pattern;
-        int r;
-
-        /* Try to guess if systemd installation is later than the specified version. This
-         * is hacky and likely to yield false negatives, particularly if the installation
-         * is non-standard. False positives should be relatively rare.
-         */
-
-        NULSTR_FOREACH(pattern,
-                       /* /lib works for systems without usr-merge, and for systems with a sane
-                        * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
-                        * for Gentoo which does a merge without making /lib a symlink.
-                        */
-                       "lib/systemd/libsystemd-shared-*.so\0"
-                       "lib64/systemd/libsystemd-shared-*.so\0"
-                       "usr/lib/systemd/libsystemd-shared-*.so\0"
-                       "usr/lib64/systemd/libsystemd-shared-*.so\0") {
-
-                _cleanup_strv_free_ char **names = NULL;
-                _cleanup_free_ char *path = NULL;
-                char *c;
-
-                path = path_join(root, pattern);
-                if (!path)
-                        return -ENOMEM;
-
-                r = glob_extend(&names, path, 0);
-                if (r == -ENOENT)
-                        continue;
-                if (r < 0)
-                        return r;
-
-                assert_se(c = endswith(path, "*.so"));
-                *c = '\0'; /* truncate the glob part */
-
-                STRV_FOREACH(name, names) {
-                        /* This is most likely to run only once, hence let's not optimize anything. */
-                        char *t, *t2;
-                        unsigned version;
-
-                        t = startswith(*name, path);
-                        if (!t)
-                                continue;
-
-                        t2 = endswith(t, ".so");
-                        if (!t2)
-                                continue;
-
-                        t2[0] = '\0'; /* truncate the suffix */
-
-                        r = safe_atou(t, &version);
-                        if (r < 0) {
-                                log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
-                                continue;
-                        }
-
-                        log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
-                                  *name, version,
-                                  version >= minimal_version ? "OK" : "too old");
-                        if (version >= minimal_version)
-                                return true;
-                }
-        }
-
-        return false;
-}
 #endif /* NM_IGNORED */
 
 bool dot_or_dot_dot(const char *path) {
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index fc41c869..c97c4540 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -45,12 +45,16 @@
 #endif
 
 static inline bool is_path(const char *p) {
-        assert(p);
+        if (!p) /* A NULL pointer is definitely not a path */
+                return false;
+
         return strchr(p, '/');
 }
 
 static inline bool path_is_absolute(const char *p) {
-        assert(p);
+        if (!p) /* A NULL pointer is definitely not an absolute path */
+                return false;
+
         return p[0] == '/';
 }
 
@@ -79,14 +83,6 @@ char* path_extend_internal(char **x, ...);
 
 char* path_simplify(char *path);
 
-enum {
-        PATH_CHECK_FATAL    = 1 << 0,  /* If not set, then error message is appended with 'ignoring'. */
-        PATH_CHECK_ABSOLUTE = 1 << 1,
-        PATH_CHECK_RELATIVE = 1 << 2,
-};
-
-int path_simplify_and_warn(char *path, unsigned flag, const char *unit, const char *filename, unsigned line, const char *lvalue);
-
 static inline bool path_equal_ptr(const char *a, const char *b) {
         return !!a == !!b && (!a || path_equal(a, b));
 }
@@ -167,10 +163,10 @@ int path_extract_directory(const char *path, char **ret);
 bool filename_is_valid(const char *p) _pure_;
 bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_;
 static inline bool path_is_valid(const char *p) {
-        return path_is_valid_full(p, true);
+        return path_is_valid_full(p, /* accept_dot_dot= */ true);
 }
 static inline bool path_is_safe(const char *p) {
-        return path_is_valid_full(p, false);
+        return path_is_valid_full(p, /* accept_dot_dot= */ false);
 }
 bool path_is_normalized(const char *p) _pure_;
 
@@ -183,8 +179,6 @@ bool is_device_path(const char *path);
 bool valid_device_node_path(const char *path);
 bool valid_device_allow_pattern(const char *path);
 
-int systemd_installation_has_version(const char *root, unsigned minimal_version);
-
 bool dot_or_dot_dot(const char *path);
 
 static inline const char *skip_dev_prefix(const char *p) {
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 83e3a71e..1c23a2f8 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -1034,7 +1034,7 @@ bool oom_score_adjust_is_valid(int oa) {
 }
 
 unsigned long personality_from_string(const char *p) {
-        int architecture;
+        Architecture architecture;
 
         if (!p)
                 return PERSONALITY_INVALID;
@@ -1058,7 +1058,7 @@ unsigned long personality_from_string(const char *p) {
 }
 
 const char* personality_to_string(unsigned long p) {
-        int architecture = _ARCHITECTURE_INVALID;
+        Architecture architecture = _ARCHITECTURE_INVALID;
 
         if (p == PER_LINUX)
                 architecture = native_architecture();
@@ -1156,7 +1156,7 @@ void reset_cached_pid(void) {
 
 pid_t getpid_cached(void) {
         static bool installed = false;
-        pid_t current_value;
+        pid_t current_value = CACHED_PID_UNSET;
 
         /* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a
          * system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally
@@ -1167,7 +1167,13 @@ pid_t getpid_cached(void) {
          * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
          */
 
-        current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY);
+        __atomic_compare_exchange_n(
+                        &cached_pid,
+                        &current_value,
+                        CACHED_PID_BUSY,
+                        false,
+                        __ATOMIC_SEQ_CST,
+                        __ATOMIC_SEQ_CST);
 
         switch (current_value) {
 
@@ -1589,6 +1595,30 @@ bool invoked_as(char *argv[], const char *token) {
         return strstr(last_path_component(argv[0]), token);
 }
 
+bool invoked_by_systemd(void) {
+        int r;
+
+        /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
+         * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
+        const char *e = getenv("SYSTEMD_EXEC_PID");
+        if (!e)
+                return false;
+
+        if (streq(e, "*"))
+                /* For testing. */
+                return true;
+
+        pid_t p;
+        r = parse_pid(e, &p);
+        if (r < 0) {
+                /* We know that systemd sets the variable correctly. Something else must have set it. */
+                log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e);
+                return false;
+        }
+
+        return getpid_cached() == p;
+}
+
 _noreturn_ void freeze(void) {
         log_close();
 
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index c488c6a8..962f12d1 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -192,6 +192,8 @@ int setpriority_closest(int priority);
 
 bool invoked_as(char *argv[], const char *token);
 
+bool invoked_by_systemd(void);
+
 _noreturn_ void freeze(void);
 
 bool argv_looks_like_help(int argc, char **argv);
diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c
index e3f4ee72..d576bff1 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.c
+++ b/src/libnm-systemd-shared/src/basic/random-util.c
@@ -32,192 +32,142 @@
 #include "missing_syscall.h"
 #include "parse-util.h"
 #include "random-util.h"
-#include "siphash24.h"
+#include "sha256.h"
 #include "time-util.h"
 
-static bool srand_called = false;
+/* This is a "best effort" kind of thing, but has no real security value.
+ * So, this should only be used by random_bytes(), which is not meant for
+ * crypto. This could be made better, but we're *not* trying to roll a
+ * userspace prng here, or even have forward secrecy, but rather just do
+ * the shortest thing that is at least better than libc rand(). */
+static void fallback_random_bytes(void *p, size_t n) {
+        static thread_local uint64_t fallback_counter = 0;
+        struct {
+                char label[32];
+                uint64_t call_id, block_id;
+                usec_t stamp_mono, stamp_real;
+                pid_t pid, tid;
+                uint8_t auxval[16];
+        } state = {
+                /* Arbitrary domain separation to prevent other usage of AT_RANDOM from clashing. */
+                .label = "systemd fallback random bytes v1",
+                .call_id = fallback_counter++,
+                .stamp_mono = now(CLOCK_MONOTONIC),
+                .stamp_real = now(CLOCK_REALTIME),
+                .pid = getpid(),
+                .tid = gettid()
+        };
 
-int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
-        static int have_syscall = -1;
-        _cleanup_close_ int fd = -1;
-
-        /* Gathers some high-quality randomness from the kernel. This call won't block, unless the RANDOM_BLOCK
-         * flag is set. If it doesn't block, it will still always return some data from the kernel, regardless
-         * of whether the random pool is fully initialized or not. When creating cryptographic key material you
-         * should always use RANDOM_BLOCK. */
-
-        if (n == 0)
-                return 0;
-
-        /* Use the getrandom() syscall unless we know we don't have it. */
-        if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
-                for (;;) {
-#if HAVE_GETRANDOM
-                        ssize_t l = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_INSECURE);
-#else
-                        /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround.
-                         * If you don't compile against a libc that provides getrandom(), you don't get it. */
-                        ssize_t l = -1;
-                        errno = ENOSYS;
+#if HAVE_SYS_AUXV_H
+        memcpy(state.auxval, ULONG_TO_PTR(getauxval(AT_RANDOM)), sizeof(state.auxval));
 #endif
 
-                        if (l > 0) {
-                                have_syscall = true;
-
-                                if ((size_t) l == n)
-                                        return 0; /* Yay, success! */
-
-                                /* We didn't get enough data, so try again */
-                                assert((size_t) l < n);
-                                p = (uint8_t*) p + l;
-                                n -= l;
-                                continue;
-
-                        } else if (l == 0) {
-                                have_syscall = true;
-                                return -EIO;
-
-                        } else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
-                                /* We lack the syscall, continue with reading from /dev/urandom. */
-                                have_syscall = false;
-                                break;
-
-                        } else if (errno == EINVAL) {
-                                /* If we previously passed GRND_INSECURE, and this flag isn't known, then
-                                 * we're likely running an old kernel which has getrandom() but not
-                                 * GRND_INSECURE. In this case, fall back to /dev/urandom. */
-                                if (!FLAGS_SET(flags, RANDOM_BLOCK))
-                                        break;
-
-                                return -errno;
-                        } else
-                                return -errno;
+        while (n > 0) {
+                struct sha256_ctx ctx;
+
+                sha256_init_ctx(&ctx);
+                sha256_process_bytes(&state, sizeof(state), &ctx);
+                if (n < SHA256_DIGEST_SIZE) {
+                        uint8_t partial[SHA256_DIGEST_SIZE];
+                        sha256_finish_ctx(&ctx, partial);
+                        memcpy(p, partial, n);
+                        break;
                 }
+                sha256_finish_ctx(&ctx, p);
+                p = (uint8_t *) p + SHA256_DIGEST_SIZE;
+                n -= SHA256_DIGEST_SIZE;
+                ++state.block_id;
         }
-
-        fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return errno == ENOENT ? -ENOSYS : -errno;
-
-        return loop_read_exact(fd, p, n, true);
 }
 
-static void clear_srand_initialization(void) {
-        srand_called = false;
-}
+void random_bytes(void *p, size_t n) {
+        static bool have_getrandom = true, have_grndinsecure = true;
+        _cleanup_close_ int fd = -1;
 
-void initialize_srand(void) {
-        static bool pthread_atfork_registered = false;
-        unsigned x;
-#if HAVE_SYS_AUXV_H
-        const void *auxv;
-#endif
-        if (srand_called)
+        if (n == 0)
                 return;
 
-#if HAVE_SYS_AUXV_H
-        /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed
-         * the pseudo-random generator. It's better than nothing... But let's first hash it to make it harder
-         * to recover the original value by watching any pseudo-random bits we generate. After all the
-         * AT_RANDOM data might be used by other stuff too (in particular: ASLR), and we probably shouldn't
-         * leak the seed for that. */
-
-        auxv = ULONG_TO_PTR(getauxval(AT_RANDOM));
-        if (auxv) {
-                static const uint8_t auxval_hash_key[16] = {
-                        0x92, 0x6e, 0xfe, 0x1b, 0xcf, 0x00, 0x52, 0x9c, 0xcc, 0x42, 0xcf, 0xdc, 0x94, 0x1f, 0x81, 0x0f
-                };
-
-                x = (unsigned) siphash24(auxv, 16, auxval_hash_key);
-        } else
-#endif
-                x = 0;
-
-        x ^= (unsigned) now(CLOCK_REALTIME);
-        x ^= (unsigned) gettid();
-
-        srand(x);
-        srand_called = true;
-
-        if (!pthread_atfork_registered) {
-                (void) pthread_atfork(NULL, NULL, clear_srand_initialization);
-                pthread_atfork_registered = true;
+        for (;;) {
+                ssize_t l;
+
+                if (!have_getrandom)
+                        break;
+
+                l = getrandom(p, n, have_grndinsecure ? GRND_INSECURE : GRND_NONBLOCK);
+                if (l > 0) {
+                        if ((size_t) l == n)
+                                return; /* Done reading, success. */
+                        p = (uint8_t *) p + l;
+                        n -= l;
+                        continue; /* Interrupted by a signal; keep going. */
+                } else if (l == 0)
+                        break; /* Weird, so fallback to /dev/urandom. */
+                else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+                        have_getrandom = false;
+                        break; /* No syscall, so fallback to /dev/urandom. */
+                } else if (errno == EINVAL && have_grndinsecure) {
+                        have_grndinsecure = false;
+                        continue; /* No GRND_INSECURE; fallback to GRND_NONBLOCK. */
+                } else if (errno == EAGAIN && !have_grndinsecure)
+                        break; /* Will block, but no GRND_INSECURE, so fallback to /dev/urandom. */
+
+                break; /* Unexpected, so just give up and fallback to /dev/urandom. */
         }
-}
-
-/* INT_MAX gives us only 31 bits, so use 24 out of that. */
-#if RAND_MAX >= INT_MAX
-assert_cc(RAND_MAX >= 16777215);
-#  define RAND_STEP 3
-#else
-/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */
-assert_cc(RAND_MAX >= 255);
-#  define RAND_STEP 1
-#endif
 
-void pseudo_random_bytes(void *p, size_t n) {
-        uint8_t *q;
-
-        /* This returns pseudo-random data using libc's rand() function. You probably never want to call this
-         * directly, because why would you use this if you can get better stuff cheaply? Use random_bytes()
-         * instead, see below: it will fall back to this function if there's nothing better to get, but only
-         * then. */
+        fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd >= 0 && loop_read_exact(fd, p, n, false) == 0)
+                return;
 
-        initialize_srand();
+        /* This is a terrible fallback. Oh well. */
+        fallback_random_bytes(p, n);
+}
 
-        for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) {
-                unsigned rr;
+int crypto_random_bytes(void *p, size_t n) {
+        static bool have_getrandom = true, seen_initialized = false;
+        _cleanup_close_ int fd = -1;
 
-                rr = (unsigned) rand();
+        if (n == 0)
+                return 0;
 
-#if RAND_STEP >= 3
-                if ((size_t) (q - (uint8_t*) p + 2) < n)
-                        q[2] = rr >> 16;
-#endif
-#if RAND_STEP >= 2
-                if ((size_t) (q - (uint8_t*) p + 1) < n)
-                        q[1] = rr >> 8;
-#endif
-                q[0] = rr;
+        for (;;) {
+                ssize_t l;
+
+                if (!have_getrandom)
+                        break;
+
+                l = getrandom(p, n, 0);
+                if (l > 0) {
+                        if ((size_t) l == n)
+                                return 0; /* Done reading, success. */
+                        p = (uint8_t *) p + l;
+                        n -= l;
+                        continue; /* Interrupted by a signal; keep going. */
+                } else if (l == 0)
+                        return -EIO; /* Weird, should never happen. */
+                else if (ERRNO_IS_NOT_SUPPORTED(errno)) {
+                        have_getrandom = false;
+                        break; /* No syscall, so fallback to /dev/urandom. */
+                }
+                return -errno;
         }
-}
 
-void random_bytes(void *p, size_t n) {
+        if (!seen_initialized) {
+                _cleanup_close_ int ready_fd = -1;
+                int r;
 
-        /* This returns high quality randomness if we can get it cheaply. If we can't because for some reason
-         * it is not available we'll try some crappy fallbacks.
-         *
-         * What this function will do:
-         *
-         *         • Use getrandom(GRND_INSECURE) or /dev/urandom, to return high-quality random values if
-         *           they are cheaply available, or less high-quality random values if they are not.
-         *
-         *         • This function will return pseudo-random data, generated via libc rand() if nothing
-         *           better is available.
-         *
-         *         • This function will work fine in early boot
-         *
-         *         • This function will always succeed
-         *
-         * What this function won't do:
-         *
-         *         • This function will never fail: it will give you randomness no matter what. It might not
-         *           be high quality, but it will return some, possibly generated via libc's rand() call.
-         *
-         *         • This function will never block: if the only way to get good randomness is a blocking,
-         *           synchronous getrandom() we'll instead provide you with pseudo-random data.
-         *
-         * This function is hence great for things like seeding hash tables, generating random numeric UNIX
-         * user IDs (that are checked for collisions before use) and such.
-         *
-         * This function is hence not useful for generating UUIDs or cryptographic key material.
-         */
-
-        if (genuine_random_bytes(p, n, 0) >= 0)
-                return;
+                ready_fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (ready_fd < 0)
+                        return -errno;
+                r = fd_wait_for_event(ready_fd, POLLIN, USEC_INFINITY);
+                if (r < 0)
+                        return r;
+                seen_initialized = true;
+        }
 
-        /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
-        pseudo_random_bytes(p, n);
+        fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fd < 0)
+                return -errno;
+        return loop_read_exact(fd, p, n, false);
 }
 
 #if 0 /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/random-util.h b/src/libnm-systemd-shared/src/basic/random-util.h
index ccee3279..2d998072 100644
--- a/src/libnm-systemd-shared/src/basic/random-util.h
+++ b/src/libnm-systemd-shared/src/basic/random-util.h
@@ -5,15 +5,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
-typedef enum RandomFlags {
-        RANDOM_BLOCK              = 1 << 0, /* Rather block than return crap randomness (only if the kernel supports that) */
-} RandomFlags;
-
-int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */
-void pseudo_random_bytes(void *p, size_t n);                    /* returns only pseudo-randommess (but possibly seeded from something better) */
-void random_bytes(void *p, size_t n);                           /* returns genuine randomness if cheaply available, and pseudo randomness if not. */
-
-void initialize_srand(void);
+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. */
 
 static inline uint64_t random_u64(void) {
         uint64_t u;
diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h
index 5cae1316..618e7297 100644
--- a/src/libnm-systemd-shared/src/basic/set.h
+++ b/src/libnm-systemd-shared/src/basic/set.h
@@ -6,12 +6,7 @@
 #include "macro.h"
 
 #define set_free_and_replace(a, b)              \
-        ({                                      \
-                set_free(a);                    \
-                (a) = (b);                      \
-                (b) = NULL;                     \
-                0;                              \
-        })
+        free_and_replace_full(a, b, set_free)
 
 Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
 #define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
@@ -127,9 +122,12 @@ int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key  HAS
 
 int set_consume(Set *s, void *value);
 
-int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p  HASHMAP_DEBUG_PARAMS);
-#define set_put_strdup_full(s, hash_ops, p) _set_put_strdup_full(s, hash_ops, p  HASHMAP_DEBUG_SRC_ARGS)
-#define set_put_strdup(s, p) set_put_strdup_full(s, &string_hash_ops_free, p)
+int _set_put_strndup_full(Set **s, const struct hash_ops *hash_ops, const char *p, size_t n  HASHMAP_DEBUG_PARAMS);
+#define set_put_strndup_full(s, hash_ops, p, n) _set_put_strndup_full(s, hash_ops, p, n  HASHMAP_DEBUG_SRC_ARGS)
+#define set_put_strdup_full(s, hash_ops, p) set_put_strndup_full(s, hash_ops, p, SIZE_MAX)
+#define set_put_strndup(s, p, n) set_put_strndup_full(s, &string_hash_ops_free, p, n)
+#define set_put_strdup(s, p) set_put_strndup(s, p, SIZE_MAX)
+
 int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l  HASHMAP_DEBUG_PARAMS);
 #define set_put_strdupv_full(s, hash_ops, l) _set_put_strdupv_full(s, hash_ops, l  HASHMAP_DEBUG_SRC_ARGS)
 #define set_put_strdupv(s, l) set_put_strdupv_full(s, &string_hash_ops_free, l)
@@ -153,3 +151,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free);
 int set_strjoin(Set *s, const char *separator, bool wrap_with_separator, char **ret);
 
 bool set_equal(Set *a, Set *b);
+
+bool set_fnmatch(Set *include_patterns, Set *exclude_patterns, const char *needle);
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c
index 6478fdee..c155e352 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.c
+++ b/src/libnm-systemd-shared/src/basic/socket-util.c
@@ -495,9 +495,7 @@ int sockaddr_pretty(
                         if (r < 0)
                                 return -ENOMEM;
                 } else {
-                        char a[INET6_ADDRSTRLEN];
-
-                        inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
+                        const char *a = IN6_ADDR_TO_STRING(&sa->in6.sin6_addr);
 
                         if (include_port) {
                                 if (asprintf(&p,
@@ -656,24 +654,24 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
 }
 
 static const char* const netlink_family_table[] = {
-        [NETLINK_ROUTE] = "route",
-        [NETLINK_FIREWALL] = "firewall",
-        [NETLINK_INET_DIAG] = "inet-diag",
-        [NETLINK_NFLOG] = "nflog",
-        [NETLINK_XFRM] = "xfrm",
-        [NETLINK_SELINUX] = "selinux",
-        [NETLINK_ISCSI] = "iscsi",
-        [NETLINK_AUDIT] = "audit",
-        [NETLINK_FIB_LOOKUP] = "fib-lookup",
-        [NETLINK_CONNECTOR] = "connector",
-        [NETLINK_NETFILTER] = "netfilter",
-        [NETLINK_IP6_FW] = "ip6-fw",
-        [NETLINK_DNRTMSG] = "dnrtmsg",
+        [NETLINK_ROUTE]          = "route",
+        [NETLINK_FIREWALL]       = "firewall",
+        [NETLINK_INET_DIAG]      = "inet-diag",
+        [NETLINK_NFLOG]          = "nflog",
+        [NETLINK_XFRM]           = "xfrm",
+        [NETLINK_SELINUX]        = "selinux",
+        [NETLINK_ISCSI]          = "iscsi",
+        [NETLINK_AUDIT]          = "audit",
+        [NETLINK_FIB_LOOKUP]     = "fib-lookup",
+        [NETLINK_CONNECTOR]      = "connector",
+        [NETLINK_NETFILTER]      = "netfilter",
+        [NETLINK_IP6_FW]         = "ip6-fw",
+        [NETLINK_DNRTMSG]        = "dnrtmsg",
         [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
-        [NETLINK_GENERIC] = "generic",
-        [NETLINK_SCSITRANSPORT] = "scsitransport",
-        [NETLINK_ECRYPTFS] = "ecryptfs",
-        [NETLINK_RDMA] = "rdma",
+        [NETLINK_GENERIC]        = "generic",
+        [NETLINK_SCSITRANSPORT]  = "scsitransport",
+        [NETLINK_ECRYPTFS]       = "ecryptfs",
+        [NETLINK_RDMA]           = "rdma",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
@@ -780,10 +778,10 @@ int fd_set_rcvbuf(int fd, size_t n, bool increase) {
 }
 
 static const char* const ip_tos_table[] = {
-        [IPTOS_LOWDELAY] = "low-delay",
-        [IPTOS_THROUGHPUT] = "throughput",
+        [IPTOS_LOWDELAY]    = "low-delay",
+        [IPTOS_THROUGHPUT]  = "throughput",
         [IPTOS_RELIABILITY] = "reliability",
-        [IPTOS_LOWCOST] = "low-cost",
+        [IPTOS_LOWCOST]     = "low-cost",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
@@ -841,7 +839,7 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
                 if (!ifname_valid_char(*t))
                         return false;
 
-                numeric = numeric && (*t >= '0' && *t <= '9');
+                numeric = numeric && ascii_isdigit(*t);
         }
 
         /* It's fully numeric but didn't parse as valid ifindex above? if so, it must be too large or zero or
@@ -1248,7 +1246,10 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
          * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that
          * do not expect non-NUL terminated file system path. */
         if (l+1 > sizeof(ret->sun_path))
-                return -EINVAL;
+                return path[0] == '@' ? -EINVAL : -ENAMETOOLONG; /* return a recognizable error if this is
+                                                                  * too long to fit into a sockaddr_un, but
+                                                                  * is a file system path, and thus might be
+                                                                  * connectible via O_PATH indirection. */
 
         *ret = (struct sockaddr_un) {
                 .sun_family = AF_UNIX,
@@ -1437,3 +1438,51 @@ int socket_get_mtu(int fd, int af, size_t *ret) {
         return 0;
 }
 #endif /* NM_IGNORED */
+
+int connect_unix_path(int fd, int dir_fd, const char *path) {
+        _cleanup_close_ int inode_fd = -1;
+        union sockaddr_union sa = {
+                .un.sun_family = AF_UNIX,
+        };
+        size_t path_len;
+        socklen_t salen;
+
+        assert(fd >= 0);
+        assert(dir_fd == AT_FDCWD || dir_fd >= 0);
+        assert(path);
+
+        /* Connects to the specified AF_UNIX socket in the file system. Works around the 108 byte size limit
+         * in sockaddr_un, by going via O_PATH if needed. This hence works for any kind of path. */
+
+        path_len = strlen(path);
+
+        /* Refuse zero length path early, to make sure AF_UNIX stack won't mistake this for an abstract
+         * namespace path, since first char is NUL */
+        if (path_len <= 0)
+                return -EINVAL;
+
+        if (dir_fd == AT_FDCWD && path_len < sizeof(sa.un.sun_path)) {
+                memcpy(sa.un.sun_path, path, path_len + 1);
+                salen = offsetof(struct sockaddr_un, sun_path) + path_len + 1;
+        } else {
+                const char *proc;
+                size_t proc_len;
+
+                /* If dir_fd is specified, then we need to go the indirect O_PATH route, because connectat()
+                 * does not exist. If the path is too long, we also need to take the indirect route, since we
+                 * can't fit this into a sockaddr_un directly. */
+
+                inode_fd = openat(dir_fd, path, O_PATH|O_CLOEXEC);
+                if (inode_fd < 0)
+                        return -errno;
+
+                proc = FORMAT_PROC_FD_PATH(inode_fd);
+                proc_len = strlen(proc);
+
+                assert(proc_len < sizeof(sa.un.sun_path));
+                memcpy(sa.un.sun_path, proc, proc_len + 1);
+                salen = offsetof(struct sockaddr_un, sun_path) + proc_len + 1;
+        }
+
+        return RET_NERRNO(connect(fd, &sa.sa, salen));
+}
diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h
index 709da206..cf9a0b35 100644
--- a/src/libnm-systemd-shared/src/basic/socket-util.h
+++ b/src/libnm-systemd-shared/src/basic/socket-util.h
@@ -131,7 +131,7 @@ static inline int fd_inc_sndbuf(int fd, size_t n) {
         return fd_set_sndbuf(fd, n, true);
 }
 int fd_set_rcvbuf(int fd, size_t n, bool increase);
-static inline int fd_inc_rcvbuf(int fd, size_t n) {
+static inline int fd_increase_rxbuf(int fd, size_t n) {
         return fd_set_rcvbuf(fd, n, true);
 }
 
@@ -226,9 +226,9 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
                          strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
         })
 
-#define SOCKADDR_LEN(sa)                                                \
+#define SOCKADDR_LEN(saddr)                                             \
         ({                                                              \
-                const union sockaddr_union *__sa = &(sa);               \
+                const union sockaddr_union *__sa = &(saddr);            \
                 size_t _len;                                            \
                 switch (__sa->sa.sa_family) {                           \
                 case AF_INET:                                           \
@@ -336,3 +336,5 @@ int socket_get_mtu(int fd, int af, size_t *ret);
 
 /* an initializer for struct ucred that initialized all fields to the invalid value appropriate for each */
 #define UCRED_INVALID { .pid = 0, .uid = UID_INVALID, .gid = GID_INVALID }
+
+int connect_unix_path(int fd, int dir_fd, const char *path);
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index fd32f25d..8c5858ff 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -39,16 +39,17 @@ int is_symlink(const char *path) {
 }
 #endif /* NM_IGNORED */
 
-int is_dir(const char* path, bool follow) {
+int is_dir_full(int atfd, const char* path, bool follow) {
         struct stat st;
         int r;
 
-        assert(path);
+        assert(atfd >= 0 || atfd == AT_FDCWD);
+        assert(atfd >= 0 || path);
 
-        if (follow)
-                r = stat(path, &st);
+        if (path)
+                r = fstatat(atfd, path, &st, follow ? 0 : AT_SYMLINK_NOFOLLOW);
         else
-                r = lstat(path, &st);
+                r = fstat(atfd, &st);
         if (r < 0)
                 return -errno;
 
@@ -56,15 +57,6 @@ int is_dir(const char* path, bool follow) {
 }
 
 #if 0 /* NM_IGNORED */
-int is_dir_fd(int fd) {
-        struct stat st;
-
-        if (fstat(fd, &st) < 0)
-                return -errno;
-
-        return !!S_ISDIR(st.st_mode);
-}
-
 int is_device_node(const char *path) {
         struct stat info;
 
@@ -76,13 +68,10 @@ int is_device_node(const char *path) {
         return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
 }
 
-int dir_is_empty_at(int dir_fd, const char *path) {
+int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
         _cleanup_close_ int fd = -1;
-        /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("."  +
-         * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */
-        DEFINE_DIRENT_BUFFER(buffer, 3);
-        struct dirent *de;
-        ssize_t n;
+        struct dirent *buf;
+        size_t m;
 
         if (path) {
                 assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
@@ -104,15 +93,30 @@ int dir_is_empty_at(int dir_fd, const char *path) {
                         return fd;
         }
 
-        n = getdents64(fd, &buffer, sizeof(buffer));
-        if (n < 0)
-                return -errno;
+        /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("."  +
+         * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If
+         * 'ignore_hidden_or_backup' is true we'll allocate a bit more, since we might skip over a bunch of
+         * entries that we end up ignoring. */
+        m = (ignore_hidden_or_backup ? 16 : 3) * DIRENT_SIZE_MAX;
+        buf = alloca(m);
+
+        for (;;) {
+                struct dirent *de;
+                ssize_t n;
+
+                n = getdents64(fd, buf, m);
+                if (n < 0)
+                        return -errno;
+                if (n == 0)
+                        break;
 
-        msan_unpoison(&buffer, n);
+                assert((size_t) n <= m);
+                msan_unpoison(buf, n);
 
-        FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n)
-                if (!dot_or_dot_dot(de->d_name))
-                        return 0;
+                FOREACH_DIRENT_IN_BUFFER(de, buf, n)
+                        if (!(ignore_hidden_or_backup ? hidden_or_backup_file(de->d_name) : dot_or_dot_dot(de->d_name)))
+                                return 0;
+        }
 
         return 1;
 }
@@ -324,101 +328,6 @@ int fd_verify_directory(int fd) {
 
         return stat_verify_directory(&st);
 }
-
-int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
-        const char *t;
-
-        /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
-
-        if (S_ISCHR(mode))
-                t = "char";
-        else if (S_ISBLK(mode))
-                t = "block";
-        else
-                return -ENODEV;
-
-        if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
-                return -ENOMEM;
-
-        return 0;
-}
-
-int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
-        _cleanup_free_ char *p = NULL;
-        int r;
-
-        /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
-
-        assert(ret);
-
-        if (major(devno) == 0 && minor(devno) == 0) {
-                char *s;
-
-                /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
-                 * /dev/block/ and /dev/char/, hence we handle them specially here. */
-
-                if (S_ISCHR(mode))
-                        s = strdup("/run/systemd/inaccessible/chr");
-                else if (S_ISBLK(mode))
-                        s = strdup("/run/systemd/inaccessible/blk");
-                else
-                        return -ENODEV;
-
-                if (!s)
-                        return -ENOMEM;
-
-                *ret = s;
-                return 0;
-        }
-
-        r = device_path_make_major_minor(mode, devno, &p);
-        if (r < 0)
-                return r;
-
-        return chase_symlinks(p, NULL, 0, ret, NULL);
-}
-
-int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
-        mode_t mode;
-        dev_t devno;
-        int r;
-
-        /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
-         * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
-         * path cannot be parsed like this.  */
-
-        if (path_equal(path, "/run/systemd/inaccessible/chr")) {
-                mode = S_IFCHR;
-                devno = makedev(0, 0);
-        } else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
-                mode = S_IFBLK;
-                devno = makedev(0, 0);
-        } else {
-                const char *w;
-
-                w = path_startswith(path, "/dev/block/");
-                if (w)
-                        mode = S_IFBLK;
-                else {
-                        w = path_startswith(path, "/dev/char/");
-                        if (!w)
-                                return -ENODEV;
-
-                        mode = S_IFCHR;
-                }
-
-                r = parse_dev(w, &devno);
-                if (r < 0)
-                        return r;
-        }
-
-        if (ret_mode)
-                *ret_mode = mode;
-        if (ret_devno)
-                *ret_devno = devno;
-
-        return 0;
-}
 #endif /* NM_IGNORED */
 
 int proc_mounted(void) {
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h
index 37513a43..56f15534 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.h
+++ b/src/libnm-systemd-shared/src/basic/stat-util.h
@@ -13,21 +13,18 @@
 #include "missing_stat.h"
 
 int is_symlink(const char *path);
-int is_dir(const char *path, bool follow);
-int is_dir_fd(int fd);
-int is_device_node(const char *path);
-
-int dir_is_empty_at(int dir_fd, const char *path);
-static inline int dir_is_empty(const char *path) {
-        return dir_is_empty_at(AT_FDCWD, path);
+int is_dir_full(int atfd, const char *fname, bool follow);
+static inline int is_dir(const char *path, bool follow) {
+        return is_dir_full(AT_FDCWD, path, follow);
 }
+static inline int is_dir_fd(int fd) {
+        return is_dir_full(fd, NULL, false);
+}
+int is_device_node(const char *path);
 
-static inline int dir_is_populated(const char *path) {
-        int r;
-        r = dir_is_empty(path);
-        if (r < 0)
-                return r;
-        return !r;
+int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup);
+static inline int dir_is_empty(const char *path, bool ignore_hidden_or_backup) {
+        return dir_is_empty_at(AT_FDCWD, path, ignore_hidden_or_backup);
 }
 
 bool null_or_empty(struct stat *st) _pure_;
@@ -71,29 +68,6 @@ int fd_verify_regular(int fd);
 int stat_verify_directory(const struct stat *st);
 int fd_verify_directory(int fd);
 
-/* 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
- * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
- * such a test would be pointless in such a case.) */
-
-#define DEVICE_MAJOR_VALID(x)                                           \
-        ({                                                              \
-                typeof(x) _x = (x), _y = 0;                             \
-                _x >= _y && _x < (UINT32_C(1) << 12);                   \
-                                                                        \
-        })
-
-#define DEVICE_MINOR_VALID(x)                                           \
-        ({                                                              \
-                typeof(x) _x = (x), _y = 0;                             \
-                _x >= _y && _x < (UINT32_C(1) << 20);                   \
-        })
-
-int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
-int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
-int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
-
 int proc_mounted(void);
 
 bool stat_inode_same(const struct stat *a, const struct stat *b);
@@ -119,9 +93,3 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
                 struct new_statx nsx;           \
         } var
 #endif
-
-static inline bool devid_set_and_equal(dev_t a, dev_t b) {
-        /* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
-         * know" and we'll return false */
-        return a == b && a != 0;
-}
diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c
index fea22ec8..7cd4b055 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.c
+++ b/src/libnm-systemd-shared/src/basic/string-util.c
@@ -1171,4 +1171,31 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
 
         return in_charset(s1, ok) && in_charset(s2, ok);
 }
+
+char *string_replace_char(char *str, char old_char, char new_char) {
+        assert(str);
+        assert(old_char != '\0');
+        assert(new_char != '\0');
+        assert(old_char != new_char);
+
+        for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
+                *p = new_char;
+
+        return str;
+}
+
+size_t strspn_from_end(const char *str, const char *accept) {
+        size_t n = 0;
+
+        if (isempty(str))
+                return 0;
+
+        if (isempty(accept))
+                return 0;
+
+        for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
+                n++;
+
+        return n;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h
index a1d88fbb..1dd46f7f 100644
--- a/src/libnm-systemd-shared/src/basic/string-util.h
+++ b/src/libnm-systemd-shared/src/basic/string-util.h
@@ -10,17 +10,18 @@
 #include "string-util-fundamental.h"
 
 /* What is interpreted as whitespace? */
-#define WHITESPACE        " \t\n\r"
-#define NEWLINE           "\n\r"
-#define QUOTES            "\"\'"
-#define COMMENTS          "#;"
-#define GLOB_CHARS        "*?["
-#define DIGITS            "0123456789"
-#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
-#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define LETTERS           LOWERCASE_LETTERS UPPERCASE_LETTERS
-#define ALPHANUMERICAL    LETTERS DIGITS
-#define HEXDIGITS         DIGITS "abcdefABCDEF"
+#define WHITESPACE          " \t\n\r"
+#define NEWLINE             "\n\r"
+#define QUOTES              "\"\'"
+#define COMMENTS            "#;"
+#define GLOB_CHARS          "*?["
+#define DIGITS              "0123456789"
+#define LOWERCASE_LETTERS   "abcdefghijklmnopqrstuvwxyz"
+#define UPPERCASE_LETTERS   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define LETTERS             LOWERCASE_LETTERS UPPERCASE_LETTERS
+#define ALPHANUMERICAL      LETTERS DIGITS
+#define HEXDIGITS           DIGITS "abcdefABCDEF"
+#define LOWERCASE_HEXDIGITS DIGITS "abcdef"
 
 static inline char* strstr_ptr(const char *haystack, const char *needle) {
         if (!haystack || !needle)
@@ -28,10 +29,6 @@ static inline char* strstr_ptr(const char *haystack, const char *needle) {
         return strstr(haystack, needle);
 }
 
-static inline const char* strempty(const char *s) {
-        return s ?: "";
-}
-
 static inline const char* strnull(const char *s) {
         return s ?: "(null)";
 }
@@ -180,13 +177,6 @@ int free_and_strndup(char **p, const char *s, size_t l);
 
 bool string_is_safe(const char *p) _pure_;
 
-static inline size_t strlen_ptr(const char *s) {
-        if (!s)
-                return 0;
-
-        return strlen(s);
-}
-
 DISABLE_WARNING_STRINGOP_TRUNCATION;
 static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
         strncpy(buf, src, buf_len);
@@ -232,3 +222,7 @@ static inline int string_contains_word(const char *string, const char *separator
 }
 
 bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
+
+char *string_replace_char(char *str, char old_char, char new_char);
+
+size_t strspn_from_end(const char *str, const char *accept);
diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c
index f2cb80fe..d9c58633 100644
--- a/src/libnm-systemd-shared/src/basic/strv.c
+++ b/src/libnm-systemd-shared/src/basic/strv.c
@@ -357,7 +357,7 @@ int strv_split_colon_pairs(char ***t, const char *s) {
 }
 #endif /* NM_IGNORED */
 
-char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
+char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) {
         char *r, *e;
         size_t n, k, m;
 
@@ -367,7 +367,7 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix,
         k = strlen(separator);
         m = strlen_ptr(prefix);
 
-        if (unescape_separators) /* If there separator is multi-char, we won't know how to escape it. */
+        if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */
                 assert(k == 1);
 
         n = 0;
@@ -375,7 +375,7 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix,
                 if (s != l)
                         n += k;
 
-                bool needs_escaping = unescape_separators && strchr(*s, separator[0]);
+                bool needs_escaping = escape_separator && strchr(*s, *separator);
 
                 n += m + strlen(*s) * (1 + needs_escaping);
         }
@@ -392,11 +392,11 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix,
                 if (prefix)
                         e = stpcpy(e, prefix);
 
-                bool needs_escaping = unescape_separators && strchr(*s, separator[0]);
+                bool needs_escaping = escape_separator && strchr(*s, *separator);
 
                 if (needs_escaping)
                         for (size_t i = 0; (*s)[i]; i++) {
-                                if ((*s)[i] == separator[0])
+                                if ((*s)[i] == *separator)
                                         *(e++) = '\\';
                                 *(e++) = (*s)[i];
                         }
@@ -409,27 +409,33 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix,
         return r;
 }
 
-int strv_push(char ***l, char *value) {
-        char **c;
-        size_t n;
+int strv_push_with_size(char ***l, size_t *n, char *value) {
+        /* n is a pointer to a variable to store the size of l.
+         * If not given (i.e. n is NULL or *n is SIZE_MAX), size will be calculated using strv_length().
+         * If n is not NULL, the size after the push will be returned.
+         * If value is empty, no action is taken and *n is not set. */
 
         if (!value)
                 return 0;
 
-        n = strv_length(*l);
+        size_t size = n ? *n : SIZE_MAX;
+        if (size == SIZE_MAX)
+                size = strv_length(*l);
 
         /* Check for overflow */
-        if (n > SIZE_MAX-2)
+        if (size > SIZE_MAX-2)
                 return -ENOMEM;
 
-        c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + 2), sizeof(char*));
+        char **c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(size + 2), sizeof(char*));
         if (!c)
                 return -ENOMEM;
 
-        c[n] = value;
-        c[n+1] = NULL;
+        c[size] = value;
+        c[size+1] = NULL;
 
         *l = c;
+        if (n)
+                *n = size + 1;
         return 0;
 }
 
@@ -490,10 +496,10 @@ int strv_insert(char ***l, size_t position, char *value) {
         return free_and_replace(*l, c);
 }
 
-int strv_consume(char ***l, char *value) {
+int strv_consume_with_size(char ***l, size_t *n, char *value) {
         int r;
 
-        r = strv_push(l, value);
+        r = strv_push_with_size(l, n, value);
         if (r < 0)
                 free(value);
 
@@ -535,7 +541,7 @@ int strv_prepend(char ***l, const char *value) {
         return strv_consume_prepend(l, v);
 }
 
-int strv_extend(char ***l, const char *value) {
+int strv_extend_with_size(char ***l, size_t *n, const char *value) {
         char *v;
 
         if (!value)
@@ -545,7 +551,7 @@ int strv_extend(char ***l, const char *value) {
         if (!v)
                 return -ENOMEM;
 
-        return strv_consume(l, v);
+        return strv_consume_with_size(l, n, v);
 }
 
 int strv_extend_front(char ***l, const char *value) {
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index 2a858326..87ec6337 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -35,18 +35,36 @@ size_t strv_length(char * const *l) _pure_;
 int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
 int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix);
 int strv_prepend(char ***l, const char *value);
-int strv_extend(char ***l, const char *value);
+
+/* _with_size() are lower-level functions where the size can be provided externally,
+ * which allows us to skip iterating over the strv to find the end, which saves
+ * a bit of time and reduces the complexity of appending from O(n²) to O(n). */
+
+int strv_extend_with_size(char ***l, size_t *n, const char *value);
+static inline int strv_extend(char ***l, const char *value) {
+        return strv_extend_with_size(l, NULL, value);
+}
+
 int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
 int strv_extend_front(char ***l, const char *value);
-int strv_push(char ***l, char *value);
+
+int strv_push_with_size(char ***l, size_t *n, char *value);
+static inline int strv_push(char ***l, char *value) {
+        return strv_push_with_size(l, NULL, value);
+}
 int strv_push_pair(char ***l, char *a, char *b);
+
 int strv_insert(char ***l, size_t position, char *value);
 
 static inline int strv_push_prepend(char ***l, char *value) {
         return strv_insert(l, 0, value);
 }
 
-int strv_consume(char ***l, char *value);
+int strv_consume_with_size(char ***l, size_t *n, char *value);
+static inline int strv_consume(char ***l, char *value) {
+        return strv_consume_with_size(l, NULL, value);
+}
+
 int strv_consume_pair(char ***l, char *a, char *b);
 int strv_consume_prepend(char ***l, char *value);
 
@@ -77,7 +95,7 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
 static inline char** strv_split(const char *s, const char *separators) {
         char **ret;
 
-        if (strv_split_full(&ret, s, separators, 0) < 0)
+        if (strv_split_full(&ret, s, separators, EXTRACT_RETAIN_ESCAPE) < 0)
                 return NULL;
 
         return ret;
@@ -101,7 +119,7 @@ static inline char** strv_split_newlines(const char *s) {
  * string in the vector is an empty string. */
 int strv_split_colon_pairs(char ***t, const char *s);
 
-char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor);
+char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator);
 static inline char *strv_join(char * const *l, const char *separator) {
         return strv_join_full(l, separator, NULL, false);
 }
@@ -122,12 +140,6 @@ static inline int strv_from_nulstr(char ***a, const char *nulstr) {
 
 bool strv_overlap(char * const *a, char * const *b) _pure_;
 
-#define _STRV_FOREACH(s, l, i)                                          \
-        for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++)
-
-#define STRV_FOREACH(s, l)                      \
-        _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ))
-
 #define _STRV_FOREACH_BACKWARDS(s, l, h, i)                             \
         for (typeof(*(l)) *s, *h = (l), *i = ({                         \
                                 size_t _len = strv_length(h);           \
@@ -246,14 +258,7 @@ int strv_extend_n(char ***l, const char *value, size_t n);
 int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
 
 #define strv_free_and_replace(a, b)             \
-        ({                                      \
-                char ***_a = &(a);              \
-                char ***_b = &(b);              \
-                strv_free(*_a);                 \
-                (*_a) = (*_b);                  \
-                (*_b) = NULL;                   \
-                0;                              \
-        })
+        free_and_replace_full(a, b, strv_free)
 
 extern const struct hash_ops string_strv_hash_ops;
 int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS);
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index 8476a650..0c8f4929 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -31,10 +31,10 @@
 
 static clockid_t map_clock_id(clockid_t c) {
 
-        /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
-         * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
-         * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
-         * those archs. */
+        /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus,
+         * clock_gettime() will fail for them. Since they are essentially the same as their non-ALARM
+         * pendants (their only difference is when timers are set on them), let's just map them
+         * accordingly. This way, we can get the correct time even on those archs. */
 
         switch (c) {
 
@@ -298,8 +298,8 @@ char *format_timestamp_style(
                 usec_t t,
                 TimestampStyle style) {
 
-        /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
-         * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
+        /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that
+         * our generated timestamps may be parsed with parse_timestamp(), and always read the same. */
         static const char * const weekdays[] = {
                 [0] = "Sun",
                 [1] = "Mon",
@@ -386,8 +386,8 @@ char *format_timestamp_style(
         /* Append the timezone */
         n = strlen(buf);
         if (utc) {
-                /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
-                 * obsolete "GMT" instead. */
+                /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r()
+                 * normally uses the obsolete "GMT" instead. */
                 if (n + 5 > l)
                         return NULL; /* "UTC" doesn't fit. */
 
@@ -402,12 +402,14 @@ char *format_timestamp_style(
                         /* The full time zone does not fit in. Yuck. */
 
                         if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
-                                return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
-
-                        /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
-                         * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
-                         * an overly long, hard to read string on the user. This should be safe, because the user will
-                         * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
+                                return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that
+                                              * case, complain that it doesn't fit. */
+
+                        /* So the time zone doesn't fit in fully, but the caller passed enough space for the
+                         * POSIX minimum time zone length. In this case suppress the timezone entirely, in
+                         * order not to dump an overly long, hard to read string on the user. This should be
+                         * safe, because the user will assume the local timezone anyway if none is shown. And
+                         * so does parse_timestamp(). */
                 } else {
                         buf[n++] = ' ';
                         strcpy(buf + n, tm.tm_zone);
@@ -593,7 +595,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
                         t = b;
                 }
 
-                n = MIN((size_t) k, l);
+                n = MIN((size_t) k, l-1);
 
                 l -= n;
                 p += n;
@@ -705,10 +707,11 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
 
                         tzset();
 
-                        /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
-                         * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
-                         * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
-                         * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
+                        /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note
+                         * that we only support the local timezones here, nothing else. Not because we
+                         * wouldn't want to, but simply because there are no nice APIs available to cover
+                         * this. By accepting the local time zone strings, we make sure that all timestamps
+                         * written by format_timestamp() can be parsed correctly, even though we don't
                          * support arbitrary timezone specifications. */
 
                         for (j = 0; j <= 1; j++) {
@@ -1414,9 +1417,8 @@ int verify_timezone(const char *name, int log_level) {
                 return -EINVAL;
 
         for (p = name; *p; p++) {
-                if (!(*p >= '0' && *p <= '9') &&
-                    !(*p >= 'a' && *p <= 'z') &&
-                    !(*p >= 'A' && *p <= 'Z') &&
+                if (!ascii_isdigit(*p) &&
+                    !ascii_isalpha(*p) &&
                     !IN_SET(*p, '-', '_', '+', '/'))
                         return -EINVAL;
 
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 1c198f6a..0ed99403 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -2,36 +2,50 @@
 #pragma once
 
 #ifndef SD_BOOT
-#include <assert.h>
+#  include <assert.h>
 #endif
 
 #include <limits.h>
-#include "types-fundamental.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
 
 #define _align_(x) __attribute__((__aligned__(x)))
+#define _alignas_(x) __attribute__((__aligned__(__alignof__(x))))
+#define _alignptr_ __attribute__((__aligned__(sizeof(void *))))
+#define _cleanup_(x) __attribute__((__cleanup__(x)))
 #define _const_ __attribute__((__const__))
-#define _pure_ __attribute__((__pure__))
-#define _section_(x) __attribute__((__section__(x)))
+#define _deprecated_ __attribute__((__deprecated__))
+#define _destructor_ __attribute__((__destructor__))
+#define _hidden_ __attribute__((__visibility__("hidden")))
+#define _likely_(x) (__builtin_expect(!!(x), 1))
+#define _malloc_ __attribute__((__malloc__))
+#define _noreturn_ _Noreturn
 #define _packed_ __attribute__((__packed__))
+#define _printf_(a, b) __attribute__((__format__(printf, a, b)))
+#define _public_ __attribute__((__visibility__("default")))
+#define _pure_ __attribute__((__pure__))
 #define _retain_ __attribute__((__retain__))
-#define _used_ __attribute__((__used__))
-#define _unused_ __attribute__((__unused__))
-#define _cleanup_(x) __attribute__((__cleanup__(x)))
-#define _likely_(x) (__builtin_expect(!!(x), 1))
+#define _returns_nonnull_ __attribute__((__returns_nonnull__))
+#define _section_(x) __attribute__((__section__(x)))
+#define _sentinel_ __attribute__((__sentinel__))
 #define _unlikely_(x) (__builtin_expect(!!(x), 0))
-#if __GNUC__ >= 7
-#define _fallthrough_ __attribute__((__fallthrough__))
+#define _unused_ __attribute__((__unused__))
+#define _used_ __attribute__((__used__))
+#define _warn_unused_result_ __attribute__((__warn_unused_result__))
+#define _weak_ __attribute__((__weak__))
+#define _weakref_(x) __attribute__((__weakref__(#x)))
+
+#ifdef __clang__
+#  define _alloc_(...)
 #else
-#define _fallthrough_
+#  define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
 #endif
-/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
- * compiler versions */
-#ifndef _noreturn_
-#if __STDC_VERSION__ >= 201112L
-#define _noreturn_ _Noreturn
+
+#if __GNUC__ >= 7 || (defined(__clang__) && __clang_major__ >= 10)
+#  define _fallthrough_ __attribute__((__fallthrough__))
 #else
-#define _noreturn_ __attribute__((__noreturn__))
-#endif
+#  define _fallthrough_
 #endif
 
 #define XSTRINGIFY(x) #x
@@ -53,7 +67,7 @@
 #define CONCATENATE(x, y) XCONCATENATE(x, y)
 
 #ifdef SD_BOOT
-        void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
+        _noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
 
         #ifdef NDEBUG
                 #define assert(expr)
@@ -62,10 +76,8 @@
                 #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
                 #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
         #endif
+        #define static_assert _Static_assert
         #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
-
-        #define memcpy(a, b, c) CopyMem((a), (b), (c))
-        #define free(a) FreePool(a)
 #endif
 
 /* This passes the argument through after (if asserts are enabled) checking that it is not null. */
@@ -83,15 +95,22 @@
                 _expr_;                         \
         })
 
-#if defined(static_assert)
-#define assert_cc(expr)                                                 \
-        static_assert(expr, #expr)
-#else
-#define assert_cc(expr)                                                 \
-        struct CONCATENATE(_assert_struct_, __COUNTER__) {              \
-                char x[(expr) ? 0 : -1];                                \
-        }
-#endif
+#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 UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq))
 #define UNIQ __COUNTER__
@@ -101,10 +120,10 @@
  * on this macro will run concurrently to all other code conditionalized
  * the same way, there's no ordering or completion enforced. */
 #define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
-#define __ONCE(o)                                                       \
-        ({                                                              \
-                static sd_bool (o) = sd_false;                          \
-                __sync_bool_compare_and_swap(&(o), sd_false, sd_true);  \
+#define __ONCE(o)                                                  \
+        ({                                                         \
+                static bool (o) = false;                           \
+                __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
         })
 
 #undef MAX
@@ -181,6 +200,27 @@
                 MIN(_c, z);                             \
         })
 
+/* Returns true if the passed integer is a positive power of two */
+#define CONST_ISPOWEROF2(x)                     \
+        ((x) > 0 && ((x) & ((x) - 1)) == 0)
+
+#if 0 /* NM_IGNORED */
+#define ISPOWEROF2(x)                                                  \
+        __builtin_choose_expr(                                         \
+                __builtin_constant_p(x),                               \
+                CONST_ISPOWEROF2(x),                                   \
+                ({                                                     \
+                        const typeof(x) _x = (x);                      \
+                        CONST_ISPOWEROF2(_x);                          \
+                }))
+#else /* NM_IGNORED */
+#define ISPOWEROF2(x)                                                  \
+                ({                                                     \
+                        const typeof(x) _x = (x);                      \
+                        CONST_ISPOWEROF2(_x);                          \
+                })
+#endif /* NM_IGNORED */
+
 #define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
 #define __LESS_BY(aq, a, bq, b)                         \
         ({                                              \
@@ -254,7 +294,7 @@
 
 #define IN_SET(x, ...)                                                  \
         ({                                                              \
-                sd_bool _found = sd_false;                              \
+                bool _found = false;                                    \
                 /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as  \
                  * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \
                  * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that  \
@@ -263,7 +303,7 @@
                 assert_cc(ELEMENTSOF(__assert_in_set) <= 20);           \
                 switch (x) {                                            \
                 FOR_EACH_MAKE_CASE(__VA_ARGS__)                         \
-                        _found = sd_true;                               \
+                        _found = true;                                  \
                        break;                                           \
                 default:                                                \
                         break;                                          \
@@ -295,19 +335,7 @@
         })
 
 static inline size_t ALIGN_TO(size_t l, size_t ali) {
-        /* sd-boot uses UINTN for size_t, let's make sure SIZE_MAX is correct. */
-        assert_cc(SIZE_MAX == ~(size_t)0);
-
-        /* Check that alignment is exponent of 2 */
-#if SIZE_MAX == UINT_MAX
-        assert(__builtin_popcount(ali) == 1);
-#elif SIZE_MAX == ULONG_MAX
-        assert(__builtin_popcountl(ali) == 1);
-#elif SIZE_MAX == ULLONG_MAX
-        assert(__builtin_popcountll(ali) == 1);
-#else
-        #error "Unexpected size_t"
-#endif
+        assert(ISPOWEROF2(ali));
 
         if (l > SIZE_MAX - (ali - 1))
                 return SIZE_MAX; /* indicate overflow */
@@ -315,12 +343,20 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
         return ((l + ali - 1) & ~(ali - 1));
 }
 
+#define ALIGN4(l) ALIGN_TO(l, 4)
+#define ALIGN8(l) ALIGN_TO(l, 8)
+#ifndef SD_BOOT
+/* libefi also provides ALIGN, and we do not use them in sd-boot explicitly. */
+#define ALIGN(l)  ALIGN_TO(l, sizeof(void*))
+#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
+#endif
+
 /* Same as ALIGN_TO but callable in constant contexts. */
 #define CONST_ALIGN_TO(l, ali)                                         \
         __builtin_choose_expr(                                         \
                 __builtin_constant_p(l) &&                             \
                 __builtin_constant_p(ali) &&                           \
-                __builtin_popcountll(ali) == 1 && /* is power of 2? */ \
+                CONST_ISPOWEROF2(ali) &&                               \
                 (l <= SIZE_MAX - (ali - 1)),      /* overflow? */      \
                 ((l) + (ali) - 1) & ~((ali) - 1),                      \
                 VOID_0)
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.c b/src/libnm-systemd-shared/src/fundamental/sha256.c
new file mode 100644
index 00000000..0f3872ae
--- /dev/null
+++ b/src/libnm-systemd-shared/src/fundamental/sha256.c
@@ -0,0 +1,293 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+/* Stolen from glibc and converted to our style. In glibc it comes with the following copyright blurb: */
+
+/* Functions to compute SHA256 message digest of files or memory blocks.
+   according to the definition of SHA256 in FIPS 180-2.
+   Copyright (C) 2007-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdbool.h>
+#ifdef SD_BOOT
+#  include "efi-string.h"
+#else
+#  include <string.h>
+#endif
+
+#include "macro-fundamental.h"
+#include "sha256.h"
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define SWAP(n)                                                        \
+        (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+# define SWAP64(n)                              \
+        (((n) << 56)                            \
+         | (((n) & 0xff00) << 40)               \
+         | (((n) & 0xff0000) << 24)             \
+         | (((n) & 0xff000000) << 8)            \
+         | (((n) >> 8) & 0xff000000)            \
+         | (((n) >> 24) & 0xff0000)             \
+         | (((n) >> 40) & 0xff00)               \
+         | ((n) >> 56))
+#else
+# define SWAP(n) (n)
+# define SWAP64(n) (n)
+#endif
+
+/* The condition below is from glibc's string/string-inline.c.
+ * See definition of _STRING_INLINE_unaligned. */
+#if !defined(__mc68020__) && !defined(__s390__) && !defined(__i386__)
+#  define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__(uint32_t) != 0)
+#else
+#  define UNALIGNED_P(p) false
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (FIPS 180-2:5.1.1)  */
+static const uint8_t fillbuf[64] = {
+        0x80, 0 /* , 0, 0, ...  */
+};
+
+/* Constants for SHA256 from FIPS 180-2:4.2.2.  */
+static const uint32_t K[64] = {
+        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+        0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+        0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+        0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+        0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+        0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+        0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+        0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+        0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+        0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+        0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+        0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+        0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+        0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+static void sha256_process_block(const void *, size_t, struct sha256_ctx *);
+
+/* Initialize structure containing state of computation.
+   (FIPS 180-2:5.3.2)  */
+void sha256_init_ctx(struct sha256_ctx *ctx) {
+        assert(ctx);
+
+        ctx->H[0] = 0x6a09e667;
+        ctx->H[1] = 0xbb67ae85;
+        ctx->H[2] = 0x3c6ef372;
+        ctx->H[3] = 0xa54ff53a;
+        ctx->H[4] = 0x510e527f;
+        ctx->H[5] = 0x9b05688c;
+        ctx->H[6] = 0x1f83d9ab;
+        ctx->H[7] = 0x5be0cd19;
+
+        ctx->total64 = 0;
+        ctx->buflen = 0;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF. */
+void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf) {
+        /* Take yet unprocessed bytes into account.  */
+        uint32_t bytes = ctx->buflen;
+        size_t pad;
+
+        assert(ctx);
+        assert(resbuf);
+
+        /* Now count remaining bytes.  */
+        ctx->total64 += bytes;
+
+        pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+        memcpy(&ctx->buffer[bytes], fillbuf, pad);
+
+        /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+        ctx->buffer32[(bytes + pad + 4) / 4] = SWAP(ctx->total[TOTAL64_low] << 3);
+        ctx->buffer32[(bytes + pad) / 4] = SWAP((ctx->total[TOTAL64_high] << 3)
+                                                | (ctx->total[TOTAL64_low] >> 29));
+
+        /* Process last bytes.  */
+        sha256_process_block(ctx->buffer, bytes + pad + 8, ctx);
+
+        /* Put result from CTX in first 32 bytes following RESBUF.  */
+        for (size_t i = 0; i < 8; ++i)
+                if (UNALIGNED_P(resbuf))
+                        memcpy((uint8_t*) resbuf + i * sizeof(uint32_t), (uint32_t[]) { SWAP(ctx->H[i]) }, sizeof(uint32_t));
+                else
+                        ((uint32_t *) resbuf)[i] = SWAP(ctx->H[i]);
+
+        return resbuf;
+}
+
+void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx) {
+        assert(buffer);
+        assert(ctx);
+
+        /* When we already have some bits in our internal buffer concatenate
+           both inputs first.  */
+
+        if (ctx->buflen != 0) {
+                size_t left_over = ctx->buflen;
+                size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+                memcpy(&ctx->buffer[left_over], buffer, add);
+                ctx->buflen += add;
+
+                if (ctx->buflen > 64) {
+                        sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx);
+
+                        ctx->buflen &= 63;
+                        /* The regions in the following copy operation cannot overlap.  */
+                        memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+                                ctx->buflen);
+                }
+
+                buffer = (const char *) buffer + add;
+                len -= add;
+        }
+
+        /* Process available complete blocks.  */
+        if (len >= 64) {
+                if (UNALIGNED_P(buffer))
+                        while (len > 64) {
+                                memcpy(ctx->buffer, buffer, 64);
+                                sha256_process_block(ctx->buffer, 64, ctx);
+                                buffer = (const char *) buffer + 64;
+                                len -= 64;
+                        }
+                else {
+                        sha256_process_block(buffer, len & ~63, ctx);
+                        buffer = (const char *) buffer + (len & ~63);
+                        len &= 63;
+                }
+        }
+
+        /* Move remaining bytes into internal buffer.  */
+        if (len > 0) {
+                size_t left_over = ctx->buflen;
+
+                memcpy(&ctx->buffer[left_over], buffer, len);
+                left_over += len;
+                if (left_over >= 64) {
+                        sha256_process_block(ctx->buffer, 64, ctx);
+                        left_over -= 64;
+                        memcpy(ctx->buffer, &ctx->buffer[64], left_over);
+                }
+                ctx->buflen = left_over;
+        }
+}
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 64 == 0.  */
+static void sha256_process_block(const void *buffer, size_t len, struct sha256_ctx *ctx) {
+        const uint32_t *words = buffer;
+        size_t nwords = len / sizeof(uint32_t);
+
+        assert(buffer);
+        assert(ctx);
+
+        uint32_t a = ctx->H[0];
+        uint32_t b = ctx->H[1];
+        uint32_t c = ctx->H[2];
+        uint32_t d = ctx->H[3];
+        uint32_t e = ctx->H[4];
+        uint32_t f = ctx->H[5];
+        uint32_t g = ctx->H[6];
+        uint32_t h = ctx->H[7];
+
+        /* First increment the byte count.  FIPS 180-2 specifies the possible
+           length of the file up to 2^64 bits.  Here we only compute the
+           number of bytes.  */
+        ctx->total64 += len;
+
+        /* Process all bytes in the buffer with 64 bytes in each round of
+           the loop.  */
+        while (nwords > 0) {
+                uint32_t W[64];
+                uint32_t a_save = a;
+                uint32_t b_save = b;
+                uint32_t c_save = c;
+                uint32_t d_save = d;
+                uint32_t e_save = e;
+                uint32_t f_save = f;
+                uint32_t g_save = g;
+                uint32_t h_save = h;
+
+                /* Operators defined in FIPS 180-2:4.1.2.  */
+#define Ch(x, y, z) ((x & y) ^ (~x & z))
+#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
+#define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22))
+#define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25))
+#define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3))
+#define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10))
+
+                /* It is unfortunate that C does not provide an operator for
+                   cyclic rotation.  Hope the C compiler is smart enough.  */
+#define CYCLIC(w, s) ((w >> s) | (w << (32 - s)))
+
+                /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2.  */
+                for (size_t t = 0; t < 16; ++t) {
+                        W[t] = SWAP (*words);
+                        ++words;
+                }
+                for (size_t t = 16; t < 64; ++t)
+                        W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16];
+
+                /* The actual computation according to FIPS 180-2:6.2.2 step 3.  */
+                for (size_t t = 0; t < 64; ++t) {
+                        uint32_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t];
+                        uint32_t T2 = S0 (a) + Maj (a, b, c);
+                        h = g;
+                        g = f;
+                        f = e;
+                        e = d + T1;
+                        d = c;
+                        c = b;
+                        b = a;
+                        a = T1 + T2;
+                }
+
+                /* Add the starting values of the context according to FIPS 180-2:6.2.2
+                   step 4.  */
+                a += a_save;
+                b += b_save;
+                c += c_save;
+                d += d_save;
+                e += e_save;
+                f += f_save;
+                g += g_save;
+                h += h_save;
+
+                /* Prepare for the next round.  */
+                nwords -= 16;
+        }
+
+        /* Put checksum in context given as argument.  */
+        ctx->H[0] = a;
+        ctx->H[1] = b;
+        ctx->H[2] = c;
+        ctx->H[3] = d;
+        ctx->H[4] = e;
+        ctx->H[5] = f;
+        ctx->H[6] = g;
+        ctx->H[7] = h;
+}
diff --git a/src/libnm-systemd-shared/src/fundamental/sha256.h b/src/libnm-systemd-shared/src/fundamental/sha256.h
new file mode 100644
index 00000000..f296f76a
--- /dev/null
+++ b/src/libnm-systemd-shared/src/fundamental/sha256.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "stdint.h"
+
+#define SHA256_DIGEST_SIZE 32
+
+struct sha256_ctx {
+        uint32_t H[8];
+
+        union {
+                uint64_t total64;
+#define TOTAL64_low (1 - (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+#define TOTAL64_high (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+                uint32_t total[2];
+        };
+
+        uint32_t buflen;
+
+        union {
+                uint8_t  buffer[128]; /* NB: always correctly aligned for UINT32.  */
+                uint32_t buffer32[32];
+                uint64_t buffer64[16];
+        };
+};
+
+void sha256_init_ctx(struct sha256_ctx *ctx);
+void *sha256_finish_ctx(struct sha256_ctx *ctx, void *resbuf);
+void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx);
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 73569765..33f2d860 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.c
@@ -3,10 +3,10 @@
 #include "nm-sd-adapt-shared.h"
 
 #ifndef SD_BOOT
-#include <ctype.h>
-
-#include "macro.h"
+#  include <ctype.h>
 #endif
+
+#include "macro-fundamental.h"
 #include "string-util-fundamental.h"
 
 sd_char *startswith(const sd_char *s, const sd_char *prefix) {
@@ -80,36 +80,27 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) {
 }
 
 #if 0 /* NM_IGNORED */
-#ifdef SD_BOOT
-static sd_bool isdigit(sd_char a) {
-        return a >= '0' && a <= '9';
-}
-#endif
-
-static sd_bool is_alpha(sd_char a) {
-        /* Locale independent version of isalpha(). */
-        return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
-}
-
-static sd_bool is_valid_version_char(sd_char a) {
-        return isdigit(a) || is_alpha(a) || IN_SET(a, '~', '-', '^', '.');
+static bool is_valid_version_char(sd_char a) {
+        return ascii_isdigit(a) || ascii_isalpha(a) || IN_SET(a, '~', '-', '^', '.');
 }
 
-sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
-
-        /* This is based on RPM's rpmvercmp(). But this explicitly handles '-' and '.', as we usually
-         * want to directly compare strings which contain both version and release; e.g.
+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.
+         * '123a' and '123.a', with '123a' being newer.
+         *
+         * It allows direct comparison of strings which contain both a version and a release; e.g.
          * '247.2-3.1.fc33.x86_64' or '5.11.0-0.rc5.20210128git76c057c84d28.137.fc34'.
-         * Unlike rpmvercmp(), this distiguishes e.g. 123a and 123.a, and 123a is newer.
          *
-         * This splits the input strings into segments. Each segment is numeric or alpha, and may be
+         * The input string is split into segments. Each segment is numeric or alphabetic, and may be
          * prefixed with the following:
          *  '~' : used for pre-releases, a segment prefixed with this is the oldest,
          *  '-' : used for the separator between version and release,
          *  '^' : used for patched releases, a segment with this is newer than one with '-'.
          *  '.' : used for point releases.
-         * Note, no prefix segment is the newest. All non-supported characters are dropped, and
-         * handled as a separator of segments, e.g., 123_a is equivalent to 123a.
+         * Note that no prefix segment is the newest. All non-supported characters are dropped, and
+         * handled as a separator of segments, e.g., '123_a' is equivalent to '123a'.
          *
          * By using this, version strings can be sorted like following:
          *  (older) 122.1
@@ -126,12 +117,12 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
          *  (newer) 124-1
          */
 
-        if (isempty(a) || isempty(b))
-                return strcmp_ptr(a, b);
+        a = strempty(a);
+        b = strempty(b);
 
         for (;;) {
                 const sd_char *aa, *bb;
-                sd_int r;
+                int r;
 
                 /* Drop leading invalid characters. */
                 while (*a != '\0' && !is_valid_version_char(*a))
@@ -152,7 +143,7 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
                 }
 
                 /* If at least one string reaches the end, then longer is newer.
-                 * Note that except for '~' prefixed segments, a string has more segments is newer.
+                 * Note that except for '~' prefixed segments, a string which has more segments is newer.
                  * So, this check must be after the '~' check. */
                 if (*a == '\0' || *b == '\0')
                         return CMP(*a, *b);
@@ -188,20 +179,25 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
                         b++;
                 }
 
-                if (isdigit(*a) || isdigit(*b)) {
+                if (ascii_isdigit(*a) || ascii_isdigit(*b)) {
+                        /* Find the leading numeric segments. One may be an empty string. So,
+                         * numeric segments are always newer than alpha segments. */
+                        for (aa = a; ascii_isdigit(*aa); aa++)
+                                ;
+                        for (bb = b; ascii_isdigit(*bb); bb++)
+                                ;
+
+                        /* Check if one of the strings was empty, but the other not. */
+                        r = CMP(a != aa, b != bb);
+                        if (r != 0)
+                                return r;
+
                         /* Skip leading '0', to make 00123 equivalent to 123. */
                         while (*a == '0')
                                 a++;
                         while (*b == '0')
                                 b++;
 
-                        /* Find the leading numeric segments. One may be an empty string. So,
-                         * numeric segments are always newer than alpha segments. */
-                        for (aa = a; isdigit(*aa); aa++)
-                                ;
-                        for (bb = b; isdigit(*bb); bb++)
-                                ;
-
                         /* To compare numeric segments without parsing their values, first compare the
                          * lengths of the segments. Eg. 12345 vs 123, longer is newer. */
                         r = CMP(aa - a, bb - b);
@@ -209,18 +205,18 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
                                 return r;
 
                         /* Then, compare them as strings. */
-                        r = strncmp(a, b, aa - a);
+                        r = CMP(strncmp(a, b, aa - a), 0);
                         if (r != 0)
                                 return r;
                 } else {
                         /* Find the leading non-numeric segments. */
-                        for (aa = a; is_alpha(*aa); aa++)
+                        for (aa = a; ascii_isalpha(*aa); aa++)
                                 ;
-                        for (bb = b; is_alpha(*bb); bb++)
+                        for (bb = b; ascii_isalpha(*bb); bb++)
                                 ;
 
                         /* Note that the segments are usually not NUL-terminated. */
-                        r = strncmp(a, b, MIN(aa - a, bb - b));
+                        r = CMP(strncmp(a, b, MIN(aa - a, bb - b)), 0);
                         if (r != 0)
                                 return r;
 
@@ -230,7 +226,7 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) {
                                 return r;
                 }
 
-                /* The current segments are equivalent. Let's compare the next one. */
+                /* The current segments are equivalent. Let's move to the next one. */
                 a = aa;
                 b = bb;
         }
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 dc0c1202..ecf32e51 100644
--- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h
@@ -2,54 +2,62 @@
 #pragma once
 
 #ifdef SD_BOOT
-#include <efi.h>
-#include <efilib.h>
+#  include <efi.h>
+#  include <efilib.h>
+#  include "efi-string.h"
 #else
-#include <string.h>
+#  include <string.h>
 #endif
 
 #include "macro-fundamental.h"
 
 #ifdef SD_BOOT
-#define strlen(a)        StrLen((a))
-#define strcmp(a, b)     StrCmp((a), (b))
-#define strncmp(a, b, n) StrnCmp((a), (b), (n))
-#define strcasecmp(a, b) StriCmp((a), (b))
-#define STR_C(str)       (L ## str)
-#define memcmp(a, b, n)  CompareMem(a, b, n)
+#  define strlen strlen16
+#  define strcmp strcmp16
+#  define strncmp strncmp16
+#  define strcasecmp strcasecmp16
+#  define strncasecmp strncasecmp16
+#  define STR_C(str)       (L ## str)
+typedef char16_t sd_char;
 #else
-#define STR_C(str)       (str)
+#  define STR_C(str)       (str)
+typedef char sd_char;
 #endif
 
 #define streq(a,b) (strcmp((a),(b)) == 0)
 #define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
 #define strcaseeq(a,b) (strcasecmp((a),(b)) == 0)
-#ifndef SD_BOOT
 #define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
-#endif
 
-static inline sd_int strcmp_ptr(const sd_char *a, const sd_char *b) {
+static inline int strcmp_ptr(const sd_char *a, const sd_char *b) {
         if (a && b)
                 return strcmp(a, b);
 
         return CMP(a, b);
 }
 
-static inline sd_int strcasecmp_ptr(const sd_char *a, const sd_char *b) {
+static inline int strcasecmp_ptr(const sd_char *a, const sd_char *b) {
         if (a && b)
                 return strcasecmp(a, b);
 
         return CMP(a, b);
 }
 
-static inline sd_bool streq_ptr(const sd_char *a, const sd_char *b) {
+static inline bool streq_ptr(const sd_char *a, const sd_char *b) {
         return strcmp_ptr(a, b) == 0;
 }
 
-static inline sd_bool strcaseeq_ptr(const sd_char *a, const sd_char *b) {
+static inline bool strcaseeq_ptr(const sd_char *a, const sd_char *b) {
         return strcasecmp_ptr(a, b) == 0;
 }
 
+static inline size_t strlen_ptr(const sd_char *s) {
+        if (!s)
+                return 0;
+
+        return strlen(s);
+}
+
 sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
 #ifndef SD_BOOT
 sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
@@ -57,15 +65,23 @@ sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
 sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;
 sd_char *endswith_no_case(const sd_char *s, const sd_char *postfix) _pure_;
 
-static inline sd_bool isempty(const sd_char *a) {
+static inline bool isempty(const sd_char *a) {
         return !a || a[0] == '\0';
 }
 
-static inline const sd_char *yes_no(sd_bool b) {
+static inline const sd_char *strempty(const sd_char *s) {
+        return s ?: STR_C("");
+}
+
+static inline const sd_char *yes_no(bool b) {
         return b ? STR_C("yes") : STR_C("no");
 }
 
-sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
+static inline const sd_char* comparison_operator(int result) {
+        return result < 0 ? STR_C("<") : result > 0 ? STR_C(">") : STR_C("==");
+}
+
+int strverscmp_improved(const sd_char *a, const sd_char *b);
 
 /* Like startswith(), but operates on arbitrary memory blocks */
 static inline void *memory_startswith(const void *p, size_t sz, const sd_char *token) {
@@ -82,3 +98,19 @@ static inline void *memory_startswith(const void *p, size_t sz, const sd_char *t
 
         return (uint8_t*) p + n;
 }
+
+#define _STRV_FOREACH(s, l, i)                                          \
+        for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++)
+
+#define STRV_FOREACH(s, l)                      \
+        _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ))
+
+static inline bool ascii_isdigit(sd_char a) {
+        /* A pure ASCII, locale independent version of isdigit() */
+        return a >= '0' && a <= '9';
+}
+
+static inline bool ascii_isalpha(sd_char a) {
+        /* A pure ASCII, locale independent version of isalpha() */
+        return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
+}
diff --git a/src/libnm-systemd-shared/src/fundamental/types-fundamental.h b/src/libnm-systemd-shared/src/fundamental/types-fundamental.h
deleted file mode 100644
index 5977e40c..00000000
--- a/src/libnm-systemd-shared/src/fundamental/types-fundamental.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/* This defines a number of basic types that are one thing in userspace and another in the UEFI environment,
- * but mostly the same in concept and behaviour.
- *
- * Note: if the definition of these types/values has slightly different semantics in userspace and in the
- * UEFI environment then please prefix its name with "sd_" to make clear these types have special semantics,
- * and *we* defined them. Otherwise, if the types are effectively 100% identical in behaviour in userspace
- * and UEFI environment you can omit the prefix. (Examples: sd_char is 8 bit in userspace and 16 bit in UEFI
- * space hence it should have the sd_ prefix; but size_t in userspace and UINTN in UEFI environment are 100%
- * defined the same way ultimately, hence it's OK to just define size_t as alias to UINTN in UEFI
- * environment, so that size_t can be used everywhere, without any "sd_" prefix.)
- *
- * Note: we generally prefer the userspace names of types and concepts. i.e. if in doubt please name types
- * after the userspace vocabulary, and let's keep UEFI vocabulary specific to the UEFI build environment. */
-
-#ifdef SD_BOOT
-#include <efi.h>
-
-typedef BOOLEAN sd_bool;
-typedef CHAR16  sd_char;
-typedef INTN    sd_int;
-typedef UINTN   size_t;
-
-#define sd_true  TRUE
-#define sd_false FALSE
-#else
-#include <stdbool.h>
-#include <stdint.h>
-
-typedef bool    sd_bool;
-typedef char    sd_char;
-typedef int     sd_int;
-
-#define sd_true  true
-#define sd_false false
-
-#endif
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c
index e932b693..7bba3b18 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.c
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.c
@@ -9,6 +9,7 @@
 
 #include "alloc-util.h"
 #include "dns-domain.h"
+#include "glyph-util.h"
 #include "hashmap.h"
 #include "hexdecoct.h"
 #include "hostname-util.h"
@@ -239,9 +240,8 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
                         sz -= 2;
 
                 } else if (IN_SET(*p, '_', '-') ||
-                           (*p >= '0' && *p <= '9') ||
-                           (*p >= 'a' && *p <= 'z') ||
-                           (*p >= 'A' && *p <= 'Z')) {
+                           ascii_isdigit(*p) ||
+                           ascii_isalpha(*p)) {
 
                         /* Proper character */
 
@@ -456,12 +456,8 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_r
                                 return r;
                 }
 
-                if (!first)
-                        n++;
-                else
-                        first = false;
-
-                n += r;
+                n += r + !first;
+                first = false;
         }
 
 finish:
@@ -919,15 +915,13 @@ static bool srv_type_label_is_valid(const char *label, size_t n) {
                 return false;
 
         /* Second char must be a letter */
-        if (!(label[1] >= 'A' && label[1] <= 'Z') &&
-            !(label[1] >= 'a' && label[1] <= 'z'))
+        if (!ascii_isalpha(label[1]))
                 return false;
 
         /* Third and further chars must be alphanumeric or a hyphen */
         for (k = 2; k < n; k++) {
-                if (!(label[k] >= 'A' && label[k] <= 'Z') &&
-                    !(label[k] >= 'a' && label[k] <= 'z') &&
-                    !(label[k] >= '0' && label[k] <= '9') &&
+                if (!ascii_isalpha(label[k]) &&
+                    !ascii_isdigit(label[k]) &&
                     label[k] != '-')
                         return false;
         }
@@ -1035,10 +1029,10 @@ static bool dns_service_name_label_is_valid(const char *label, size_t n) {
         return dns_service_name_is_valid(s);
 }
 
-int dns_service_split(const char *joined, char **_name, char **_type, char **_domain) {
+int dns_service_split(const char *joined, char **ret_name, char **ret_type, char **ret_domain) {
         _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
-        const char *p = joined, *q = NULL, *d = NULL;
-        char a[DNS_LABEL_MAX], b[DNS_LABEL_MAX], c[DNS_LABEL_MAX];
+        const char *p = joined, *q = NULL, *d = joined;
+        char a[DNS_LABEL_MAX+1], b[DNS_LABEL_MAX+1], c[DNS_LABEL_MAX+1];
         int an, bn, cn, r;
         unsigned x = 0;
 
@@ -1058,6 +1052,9 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
                         return bn;
 
                 if (bn > 0) {
+                        if (!srv_type_label_is_valid(b, bn))
+                                goto finish;
+
                         x++;
 
                         /* If there was a second label, try to get the third one */
@@ -1066,64 +1063,58 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
                         if (cn < 0)
                                 return cn;
 
-                        if (cn > 0)
+                        if (cn > 0 && srv_type_label_is_valid(c, cn))
                                 x++;
-                } else
-                        cn = 0;
-        } else
-                an = 0;
-
-        if (x >= 2 && srv_type_label_is_valid(b, bn)) {
+                }
+        }
 
-                if (x >= 3 && srv_type_label_is_valid(c, cn)) {
+        switch (x) {
+        case 2:
+                if (!srv_type_label_is_valid(a, an))
+                        break;
 
-                        if (dns_service_name_label_is_valid(a, an)) {
-                                /* OK, got <name> . <type> . <type2> . <domain> */
+                /* OK, got <type> . <type2> . <domain> */
 
-                                name = strndup(a, an);
-                                if (!name)
-                                        return -ENOMEM;
+                name = NULL;
 
-                                type = strjoin(b, ".", c);
-                                if (!type)
-                                        return -ENOMEM;
+                type = strjoin(a, ".", b);
+                if (!type)
+                        return -ENOMEM;
 
-                                d = p;
-                                goto finish;
-                        }
+                d = q;
+                break;
 
-                } else if (srv_type_label_is_valid(a, an)) {
+        case 3:
+                if (!dns_service_name_label_is_valid(a, an))
+                        break;
 
-                        /* OK, got <type> . <type2> . <domain> */
+                /* OK, got <name> . <type> . <type2> . <domain> */
 
-                        name = NULL;
+                name = strndup(a, an);
+                if (!name)
+                        return -ENOMEM;
 
-                        type = strjoin(a, ".", b);
-                        if (!type)
-                                return -ENOMEM;
+                type = strjoin(b, ".", c);
+                if (!type)
+                        return -ENOMEM;
 
-                        d = q;
-                        goto finish;
-                }
+                d = p;
+                break;
         }
 
-        name = NULL;
-        type = NULL;
-        d = joined;
-
 finish:
         r = dns_name_normalize(d, 0, &domain);
         if (r < 0)
                 return r;
 
-        if (_domain)
-                *_domain = TAKE_PTR(domain);
+        if (ret_domain)
+                *ret_domain = TAKE_PTR(domain);
 
-        if (_type)
-                *_type = TAKE_PTR(type);
+        if (ret_type)
+                *ret_type = TAKE_PTR(type);
 
-        if (_name)
-                *_name = TAKE_PTR(name);
+        if (ret_name)
+                *ret_name = TAKE_PTR(name);
 
         return 0;
 }
@@ -1303,7 +1294,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", name, t);
+        log_debug("idn2_lookup_u8: %s %s %s", name, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), t);
         if (r == IDN2_OK) {
                 if (!startswith(name, "xn--")) {
                         _cleanup_free_ char *s = NULL;
@@ -1317,8 +1308,9 @@ int dns_name_apply_idna(const char *name, char **ret) {
                         }
 
                         if (!streq_ptr(name, s)) {
-                                log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.",
-                                          name, t, 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);
                                 *ret = NULL;
                                 return 0;
                         }
diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.h b/src/libnm-systemd-shared/src/shared/dns-domain.h
index ec174c0e..828e924d 100644
--- a/src/libnm-systemd-shared/src/shared/dns-domain.h
+++ b/src/libnm-systemd-shared/src/shared/dns-domain.h
@@ -42,8 +42,8 @@ static inline int dns_name_normalize(const char *s, DNSLabelFlags flags, char **
 static inline int dns_name_is_valid(const char *s) {
         int r;
 
-        /* dns_name_normalize() verifies as a side effect */
-        r = dns_name_normalize(s, 0, NULL);
+        /* dns_name_concat() verifies as a side effect */
+        r = dns_name_concat(s, NULL, 0, NULL);
         if (r == -EINVAL)
                 return 0;
         if (r < 0)
@@ -90,7 +90,7 @@ bool dnssd_srv_type_is_valid(const char *name);
 bool dns_service_name_is_valid(const char *name);
 
 int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
-int dns_service_split(const char *joined, char **name, char **type, char **domain);
+int dns_service_split(const char *joined, char **ret_name, char **ret_type, char **ret_domain);
 
 int dns_name_suffix(const char *name, unsigned n_labels, const char **ret);
 int dns_name_count_labels(const char *name);