about summary refs log tree commit diff
path: root/shared
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-04-21 21:09:51 +0200
committerMichael Biebl <biebl@debian.org>2019-04-21 21:09:51 +0200
commit85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (patch)
treecce7b0b02d28fae2df9fdf2c1804cacd1500f2d7 /shared
parent9a6dcbf895f9da01768e64b73cec88c16157d91e (diff)
New upstream version 1.18.0 upstream/1.18.0
Diffstat (limited to 'shared')
-rw-r--r--shared/c-rbtree/src/c-rbtree.c1
-rw-r--r--shared/meson.build125
-rw-r--r--shared/n-acd/src/n-acd-probe.c1
-rw-r--r--shared/n-acd/src/util/timer.c2
-rw-r--r--shared/nm-default.h8
-rw-r--r--shared/nm-glib-aux/nm-c-list.h (renamed from shared/nm-utils/nm-c-list.h)0
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.c (renamed from shared/nm-utils/nm-dedup-multi.c)0
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.h (renamed from shared/nm-utils/nm-dedup-multi.h)2
-rw-r--r--shared/nm-glib-aux/nm-enum-utils.c (renamed from shared/nm-utils/nm-enum-utils.c)0
-rw-r--r--shared/nm-glib-aux/nm-enum-utils.h (renamed from shared/nm-utils/nm-enum-utils.h)0
-rw-r--r--shared/nm-glib-aux/nm-errno.c (renamed from shared/nm-utils/nm-errno.c)0
-rw-r--r--shared/nm-glib-aux/nm-errno.h (renamed from shared/nm-utils/nm-errno.h)0
-rw-r--r--shared/nm-glib-aux/nm-glib.h (renamed from shared/nm-utils/nm-glib.h)0
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.c (renamed from shared/nm-utils/nm-hash-utils.c)0
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.h (renamed from shared/nm-utils/nm-hash-utils.h)27
-rw-r--r--shared/nm-glib-aux/nm-io-utils.c (renamed from shared/nm-utils/nm-io-utils.c)0
-rw-r--r--shared/nm-glib-aux/nm-io-utils.h (renamed from shared/nm-utils/nm-io-utils.h)0
-rw-r--r--shared/nm-glib-aux/nm-jansson.h (renamed from shared/nm-utils/nm-jansson.h)0
-rw-r--r--shared/nm-glib-aux/nm-logging-fwd.h (renamed from shared/nm-utils/nm-logging-fwd.h)0
-rw-r--r--shared/nm-glib-aux/nm-macros-internal.h (renamed from shared/nm-utils/nm-macros-internal.h)160
-rw-r--r--shared/nm-glib-aux/nm-obj.h (renamed from shared/nm-utils/nm-obj.h)0
-rw-r--r--shared/nm-glib-aux/nm-random-utils.c (renamed from shared/nm-utils/nm-random-utils.c)0
-rw-r--r--shared/nm-glib-aux/nm-random-utils.h (renamed from shared/nm-utils/nm-random-utils.h)0
-rw-r--r--shared/nm-glib-aux/nm-secret-utils.c (renamed from shared/nm-utils/nm-secret-utils.c)19
-rw-r--r--shared/nm-glib-aux/nm-secret-utils.h (renamed from shared/nm-utils/nm-secret-utils.h)0
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c (renamed from shared/nm-utils/nm-shared-utils.c)526
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h (renamed from shared/nm-utils/nm-shared-utils.h)269
-rw-r--r--shared/nm-glib-aux/nm-time-utils.c (renamed from shared/nm-utils/nm-time-utils.c)0
-rw-r--r--shared/nm-glib-aux/nm-time-utils.h (renamed from shared/nm-utils/nm-time-utils.h)0
-rw-r--r--shared/nm-libnm-core-aux/nm-dispatcher-api.h (renamed from shared/nm-dispatcher-api.h)4
-rw-r--r--shared/nm-libnm-core-intern/nm-common-macros.h (renamed from shared/nm-common-macros.h)0
-rw-r--r--shared/nm-libnm-core-intern/nm-ethtool-utils.c (renamed from shared/nm-ethtool-utils.c)0
-rw-r--r--shared/nm-libnm-core-intern/nm-ethtool-utils.h (renamed from shared/nm-ethtool-utils.h)0
-rw-r--r--shared/nm-libnm-core-intern/nm-libnm-core-utils.c76
-rw-r--r--shared/nm-libnm-core-intern/nm-libnm-core-utils.h113
-rw-r--r--shared/nm-meta-setting.c8
-rw-r--r--shared/nm-meta-setting.h12
-rw-r--r--shared/nm-std-aux/c-list-util.c (renamed from shared/nm-utils/c-list-util.c)0
-rw-r--r--shared/nm-std-aux/c-list-util.h (renamed from shared/nm-utils/c-list-util.h)23
-rw-r--r--shared/nm-std-aux/nm-dbus-compat.h (renamed from shared/nm-dbus-compat.h)0
-rw-r--r--shared/nm-std-aux/unaligned.h (renamed from shared/nm-utils/unaligned.h)0
-rw-r--r--shared/nm-test-utils-impl.c2
-rw-r--r--shared/nm-udev-aux/nm-udev-utils.c (renamed from shared/nm-utils/nm-udev-utils.c)0
-rw-r--r--shared/nm-udev-aux/nm-udev-utils.h (renamed from shared/nm-utils/nm-udev-utils.h)0
-rw-r--r--shared/nm-utils/nm-test-utils.h11
-rw-r--r--shared/nm-utils/nm-vpn-editor-plugin-call.h2
-rw-r--r--shared/nm-utils/tests/test-shared-general.c203
-rw-r--r--shared/nm-version-macros.h3
-rw-r--r--shared/nm-version-macros.h.in1
-rw-r--r--shared/systemd/nm-logging-stub.c2
-rw-r--r--shared/systemd/nm-sd-utils-shared.c5
-rw-r--r--shared/systemd/nm-sd-utils-shared.h6
-rw-r--r--shared/systemd/sd-adapt-shared/missing.h2
-rw-r--r--shared/systemd/sd-adapt-shared/namespace-util.h (renamed from shared/systemd/sd-adapt-shared/missing_socket.h)0
-rw-r--r--shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h2
-rw-r--r--shared/systemd/sd-adapt-shared/nulstr-util.h3
-rw-r--r--shared/systemd/sd-adapt-shared/strxcpyx.h3
-rw-r--r--shared/systemd/sd-adapt-shared/unaligned.h2
-rw-r--r--shared/systemd/src/basic/alloc-util.c20
-rw-r--r--shared/systemd/src/basic/alloc-util.h14
-rw-r--r--shared/systemd/src/basic/env-file.c2
-rw-r--r--shared/systemd/src/basic/errno-util.h69
-rw-r--r--shared/systemd/src/basic/fd-util.c11
-rw-r--r--shared/systemd/src/basic/fd-util.h12
-rw-r--r--shared/systemd/src/basic/fileio.c104
-rw-r--r--shared/systemd/src/basic/fileio.h20
-rw-r--r--shared/systemd/src/basic/fs-util.c104
-rw-r--r--shared/systemd/src/basic/fs-util.h4
-rw-r--r--shared/systemd/src/basic/hashmap.c7
-rw-r--r--shared/systemd/src/basic/hashmap.h2
-rw-r--r--shared/systemd/src/basic/hexdecoct.c77
-rw-r--r--shared/systemd/src/basic/hexdecoct.h5
-rw-r--r--shared/systemd/src/basic/in-addr-util.c134
-rw-r--r--shared/systemd/src/basic/in-addr-util.h2
-rw-r--r--shared/systemd/src/basic/log.h22
-rw-r--r--shared/systemd/src/basic/memory-util.c59
-rw-r--r--shared/systemd/src/basic/memory-util.h84
-rw-r--r--shared/systemd/src/basic/mempool.c1
-rw-r--r--shared/systemd/src/basic/missing_socket.h66
-rw-r--r--shared/systemd/src/basic/missing_stat.h53
-rw-r--r--shared/systemd/src/basic/path-util.c51
-rw-r--r--shared/systemd/src/basic/process-util.c53
-rw-r--r--shared/systemd/src/basic/process-util.h4
-rw-r--r--shared/systemd/src/basic/random-util.c11
-rw-r--r--shared/systemd/src/basic/refcnt.h54
-rw-r--r--shared/systemd/src/basic/socket-util.c72
-rw-r--r--shared/systemd/src/basic/socket-util.h3
-rw-r--r--shared/systemd/src/basic/sort-util.h70
-rw-r--r--shared/systemd/src/basic/stat-util.c46
-rw-r--r--shared/systemd/src/basic/stat-util.h3
-rw-r--r--shared/systemd/src/basic/stdio-util.h2
-rw-r--r--shared/systemd/src/basic/string-table.h8
-rw-r--r--shared/systemd/src/basic/string-util.c35
-rw-r--r--shared/systemd/src/basic/string-util.h43
-rw-r--r--shared/systemd/src/basic/strv.c7
-rw-r--r--shared/systemd/src/basic/strv.h2
-rw-r--r--shared/systemd/src/basic/time-util.c2
-rw-r--r--shared/systemd/src/basic/utf8.c38
-rw-r--r--shared/systemd/src/basic/utf8.h2
-rw-r--r--shared/systemd/src/basic/util.c339
-rw-r--r--shared/systemd/src/basic/util.h181
101 files changed, 2296 insertions, 1225 deletions
diff --git a/shared/c-rbtree/src/c-rbtree.c b/shared/c-rbtree/src/c-rbtree.c
index f58db849..31d74300 100644
--- a/shared/c-rbtree/src/c-rbtree.c
+++ b/shared/c-rbtree/src/c-rbtree.c
@@ -460,6 +460,7 @@ _public_ void c_rbtree_move(CRBTree *to, CRBTree *from) {
         if (from->root) {
                 t = c_rbnode_pop_root(from->root);
                 assert(t == from);
+                (void)t;
 
                 to->root = from->root;
                 from->root = NULL;
diff --git a/shared/meson.build b/shared/meson.build
index a6e94d6b..ed9bf03f 100644
--- a/shared/meson.build
+++ b/shared/meson.build
@@ -1,5 +1,7 @@
 shared_inc = include_directories('.')
 
+###############################################################################
+
 shared_c_siphash = static_library(
     'c-siphash',
     sources: 'c-siphash/src/c-siphash.c',
@@ -10,6 +12,8 @@ shared_c_siphash_dep = declare_dependency(
     link_with: shared_c_siphash,
 )
 
+###############################################################################
+
 shared_c_rbtree = static_library(
     'c-rbtree',
     c_args: '-std=c11',
@@ -23,6 +27,7 @@ shared_c_rbtree_dep = declare_dependency(
     link_with: shared_c_rbtree,
 )
 
+###############################################################################
 
 if enable_ebpf
     shared_n_acd_bpf_files = files('n-acd/src/n-acd-bpf.c')
@@ -62,6 +67,8 @@ shared_n_acd_dep = declare_dependency(
     link_with: shared_n_acd,
 )
 
+###############################################################################
+
 version_conf = configuration_data()
 version_conf.set('NM_MAJOR_VERSION', nm_major_version)
 version_conf.set('NM_MINOR_VERSION', nm_minor_version)
@@ -73,8 +80,6 @@ version_header = configure_file(
     configuration: version_conf,
 )
 
-shared_nm_ethtool_utils_c = files('nm-ethtool-utils.c')
-
 shared_nm_meta_setting_c = files('nm-meta-setting.c')
 
 shared_nm_test_utils_impl_c = files('nm-test-utils-impl.c')
@@ -83,35 +88,60 @@ shared_nm_utils_nm_vpn_plugin_utils_c = files('nm-utils/nm-vpn-plugin-utils.c')
 
 ###############################################################################
 
-shared_nm_utils_c_args = [
+shared_nm_std_aux = static_library(
+    'nm-std-aux',
+    sources: files('nm-std-aux/c-list-util.c'),
+    c_args: [
+      '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
+      '-DNETWORKMANAGER_COMPILATION=0',
+    ],
+    include_directories: [
+        top_inc,
+        shared_inc,
+    ],
+    dependencies: [
+    ],
+)
+
+shared_nm_std_aux_dep = declare_dependency(
+    link_with: shared_nm_std_aux,
+    include_directories: [
+        top_inc,
+        shared_inc,
+    ],
+)
+
+###############################################################################
+
+shared_nm_glib_aux_c_args = [
     '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
     '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB)',
 ]
 
-shared_nm_utils_base = static_library(
+shared_nm_glib_aux = static_library(
     'nm-utils-base',
-    sources: files('nm-utils/c-list-util.c',
-                   'nm-utils/nm-dedup-multi.c',
-                   'nm-utils/nm-enum-utils.c',
-                   'nm-utils/nm-errno.c',
-                   'nm-utils/nm-hash-utils.c',
-                   'nm-utils/nm-io-utils.c',
-                   'nm-utils/nm-random-utils.c',
-                   'nm-utils/nm-secret-utils.c',
-                   'nm-utils/nm-shared-utils.c',
-                   'nm-utils/nm-time-utils.c'),
-    c_args: shared_nm_utils_c_args,
+    sources: files('nm-glib-aux/nm-dedup-multi.c',
+                   'nm-glib-aux/nm-enum-utils.c',
+                   'nm-glib-aux/nm-errno.c',
+                   'nm-glib-aux/nm-hash-utils.c',
+                   'nm-glib-aux/nm-io-utils.c',
+                   'nm-glib-aux/nm-random-utils.c',
+                   'nm-glib-aux/nm-secret-utils.c',
+                   'nm-glib-aux/nm-shared-utils.c',
+                   'nm-glib-aux/nm-time-utils.c'),
+    c_args: shared_nm_glib_aux_c_args,
     include_directories: [
         top_inc,
         shared_inc,
     ],
     dependencies: [
         glib_dep,
+        shared_nm_std_aux_dep,
     ],
 )
 
-shared_nm_utils_base_dep = declare_dependency(
-    link_with: shared_nm_utils_base,
+shared_nm_glib_aux_dep = declare_dependency(
+    link_with: shared_nm_glib_aux,
     include_directories: [
         top_inc,
         shared_inc,
@@ -119,54 +149,38 @@ shared_nm_utils_base_dep = declare_dependency(
     dependencies: glib_dep,
 )
 
-shared_nm_utils_udev = static_library(
-    'nm-utils-udev',
-    sources: files('nm-utils/nm-udev-utils.c'),
-    c_args: shared_nm_utils_c_args,
+###############################################################################
+
+shared_nm_udev_aux = static_library(
+    'nm-udev-aux',
+    sources: files('nm-udev-aux/nm-udev-utils.c'),
+    c_args: shared_nm_glib_aux_c_args,
     include_directories: [
         top_inc,
         shared_inc,
     ],
     dependencies: [
         glib_dep,
-        shared_nm_utils_base_dep,
+        shared_nm_glib_aux_dep,
         libudev_dep,
     ],
 )
 
-shared_nm_utils_udev_dep = declare_dependency(
-    link_with: shared_nm_utils_udev,
+shared_nm_udev_aux_dep = declare_dependency(
+    link_with: shared_nm_udev_aux,
     include_directories: [
         top_inc,
         shared_inc,
     ],
     dependencies: [
         glib_dep,
-        shared_nm_utils_base_dep,
+        shared_nm_glib_aux_dep,
         libudev_dep,
     ],
 )
 
 ###############################################################################
 
-test_shared_general = executable(
-  'nm-utils/tests/test-shared-general',
-  [ 'nm-utils/tests/test-shared-general.c', ],
-  c_args: [
-      '-DNETWORKMANAGER_COMPILATION_TEST',
-      '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)',
-    ],
-  dependencies: shared_nm_utils_base_dep,
-  link_with: shared_c_siphash,
-)
-test(
-  'shared/nm-utils/test-shared-general',
-  test_script,
-  args: test_args + [test_shared_general.full_path()]
-)
-
-###############################################################################
-
 libnm_systemd_shared = static_library(
   'nm-systemd-shared',
   sources: files(
@@ -185,6 +199,7 @@ libnm_systemd_shared = static_library(
     'systemd/src/basic/hostname-util.c',
     'systemd/src/basic/in-addr-util.c',
     'systemd/src/basic/io-util.c',
+    'systemd/src/basic/memory-util.c',
     'systemd/src/basic/mempool.c',
     'systemd/src/basic/parse-util.c',
     'systemd/src/basic/path-util.c',
@@ -206,7 +221,7 @@ libnm_systemd_shared = static_library(
     'systemd/sd-adapt-shared',
     'systemd/src/basic',
   ),
-  dependencies: shared_nm_utils_base_dep,
+  dependencies: shared_nm_glib_aux_dep,
   c_args: [
     '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED',
     '-DG_LOG_DOMAIN="libnm"',
@@ -219,7 +234,7 @@ libnm_systemd_shared_dep = declare_dependency(
     'systemd/src/basic',
   ),
   dependencies: [
-    shared_nm_utils_base_dep,
+    shared_nm_glib_aux_dep,
   ],
   link_with: [
     libnm_systemd_shared,
@@ -235,7 +250,7 @@ libnm_systemd_logging_stub = static_library(
     'systemd/sd-adapt-shared',
     'systemd/src/basic',
   ),
-  dependencies: shared_nm_utils_base_dep,
+  dependencies: shared_nm_glib_aux_dep,
   c_args: [
     '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED',
     '-DG_LOG_DOMAIN="libnm"',
@@ -250,3 +265,21 @@ libnm_systemd_shared_no_logging_dep = declare_dependency(
     libnm_systemd_logging_stub,
   ],
 )
+
+###############################################################################
+
+test_shared_general = executable(
+  'nm-utils/tests/test-shared-general',
+  [ 'nm-utils/tests/test-shared-general.c', ],
+  c_args: [
+      '-DNETWORKMANAGER_COMPILATION_TEST',
+      '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)',
+    ],
+  dependencies: shared_nm_glib_aux_dep,
+  link_with: shared_c_siphash,
+)
+test(
+  'shared/nm-utils/test-shared-general',
+  test_script,
+  args: test_args + [test_shared_general.full_path()]
+)
diff --git a/shared/n-acd/src/n-acd-probe.c b/shared/n-acd/src/n-acd-probe.c
index 8c233b56..d4da0fd5 100644
--- a/shared/n-acd/src/n-acd-probe.c
+++ b/shared/n-acd/src/n-acd-probe.c
@@ -215,6 +215,7 @@ static void n_acd_probe_unlink(NAcdProbe *probe) {
         if (n_acd_probe_is_unique(probe)) {
                 r = n_acd_bpf_map_remove(probe->acd->fd_bpf_map, &probe->ip);
                 assert(r >= 0);
+                (void)r;
                 --probe->acd->n_bpf_map;
         }
         c_rbnode_unlink(&probe->ip_node);
diff --git a/shared/n-acd/src/util/timer.c b/shared/n-acd/src/util/timer.c
index 29627af7..07dbf34e 100644
--- a/shared/n-acd/src/util/timer.c
+++ b/shared/n-acd/src/util/timer.c
@@ -44,6 +44,7 @@ void timer_now(Timer *timer, uint64_t *nowp) {
 
         r = clock_gettime(timer->clock, &ts);
         assert(r >= 0);
+        (void)r;
 
         *nowp = ts.tv_sec * UINT64_C(1000000000) + ts.tv_nsec;
 }
@@ -74,6 +75,7 @@ void timer_rearm(Timer *timer) {
                                     },
                                     NULL);
                 assert(r >= 0);
+                (void)r;
 
                 timer->scheduled_timeout = time;
         }
diff --git a/shared/nm-default.h b/shared/nm-default.h
index 26d6476a..54e99167 100644
--- a/shared/nm-default.h
+++ b/shared/nm-default.h
@@ -290,14 +290,14 @@ _nm_g_return_if_fail_warning (const char *log_domain,
 
 /*****************************************************************************/
 
-#include "nm-utils/nm-macros-internal.h"
-#include "nm-utils/nm-shared-utils.h"
-#include "nm-utils/nm-errno.h"
+#include "nm-glib-aux/nm-macros-internal.h"
+#include "nm-glib-aux/nm-shared-utils.h"
+#include "nm-glib-aux/nm-errno.h"
 
 #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_UTIL
 /* no hash-utils in legacy code. */
 #else
-#include "nm-utils/nm-hash-utils.h"
+#include "nm-glib-aux/nm-hash-utils.h"
 #endif
 
 /*****************************************************************************/
diff --git a/shared/nm-utils/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h
index 5c73f574..5c73f574 100644
--- a/shared/nm-utils/nm-c-list.h
+++ b/shared/nm-glib-aux/nm-c-list.h
diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c
index 5bdc3e3c..5bdc3e3c 100644
--- a/shared/nm-utils/nm-dedup-multi.c
+++ b/shared/nm-glib-aux/nm-dedup-multi.c
diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h
index 845b4c3e..82c6f1e9 100644
--- a/shared/nm-utils/nm-dedup-multi.h
+++ b/shared/nm-glib-aux/nm-dedup-multi.h
@@ -23,7 +23,7 @@
 #define __NM_DEDUP_MULTI_H__
 
 #include "nm-obj.h"
-#include "c-list-util.h"
+#include "nm-std-aux/c-list-util.h"
 
 /*****************************************************************************/
 
diff --git a/shared/nm-utils/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c
index a4f6e809..a4f6e809 100644
--- a/shared/nm-utils/nm-enum-utils.c
+++ b/shared/nm-glib-aux/nm-enum-utils.c
diff --git a/shared/nm-utils/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h
index 1827fdf4..1827fdf4 100644
--- a/shared/nm-utils/nm-enum-utils.h
+++ b/shared/nm-glib-aux/nm-enum-utils.h
diff --git a/shared/nm-utils/nm-errno.c b/shared/nm-glib-aux/nm-errno.c
index 30eb9a8e..30eb9a8e 100644
--- a/shared/nm-utils/nm-errno.c
+++ b/shared/nm-glib-aux/nm-errno.c
diff --git a/shared/nm-utils/nm-errno.h b/shared/nm-glib-aux/nm-errno.h
index d77735a7..d77735a7 100644
--- a/shared/nm-utils/nm-errno.h
+++ b/shared/nm-glib-aux/nm-errno.h
diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-glib-aux/nm-glib.h
index e941e067..e941e067 100644
--- a/shared/nm-utils/nm-glib.h
+++ b/shared/nm-glib-aux/nm-glib.h
diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c
index 6e728e6b..6e728e6b 100644
--- a/shared/nm-utils/nm-hash-utils.c
+++ b/shared/nm-glib-aux/nm-hash-utils.c
diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h
index 1a1e44f5..3f622f99 100644
--- a/shared/nm-utils/nm-hash-utils.h
+++ b/shared/nm-glib-aux/nm-hash-utils.h
@@ -83,7 +83,11 @@ nm_hash_complete_u64 (NMHashState *state)
 	 * from nm_hash_complete() in two ways:
 	 *
 	 * - the type, guint64 vs. guint.
-	 * - nm_hash_complete() never returns zero. */
+	 * - nm_hash_complete() never returns zero.
+	 *
+	 * In practice, nm_hash*() API is implemented via siphash24, so this returns
+	 * the siphash24 value. But that is not guaranteed by the API, and if you need
+	 * siphash24 directly, use c_siphash_*() and nm_hash_siphash42*() API. */
 	return c_siphash_finalize (&state->_state);
 }
 
@@ -287,4 +291,25 @@ gboolean nm_pstr_equal (gconstpointer a, gconstpointer b);
 
 /*****************************************************************************/
 
+#define NM_HASH_OBFUSCATE_PTR_FMT "%016llx"
+
+/* sometimes we want to log a pointer directly, for providing context/information about
+ * the message that get logged. Logging pointer values directly defeats ASLR, so we should
+ * not do that. This returns a "unsigned long long" value that can be used
+ * instead.
+ *
+ * Note that there is a chance that two different pointer values hash to the same obfuscated
+ * value. So beware of that when reviewing logs. However, such a collision is very unlikely. */
+#define nm_hash_obfuscate_ptr(static_seed, val) \
+	({ \
+		NMHashState _h; \
+		const void *_val_obf_ptr = (val); \
+		\
+		nm_hash_init (&_h, (static_seed)); \
+		nm_hash_update_val (&_h, _val_obf_ptr); \
+		(unsigned long long) nm_hash_complete_u64 (&_h); \
+	})
+
+/*****************************************************************************/
+
 #endif /* __NM_HASH_UTILS_H__ */
diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c
index 51312748..51312748 100644
--- a/shared/nm-utils/nm-io-utils.c
+++ b/shared/nm-glib-aux/nm-io-utils.c
diff --git a/shared/nm-utils/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h
index dc72a2a6..dc72a2a6 100644
--- a/shared/nm-utils/nm-io-utils.h
+++ b/shared/nm-glib-aux/nm-io-utils.h
diff --git a/shared/nm-utils/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h
index 5a73231f..5a73231f 100644
--- a/shared/nm-utils/nm-jansson.h
+++ b/shared/nm-glib-aux/nm-jansson.h
diff --git a/shared/nm-utils/nm-logging-fwd.h b/shared/nm-glib-aux/nm-logging-fwd.h
index 900dfff8..900dfff8 100644
--- a/shared/nm-utils/nm-logging-fwd.h
+++ b/shared/nm-glib-aux/nm-logging-fwd.h
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h
index 42299c96..2e46cd2d 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-glib-aux/nm-macros-internal.h
@@ -267,6 +267,8 @@ NM_AUTO_DEFINE_FCN0 (GVariantIter *, _nm_auto_free_variant_iter, g_variant_iter_
 NM_AUTO_DEFINE_FCN0 (GVariantBuilder *, _nm_auto_unref_variant_builder, g_variant_builder_unref)
 #define nm_auto_unref_variant_builder nm_auto(_nm_auto_unref_variant_builder)
 
+#define nm_auto_clear_variant_builder nm_auto(g_variant_builder_clear)
+
 NM_AUTO_DEFINE_FCN0 (GList *, _nm_auto_free_list, g_list_free)
 #define nm_auto_free_list nm_auto(_nm_auto_free_list)
 
@@ -323,6 +325,9 @@ _nm_auto_protect_errno (int *p_saved_errno)
 NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_unref_gsource, g_source_unref);
 #define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource)
 
+NM_AUTO_DEFINE_FCN0 (GMainLoop *, _nm_auto_unref_gmainloop, g_main_loop_unref);
+#define nm_auto_unref_gmainloop nm_auto(_nm_auto_unref_gmainloop)
+
 static inline void
 _nm_auto_freev (gpointer ptr)
 {
@@ -1334,16 +1339,19 @@ _NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_t
 
 /*****************************************************************************/
 
+/* mirrors g_ascii_isspace() and what we consider spaces in general. */
+#define NM_ASCII_SPACES "\t\n\f\r "
+
 #define nm_str_skip_leading_spaces(str) \
 	({ \
-		typeof (*(str)) *_str = (str); \
-		_nm_unused const char *_str_type_check = _str; \
+		typeof (*(str)) *_str_sls = (str); \
+		_nm_unused const char *const _str_type_check = _str_sls; \
 		\
-		if (_str) { \
-			while (g_ascii_isspace (_str[0])) \
-				_str++; \
+		if (_str_sls) { \
+			while (g_ascii_isspace (_str_sls[0])) \
+				_str_sls++; \
 		} \
-		_str; \
+		_str_sls; \
 	})
 
 static inline char *
@@ -1380,6 +1388,34 @@ nm_strstrip_avoid_copy (const char *str, char **str_free)
 	return s;
 }
 
+#define nm_strstrip_avoid_copy_a(alloca_maxlen, str, out_str_free) \
+	({ \
+		const char *_str_ssac = (str); \
+		char **_out_str_free_ssac = (out_str_free); \
+		\
+		G_STATIC_ASSERT_EXPR ((alloca_maxlen) > 0); \
+		\
+		nm_assert ( _out_str_free_ssac || ((alloca_maxlen) > (str ? strlen (str) : 0u))); \
+		nm_assert (!_out_str_free_ssac || !*_out_str_free_ssac); \
+		\
+		if (_str_ssac) { \
+			_str_ssac = nm_str_skip_leading_spaces (_str_ssac); \
+			if (_str_ssac[0] != '\0') { \
+				gsize _l = strlen (_str_ssac); \
+				\
+				if (g_ascii_isspace (_str_ssac[--_l])) { \
+					while (   _l > 0 \
+					       && g_ascii_isspace (_str_ssac[_l - 1])) { \
+						_l--; \
+					} \
+					_str_ssac = nm_strndup_a ((alloca_maxlen), _str_ssac, _l, _out_str_free_ssac); \
+				} \
+			} \
+		} \
+		\
+		_str_ssac; \
+	})
+
 /* g_ptr_array_sort()'s compare function takes pointers to the
  * value. Thus, you cannot use strcmp directly. You can use
  * nm_strcmp_p().
@@ -1458,6 +1494,118 @@ nm_strcmp_p (gconstpointer a, gconstpointer b)
 
 /*****************************************************************************/
 
+/* like g_memdup(). The difference is that the @size argument is of type
+ * gsize, while g_memdup() has type guint. Since, the size of container types
+ * like GArray is guint as well, this means trying to g_memdup() an
+ * array,
+ *    g_memdup (array->data, array->len * sizeof (ElementType))
+ * will lead to integer overflow, if there are more than G_MAXUINT/sizeof(ElementType)
+ * bytes. That seems unnecessarily dangerous to me.
+ * nm_memdup() avoids that, because its size argument is always large enough
+ * to contain all data that a GArray can hold.
+ *
+ * Another minor difference to g_memdup() is that the glib version also
+ * returns %NULL if @data is %NULL. E.g. g_memdup(NULL, 1)
+ * gives %NULL, but nm_memdup(NULL, 1) crashes. I think that
+ * is desirable, because @size MUST be correct at all times. @size
+ * may be zero, but one must not claim to have non-zero bytes when
+ * passing a %NULL @data pointer.
+ */
+static inline gpointer
+nm_memdup (gconstpointer data, gsize size)
+{
+	gpointer p;
+
+	if (size == 0)
+		return NULL;
+	p = g_malloc (size);
+	memcpy (p, data, size);
+	return p;
+}
+
+static inline char *
+_nm_strndup_a_step (char *s, const char *str, gsize len)
+{
+	NM_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation");
+	if (len > 0)
+		strncpy (s, str, len);
+	s[len] = '\0';
+	return s;
+	NM_PRAGMA_WARNING_REENABLE;
+}
+
+/* Similar to g_strndup(), however, if the string (including the terminating
+ * NUL char) fits into alloca_maxlen, this will alloca() the memory.
+ *
+ * It's a mix of strndup() and strndupa(), but deciding based on @alloca_maxlen
+ * which one to use.
+ *
+ * In case malloc() is necessary, @out_str_free will be set (this string
+ * must be freed afterwards). It is permissible to pass %NULL as @out_str_free,
+ * if you ensure that len < alloca_maxlen.
+ *
+ * Note that just like g_strndup(), this always returns a buffer with @len + 1
+ * bytes, even if strlen(@str) is shorter than that (NUL terminated early). We fill
+ * the buffer with strncpy(), which means, that @str is copied up to the first
+ * NUL character and then filled with NUL characters. */
+#define nm_strndup_a(alloca_maxlen, str, len, out_str_free) \
+	({ \
+		const gsize _alloca_maxlen_snd = (alloca_maxlen); \
+		const char *const _str_snd = (str); \
+		const gsize _len_snd = (len); \
+		char **const _out_str_free_snd = (out_str_free); \
+		char *_s_snd; \
+		\
+		G_STATIC_ASSERT_EXPR ((alloca_maxlen) <= 300); \
+		\
+		if (   _out_str_free_snd \
+		    && _len_snd >= _alloca_maxlen_snd) { \
+			_s_snd = g_malloc (_len_snd + 1); \
+			*_out_str_free_snd = _s_snd; \
+		} else { \
+			g_assert (_len_snd < _alloca_maxlen_snd); \
+			_s_snd = g_alloca (_len_snd + 1); \
+		} \
+		_nm_strndup_a_step (_s_snd, _str_snd, _len_snd); \
+	})
+
+/*****************************************************************************/
+
+/* generic macro to convert an int to a (heap allocated) string.
+ *
+ * Usually, an inline function nm_strdup_int64() would be enough. However,
+ * that cannot be used for guint64. So, we would also need nm_strdup_uint64().
+ * This causes subtle error potential, because the caller needs to ensure to
+ * use the right one (and compiler isn't going to help as it silently casts).
+ *
+ * Instead, this generic macro is supposed to handle all integers correctly. */
+#if _NM_CC_SUPPORT_GENERIC
+#define nm_strdup_int(val) \
+	_Generic ((val), \
+	          char:               g_strdup_printf ("%d",   (int)                (val)), \
+	          \
+	          signed char:        g_strdup_printf ("%d",   (signed)             (val)), \
+	          signed short:       g_strdup_printf ("%d",   (signed)             (val)), \
+	          signed:             g_strdup_printf ("%d",   (signed)             (val)), \
+	          signed long:        g_strdup_printf ("%ld",  (signed long)        (val)), \
+	          signed long long:   g_strdup_printf ("%lld", (signed long long)   (val)), \
+	          \
+	          unsigned char:      g_strdup_printf ("%u",   (unsigned)           (val)), \
+	          unsigned short:     g_strdup_printf ("%u",   (unsigned)           (val)), \
+	          unsigned:           g_strdup_printf ("%u",   (unsigned)           (val)), \
+	          unsigned long:      g_strdup_printf ("%lu",  (unsigned long)      (val)), \
+	          unsigned long long: g_strdup_printf ("%llu", (unsigned long long) (val))  \
+	)
+#else
+#define nm_strdup_int(val) \
+	(  (   sizeof (val) == sizeof (guint64) \
+	    && ((typeof (val)) -1) > 0) \
+	 ? g_strdup_printf ("%"G_GUINT64_FORMAT, (guint64) (val)) \
+	 : g_strdup_printf ("%"G_GINT64_FORMAT, (gint64) (val)))
+#endif
+
+/*****************************************************************************/
+
 static inline guint
 nm_encode_version (guint major, guint minor, guint micro)
 {
diff --git a/shared/nm-utils/nm-obj.h b/shared/nm-glib-aux/nm-obj.h
index 4edd1f3e..4edd1f3e 100644
--- a/shared/nm-utils/nm-obj.h
+++ b/shared/nm-glib-aux/nm-obj.h
diff --git a/shared/nm-utils/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c
index d7c7da42..d7c7da42 100644
--- a/shared/nm-utils/nm-random-utils.c
+++ b/shared/nm-glib-aux/nm-random-utils.c
diff --git a/shared/nm-utils/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h
index 15a118d3..15a118d3 100644
--- a/shared/nm-utils/nm-random-utils.h
+++ b/shared/nm-glib-aux/nm-random-utils.h
diff --git a/shared/nm-utils/nm-secret-utils.c b/shared/nm-glib-aux/nm-secret-utils.c
index ec5cc6b1..81f8b5ae 100644
--- a/shared/nm-utils/nm-secret-utils.c
+++ b/shared/nm-glib-aux/nm-secret-utils.c
@@ -30,15 +30,22 @@ void
 nm_explicit_bzero (void *s, gsize n)
 {
 	/* gracefully handle n == 0. This is important, callers rely on it. */
-	if (n > 0) {
-		nm_assert (s);
+	if (n == 0)
+		return;
+
+	nm_assert (s);
+
 #if defined (HAVE_DECL_EXPLICIT_BZERO) && HAVE_DECL_EXPLICIT_BZERO
-		explicit_bzero (s, n);
+	explicit_bzero (s, n);
 #else
-		/* don't bother with a workaround. Use a reasonable glibc. */
-		memset (s, 0, n);
-#endif
+	{
+		volatile guint8 *p = s;
+
+		memset (s, '\0', n);
+		while (n-- > 0)
+			*(p++) = '\0';
 	}
+#endif
 }
 
 /*****************************************************************************/
diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h
index 034ef7bd..034ef7bd 100644
--- a/shared/nm-utils/nm-secret-utils.h
+++ b/shared/nm-glib-aux/nm-secret-utils.h
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index 6a43c670..cf08a77f 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -635,8 +635,16 @@ nm_utils_parse_inaddr (int addr_family,
 	NMIPAddr addrbin;
 	char addrstr_buf[MAX (INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
 
-	if (!nm_utils_parse_inaddr_bin (addr_family, text, &addr_family, &addrbin))
+	g_return_val_if_fail (text, FALSE);
+
+	if (addr_family == AF_UNSPEC)
+		addr_family = strchr (text, ':') ? AF_INET6 : AF_INET;
+	else
+		g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
+
+	if (inet_pton (addr_family, text, &addrbin) != 1)
 		return FALSE;
+
 	NM_SET_OUT (out_addr, g_strdup (inet_ntop (addr_family, &addrbin, addrstr_buf, sizeof (addrstr_buf))));
 	return TRUE;
 }
@@ -954,134 +962,336 @@ comp_l:
 
 /*****************************************************************************/
 
+static void
+_char_lookup_table_init (guint8 lookup[static 256],
+                         const char *candidates)
+{
+	memset (lookup, 0, 256);
+	while (candidates[0] != '\0')
+		lookup[(guint8) ((candidates++)[0])] = 1;
+}
+
+static gboolean
+_char_lookup_has (const guint8 lookup[static 256],
+                  char ch)
+{
+	nm_assert (lookup[(guint8) '\0'] == 0);
+	return lookup[(guint8) ch] != 0;
+}
+
 /**
- * nm_utils_strsplit_set:
+ * nm_utils_strsplit_set_full:
  * @str: the string to split.
- * @delimiters: the set of delimiters. If %NULL, defaults to " \t\n",
- *   like bash's $IFS.
- * @allow_escaping: whether delimiters can be escaped by a backslash
+ * @delimiters: the set of delimiters.
+ * @flags: additional flags for controlling the operation.
  *
  * This is a replacement for g_strsplit_set() which avoids copying
  * each word once (the entire strv array), but instead copies it once
  * and all words point into that internal copy.
  *
- * Another difference from g_strsplit_set() is that this never returns
- * empty words. Multiple delimiters are combined and treated as one.
+ * Note that for @str %NULL and "", this always returns %NULL too. That differs
+ * from g_strsplit_set(), which would return an empty strv array for "".
+ *
+ * Note that g_strsplit_set() returns empty words as well. By default,
+ * nm_utils_strsplit_set_full() strips all empty tokens (that is, repeated
+ * delimiters. With %NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, empty tokens
+ * are not removed.
  *
- * If @allow_escaping is %TRUE, delimiters prefixed by a backslash are
- * not treated as a separator. Such delimiters and their escape
- * character are copied to the current word without unescaping them.
+ * If @flags has %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING, delimiters prefixed
+ * by a backslash are not treated as a separator. Such delimiters and their escape
+ * character are copied to the current word without unescaping them. In general,
+ * nm_utils_strsplit_set_full() does not remove any backslash escape characters
+ * and does not unescaping. It only considers them for skipping to split at
+ * an escaped delimiter.
  *
- * Returns: %NULL if @str is %NULL or contains only delimiters.
- *   Otherwise, a %NULL terminated strv array containing non-empty
- *   words, split at the delimiter characters (delimiter characters
- *   are removed).
+ * Returns: %NULL if @str is %NULL or "".
+ *   If @str only contains delimiters and %NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY
+ *   is not set, it also returns %NULL.
+ *   Otherwise, a %NULL terminated strv array containing the split words.
+ *   (delimiter characters are removed).
  *   The strings to which the result strv array points to are allocated
  *   after the returned result itself. Don't free the strings themself,
  *   but free everything with g_free().
+ *   It is however safe and allowed to modify the indiviual strings,
+ *   like "g_strstrip((char *) iter[0])".
  */
 const char **
-nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping)
-{
-	const char **ptr, **ptr0;
-	gsize alloc_size, plen, i;
-	gsize str_len;
-	char *s0;
+nm_utils_strsplit_set_full (const char *str,
+                            const char *delimiters,
+                            NMUtilsStrsplitSetFlags flags)
+{
+	const char **ptr;
+	gsize num_tokens;
+	gsize i_token;
+	gsize str_len_p1;
+	const char *c_str;
 	char *s;
-	guint8 delimiters_table[256];
-	gboolean escaped = FALSE;
+	guint8 ch_lookup[256];
+	const gboolean f_escaped = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED);
+	const gboolean f_allow_escaping = f_escaped || NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
+	const gboolean f_preserve_empty = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY);
+	const gboolean f_strstrip = NM_FLAGS_HAS (flags, NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
 
 	if (!str)
 		return NULL;
 
-	/* initialize lookup table for delimiter */
-	if (!delimiters)
+	if (!delimiters) {
+		nm_assert_not_reached ();
 		delimiters = " \t\n";
-	memset (delimiters_table, 0, sizeof (delimiters_table));
-	for (i = 0; delimiters[i]; i++)
-		delimiters_table[(guint8) delimiters[i]] = 1;
-
-#define _is_delimiter(ch, delimiters_table, allow_esc, esc) \
-	((delimiters_table)[(guint8) (ch)] != 0 && (!allow_esc || !esc))
+	}
+	_char_lookup_table_init (ch_lookup, delimiters);
 
-#define next_char(p, esc) \
-	G_STMT_START { \
-		if (esc) \
-			esc = FALSE; \
-		else \
-			esc = p[0] == '\\'; \
-		p++; \
-	} G_STMT_END
+	nm_assert (   !f_allow_escaping
+	           || !_char_lookup_has (ch_lookup, '\\'));
 
-	/* skip initial delimiters, and return of the remaining string is
-	 * empty. */
-	while (_is_delimiter (str[0], delimiters_table, allow_escaping, escaped))
-		next_char (str, escaped);
+	if (!f_preserve_empty) {
+		while (_char_lookup_has (ch_lookup, str[0]))
+			str++;
+	}
 
-	if (!str[0])
+	if (!str[0]) {
+		/* We return %NULL here, also with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY.
+		 * That makes nm_utils_strsplit_set_full() with NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY
+		 * different from g_strsplit_set(), which would in this case return an empty array.
+		 * If you need to handle %NULL, and "" specially, then check the input string first. */
 		return NULL;
+	}
 
-	str_len = strlen (str) + 1;
-	alloc_size = 8;
+#define _char_is_escaped(str_start, str_cur) \
+	({ \
+		const char *const _str_start = (str_start); \
+		const char *const _str_cur = (str_cur); \
+		const char *_str_i = (_str_cur); \
+		\
+		while (   _str_i > _str_start \
+		       && _str_i[-1] == '\\') \
+			_str_i--; \
+		(((_str_cur - _str_i) % 2) != 0); \
+	})
+
+	num_tokens = 1;
+	c_str = str;
+	while (TRUE) {
+
+		while (G_LIKELY (!_char_lookup_has (ch_lookup, c_str[0]))) {
+			if (c_str[0] == '\0')
+				goto done1;
+			c_str++;
+		}
 
-	/* we allocate the buffer larger, so to copy @str at the
-	 * end of it as @s0. */
-	ptr0 = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
-	s0 = (char *) &ptr0[alloc_size + 1];
-	memcpy (s0, str, str_len);
+		/* we assume escapings are not frequent. After we found
+		 * this delimiter, check whether it was escaped by counting
+		 * the backslashed before. */
+		if (   f_allow_escaping
+		    && _char_is_escaped (str, c_str)) {
+			/* the delimiter is escaped. This was not an accepted delimiter. */
+			c_str++;
+			continue;
+		}
 
-	plen = 0;
-	s = s0;
-	ptr = ptr0;
+		c_str++;
 
-	while (TRUE) {
-		if (plen >= alloc_size) {
-			const char **ptr_old = ptr;
-
-			/* reallocate the buffer. Note that for now the string
-			 * continues to be in ptr0/s0. We fix that at the end. */
-			alloc_size *= 2;
-			ptr = g_malloc ((sizeof (const char *) * (alloc_size + 1)) + str_len);
-			memcpy (ptr, ptr_old, sizeof (const char *) * plen);
-			if (ptr_old != ptr0)
-				g_free (ptr_old);
+		/* if we drop empty tokens, then we now skip over all consecutive delimiters. */
+		if (!f_preserve_empty) {
+			while (_char_lookup_has (ch_lookup, c_str[0]))
+				c_str++;
+			if (c_str[0] == '\0')
+				break;
 		}
 
-		ptr[plen++] = s;
+		num_tokens++;
+	}
 
-		nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table, allow_escaping, escaped));
+done1:
 
-		while (TRUE) {
-			next_char (s, escaped);
-			if (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped))
-				break;
-			if (s[0] == '\0')
-				goto done;
+	nm_assert (c_str[0] == '\0');
+
+	str_len_p1 = (c_str - str) + 1;
+
+	nm_assert (str[str_len_p1 - 1] == '\0');
+
+	ptr = g_malloc ((sizeof (const char *) * (num_tokens + 1)) + str_len_p1);
+	s = (char *) &ptr[num_tokens + 1];
+	memcpy (s, str, str_len_p1);
+
+	i_token = 0;
+
+	while (TRUE) {
+
+		nm_assert (i_token < num_tokens);
+		ptr[i_token++] = s;
+
+		if (s[0] == '\0') {
+			nm_assert (f_preserve_empty);
+			goto done2;
+		}
+		nm_assert (   f_preserve_empty
+		           || !_char_lookup_has (ch_lookup, s[0]));
+
+		while (!_char_lookup_has (ch_lookup, s[0])) {
+			if (G_UNLIKELY (   s[0] == '\\'
+			                && f_allow_escaping)) {
+				s++;
+				if (s[0] == '\0')
+					goto done2;
+				s++;
+			} else if (s[0] == '\0')
+				goto done2;
+			else
+				s++;
 		}
 
+		nm_assert (_char_lookup_has (ch_lookup, s[0]));
 		s[0] = '\0';
-		next_char (s, escaped);
-		while (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped))
-			next_char (s, escaped);
-		if (s[0] == '\0')
-			break;
+		s++;
+
+		if (!f_preserve_empty) {
+			while (_char_lookup_has (ch_lookup, s[0]))
+				s++;
+			if (s[0] == '\0')
+				goto done2;
+		}
 	}
-done:
-	ptr[plen] = NULL;
 
-	if (ptr != ptr0) {
-		/* we reallocated the buffer. We must copy over the
-		 * string @s0 and adjust the pointers. */
-		s = (char *) &ptr[alloc_size + 1];
-		memcpy (s, s0, str_len);
-		for (i = 0; i < plen; i++)
-			ptr[i] = &s[ptr[i] - s0];
-		g_free (ptr0);
+done2:
+	nm_assert (i_token == num_tokens);
+	ptr[i_token] = NULL;
+
+	if (f_strstrip) {
+		gsize i;
+
+		i_token = 0;
+		for (i = 0; ptr[i]; i++) {
+
+			s = (char *) nm_str_skip_leading_spaces (ptr[i]);
+			if (s[0] != '\0') {
+				char *s_last;
+
+				s_last = &s[strlen (s) - 1];
+				while (   s_last > s
+				       && g_ascii_isspace (s_last[0])
+				       && (   ! f_allow_escaping
+				           || !_char_is_escaped (s, s_last)))
+					(s_last--)[0] = '\0';
+			}
+
+			if (   !f_preserve_empty
+			    && s[0] == '\0')
+				continue;
+
+			ptr[i_token++] = s;
+		}
+
+		if (i_token == 0) {
+			g_free (ptr);
+			return NULL;
+		}
+		ptr[i_token] = NULL;
+	}
+
+	if (f_escaped) {
+		gsize i, j;
+
+		/* We no longer need ch_lookup for its original purpose. Modify it, so it
+		 * can detect the delimiters, '\\', and (optionally) whitespaces. */
+		ch_lookup[((guint8) '\\')] = 1;
+		if (f_strstrip) {
+			for (i = 0; NM_ASCII_SPACES[i]; i++)
+				ch_lookup[((guint8) (NM_ASCII_SPACES[i]))] = 1;
+		}
+
+		for (i_token = 0; ptr[i_token]; i_token++) {
+			s = (char *) ptr[i_token];
+			j = 0;
+			for (i = 0; s[i] != '\0'; ) {
+				if (   s[i] == '\\'
+				    && _char_lookup_has (ch_lookup, s[i + 1]))
+					i++;
+				s[j++] = s[i++];
+			}
+			s[j] = '\0';
+		}
 	}
 
 	return ptr;
 }
 
+/*****************************************************************************/
+
+const char *
+nm_utils_escaped_tokens_escape (const char *str,
+                                const char *delimiters,
+                                char **out_to_free)
+{
+	guint8 ch_lookup[256];
+	char *ret;
+	gsize str_len;
+	gsize alloc_len;
+	gsize n_escapes;
+	gsize i, j;
+	gboolean escape_trailing_space;
+
+	if (!delimiters) {
+		nm_assert (delimiters);
+		delimiters = NM_ASCII_SPACES;
+	}
+
+	if (!str || str[0] == '\0') {
+		*out_to_free = NULL;
+		return str;
+	}
+
+	_char_lookup_table_init (ch_lookup, delimiters);
+
+	/* also mark '\\' as requiring escaping. */
+	ch_lookup[((guint8) '\\')] = 1;
+
+	n_escapes = 0;
+	for (i = 0; str[i] != '\0'; i++) {
+		if (_char_lookup_has (ch_lookup, str[i]))
+			n_escapes++;
+	}
+
+	str_len = i;
+	nm_assert (str_len > 0 && strlen (str) == str_len);
+
+	escape_trailing_space =    !_char_lookup_has (ch_lookup, str[str_len - 1])
+	                        && g_ascii_isspace (str[str_len - 1]);
+
+	if (   n_escapes == 0
+	    && !escape_trailing_space) {
+		*out_to_free = NULL;
+		return str;
+	}
+
+	alloc_len = str_len + n_escapes + ((gsize) escape_trailing_space) + 1;
+	ret = g_new (char, alloc_len);
+
+	j = 0;
+	for (i = 0; str[i] != '\0'; i++) {
+		if (_char_lookup_has (ch_lookup, str[i])) {
+			nm_assert (j < alloc_len);
+			ret[j++] = '\\';
+		}
+		nm_assert (j < alloc_len);
+		ret[j++] = str[i];
+	}
+	if (escape_trailing_space) {
+		nm_assert (!_char_lookup_has (ch_lookup, ret[j - 1]) && g_ascii_isspace (ret[j - 1]));
+		ret[j] = ret[j - 1];
+		ret[j - 1] = '\\';
+		j++;
+	}
+
+	nm_assert (j == alloc_len - 1);
+	ret[j] = '\0';
+
+	*out_to_free = ret;
+	return ret;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_utils_strv_find_first:
  * @list: the strv list to search
@@ -1163,31 +1373,27 @@ int
 _nm_utils_ascii_str_to_bool (const char *str,
                              int default_value)
 {
-	gsize len;
-	char *s = NULL;
+	gs_free char *str_free = NULL;
 
 	if (!str)
 		return default_value;
 
-	while (str[0] && g_ascii_isspace (str[0]))
-		str++;
-
-	if (!str[0])
+	str = nm_strstrip_avoid_copy_a (300, str, &str_free);
+	if (str[0] == '\0')
 		return default_value;
 
-	len = strlen (str);
-	if (g_ascii_isspace (str[len - 1])) {
-		s = g_strdup (str);
-		g_strchomp (s);
-		str = s;
-	}
+	if (   !g_ascii_strcasecmp (str, "true")
+	    || !g_ascii_strcasecmp (str, "yes")
+	    || !g_ascii_strcasecmp (str, "on")
+	    || !g_ascii_strcasecmp (str, "1"))
+		return TRUE;
+
+	if (   !g_ascii_strcasecmp (str, "false")
+	    || !g_ascii_strcasecmp (str, "no")
+	    || !g_ascii_strcasecmp (str, "off")
+	    || !g_ascii_strcasecmp (str, "0"))
+		return FALSE;
 
-	if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
-		default_value = TRUE;
-	else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
-		default_value = FALSE;
-	if (s)
-		g_free (s);
 	return default_value;
 }
 
@@ -2258,7 +2464,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 
 	state = p[0];
 
-	tokens = nm_utils_strsplit_set (p, " ", FALSE);
+	tokens = nm_utils_strsplit_set (p, " ");
 
 	if (NM_PTRARRAY_LEN (tokens) < 20)
 		goto fail;
@@ -2318,6 +2524,57 @@ _nm_utils_strv_sort (const char **strv, gssize len)
 	                   NULL);
 }
 
+/**
+ * _nm_utils_strv_cmp_n:
+ * @strv1: a string array
+ * @len1: the length of @strv1, or -1 for NULL terminated array.
+ * @strv2: a string array
+ * @len2: the length of @strv2, or -1 for NULL terminated array.
+ *
+ * Note that
+ *   - len == -1 && strv == NULL
+ * is treated like a %NULL argument and compares differently from
+ * other arrays.
+ *
+ * Note that an empty array can be represented as
+ *   - len == -1 &&  strv && !strv[0]
+ *   - len ==  0 && !strv
+ *   - len ==  0 &&  strv
+ * These 3 forms all compare equal.
+ * It also means, if length is 0, then it is permissible for strv to be %NULL.
+ *
+ * The strv arrays may contain %NULL strings (if len is positive).
+ *
+ * Returns: 0 if the arrays are equal (using strcmp).
+ **/
+int
+_nm_utils_strv_cmp_n (const char *const*strv1,
+                      gssize len1,
+                      const char *const*strv2,
+                      gssize len2)
+{
+	gsize n, n2;
+
+	if (len1 < 0) {
+		if (!strv1)
+			return (len2 < 0 && !strv2) ? 0 : -1;
+		n = NM_PTRARRAY_LEN (strv1);
+	} else
+		n = len1;
+
+	if (len2 < 0) {
+		if (!strv2)
+			return 1;
+		n2 = NM_PTRARRAY_LEN (strv2);
+	} else
+		n2 = len2;
+
+	NM_CMP_DIRECT (n, n2);
+	for (; n > 0; n--, strv1++, strv2++)
+		NM_CMP_DIRECT_STRCMP0 (*strv1, *strv2);
+	return 0;
+}
+
 /*****************************************************************************/
 
 gpointer
@@ -2361,63 +2618,6 @@ _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...)
 
 /*****************************************************************************/
 
-#define IS_SPACE(c) NM_IN_SET ((c), ' ', '\t')
-
-const char *
-_nm_utils_escape_spaces (const char *str, char **to_free)
-{
-	const char *ptr = str;
-	char *ret, *r;
-
-	*to_free = NULL;
-
-	if (!str)
-		return NULL;
-
-	while (TRUE) {
-		if (!*ptr)
-			return str;
-		if (IS_SPACE (*ptr))
-			break;
-		ptr++;
-	}
-
-	ptr = str;
-	ret = g_new (char, strlen (str) * 2 + 1);
-	r = ret;
-	*to_free = ret;
-	while (*ptr) {
-		if (IS_SPACE (*ptr))
-			*r++ = '\\';
-		*r++ = *ptr++;
-	}
-	*r = '\0';
-
-	return ret;
-}
-
-char *
-_nm_utils_unescape_spaces (char *str)
-{
-	guint i, j = 0;
-
-	if (!str)
-		return NULL;
-
-	for (i = 0; str[i]; i++) {
-		if (str[i] == '\\' && IS_SPACE (str[i+1]))
-			i++;
-		str[j++] = str[i];
-	}
-	str[j] = '\0';
-
-	return str;
-}
-
-#undef IS_SPACE
-
-/*****************************************************************************/
-
 typedef struct {
 	gpointer callback_user_data;
 	GCancellable *cancellable;
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index 65e34959..af3c2f83 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-glib-aux/nm-shared-utils.h
@@ -126,6 +126,16 @@ typedef struct {
 
 extern const NMIPAddr nm_ip_addr_zero;
 
+static inline gboolean
+nm_ip_addr_is_null (int addr_family, gconstpointer addr)
+{
+	nm_assert (addr);
+	if (addr_family == AF_INET6)
+		return IN6_IS_ADDR_UNSPECIFIED ((const struct in6_addr *) addr);
+	nm_assert (addr_family == AF_INET);
+	return ((const struct in_addr *) addr)->s_addr == 0;
+}
+
 static inline void
 nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src)
 {
@@ -248,118 +258,6 @@ gboolean nm_utils_memeqzero (gconstpointer data, gsize length);
 
 /*****************************************************************************/
 
-/* like g_memdup(). The difference is that the @size argument is of type
- * gsize, while g_memdup() has type guint. Since, the size of container types
- * like GArray is guint as well, this means trying to g_memdup() an
- * array,
- *    g_memdup (array->data, array->len * sizeof (ElementType))
- * will lead to integer overflow, if there are more than G_MAXUINT/sizeof(ElementType)
- * bytes. That seems unnecessarily dangerous to me.
- * nm_memdup() avoids that, because its size argument is always large enough
- * to contain all data that a GArray can hold.
- *
- * Another minor difference to g_memdup() is that the glib version also
- * returns %NULL if @data is %NULL. E.g. g_memdup(NULL, 1)
- * gives %NULL, but nm_memdup(NULL, 1) crashes. I think that
- * is desirable, because @size MUST be correct at all times. @size
- * may be zero, but one must not claim to have non-zero bytes when
- * passing a %NULL @data pointer.
- */
-static inline gpointer
-nm_memdup (gconstpointer data, gsize size)
-{
-	gpointer p;
-
-	if (size == 0)
-		return NULL;
-	p = g_malloc (size);
-	memcpy (p, data, size);
-	return p;
-}
-
-static inline char *
-_nm_strndup_a_step (char *s, const char *str, gsize len)
-{
-	NM_PRAGMA_WARNING_DISABLE ("-Wstringop-truncation");
-	if (len > 0)
-		strncpy (s, str, len);
-	s[len] = '\0';
-	return s;
-	NM_PRAGMA_WARNING_REENABLE;
-}
-
-/* Similar to g_strndup(), however, if the string (including the terminating
- * NUL char) fits into alloca_maxlen, this will alloca() the memory.
- *
- * It's a mix of strndup() and strndupa(), but deciding based on @alloca_maxlen
- * which one to use.
- *
- * In case malloc() is necessary, @out_str_free will be set (this string
- * must be freed afterwards). It is permissible to pass %NULL as @out_str_free,
- * if you ensure that len < alloca_maxlen.
- *
- * Note that just like g_strndup(), this always returns a buffer with @len + 1
- * bytes, even if strlen(@str) is shorter than that (NUL terminated early). We fill
- * the buffer with strncpy(), which means, that @str is copied up to the first
- * NUL character and then filled with NUL characters. */
-#define nm_strndup_a(alloca_maxlen, str, len, out_str_free) \
-	({ \
-		const gsize _alloca_maxlen = (alloca_maxlen); \
-		const char *const _str = (str); \
-		const gsize _len = (len); \
-		char **const _out_str_free = (out_str_free); \
-		char *_s; \
-		\
-		G_STATIC_ASSERT_EXPR ((alloca_maxlen) <= 300); \
-		\
-		if (   _out_str_free \
-		    && _len >= _alloca_maxlen) { \
-			_s = g_malloc (_len + 1); \
-			*_out_str_free = _s; \
-		} else { \
-			g_assert (_len < _alloca_maxlen); \
-			_s = g_alloca (_len + 1); \
-		} \
-		_nm_strndup_a_step (_s, _str, _len); \
-	})
-
-/*****************************************************************************/
-
-/* generic macro to convert an int to a (heap allocated) string.
- *
- * Usually, an inline function nm_strdup_int64() would be enough. However,
- * that cannot be used for guint64. So, we would also need nm_strdup_uint64().
- * This causes subtle error potential, because the caller needs to ensure to
- * use the right one (and compiler isn't going to help as it silently casts).
- *
- * Instead, this generic macro is supposed to handle all integers correctly. */
-#if _NM_CC_SUPPORT_GENERIC
-#define nm_strdup_int(val) \
-	_Generic ((val), \
-	          char:               g_strdup_printf ("%d",   (int)                (val)), \
-	          \
-	          signed char:        g_strdup_printf ("%d",   (signed)             (val)), \
-	          signed short:       g_strdup_printf ("%d",   (signed)             (val)), \
-	          signed:             g_strdup_printf ("%d",   (signed)             (val)), \
-	          signed long:        g_strdup_printf ("%ld",  (signed long)        (val)), \
-	          signed long long:   g_strdup_printf ("%lld", (signed long long)   (val)), \
-	          \
-	          unsigned char:      g_strdup_printf ("%u",   (unsigned)           (val)), \
-	          unsigned short:     g_strdup_printf ("%u",   (unsigned)           (val)), \
-	          unsigned:           g_strdup_printf ("%u",   (unsigned)           (val)), \
-	          unsigned long:      g_strdup_printf ("%lu",  (unsigned long)      (val)), \
-	          unsigned long long: g_strdup_printf ("%llu", (unsigned long long) (val))  \
-	)
-#else
-#define nm_strdup_int(val) \
-	(  (   sizeof (val) == sizeof (guint64) \
-	    && ((typeof (val)) -1) > 0) \
-	 ? g_strdup_printf ("%"G_GUINT64_FORMAT, (guint64) (val)) \
-	 : g_strdup_printf ("%"G_GINT64_FORMAT, (gint64) (val)))
-#endif
-
-/*****************************************************************************/
-
 extern const void *const _NM_PTRARRAY_EMPTY[1];
 
 #define NM_PTRARRAY_EMPTY(type) ((type const*) _NM_PTRARRAY_EMPTY)
@@ -434,7 +332,71 @@ int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b);
 
 /*****************************************************************************/
 
-const char **nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping);
+typedef enum {
+	NM_UTILS_STRSPLIT_SET_FLAGS_NONE           = 0,
+	NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY = (1u << 0),
+	NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING = (1u << 1),
+
+	/* If flag is set, does the same as g_strstrip() on the returned tokens.
+	 * This will remove leading and trailing ascii whitespaces (g_ascii_isspace()
+	 * and NM_ASCII_SPACES).
+	 *
+	 * - when combined with !%NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
+	 *   empty tokens will be removed (and %NULL will be returned if that
+	 *   results in an empty string array).
+	 * - when combined with %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING,
+	 *   trailing whitespace escaped by backslash are not stripped. */
+	NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP       = (1u << 2),
+
+	/* This implies %NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING.
+	 *
+	 * This will do a final run over all tokens and remove all backslash
+	 * escape characters that
+	 *   - precede a delimiter.
+	 *   - precede a backslash.
+	 *   - preceed a whitespace (with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP).
+	 *
+	 *  Note that with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP, it is only
+	 *  necessary to escape the very last whitespace (if the delimiters
+	 *  are not whitespace themself). So, technically, it would be sufficient
+	 *  to only unescape a backslash before the last whitespace and the user
+	 *  still could express everything. However, such a rule would be complicated
+	 *  to understand, so when using backslash escaping with nm_utils_strsplit_set_full(),
+	 *  then all characters (including backslash) are treated verbatim, except:
+	 *
+	 *    - "\\$DELIMITER" (escaped delimiter)
+	 *    - "\\\\" (escaped backslash)
+	 *    - "\\$SPACE" (escaped space) (with %NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP).
+	 *
+	 * Note that all other escapes like "\\n" or "\\001" are left alone.
+	 * That makes the escaping/unescaping rules simple. Also, for the most part
+	 * a text is just taken as-is, with little additional rules. Only backslashes
+	 * need extra care, and then only if they proceed one of the relevant characters.
+	 */
+	NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED        = (1u << 3),
+} NMUtilsStrsplitSetFlags;
+
+const char **nm_utils_strsplit_set_full (const char *str,
+                                         const char *delimiter,
+                                         NMUtilsStrsplitSetFlags flags);
+
+static inline const char **
+nm_utils_strsplit_set_with_empty (const char *str,
+                                  const char *delimiters)
+{
+	/* this returns the same result as g_strsplit_set(str, delimiters, -1), except
+	 * it does not deep-clone the strv array.
+	 * Also, for @str == "", this returns %NULL while g_strsplit_set() would return
+	 * an empty strv array. */
+	return nm_utils_strsplit_set_full (str, delimiters, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY);
+}
+
+static inline const char **
+nm_utils_strsplit_set (const char *str,
+                       const char *delimiters)
+{
+	return nm_utils_strsplit_set_full (str, delimiters, NM_UTILS_STRSPLIT_SET_FLAGS_NONE);
+}
 
 gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle);
 
@@ -445,6 +407,70 @@ char **_nm_utils_strv_cleanup (char **strv,
 
 /*****************************************************************************/
 
+static inline const char **
+nm_utils_escaped_tokens_split (const char *str,
+                               const char *delimiters)
+{
+	return nm_utils_strsplit_set_full (str,
+	                                   delimiters,
+	                                     NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
+	                                   | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP);
+}
+
+const char *nm_utils_escaped_tokens_escape (const char *str,
+                                            const char *delimiters,
+                                            char **out_to_free);
+
+static inline GString *
+nm_utils_escaped_tokens_escape_gstr_assert (const char *str,
+                                            const char *delimiters,
+                                            GString *gstring)
+{
+#if NM_MORE_ASSERTS > 0
+
+	/* Just appends @str to @gstring, but also assert that
+	 * no escaping is necessary.
+	 *
+	 * Use nm_utils_escaped_tokens_escape_gstr_assert() instead
+	 * of nm_utils_escaped_tokens_escape_gstr(), if you *know* that
+	 * @str contains no delimiters, no backslashes, and no trailing
+	 * whitespace that requires escaping. */
+
+	nm_assert (str);
+	nm_assert (gstring);
+	nm_assert (delimiters);
+
+	{
+		gs_free char *str_to_free = NULL;
+		const char *str0;
+
+		str0 = nm_utils_escaped_tokens_escape (str, delimiters, &str_to_free);
+		nm_assert (str0 == str);
+		nm_assert (!str_to_free);
+	}
+#endif
+
+	g_string_append (gstring, str);
+	return gstring;
+}
+
+static inline GString *
+nm_utils_escaped_tokens_escape_gstr (const char *str,
+                                     const char *delimiters,
+                                     GString *gstring)
+{
+	gs_free char *str_to_free = NULL;
+
+	nm_assert (str);
+	nm_assert (gstring);
+
+	g_string_append (gstring,
+	                 nm_utils_escaped_tokens_escape (str, delimiters, &str_to_free));
+	return gstring;
+}
+
+/*****************************************************************************/
+
 #define NM_UTILS_CHECKSUM_LENGTH_MD5          16
 #define NM_UTILS_CHECKSUM_LENGTH_SHA1         20
 #define NM_UTILS_CHECKSUM_LENGTH_SHA256       32
@@ -941,6 +967,18 @@ gboolean nm_utils_hash_table_equal (const GHashTable *a,
 void _nm_utils_strv_sort (const char **strv, gssize len);
 #define nm_utils_strv_sort(strv, len) _nm_utils_strv_sort (NM_CAST_STRV_MC (strv), len)
 
+int _nm_utils_strv_cmp_n (const char *const*strv1,
+                          gssize len1,
+                          const char *const*strv2,
+                          gssize len2);
+
+static inline gboolean
+_nm_utils_strv_equal (char **strv1, char **strv2)
+{
+	return _nm_utils_strv_cmp_n ((const char *const*) strv1, -1,
+	                             (const char *const*) strv2, -1) == 0;
+}
+
 /*****************************************************************************/
 
 #define NM_UTILS_NS_PER_SECOND   ((gint64) 1000000000)
@@ -1072,11 +1110,6 @@ void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...);
 
 /*****************************************************************************/
 
-const char *_nm_utils_escape_spaces (const char *str, char **to_free);
-char *_nm_utils_unescape_spaces (char *str);
-
-/*****************************************************************************/
-
 typedef void (*NMUtilsInvokeOnIdleCallback) (gpointer callback_user_data,
                                              GCancellable *cancellable);
 
diff --git a/shared/nm-utils/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c
index ae526c34..ae526c34 100644
--- a/shared/nm-utils/nm-time-utils.c
+++ b/shared/nm-glib-aux/nm-time-utils.c
diff --git a/shared/nm-utils/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h
index 7e4f4f25..7e4f4f25 100644
--- a/shared/nm-utils/nm-time-utils.h
+++ b/shared/nm-glib-aux/nm-time-utils.h
diff --git a/shared/nm-dispatcher-api.h b/shared/nm-libnm-core-aux/nm-dispatcher-api.h
index b1f28e71..e6d0d92f 100644
--- a/shared/nm-dispatcher-api.h
+++ b/shared/nm-libnm-core-aux/nm-dispatcher-api.h
@@ -18,6 +18,9 @@
  * Copyright (C) 2008 - 2012 Red Hat, Inc.
  */
 
+#ifndef __NM_DISPACHER_API_H__
+#define __NM_DISPACHER_API_H__
+
 #define NMD_SCRIPT_DIR_DEFAULT  NMCONFDIR "/dispatcher.d"
 #define NMD_SCRIPT_DIR_PRE_UP   NMD_SCRIPT_DIR_DEFAULT "/pre-up.d"
 #define NMD_SCRIPT_DIR_PRE_DOWN NMD_SCRIPT_DIR_DEFAULT "/pre-down.d"
@@ -59,3 +62,4 @@ typedef enum {
 	DISPATCH_RESULT_TIMEOUT = 4,
 } DispatchResult;
 
+#endif /* __NM_DISPACHER_API_H__ */
diff --git a/shared/nm-common-macros.h b/shared/nm-libnm-core-intern/nm-common-macros.h
index f5aa3a1e..f5aa3a1e 100644
--- a/shared/nm-common-macros.h
+++ b/shared/nm-libnm-core-intern/nm-common-macros.h
diff --git a/shared/nm-ethtool-utils.c b/shared/nm-libnm-core-intern/nm-ethtool-utils.c
index 3313274a..3313274a 100644
--- a/shared/nm-ethtool-utils.c
+++ b/shared/nm-libnm-core-intern/nm-ethtool-utils.c
diff --git a/shared/nm-ethtool-utils.h b/shared/nm-libnm-core-intern/nm-ethtool-utils.h
index 5f22a9a0..5f22a9a0 100644
--- a/shared/nm-ethtool-utils.h
+++ b/shared/nm-libnm-core-intern/nm-ethtool-utils.h
diff --git a/shared/nm-libnm-core-intern/nm-libnm-core-utils.c b/shared/nm-libnm-core-intern/nm-libnm-core-utils.c
new file mode 100644
index 00000000..d1e5f754
--- /dev/null
+++ b/shared/nm-libnm-core-intern/nm-libnm-core-utils.c
@@ -0,0 +1,76 @@
+/*
+ * This 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 of the License, or (at your option) any later version.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ */
+
+#include "nm-default.h"
+
+#include "nm-libnm-core-utils.h"
+
+/*****************************************************************************/
+
+gboolean
+nm_utils_vlan_priority_map_parse_str (NMVlanPriorityMap map_type,
+                                      const char *str,
+                                      gboolean allow_wildcard_to,
+                                      guint32 *out_from,
+                                      guint32 *out_to,
+                                      gboolean *out_has_wildcard_to)
+{
+	const char *s2;
+	gint64 v1, v2;
+
+	nm_assert (str);
+
+	s2 = strchr (str, ':');
+
+	if (!s2) {
+		if (!allow_wildcard_to)
+			return FALSE;
+		v1 = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1);
+		v2 = -1;
+	} else {
+		gs_free char *s1_free = NULL;
+		gsize s1_len = (s2 - str);
+
+		s2 = nm_str_skip_leading_spaces (&s2[1]);
+		if (   s2[0] == '\0'
+		    || (   s2[0] == '*'
+		        && NM_STRCHAR_ALL (&s2[1], ch, g_ascii_isspace (ch)))) {
+			if (!allow_wildcard_to)
+				return FALSE;
+			v2 = -1;
+		} else {
+			v2 = _nm_utils_ascii_str_to_int64 (s2, 10, 0, G_MAXUINT32, -1);
+			if (   v2 < 0
+			    || (guint32) v2 > nm_utils_vlan_priority_map_get_max_prio (map_type, FALSE))
+				return FALSE;
+		}
+
+		v1 = _nm_utils_ascii_str_to_int64 (nm_strndup_a (100, str, s1_len, &s1_free),
+		                                   10, 0, G_MAXUINT32, -1);
+	}
+
+	if (   v1 < 0
+	    || (guint32) v1 > nm_utils_vlan_priority_map_get_max_prio (map_type, TRUE))
+		return FALSE;
+
+	NM_SET_OUT (out_from, v1);
+	NM_SET_OUT (out_to,   v2 < 0
+	                    ? 0u
+	                    : (guint) v2);
+	NM_SET_OUT (out_has_wildcard_to, v2 < 0);
+	return TRUE;
+}
diff --git a/shared/nm-libnm-core-intern/nm-libnm-core-utils.h b/shared/nm-libnm-core-intern/nm-libnm-core-utils.h
new file mode 100644
index 00000000..35d6c5ad
--- /dev/null
+++ b/shared/nm-libnm-core-intern/nm-libnm-core-utils.h
@@ -0,0 +1,113 @@
+/*
+ * This 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 of the License, or (at your option) any later version.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __NM_LIBNM_SHARED_UTILS_H__
+#define __NM_LIBNM_SHARED_UTILS_H__
+
+/****************************************************************************/
+
+#include "nm-setting-bridge.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-ip-config.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-sriov.h"
+#include "nm-setting-team.h"
+#include "nm-setting-vlan.h"
+#include "nm-setting-wireguard.h"
+
+/****************************************************************************/
+
+#define nm_auto_unref_ip_address nm_auto (_nm_ip_address_unref)
+NM_AUTO_DEFINE_FCN0 (NMIPAddress *, _nm_ip_address_unref, nm_ip_address_unref)
+
+#define nm_auto_unref_ip_route nm_auto (_nm_auto_unref_ip_route)
+NM_AUTO_DEFINE_FCN0 (NMIPRoute *, _nm_auto_unref_ip_route, nm_ip_route_unref)
+
+#define nm_auto_unref_ip_routing_rule nm_auto(_nm_auto_unref_ip_routing_rule)
+NM_AUTO_DEFINE_FCN0 (NMIPRoutingRule *, _nm_auto_unref_ip_routing_rule, nm_ip_routing_rule_unref)
+
+#define nm_auto_unref_sriov_vf nm_auto (_nm_auto_unref_sriov_vf)
+NM_AUTO_DEFINE_FCN0 (NMSriovVF *, _nm_auto_unref_sriov_vf, nm_sriov_vf_unref)
+
+#define nm_auto_unref_tc_qdisc nm_auto (_nm_auto_unref_tc_qdisc)
+NM_AUTO_DEFINE_FCN0 (NMTCQdisc *, _nm_auto_unref_tc_qdisc, nm_tc_qdisc_unref)
+
+#define nm_auto_unref_tc_tfilter nm_auto (_nm_auto_unref_tc_tfilter)
+NM_AUTO_DEFINE_FCN0 (NMTCTfilter *, _nm_auto_unref_tc_tfilter, nm_tc_tfilter_unref)
+
+#define nm_auto_unref_bridge_vlan nm_auto (_nm_auto_unref_bridge_vlan)
+NM_AUTO_DEFINE_FCN0 (NMBridgeVlan *, _nm_auto_unref_bridge_vlan, nm_bridge_vlan_unref)
+
+#define nm_auto_unref_team_link_watcher nm_auto (_nm_auto_unref_team_link_watcher)
+NM_AUTO_DEFINE_FCN0 (NMTeamLinkWatcher *, _nm_auto_unref_team_link_watcher, nm_team_link_watcher_unref)
+
+#define nm_auto_unref_wgpeer nm_auto (_nm_auto_unref_wgpeer)
+NM_AUTO_DEFINE_FCN0 (NMWireGuardPeer *, _nm_auto_unref_wgpeer, nm_wireguard_peer_unref)
+
+/****************************************************************************/
+
+static inline guint32
+nm_utils_vlan_priority_map_get_max_prio (NMVlanPriorityMap map, gboolean from)
+{
+	if (map == NM_VLAN_INGRESS_MAP) {
+		return   from
+		       ? 7u /* MAX_8021P_PRIO */
+		       : (guint32) G_MAXUINT32 /* MAX_SKB_PRIO */;
+	}
+	nm_assert (map == NM_VLAN_EGRESS_MAP);
+	return   from
+	       ? (guint32) G_MAXUINT32 /* MAX_SKB_PRIO */
+	       : 7u /* MAX_8021P_PRIO */;
+}
+
+gboolean nm_utils_vlan_priority_map_parse_str (NMVlanPriorityMap map_type,
+                                               const char *str,
+                                               gboolean allow_wildcard_to,
+                                               guint32 *out_from,
+                                               guint32 *out_to,
+                                               gboolean *out_has_wildcard_to);
+
+/*****************************************************************************/
+
+static inline int
+nm_setting_ip_config_get_addr_family (NMSettingIPConfig *s_ip)
+{
+	if (NM_IS_SETTING_IP4_CONFIG (s_ip))
+		return AF_INET;
+	if (NM_IS_SETTING_IP6_CONFIG (s_ip))
+		return AF_INET6;
+	g_return_val_if_reached (AF_UNSPEC);
+}
+
+/*****************************************************************************/
+
+/* The maximum MTU for infiniband.
+ *
+ * This is both in transport-mode "datagram" and "connected"
+ * and they both have the same maximum define.
+ *
+ * Note that in the past, MTU in "datagram" mode was restricted
+ * to 2044 bytes. That is no longer the case and we accept large
+ * MTUs.
+ *
+ * This define is the maxiumum for the MTU in a connection profile (the
+ * setting). Whether large MTUs can be configured later (at activation time)
+ * depends on other factors. */
+#define NM_INFINIBAND_MAX_MTU ((guint) 65520)
+
+#endif /* __NM_LIBNM_SHARED_UTILS_H__ */
diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c
index e666e0b2..8d1d4ecd 100644
--- a/shared/nm-meta-setting.c
+++ b/shared/nm-meta-setting.c
@@ -82,6 +82,7 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = {
 		.uri_func               = nm_setting_802_1x_get_ca_cert_uri,
 		.passwd_func            = nm_setting_802_1x_get_ca_cert_password,
 		.pwflag_func            = nm_setting_802_1x_get_ca_cert_password_flags,
+		.set_cert_func          = nm_setting_802_1x_set_ca_cert,
 		.file_suffix            = "ca-cert",
 	},
 
@@ -94,6 +95,7 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = {
 		.uri_func               = nm_setting_802_1x_get_phase2_ca_cert_uri,
 		.passwd_func            = nm_setting_802_1x_get_phase2_ca_cert_password,
 		.pwflag_func            = nm_setting_802_1x_get_phase2_ca_cert_password_flags,
+		.set_cert_func          = nm_setting_802_1x_set_phase2_ca_cert,
 		.file_suffix            = "inner-ca-cert",
 	},
 
@@ -106,6 +108,7 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = {
 		.uri_func               = nm_setting_802_1x_get_client_cert_uri,
 		.passwd_func            = nm_setting_802_1x_get_client_cert_password,
 		.pwflag_func            = nm_setting_802_1x_get_client_cert_password_flags,
+		.set_cert_func          = nm_setting_802_1x_set_client_cert,
 		.file_suffix            = "client-cert",
 	},
 
@@ -118,6 +121,7 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = {
 		.uri_func               = nm_setting_802_1x_get_phase2_client_cert_uri,
 		.passwd_func            = nm_setting_802_1x_get_phase2_client_cert_password,
 		.pwflag_func            = nm_setting_802_1x_get_phase2_client_cert_password_flags,
+		.set_cert_func          = nm_setting_802_1x_set_phase2_client_cert,
 		.file_suffix            = "inner-client-cert",
 	},
 
@@ -130,7 +134,9 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = {
 		.uri_func               = nm_setting_802_1x_get_private_key_uri,
 		.passwd_func            = nm_setting_802_1x_get_private_key_password,
 		.pwflag_func            = nm_setting_802_1x_get_private_key_password_flags,
+		.set_private_key_func   = nm_setting_802_1x_set_private_key,
 		.file_suffix            = "private-key",
+		.is_secret              = TRUE,
 	},
 
 	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = {
@@ -142,7 +148,9 @@ const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[] = {
 		.uri_func               = nm_setting_802_1x_get_phase2_private_key_uri,
 		.passwd_func            = nm_setting_802_1x_get_phase2_private_key_password,
 		.pwflag_func            = nm_setting_802_1x_get_phase2_private_key_password_flags,
+		.set_private_key_func   = nm_setting_802_1x_set_phase2_private_key,
 		.file_suffix            = "inner-private-key",
+		.is_secret              = TRUE,
 	},
 
 	[NM_SETTING_802_1X_SCHEME_TYPE_UNKNOWN] = { NULL },
diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h
index 18727a16..73ee103e 100644
--- a/shared/nm-meta-setting.h
+++ b/shared/nm-meta-setting.h
@@ -89,7 +89,19 @@ typedef struct {
 	const char *           (*uri_func)    (NMSetting8021x *setting);
 	const char *           (*passwd_func) (NMSetting8021x *setting);
 	NMSettingSecretFlags   (*pwflag_func) (NMSetting8021x *setting);
+	gboolean (*set_cert_func) (NMSetting8021x *setting,
+	                           const char *value,
+	                           NMSetting8021xCKScheme scheme,
+	                           NMSetting8021xCKFormat *out_format,
+	                           GError **error);
+	gboolean (*set_private_key_func) (NMSetting8021x *setting,
+	                                  const char *value,
+	                                  const char *password,
+	                                  NMSetting8021xCKScheme scheme,
+	                                  NMSetting8021xCKFormat *out_format,
+	                                  GError **error);
 	const char *file_suffix;
+	bool is_secret:1;
 } NMSetting8021xSchemeVtable;
 
 extern const NMSetting8021xSchemeVtable nm_setting_8021x_scheme_vtable[_NM_SETTING_802_1X_SCHEME_TYPE_NUM + 1];
diff --git a/shared/nm-utils/c-list-util.c b/shared/nm-std-aux/c-list-util.c
index 44ca26a5..44ca26a5 100644
--- a/shared/nm-utils/c-list-util.c
+++ b/shared/nm-std-aux/c-list-util.c
diff --git a/shared/nm-utils/c-list-util.h b/shared/nm-std-aux/c-list-util.h
index e87f1c19..648bacc7 100644
--- a/shared/nm-utils/c-list-util.h
+++ b/shared/nm-std-aux/c-list-util.h
@@ -40,4 +40,27 @@ void c_list_sort (CList *head,
                   CListSortCmp cmp,
                   const void *user_data);
 
+/* c_list_length_is:
+ * @list: the #CList list head
+ * @check_len: the length to compare
+ *
+ * Returns: basically the same as (c_list_length (@list) == @check_len),
+ *   but does not require to iterate the entire list first. There is only
+ *   one real use: to find out whether there is exactly one element in the
+ *   list, by passing @check_len as 1.
+ */
+static inline int
+c_list_length_is (const CList *list, unsigned long check_len) {
+	unsigned long n = 0;
+	const CList *iter;
+
+	c_list_for_each (iter, list) {
+		++n;
+		if (n > check_len)
+			return 0;
+	}
+
+	return n == check_len;
+}
+
 #endif /* __C_LIST_UTIL_H__ */
diff --git a/shared/nm-dbus-compat.h b/shared/nm-std-aux/nm-dbus-compat.h
index dd97b5fd..dd97b5fd 100644
--- a/shared/nm-dbus-compat.h
+++ b/shared/nm-std-aux/nm-dbus-compat.h
diff --git a/shared/nm-utils/unaligned.h b/shared/nm-std-aux/unaligned.h
index 00c17f87..00c17f87 100644
--- a/shared/nm-utils/unaligned.h
+++ b/shared/nm-std-aux/unaligned.h
diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c
index 509b235a..02d71593 100644
--- a/shared/nm-test-utils-impl.c
+++ b/shared/nm-test-utils-impl.c
@@ -23,7 +23,7 @@
 #include <sys/wait.h>
 
 #include "NetworkManager.h"
-#include "nm-dbus-compat.h"
+#include "nm-std-aux/nm-dbus-compat.h"
 
 #include "nm-test-libnm-utils.h"
 
diff --git a/shared/nm-utils/nm-udev-utils.c b/shared/nm-udev-aux/nm-udev-utils.c
index 5d0919b3..5d0919b3 100644
--- a/shared/nm-utils/nm-udev-utils.c
+++ b/shared/nm-udev-aux/nm-udev-utils.c
diff --git a/shared/nm-utils/nm-udev-utils.h b/shared/nm-udev-aux/nm-udev-utils.h
index 911e8a27..911e8a27 100644
--- a/shared/nm-utils/nm-udev-utils.h
+++ b/shared/nm-udev-aux/nm-udev-utils.h
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index c5ea5e3f..d0ec7d9f 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -1144,12 +1144,13 @@ nmtst_uuid_generate (void)
 
 #endif
 
-#define NMTST_SWAP(x,y) \
+#define NMTST_SWAP(x, y) \
 	G_STMT_START { \
-		char __nmtst_swap_temp[sizeof(x) == sizeof(y) ? (signed) sizeof(x) : -1]; \
-		memcpy(__nmtst_swap_temp, &y, sizeof(x)); \
-		memcpy(&y,                &x, sizeof(x)); \
-		memcpy(&x, __nmtst_swap_temp, sizeof(x)); \
+		char __nmtst_swap_temp[sizeof((x)) == sizeof((y)) ? (signed) sizeof((x)) : -1]; \
+		\
+		memcpy(__nmtst_swap_temp, &(y),              sizeof (__nmtst_swap_temp)); \
+		memcpy(&(y),              &(x),              sizeof (__nmtst_swap_temp)); \
+		memcpy(&(x),              __nmtst_swap_temp, sizeof (__nmtst_swap_temp)); \
 	} G_STMT_END
 
 #define nmtst_assert_str_has_substr(str, substr) \
diff --git a/shared/nm-utils/nm-vpn-editor-plugin-call.h b/shared/nm-utils/nm-vpn-editor-plugin-call.h
index 78d041df..fd982acf 100644
--- a/shared/nm-utils/nm-vpn-editor-plugin-call.h
+++ b/shared/nm-utils/nm-vpn-editor-plugin-call.h
@@ -32,7 +32,7 @@
 #include <NetworkManager.h>
 
 /* we make use of other internal header files, you need those too. */
-#include "nm-macros-internal.h"
+#include "nm-glib-aux/nm-macros-internal.h"
 
 /*****************************************************************************/
 
diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c
index d53b21d9..83cffd7f 100644
--- a/shared/nm-utils/tests/test-shared-general.c
+++ b/shared/nm-utils/tests/test-shared-general.c
@@ -21,9 +21,9 @@
 
 #include "nm-default.h"
 
-#include "nm-utils/nm-time-utils.h"
-#include "nm-utils/nm-random-utils.h"
-#include "nm-utils/unaligned.h"
+#include "nm-std-aux/unaligned.h"
+#include "nm-glib-aux/nm-random-utils.h"
+#include "nm-glib-aux/nm-time-utils.h"
 
 #include "nm-utils/nm-test-utils.h"
 
@@ -248,6 +248,201 @@ test_unaligned (void)
 
 /*****************************************************************************/
 
+static void
+_strv_cmp_fuzz_input (const char *const*in,
+                      gssize l,
+                      const char ***out_strv_free_shallow,
+                      char ***out_strv_free_deep,
+                      const char *const* *out_s1,
+                      const char *const* *out_s2)
+{
+	const char **strv;
+	gsize i;
+
+	/* Fuzz the input argument. It will return two output arrays that are semantically
+	 * equal the input. */
+
+	if (nmtst_get_rand_bool ()) {
+		char **ss;
+
+		if (l < 0)
+			ss = g_strdupv ((char **) in);
+		else if (l == 0) {
+			ss =   nmtst_get_rand_bool ()
+			     ? NULL
+			     : g_new0 (char *, 1);
+		} else {
+			ss = nm_memdup (in, sizeof (const char *) * l);
+			for (i = 0; i < (gsize) l; i++)
+				ss[i] = g_strdup (ss[i]);
+		}
+		strv = (const char **) ss;
+		*out_strv_free_deep = ss;
+	} else {
+		if (l < 0) {
+			strv =   in
+			       ? nm_memdup (in, sizeof (const char *) * (NM_PTRARRAY_LEN (in) + 1))
+			       : NULL;
+		} else if (l == 0) {
+			strv =   nmtst_get_rand_bool ()
+			       ? NULL
+			       : g_new0 (const char *, 1);
+		} else
+			strv = nm_memdup (in, sizeof (const char *) * l);
+		*out_strv_free_shallow = strv;
+	}
+
+	*out_s1 = in;
+	*out_s2 = strv;
+
+	if (nmtst_get_rand_bool ()) {
+		/* randomly swap the original and the clone. That means, out_s1 is either
+		 * the input argument (as-is) or the sementically equal clone. */
+		NMTST_SWAP (*out_s1, *out_s2);
+	}
+	if (nmtst_get_rand_bool ()) {
+		/* randomly make s1 and s2 the same. This is for testing that
+		 * comparing two identical pointers yields the same result. */
+		*out_s2 = *out_s1;
+	}
+}
+
+static void
+_strv_cmp_free_deep (char **strv,
+                     gssize len)
+{
+	gssize i;
+
+	if (strv) {
+		if (len < 0)
+			g_strfreev (strv);
+		else {
+			for (i = 0; i < len; i++)
+				g_free (strv[i]);
+			g_free (strv);
+		}
+	}
+}
+
+static void
+test_strv_cmp (void)
+{
+	const char *const strv0[1] = { };
+	const char *const strv1[2] = { "", };
+
+#define _STRV_CMP(a1, l1, a2, l2, equal) \
+	G_STMT_START { \
+		gssize _l1 = (l1); \
+		gssize _l2 = (l2); \
+		const char *const*_a1; \
+		const char *const*_a2; \
+		const char *const*_a1x; \
+		const char *const*_a2x; \
+		char **_a1_free_deep = NULL; \
+		char **_a2_free_deep = NULL; \
+		gs_free const char **_a1_free_shallow = NULL; \
+		gs_free const char **_a2_free_shallow = NULL; \
+		int _c1, _c2; \
+		\
+		_strv_cmp_fuzz_input ((a1), _l1, &_a1_free_shallow, &_a1_free_deep, &_a1, &_a1x); \
+		_strv_cmp_fuzz_input ((a2), _l2, &_a2_free_shallow, &_a2_free_deep, &_a2, &_a2x); \
+		\
+		_c1 = _nm_utils_strv_cmp_n (_a1, _l1, _a2, _l2); \
+		_c2 = _nm_utils_strv_cmp_n (_a2, _l2, _a1, _l1); \
+		if (equal) { \
+			g_assert_cmpint (_c1, ==, 0); \
+			g_assert_cmpint (_c2, ==, 0); \
+		} else { \
+			g_assert_cmpint (_c1, ==, -1); \
+			g_assert_cmpint (_c2, ==, 1); \
+		} \
+		\
+		/* Compare with self. _strv_cmp_fuzz_input() randomly swapped the arguments (_a1 and _a1x).
+		 * Either way, the arrays must compare equal to their semantically equal alternative. */ \
+		g_assert_cmpint (_nm_utils_strv_cmp_n (_a1, _l1, _a1x, _l1), ==, 0); \
+		g_assert_cmpint (_nm_utils_strv_cmp_n (_a2, _l2, _a2x, _l2), ==, 0); \
+		\
+		_strv_cmp_free_deep (_a1_free_deep, _l1); \
+		_strv_cmp_free_deep (_a2_free_deep, _l2); \
+	} G_STMT_END
+
+	_STRV_CMP (NULL,  -1, NULL,  -1, TRUE);
+
+	_STRV_CMP (NULL,  -1, NULL,   0, FALSE);
+	_STRV_CMP (NULL,  -1, strv0,  0, FALSE);
+	_STRV_CMP (NULL,  -1, strv0, -1, FALSE);
+
+	_STRV_CMP (NULL,   0, NULL,   0, TRUE);
+	_STRV_CMP (NULL,   0, strv0,  0, TRUE);
+	_STRV_CMP (NULL,   0, strv0, -1, TRUE);
+	_STRV_CMP (strv0,  0, strv0,  0, TRUE);
+	_STRV_CMP (strv0,  0, strv0, -1, TRUE);
+	_STRV_CMP (strv0, -1, strv0, -1, TRUE);
+
+	_STRV_CMP (NULL,   0, strv1, -1, FALSE);
+	_STRV_CMP (NULL,   0, strv1,  1, FALSE);
+	_STRV_CMP (strv0,  0, strv1, -1, FALSE);
+	_STRV_CMP (strv0,  0, strv1,  1, FALSE);
+	_STRV_CMP (strv0, -1, strv1, -1, FALSE);
+	_STRV_CMP (strv0, -1, strv1,  1, FALSE);
+
+	_STRV_CMP (strv1, -1, strv1,  1, TRUE);
+	_STRV_CMP (strv1,  1, strv1,  1, TRUE);
+}
+
+/*****************************************************************************/
+
+static void
+_do_strstrip_avoid_copy (const char *str)
+{
+	gs_free char *str1 = g_strdup (str);
+	gs_free char *str2 = g_strdup (str);
+	gs_free char *str3 = NULL;
+	gs_free char *str4 = NULL;
+	const char *s3;
+	const char *s4;
+
+	if (str1)
+		g_strstrip (str1);
+
+	nm_strstrip (str2);
+
+	g_assert_cmpstr (str1, ==, str2);
+
+	s3 = nm_strstrip_avoid_copy (str, &str3);
+	g_assert_cmpstr (str1, ==, s3);
+
+	s4 = nm_strstrip_avoid_copy_a (10, str, &str4);
+	g_assert_cmpstr (str1, ==, s4);
+	g_assert (!str == !s4);
+	g_assert (!s4 || strlen (s4) <= strlen (str));
+	if (s4 && s4 == &str[strlen (str) - strlen (s4)]) {
+		g_assert (!str4);
+		g_assert (s3 == s4);
+	} else if (s4 && strlen (s4) >= 10) {
+		g_assert (str4);
+		g_assert (s4 == str4);
+	} else
+		g_assert (!str4);
+
+	if (!nm_streq0 (str1, str))
+		_do_strstrip_avoid_copy (str1);
+}
+
+static void
+test_strstrip_avoid_copy (void)
+{
+	_do_strstrip_avoid_copy (NULL);
+	_do_strstrip_avoid_copy ("");
+	_do_strstrip_avoid_copy (" ");
+	_do_strstrip_avoid_copy (" a ");
+	_do_strstrip_avoid_copy (" 012345678 ");
+	_do_strstrip_avoid_copy (" 0123456789 ");
+	_do_strstrip_avoid_copy (" 01234567890 ");
+	_do_strstrip_avoid_copy (" 012345678901 ");
+}
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
@@ -261,6 +456,8 @@ int main (int argc, char **argv)
 	g_test_add_func ("/general/test_nm_strndup_a", test_nm_strndup_a);
 	g_test_add_func ("/general/test_nm_ip4_addr_is_localhost", test_nm_ip4_addr_is_localhost);
 	g_test_add_func ("/general/test_unaligned", test_unaligned);
+	g_test_add_func ("/general/test_strv_cmp", test_strv_cmp);
+	g_test_add_func ("/general/test_strstrip_avoid_copy", test_strstrip_avoid_copy);
 
 	return g_test_run ();
 }
diff --git a/shared/nm-version-macros.h b/shared/nm-version-macros.h
index 3906e7c8..6c5e8557 100644
--- a/shared/nm-version-macros.h
+++ b/shared/nm-version-macros.h
@@ -37,7 +37,7 @@
  * Evaluates to the minor version number of NetworkManager which this source
  * is compiled against.
  */
-#define NM_MINOR_VERSION (16)
+#define NM_MINOR_VERSION (18)
 
 /**
  * NM_MICRO_VERSION:
@@ -75,6 +75,7 @@
 #define NM_VERSION_1_12   (NM_ENCODE_VERSION (1, 12, 0))
 #define NM_VERSION_1_14   (NM_ENCODE_VERSION (1, 14, 0))
 #define NM_VERSION_1_16   (NM_ENCODE_VERSION (1, 16, 0))
+#define NM_VERSION_1_18   (NM_ENCODE_VERSION (1, 18, 0))
 
 /* For releases, NM_API_VERSION is equal to NM_VERSION.
  *
diff --git a/shared/nm-version-macros.h.in b/shared/nm-version-macros.h.in
index 22af1428..4b57529a 100644
--- a/shared/nm-version-macros.h.in
+++ b/shared/nm-version-macros.h.in
@@ -75,6 +75,7 @@
 #define NM_VERSION_1_12   (NM_ENCODE_VERSION (1, 12, 0))
 #define NM_VERSION_1_14   (NM_ENCODE_VERSION (1, 14, 0))
 #define NM_VERSION_1_16   (NM_ENCODE_VERSION (1, 16, 0))
+#define NM_VERSION_1_18   (NM_ENCODE_VERSION (1, 18, 0))
 
 /* For releases, NM_API_VERSION is equal to NM_VERSION.
  *
diff --git a/shared/systemd/nm-logging-stub.c b/shared/systemd/nm-logging-stub.c
index 5be69b4b..59699228 100644
--- a/shared/systemd/nm-logging-stub.c
+++ b/shared/systemd/nm-logging-stub.c
@@ -19,7 +19,7 @@
 
 #include "nm-default.h"
 
-#include "nm-utils/nm-logging-fwd.h"
+#include "nm-glib-aux/nm-logging-fwd.h"
 
 /*****************************************************************************/
 
diff --git a/shared/systemd/nm-sd-utils-shared.c b/shared/systemd/nm-sd-utils-shared.c
index 0e89fbb7..ecd27492 100644
--- a/shared/systemd/nm-sd-utils-shared.c
+++ b/shared/systemd/nm-sd-utils-shared.c
@@ -62,6 +62,8 @@ nm_sd_utils_unbase64char (char ch, gboolean accept_padding_equal)
  *   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_explict_bzero()).
  * @mem: (transfer full): the decoded buffer on success.
  * @len: the length of @mem on success.
  *
@@ -75,8 +77,9 @@ 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)
 {
-	return unbase64mem (p, l, (void **) mem, len);
+	return unbase64mem_full (p, l, secure, (void **) mem, len);
 }
diff --git a/shared/systemd/nm-sd-utils-shared.h b/shared/systemd/nm-sd-utils-shared.h
index eddf0c28..b3b77c88 100644
--- a/shared/systemd/nm-sd-utils-shared.h
+++ b/shared/systemd/nm-sd-utils-shared.h
@@ -31,7 +31,11 @@ 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, guint8 **mem, size_t *len);
+int nm_sd_utils_unbase64mem (const char *p,
+                             size_t l,
+                             gboolean secure,
+                             guint8 **mem,
+                             size_t *len);
 
 /*****************************************************************************/
 
diff --git a/shared/systemd/sd-adapt-shared/missing.h b/shared/systemd/sd-adapt-shared/missing.h
index 2ee34b6a..d0b460a7 100644
--- a/shared/systemd/sd-adapt-shared/missing.h
+++ b/shared/systemd/sd-adapt-shared/missing.h
@@ -3,4 +3,6 @@
 /* dummy header */
 
 #include "missing_fcntl.h"
+#include "missing_socket.h"
+#include "missing_stat.h"
 #include "missing_type.h"
diff --git a/shared/systemd/sd-adapt-shared/missing_socket.h b/shared/systemd/sd-adapt-shared/namespace-util.h
index 637892c2..637892c2 100644
--- a/shared/systemd/sd-adapt-shared/missing_socket.h
+++ b/shared/systemd/sd-adapt-shared/namespace-util.h
diff --git a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h
index b10722d7..a285c3cd 100644
--- a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h
+++ b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h
@@ -23,7 +23,7 @@
 
 #include <syslog.h>
 
-#include "nm-utils/nm-logging-fwd.h"
+#include "nm-glib-aux/nm-logging-fwd.h"
 
 /*****************************************************************************/
 
diff --git a/shared/systemd/sd-adapt-shared/nulstr-util.h b/shared/systemd/sd-adapt-shared/nulstr-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/shared/systemd/sd-adapt-shared/nulstr-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/shared/systemd/sd-adapt-shared/strxcpyx.h b/shared/systemd/sd-adapt-shared/strxcpyx.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/shared/systemd/sd-adapt-shared/strxcpyx.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/shared/systemd/sd-adapt-shared/unaligned.h b/shared/systemd/sd-adapt-shared/unaligned.h
index 17dc0444..ac1a6928 100644
--- a/shared/systemd/sd-adapt-shared/unaligned.h
+++ b/shared/systemd/sd-adapt-shared/unaligned.h
@@ -1,3 +1,3 @@
 #pragma once
 
-#include "nm-utils/unaligned.h"
+#include "nm-std-aux/unaligned.h"
diff --git a/shared/systemd/src/basic/alloc-util.c b/shared/systemd/src/basic/alloc-util.c
index d23624d8..92b350bc 100644
--- a/shared/systemd/src/basic/alloc-util.c
+++ b/shared/systemd/src/basic/alloc-util.c
@@ -2,12 +2,13 @@
 
 #include "nm-sd-adapt-shared.h"
 
+#include <malloc.h>
 #include <stdint.h>
 #include <string.h>
 
 #include "alloc-util.h"
 #include "macro.h"
-#include "util.h"
+#include "memory-util.h"
 
 void* memdup(const void *p, size_t l) {
         void *ret;
@@ -29,6 +30,9 @@ void* memdup_suffix0(const void *p, size_t l) {
 
         /* The same as memdup() but place a safety NUL byte after the allocated memory */
 
+        if (_unlikely_(l == SIZE_MAX)) /* prevent overflow */
+                return NULL;
+
         ret = malloc(l + 1);
         if (!ret)
                 return NULL;
@@ -47,19 +51,23 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
         if (*allocated >= need)
                 return *p;
 
-        newalloc = MAX(need * 2, 64u / size);
-        a = newalloc * size;
+        if (_unlikely_(need > SIZE_MAX/2)) /* Overflow check */
+                return NULL;
 
-        /* check for overflows */
-        if (a < size * need)
+        newalloc = need * 2;
+        if (size_multiply_overflow(newalloc, size))
                 return NULL;
 
+        a = newalloc * size;
+        if (a < 64) /* Allocate at least 64 bytes */
+                a = 64;
+
         q = realloc(*p, a);
         if (!q)
                 return NULL;
 
         *p = q;
-        *allocated = newalloc;
+        *allocated = _unlikely_(size == 0) ? newalloc : malloc_usable_size(q) / size;
         return q;
 }
 
diff --git a/shared/systemd/src/basic/alloc-util.h b/shared/systemd/src/basic/alloc-util.h
index 893a1238..9b20be47 100644
--- a/shared/systemd/src/basic/alloc-util.h
+++ b/shared/systemd/src/basic/alloc-util.h
@@ -8,6 +8,10 @@
 
 #include "macro.h"
 
+#if HAS_FEATURE_MEMORY_SANITIZER
+#  include <sanitizer/msan_interface.h>
+#endif
+
 typedef void (*free_func_t)(void *p);
 
 /* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than
@@ -152,11 +156,17 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
                 (void*)memset(_new_, 0, _xsize_);                       \
         })
 
-/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to
- * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
+/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time
+ * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
 #define TAKE_PTR(ptr)                           \
         ({                                      \
                 typeof(ptr) _ptr_ = (ptr);      \
                 (ptr) = NULL;                   \
                 _ptr_;                          \
         })
+
+#if HAS_FEATURE_MEMORY_SANITIZER
+#  define msan_unpoison(r, s) __msan_unpoison(r, s)
+#else
+#  define msan_unpoison(r, s)
+#endif
diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c
index 4babe753..4a0f9c39 100644
--- a/shared/systemd/src/basic/env-file.c
+++ b/shared/systemd/src/basic/env-file.c
@@ -562,7 +562,7 @@ int write_env_file(const char *fname, char **l) {
                 r = -errno;
         }
 
-        unlink(p);
+        (void) unlink(p);
         return r;
 }
 #endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h
new file mode 100644
index 00000000..d7a5ea77
--- /dev/null
+++ b/shared/systemd/src/basic/errno-util.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "macro.h"
+
+static inline void _reset_errno_(int *saved_errno) {
+        if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */
+                return;
+
+        errno = *saved_errno;
+}
+
+#define PROTECT_ERRNO                                                   \
+        _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
+
+#define UNPROTECT_ERRNO                         \
+        do {                                    \
+                errno = _saved_errno_;          \
+                _saved_errno_ = -1;             \
+        } while (false)
+
+static inline int negative_errno(void) {
+        /* This helper should be used to shut up gcc if you know 'errno' is
+         * negative. Instead of "return -errno;", use "return negative_errno();"
+         * It will suppress bogus gcc warnings in case it assumes 'errno' might
+         * be 0 and thus the caller's error-handling might not be triggered. */
+        assert_return(errno > 0, -EINVAL);
+        return -errno;
+}
+
+/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
+ *
+ * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases.  See the
+ *          icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */
+static inline bool ERRNO_IS_DISCONNECT(int r) {
+        return IN_SET(abs(r),
+                      ECONNABORTED,
+                      ECONNREFUSED,
+                      ECONNRESET,
+                      EHOSTDOWN,
+                      EHOSTUNREACH,
+                      ENETDOWN,
+                      ENETRESET,
+                      ENETUNREACH,
+                      ENONET,
+                      ENOPROTOOPT,
+                      ENOTCONN,
+                      EPIPE,
+                      EPROTO,
+                      ESHUTDOWN);
+}
+
+/* Transient errors we might get on accept() that we should ignore. As per error handling comment in
+ * the accept(2) man page. */
+static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) {
+        return ERRNO_IS_DISCONNECT(r) ||
+                IN_SET(abs(r),
+                       EAGAIN,
+                       EINTR,
+                       EOPNOTSUPP);
+}
+
+/* Resource exhaustion, could be our fault or general system trouble */
+static inline bool ERRNO_IS_RESOURCE(int r) {
+        return IN_SET(abs(r),
+                      EMFILE,
+                      ENFILE,
+                      ENOMEM);
+}
diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c
index 0cc0c6b5..941053bf 100644
--- a/shared/systemd/src/basic/fd-util.c
+++ b/shared/systemd/src/basic/fd-util.c
@@ -27,6 +27,10 @@
 #include "util.h"
 #include "tmpfile-util.h"
 
+/* The maximum number of iterations in the loop to close descriptors in the fallback case
+ * when /proc/self/fd/ is inaccessible. */
+#define MAX_FD_LOOP_LIMIT (1024*1024)
+
 int close_nointr(int fd) {
         assert(fd >= 0);
 
@@ -231,6 +235,13 @@ int close_all_fds(const int except[], size_t n_except) {
                 if (max_fd < 0)
                         return max_fd;
 
+                /* Refuse to do the loop over more too many elements. It's better to fail immediately than to
+                 * spin the CPU for a long time. */
+                if (max_fd > MAX_FD_LOOP_LIMIT)
+                        return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
+                                               "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.",
+                                               max_fd);
+
                 for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
                         int q;
 
diff --git a/shared/systemd/src/basic/fd-util.h b/shared/systemd/src/basic/fd-util.h
index 4085a244..e490753c 100644
--- a/shared/systemd/src/basic/fd-util.h
+++ b/shared/systemd/src/basic/fd-util.h
@@ -77,18 +77,6 @@ int acquire_data_fd(const void *data, size_t size, unsigned flags);
 
 int fd_duplicate_data_fd(int fd);
 
-/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
-/* The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases.
- * See the icmp_err_convert[] in net/ipv4/icmp.c in the kernel sources */
-#define ERRNO_IS_DISCONNECT(r)                                          \
-        IN_SET(r,                                                       \
-               ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, \
-               ENETUNREACH, EHOSTUNREACH, ENOPROTOOPT, EHOSTDOWN, ENONET)
-
-/* Resource exhaustion, could be our fault or general system trouble */
-#define ERRNO_IS_RESOURCE(r) \
-        IN_SET(r, ENOMEM, EMFILE, ENFILE)
-
 int fd_move_above_stdio(int fd);
 
 int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd);
diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c
index ee66190f..0dfb4574 100644
--- a/shared/systemd/src/basic/fileio.c
+++ b/shared/systemd/src/basic/fileio.c
@@ -19,6 +19,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "hexdecoct.h"
 #include "log.h"
 #include "macro.h"
 #include "missing.h"
@@ -268,26 +269,29 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
 }
 #endif /* NM_IGNORED */
 
-int read_full_stream(
+int read_full_stream_full(
                 FILE *f,
+                const char *filename,
+                ReadFullFileFlags flags,
                 char **ret_contents,
                 size_t *ret_size) {
 
         _cleanup_free_ char *buf = NULL;
         struct stat st;
-        size_t n, l;
-        int fd;
+        size_t n, n_next, l;
+        int fd, r;
 
         assert(f);
         assert(ret_contents);
+        assert(!(flags & READ_FULL_FILE_UNBASE64) || ret_size);
 
-        n = LINE_MAX; /* Start size */
+        n_next = LINE_MAX; /* Start size */
 
         fd = fileno(f);
         if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
                         * optimize our buffering) */
 
-                if (fstat(fileno(f), &st) < 0)
+                if (fstat(fd, &st) < 0)
                         return -errno;
 
                 if (S_ISREG(st.st_mode)) {
@@ -300,27 +304,44 @@ int read_full_stream(
                          * size of 0. Note that we increase the size to read here by one, so that the first read attempt
                          * already makes us notice the EOF. */
                         if (st.st_size > 0)
-                                n = st.st_size + 1;
+                                n_next = st.st_size + 1;
+
+                        if (flags & READ_FULL_FILE_SECURE)
+                                (void) warn_file_is_world_accessible(filename, &st, NULL, 0);
                 }
         }
 
-        l = 0;
+        n = l = 0;
         for (;;) {
                 char *t;
                 size_t k;
 
-                t = realloc(buf, n + 1);
-                if (!t)
-                        return -ENOMEM;
+                if (flags & READ_FULL_FILE_SECURE) {
+                        t = malloc(n_next + 1);
+                        if (!t) {
+                                r = -ENOMEM;
+                                goto finalize;
+                        }
+                        memcpy_safe(t, buf, n);
+                        explicit_bzero_safe(buf, n);
+                } else {
+                        t = realloc(buf, n_next + 1);
+                        if (!t)
+                                return -ENOMEM;
+                }
 
                 buf = t;
+                n = n_next;
+
                 errno = 0;
                 k = fread(buf + l, 1, n - l, f);
                 if (k > 0)
                         l += k;
 
-                if (ferror(f))
-                        return errno > 0 ? -errno : -EIO;
+                if (ferror(f)) {
+                        r = errno > 0 ? -errno : -EIO;
+                        goto finalize;
+                }
 
                 if (feof(f))
                         break;
@@ -331,10 +352,18 @@ int read_full_stream(
                 assert(l == n);
 
                 /* Safety check */
-                if (n >= READ_FULL_BYTES_MAX)
-                        return -E2BIG;
+                if (n >= READ_FULL_BYTES_MAX) {
+                        r = -E2BIG;
+                        goto finalize;
+                }
+
+                n_next = MIN(n * 2, READ_FULL_BYTES_MAX);
+        }
 
-                n = MIN(n * 2, READ_FULL_BYTES_MAX);
+        if (flags & READ_FULL_FILE_UNBASE64) {
+                buf[l++] = 0;
+                r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
+                goto finalize;
         }
 
         if (!ret_size) {
@@ -342,8 +371,10 @@ int read_full_stream(
                  * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise
                  * there'd be ambiguity about what we just read. */
 
-                if (memchr(buf, 0, l))
-                        return -EBADMSG;
+                if (memchr(buf, 0, l)) {
+                        r = -EBADMSG;
+                        goto finalize;
+                }
         }
 
         buf[l] = 0;
@@ -353,21 +384,27 @@ int read_full_stream(
                 *ret_size = l;
 
         return 0;
+
+finalize:
+        if (flags & READ_FULL_FILE_SECURE)
+                explicit_bzero_safe(buf, n);
+
+        return r;
 }
 
-int read_full_file(const char *fn, char **contents, size_t *size) {
+int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) {
         _cleanup_fclose_ FILE *f = NULL;
 
-        assert(fn);
+        assert(filename);
         assert(contents);
 
-        f = fopen(fn, "re");
+        f = fopen(filename, "re");
         if (!f)
                 return -errno;
 
         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
-        return read_full_stream(f, contents, size);
+        return read_full_stream_full(f, filename, flags, contents, size);
 }
 
 #if 0 /* NM_IGNORED */
@@ -830,3 +867,28 @@ int safe_fgetc(FILE *f, char *ret) {
         return 1;
 }
 #endif /* NM_IGNORED */
+
+int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line) {
+        struct stat _st;
+
+        if (!filename)
+                return 0;
+
+        if (!st) {
+                if (stat(filename, &_st) < 0)
+                        return -errno;
+                st = &_st;
+        }
+
+        if ((st->st_mode & S_IRWXO) == 0)
+                return 0;
+
+        if (unit)
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "%s has %04o mode that is too permissive, please adjust the access mode.",
+                           filename, st->st_mode & 07777);
+        else
+                log_warning("%s has %04o mode that is too permissive, please adjust the access mode.",
+                            filename, st->st_mode & 07777);
+        return 0;
+}
diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h
index 53e3f4ef..760e7386 100644
--- a/shared/systemd/src/basic/fileio.h
+++ b/shared/systemd/src/basic/fileio.h
@@ -5,6 +5,7 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 
 #include "macro.h"
@@ -27,6 +28,11 @@ typedef enum {
 
 } WriteStringFileFlags;
 
+typedef enum {
+        READ_FULL_FILE_SECURE   = 1 << 0,
+        READ_FULL_FILE_UNBASE64 = 1 << 1,
+} ReadFullFileFlags;
+
 int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts);
 static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) {
         return write_string_stream_ts(f, line, flags, NULL);
@@ -38,9 +44,15 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin
 
 int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
 
-int read_one_line_file(const char *fn, char **line);
-int read_full_file(const char *fn, char **contents, size_t *size);
-int read_full_stream(FILE *f, char **contents, size_t *size);
+int read_one_line_file(const char *filename, char **line);
+int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);
+static inline int read_full_file(const char *filename, char **contents, size_t *size) {
+        return read_full_file_full(filename, 0, contents, size);
+}
+int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);
+static inline int read_full_stream(FILE *f, char **contents, size_t *size) {
+        return read_full_stream_full(f, NULL, 0, contents, size);
+}
 
 int verify_file(const char *fn, const char *blob, bool accept_extra_nl);
 
@@ -76,3 +88,5 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
 }
 
 int safe_fgetc(FILE *f, char *ret);
+
+int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c
index 4b234139..be85eef1 100644
--- a/shared/systemd/src/basic/fs-util.c
+++ b/shared/systemd/src/basic/fs-util.c
@@ -8,6 +8,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <linux/falloc.h>
 #include <linux/magic.h>
 #include <time.h>
 #include <unistd.h>
@@ -219,64 +220,109 @@ int readlink_and_make_absolute(const char *p, char **r) {
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
         char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
         _cleanup_close_ int fd = -1;
+        bool st_valid = false;
+        struct stat st;
+        int r;
+
         assert(path);
 
-        /* Under the assumption that we are running privileged we first change the access mode and only then hand out
-         * ownership to avoid a window where access is too open. */
+        /* Under the assumption that we are running privileged we first change the access mode and only then
+         * hand out ownership to avoid a window where access is too open. */
 
-        fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change mode/owner
-                                                       * on the same file */
+        fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change
+                                                       * mode/owner on the same file */
         if (fd < 0)
                 return -errno;
 
         xsprintf(fd_path, "/proc/self/fd/%i", fd);
 
         if (mode != MODE_INVALID) {
-
                 if ((mode & S_IFMT) != 0) {
-                        struct stat st;
 
                         if (stat(fd_path, &st) < 0)
                                 return -errno;
 
                         if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
                                 return -EINVAL;
+
+                        st_valid = true;
                 }
 
-                if (chmod(fd_path, mode & 07777) < 0)
-                        return -errno;
+                if (chmod(fd_path, mode & 07777) < 0) {
+                        r = -errno;
+
+                        if (!st_valid && stat(fd_path, &st) < 0)
+                                return -errno;
+
+                        if ((mode & 07777) != (st.st_mode & 07777))
+                                return r;
+
+                        st_valid = true;
+                }
         }
 
-        if (uid != UID_INVALID || gid != GID_INVALID)
-                if (chown(fd_path, uid, gid) < 0)
-                        return -errno;
+        if (uid != UID_INVALID || gid != GID_INVALID) {
+                if (chown(fd_path, uid, gid) < 0) {
+                        r = -errno;
+
+                        if (!st_valid && stat(fd_path, &st) < 0)
+                                return -errno;
+
+                        if (uid != UID_INVALID && st.st_uid != uid)
+                                return r;
+                        if (gid != GID_INVALID && st.st_gid != gid)
+                                return r;
+                }
+        }
 
         return 0;
 }
 
 int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
+        bool st_valid = false;
+        struct stat st;
+        int r;
+
         /* Under the assumption that we are running privileged we first change the access mode and only then hand out
          * ownership to avoid a window where access is too open. */
 
         if (mode != MODE_INVALID) {
-
                 if ((mode & S_IFMT) != 0) {
-                        struct stat st;
 
                         if (fstat(fd, &st) < 0)
                                 return -errno;
 
                         if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
                                 return -EINVAL;
+
+                        st_valid = true;
                 }
 
-                if (fchmod(fd, mode & 0777) < 0)
-                        return -errno;
+                if (fchmod(fd, mode & 07777) < 0) {
+                        r = -errno;
+
+                        if (!st_valid && fstat(fd, &st) < 0)
+                                return -errno;
+
+                        if ((mode & 07777) != (st.st_mode & 07777))
+                                return r;
+
+                        st_valid = true;
+                }
         }
 
         if (uid != UID_INVALID || gid != GID_INVALID)
-                if (fchown(fd, uid, gid) < 0)
-                        return -errno;
+                if (fchown(fd, uid, gid) < 0) {
+                        r = -errno;
+
+                        if (!st_valid && fstat(fd, &st) < 0)
+                                return -errno;
+
+                        if (uid != UID_INVALID && st.st_uid != uid)
+                                return r;
+                        if (gid != GID_INVALID && st.st_gid != gid)
+                                return r;
+                }
 
         return 0;
 }
@@ -314,6 +360,10 @@ int fd_warn_permissions(const char *path, int fd) {
         if (fstat(fd, &st) < 0)
                 return -errno;
 
+        /* Don't complain if we are reading something that is not a file, for example /dev/null */
+        if (!S_ISREG(st.st_mode))
+                return 0;
+
         if (st.st_mode & 0111)
                 log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
 
@@ -934,6 +984,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                 if (fstat(child, &st) < 0)
                         return -errno;
                 if ((flags & CHASE_SAFE) &&
+                    (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) &&
                     unsafe_transition(&previous_stat, &st))
                         return log_unsafe_transition(fd, child, path, flags);
 
@@ -1338,6 +1389,21 @@ int fsync_path_at(int at_fd, const char *path) {
         return 0;
 }
 
+int syncfs_path(int atfd, const char *path) {
+        _cleanup_close_ int fd = -1;
+
+        assert(path);
+
+        fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK);
+        if (fd < 0)
+                return -errno;
+
+        if (syncfs(fd) < 0)
+                return -errno;
+
+        return 0;
+}
+
 int open_parent(const char *path, int flags, mode_t mode) {
         _cleanup_free_ char *parent = NULL;
         int fd;
@@ -1354,9 +1420,9 @@ int open_parent(const char *path, int flags, mode_t mode) {
         /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an
          * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */
 
-        if ((flags & O_PATH) == O_PATH)
+        if (FLAGS_SET(flags, O_PATH))
                 flags |= O_DIRECTORY;
-        else if ((flags & O_TMPFILE) != O_TMPFILE)
+        else if (!FLAGS_SET(flags, O_TMPFILE))
                 flags |= O_DIRECTORY|O_RDONLY;
 
         fd = open(parent, flags, mode);
diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h
index 7ad030be..b9651205 100644
--- a/shared/systemd/src/basic/fs-util.h
+++ b/shared/systemd/src/basic/fs-util.h
@@ -10,8 +10,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "errno-util.h"
 #include "time-util.h"
-#include "util.h"
 
 int unlink_noerrno(const char *path);
 
@@ -108,4 +108,6 @@ int unlinkat_deallocate(int fd, const char *name, int flags);
 int fsync_directory_of_file(int fd);
 int fsync_path_at(int at_fd, const char *path);
 
+int syncfs_path(int atfd, const char *path);
+
 int open_parent(const char *path, int flags, mode_t mode);
diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c
index c0655831..9418dbd8 100644
--- a/shared/systemd/src/basic/hashmap.c
+++ b/shared/systemd/src/basic/hashmap.c
@@ -11,6 +11,7 @@
 #include "fileio.h"
 #include "hashmap.h"
 #include "macro.h"
+#include "memory-util.h"
 #include "mempool.h"
 #include "process-util.h"
 #include "random-util.h"
@@ -18,7 +19,6 @@
 #include "siphash24.h"
 #include "string-util.h"
 #include "strv.h"
-#include "util.h"
 
 #if ENABLE_DEBUG_HASHMAP
 #include <pthread.h>
@@ -1538,7 +1538,6 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r
 }
 
 unsigned internal_hashmap_size(HashmapBase *h) {
-
         if (!h)
                 return 0;
 
@@ -1546,7 +1545,6 @@ unsigned internal_hashmap_size(HashmapBase *h) {
 }
 
 unsigned internal_hashmap_buckets(HashmapBase *h) {
-
         if (!h)
                 return 0;
 
@@ -1906,8 +1904,7 @@ IteratedCache *iterated_cache_free(IteratedCache *cache) {
         if (cache) {
                 free(cache->keys.ptr);
                 free(cache->values.ptr);
-                free(cache);
         }
 
-        return NULL;
+        return mfree(cache);
 }
diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h
index e16a9f9e..41c8adb1 100644
--- a/shared/systemd/src/basic/hashmap.h
+++ b/shared/systemd/src/basic/hashmap.h
@@ -412,9 +412,11 @@ static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_key);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_key);
 DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free);
 
 #define _cleanup_hashmap_free_ _cleanup_(hashmap_freep)
diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c
index 7c66cc62..c81c09e8 100644
--- a/shared/systemd/src/basic/hexdecoct.c
+++ b/shared/systemd/src/basic/hexdecoct.c
@@ -10,8 +10,8 @@
 #include "alloc-util.h"
 #include "hexdecoct.h"
 #include "macro.h"
+#include "memory-util.h"
 #include "string-util.h"
-#include "util.h"
 
 char octchar(int x) {
         return '0' + (x & 7);
@@ -691,11 +691,12 @@ static int unbase64_next(const char **p, size_t *l) {
         return ret;
 }
 
-int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
+int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) {
         _cleanup_free_ uint8_t *buf = NULL;
         const char *x;
         uint8_t *z;
         size_t len;
+        int r;
 
         assert(p || l == 0);
         assert(ret);
@@ -718,36 +719,54 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
                 a = unbase64_next(&x, &l);
                 if (a == -EPIPE) /* End of string */
                         break;
-                if (a < 0)
-                        return a;
-                if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
-                        return -EINVAL;
+                if (a < 0) {
+                        r = a;
+                        goto on_failure;
+                }
+                if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */
+                        r = -EINVAL;
+                        goto on_failure;
+                }
 
                 b = unbase64_next(&x, &l);
-                if (b < 0)
-                        return b;
-                if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
-                        return -EINVAL;
+                if (b < 0) {
+                        r = b;
+                        goto on_failure;
+                }
+                if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */
+                        r = -EINVAL;
+                        goto on_failure;
+                }
 
                 c = unbase64_next(&x, &l);
-                if (c < 0)
-                        return c;
+                if (c < 0) {
+                        r = c;
+                        goto on_failure;
+                }
 
                 d = unbase64_next(&x, &l);
-                if (d < 0)
-                        return d;
+                if (d < 0) {
+                        r = d;
+                        goto on_failure;
+                }
 
                 if (c == INT_MAX) { /* Padding at the third character */
 
-                        if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
-                                return -EINVAL;
+                        if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */
+                                r = -EINVAL;
+                                goto on_failure;
+                        }
 
                         /* b == 00YY0000 */
-                        if (b & 15)
-                                return -EINVAL;
+                        if (b & 15) {
+                                r = -EINVAL;
+                                goto on_failure;
+                        }
 
-                        if (l > 0) /* Trailing rubbish? */
-                                return -ENAMETOOLONG;
+                        if (l > 0) { /* Trailing rubbish? */
+                                r = -ENAMETOOLONG;
+                                goto on_failure;
+                        }
 
                         *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
                         break;
@@ -755,11 +774,15 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
 
                 if (d == INT_MAX) {
                         /* c == 00ZZZZ00 */
-                        if (c & 3)
-                                return -EINVAL;
+                        if (c & 3) {
+                                r = -EINVAL;
+                                goto on_failure;
+                        }
 
-                        if (l > 0) /* Trailing rubbish? */
-                                return -ENAMETOOLONG;
+                        if (l > 0) { /* Trailing rubbish? */
+                                r = -ENAMETOOLONG;
+                                goto on_failure;
+                        }
 
                         *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
                         *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
@@ -777,6 +800,12 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
         *ret = TAKE_PTR(buf);
 
         return 0;
+
+on_failure:
+        if (secure)
+                explicit_bzero_safe(buf, len);
+
+        return r;
 }
 
 #if 0 /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/hexdecoct.h b/shared/systemd/src/basic/hexdecoct.h
index 9477d16e..fa6013ee 100644
--- a/shared/systemd/src/basic/hexdecoct.h
+++ b/shared/systemd/src/basic/hexdecoct.h
@@ -33,6 +33,9 @@ ssize_t base64mem(const void *p, size_t l, char **out);
 int base64_append(char **prefix, int plen,
                   const void *p, size_t l,
                   int margin, int width);
-int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
+int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len);
+static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) {
+        return unbase64mem_full(p, l, false, mem, len);
+}
 
 void hexdump(FILE *f, const void *p, size_t s);
diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c
index 5ced3501..5899f62f 100644
--- a/shared/systemd/src/basic/in-addr-util.c
+++ b/shared/systemd/src/basic/in-addr-util.c
@@ -7,12 +7,15 @@
 #include <errno.h>
 #include <net/if.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
 
 #include "alloc-util.h"
 #include "in-addr-util.h"
 #include "macro.h"
 #include "parse-util.h"
+#include "random-util.h"
+#include "strxcpyx.h"
 #include "util.h"
 
 bool in4_addr_is_null(const struct in_addr *a) {
@@ -107,6 +110,7 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_
         return -EAFNOSUPPORT;
 }
 
+#if 0 /* NM_IGNORED */
 int in_addr_prefix_intersect(
                 int family,
                 const union in_addr_union *a,
@@ -217,8 +221,86 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen)
         return -EAFNOSUPPORT;
 }
 
+int in_addr_random_prefix(
+                int family,
+                union in_addr_union *u,
+                unsigned prefixlen_fixed_part,
+                unsigned prefixlen) {
+
+        assert(u);
+
+        /* Random network part of an address by one. */
+
+        if (prefixlen <= 0)
+                return 0;
+
+        if (family == AF_INET) {
+                uint32_t c, n;
+
+                if (prefixlen_fixed_part > 32)
+                        prefixlen_fixed_part = 32;
+                if (prefixlen > 32)
+                        prefixlen = 32;
+                if (prefixlen_fixed_part >= prefixlen)
+                        return -EINVAL;
+
+                c = be32toh(u->in.s_addr);
+                c &= ((UINT32_C(1) << prefixlen_fixed_part) - 1) << (32 - prefixlen_fixed_part);
+
+                random_bytes(&n, sizeof(n));
+                n &= ((UINT32_C(1) << (prefixlen - prefixlen_fixed_part)) - 1) << (32 - prefixlen);
+
+                u->in.s_addr = htobe32(n | c);
+                return 1;
+        }
+
+        if (family == AF_INET6) {
+                struct in6_addr n;
+                unsigned i, j;
+
+                if (prefixlen_fixed_part > 128)
+                        prefixlen_fixed_part = 128;
+                if (prefixlen > 128)
+                        prefixlen = 128;
+                if (prefixlen_fixed_part >= prefixlen)
+                        return -EINVAL;
+
+                random_bytes(&n, sizeof(n));
+
+                for (i = 0; i < 16; i++) {
+                        uint8_t mask_fixed_part = 0, mask = 0;
+
+                        if (i < (prefixlen_fixed_part + 7) / 8) {
+                                if (i < prefixlen_fixed_part / 8)
+                                        mask_fixed_part = 0xffu;
+                                else {
+                                        j = prefixlen_fixed_part % 8;
+                                        mask_fixed_part = ((UINT8_C(1) << (j + 1)) - 1) << (8 - j);
+                                }
+                        }
+
+                        if (i < (prefixlen + 7) / 8) {
+                                if (i < prefixlen / 8)
+                                        mask = 0xffu ^ mask_fixed_part;
+                                else {
+                                        j = prefixlen % 8;
+                                        mask = (((UINT8_C(1) << (j + 1)) - 1) << (8 - j)) ^ mask_fixed_part;
+                                }
+                        }
+
+                        u->in6.s6_addr[i] &= mask_fixed_part;
+                        u->in6.s6_addr[i] |= n.s6_addr[i] & mask;
+                }
+
+                return 1;
+        }
+
+        return -EAFNOSUPPORT;
+}
+#endif /* NM_IGNORED */
+
 int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
-        char *x;
+        _cleanup_free_ char *x = NULL;
         size_t l;
 
         assert(u);
@@ -236,18 +318,52 @@ 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)) {
-                free(x);
+        if (!inet_ntop(family, u, x, l))
                 return errno > 0 ? -errno : -EINVAL;
-        }
 
-        *ret = x;
+        *ret = TAKE_PTR(x);
+        return 0;
+}
+
+#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;
+
+        assert(u);
+        assert(ret);
+
+        if (family == AF_INET)
+                l = INET_ADDRSTRLEN + 3;
+        else if (family == AF_INET6)
+                l = INET6_ADDRSTRLEN + 4;
+        else
+                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 > 0 ? -errno : -EINVAL;
+
+        p = x + strlen(x);
+        l -= strlen(x);
+        (void) strpcpyf(&p, l, "/%u", prefixlen);
+
+        *ret = TAKE_PTR(x);
         return 0;
 }
+#endif /* NM_IGNORED */
 
 int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
+        _cleanup_free_ char *x = NULL;
         size_t l;
-        char *x;
         int r;
 
         assert(u);
@@ -273,14 +389,12 @@ int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifin
                 return -ENOMEM;
 
         errno = 0;
-        if (!inet_ntop(family, u, x, l)) {
-                free(x);
+        if (!inet_ntop(family, u, x, l))
                 return errno > 0 ? -errno : -EINVAL;
-        }
 
         sprintf(strchr(x, 0), "%%%i", ifindex);
-        *ret = x;
 
+        *ret = TAKE_PTR(x);
         return 0;
 
 fallback:
diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h
index c2156712..a6a685b9 100644
--- a/shared/systemd/src/basic/in-addr-util.h
+++ b/shared/systemd/src/basic/in-addr-util.h
@@ -35,7 +35,9 @@ bool in4_addr_is_non_local(const struct in_addr *a);
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
 int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
+int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen);
 int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
+int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
 int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
 int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
 int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
diff --git a/shared/systemd/src/basic/log.h b/shared/systemd/src/basic/log.h
index 364b8a49..98c5f4d7 100644
--- a/shared/systemd/src/basic/log.h
+++ b/shared/systemd/src/basic/log.h
@@ -120,6 +120,19 @@ int log_internalv_realm(
         log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__)
 
 /* Realm is fixed to LOG_REALM_SYSTEMD for those */
+int log_object_internalv(
+                int level,
+                int error,
+                const char *file,
+                int line,
+                const char *func,
+                const char *object_field,
+                const char *object,
+                const char *extra_field,
+                const char *extra,
+                const char *format,
+                va_list ap) _printf_(10,0);
+
 int log_object_internal(
                 int level,
                 int error,
@@ -145,7 +158,12 @@ int log_oom_internal(
                 const char *file,
                 int line,
                 const char *func);
+#endif /* NM_IGNORED */
+#define log_oom_internal(realm, file, line, func) \
+    log_internal_realm (LOG_REALM_PLUS_LEVEL (realm, LOG_ERR), \
+                        ENOMEM, file, line, func, "Out of memory.")
 
+#if 0 /* NM_IGNORED */
 int log_format_iovec(
                 struct iovec *iovec,
                 size_t iovec_len,
@@ -314,8 +332,8 @@ int log_syntax_invalid_utf8_internal(
         ({                                                              \
                 int _level = (level), _e = (error);                     \
                 (log_get_max_level() >= LOG_PRI(_level))                \
-                        ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
-                        : -abs(_e);                                     \
+                        ? log_internal_realm(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+                        : -ERRNO_VALUE(_e);                             \
         })
 
 #define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
diff --git a/shared/systemd/src/basic/memory-util.c b/shared/systemd/src/basic/memory-util.c
new file mode 100644
index 00000000..bd1f5d73
--- /dev/null
+++ b/shared/systemd/src/basic/memory-util.c
@@ -0,0 +1,59 @@
+#include "nm-sd-adapt-shared.h"
+
+#include <unistd.h>
+
+#include "memory-util.h"
+
+size_t page_size(void) {
+        static thread_local size_t pgsz = 0;
+        long r;
+
+        if (_likely_(pgsz > 0))
+                return pgsz;
+
+        r = sysconf(_SC_PAGESIZE);
+        assert(r > 0);
+
+        pgsz = (size_t) r;
+        return pgsz;
+}
+
+bool memeqzero(const void *data, size_t length) {
+        /* Does the buffer consist entirely of NULs?
+         * Copied from https://github.com/systemd/casync/, copied in turn from
+         * https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92,
+         * which is licensed CC-0.
+         */
+
+        const uint8_t *p = data;
+        size_t i;
+
+        /* Check first 16 bytes manually */
+        for (i = 0; i < 16; i++, length--) {
+                if (length == 0)
+                        return true;
+                if (p[i])
+                        return false;
+        }
+
+        /* Now we know first 16 bytes are NUL, memcmp with self.  */
+        return memcmp(data, p + i, length) == 0;
+}
+
+#if !HAVE_EXPLICIT_BZERO
+/*
+ * The pointer to memset() is volatile so that compiler must de-reference the pointer and can't assume that
+ * it points to any function in particular (such as memset(), which it then might further "optimize"). This
+ * approach is inspired by openssl's crypto/mem_clr.c.
+ */
+typedef void *(*memset_t)(void *,int,size_t);
+
+static volatile memset_t memset_func = memset;
+
+void* explicit_bzero_safe(void *p, size_t l) {
+        if (l > 0)
+                memset_func(p, '\0', l);
+
+        return p;
+}
+#endif
diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h
new file mode 100644
index 00000000..915c24a5
--- /dev/null
+++ b/shared/systemd/src/basic/memory-util.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+size_t page_size(void) _pure_;
+#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+
+/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
+static inline void memcpy_safe(void *dst, const void *src, size_t n) {
+        if (n == 0)
+                return;
+        assert(src);
+        memcpy(dst, src, n);
+}
+
+/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */
+static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
+        if (n == 0)
+                return 0;
+        assert(s1);
+        assert(s2);
+        return memcmp(s1, s2, n);
+}
+
+/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */
+static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) {
+        return memcmp_safe(s1, s2, MIN(n1, n2))
+            ?: CMP(n1, n2);
+}
+
+#define memzero(x,l)                                            \
+        ({                                                      \
+                size_t _l_ = (l);                               \
+                void *_x_ = (x);                                \
+                _l_ == 0 ? _x_ : memset(_x_, 0, _l_);           \
+        })
+
+#define zero(x) (memzero(&(x), sizeof(x)))
+
+bool memeqzero(const void *data, size_t length);
+
+#define eqzero(x) memeqzero(x, sizeof(x))
+
+static inline void *mempset(void *s, int c, size_t n) {
+        memset(s, c, n);
+        return (uint8_t*)s + n;
+}
+
+/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
+static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
+
+        if (needlelen <= 0)
+                return (void*) haystack;
+
+        if (haystacklen < needlelen)
+                return NULL;
+
+        assert(haystack);
+        assert(needle);
+
+        return memmem(haystack, haystacklen, needle, needlelen);
+}
+
+#if HAVE_EXPLICIT_BZERO
+static inline void* explicit_bzero_safe(void *p, size_t l) {
+        if (l > 0)
+                explicit_bzero(p, l);
+
+        return p;
+}
+#else
+void *explicit_bzero_safe(void *p, size_t l);
+#endif
+
+/* Use with _cleanup_ to erase a single 'char' when leaving scope */
+static inline void erase_char(char *p) {
+        explicit_bzero_safe(p, sizeof(char));
+}
diff --git a/shared/systemd/src/basic/mempool.c b/shared/systemd/src/basic/mempool.c
index 0fa51fba..8b8337db 100644
--- a/shared/systemd/src/basic/mempool.c
+++ b/shared/systemd/src/basic/mempool.c
@@ -7,6 +7,7 @@
 
 #include "env-util.h"
 #include "macro.h"
+#include "memory-util.h"
 #include "mempool.h"
 #include "process-util.h"
 #include "util.h"
diff --git a/shared/systemd/src/basic/missing_socket.h b/shared/systemd/src/basic/missing_socket.h
new file mode 100644
index 00000000..29828dba
--- /dev/null
+++ b/shared/systemd/src/basic/missing_socket.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/socket.h>
+
+#if 0 /* NM_IGNORED */
+#if HAVE_LINUX_VM_SOCKETS_H
+#include <linux/vm_sockets.h>
+#else
+#define VMADDR_CID_ANY -1U
+struct sockaddr_vm {
+        unsigned short svm_family;
+        unsigned short svm_reserved1;
+        unsigned int svm_port;
+        unsigned int svm_cid;
+        unsigned char svm_zero[sizeof(struct sockaddr) -
+                               sizeof(unsigned short) -
+                               sizeof(unsigned short) -
+                               sizeof(unsigned int) -
+                               sizeof(unsigned int)];
+};
+#endif /* !HAVE_LINUX_VM_SOCKETS_H */
+#endif /* NM_IGNORED */
+
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+
+#ifndef SO_REUSEPORT
+#define SO_REUSEPORT 15
+#endif
+
+#ifndef SO_PEERGROUPS
+#define SO_PEERGROUPS 59
+#endif
+
+#ifndef SO_BINDTOIFINDEX
+#define SO_BINDTOIFINDEX 62
+#endif
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+/* Not exposed yet. Defined in include/linux/socket.h. */
+#ifndef SOL_SCTP
+#define SOL_SCTP 132
+#endif
+
+/* Not exposed yet. Defined in include/linux/socket.h */
+#ifndef SCM_SECURITY
+#define SCM_SECURITY 0x03
+#endif
+
+/* netinet/in.h */
+#ifndef IP_FREEBIND
+#define IP_FREEBIND 15
+#endif
+
+#ifndef IP_TRANSPARENT
+#define IP_TRANSPARENT 19
+#endif
diff --git a/shared/systemd/src/basic/missing_stat.h b/shared/systemd/src/basic/missing_stat.h
new file mode 100644
index 00000000..7d89a7bb
--- /dev/null
+++ b/shared/systemd/src/basic/missing_stat.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/types.h>
+#include <sys/stat.h>
+
+#if 0 /* NM_IGNORED */
+#if WANT_LINUX_STAT_H
+#include <linux/stat.h>
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#if !HAVE_STRUCT_STATX
+struct statx_timestamp {
+        __s64 tv_sec;
+        __u32 tv_nsec;
+        __s32 __reserved;
+};
+struct statx {
+        __u32 stx_mask;
+        __u32 stx_blksize;
+        __u64 stx_attributes;
+        __u32 stx_nlink;
+        __u32 stx_uid;
+        __u32 stx_gid;
+        __u16 stx_mode;
+        __u16 __spare0[1];
+        __u64 stx_ino;
+        __u64 stx_size;
+        __u64 stx_blocks;
+        __u64 stx_attributes_mask;
+        struct statx_timestamp stx_atime;
+        struct statx_timestamp stx_btime;
+        struct statx_timestamp stx_ctime;
+        struct statx_timestamp stx_mtime;
+        __u32 stx_rdev_major;
+        __u32 stx_rdev_minor;
+        __u32 stx_dev_major;
+        __u32 stx_dev_minor;
+        __u64 __spare2[14];
+};
+#endif
+#endif /* NM_IGNORED */
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef STATX_BTIME
+#define STATX_BTIME 0x00000800U
+#endif
+
+/* a528d35e8bfcc521d7cb70aaf03e1bd296c8493f (4.11) */
+#ifndef AT_STATX_DONT_SYNC
+#define AT_STATX_DONT_SYNC 0x4000
+#endif
diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c
index 6c5e725d..29955a36 100644
--- a/shared/systemd/src/basic/path-util.c
+++ b/shared/systemd/src/basic/path-util.c
@@ -23,6 +23,7 @@
 #include "log.h"
 #include "macro.h"
 #include "missing.h"
+#include "nulstr-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "stat-util.h"
@@ -1112,48 +1113,40 @@ int path_simplify_and_warn(
                 unsigned line,
                 const char *lvalue) {
 
-        bool absolute, fatal = flag & PATH_CHECK_FATAL;
+        bool fatal = flag & PATH_CHECK_FATAL;
 
         assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE));
 
-        if (!utf8_is_valid(path)) {
-                log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path);
-                return -EINVAL;
-        }
+        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)) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
-                                   "%s= path is not absolute%s: %s",
-                                   lvalue, fatal ? "" : ", ignoring", path);
-                        return -EINVAL;
-                }
+                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)) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
-                                   "%s= path is absolute%s: %s",
-                                   lvalue, fatal ? "" : ", ignoring", path);
-                        return -EINVAL;
-                }
+                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, true);
 
-        if (!path_is_normalized(path)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
-                           "%s= path is not normalized%s: %s",
-                           lvalue, fatal ? "" : ", ignoring", path);
-                return -EINVAL;
-        }
+        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_valid(path)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
-                           "%s= path has invalid length (%zu bytes)%s.",
-                           lvalue, strlen(path), fatal ? "" : ", ignoring");
-                return -EINVAL;
-        }
+        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;
 }
diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c
index b0afb5c8..7431be3e 100644
--- a/shared/systemd/src/basic/process-util.c
+++ b/shared/systemd/src/basic/process-util.c
@@ -36,7 +36,9 @@
 #include "ioprio.h"
 #include "log.h"
 #include "macro.h"
+#include "memory-util.h"
 #include "missing.h"
+#include "namespace-util.h"
 #include "process-util.h"
 #include "raw-clone.h"
 #include "rlimit-util.h"
@@ -46,7 +48,6 @@
 #include "string-util.h"
 #include "terminal-util.h"
 #include "user-util.h"
-#include "util.h"
 
 #if 0 /* NM_IGNORED */
 int get_process_state(pid_t pid) {
@@ -938,6 +939,20 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
         return 0;
 }
 
+int pid_is_my_child(pid_t pid) {
+        pid_t ppid;
+        int r;
+
+        if (pid <= 1)
+                return false;
+
+        r = get_process_ppid(pid, &ppid);
+        if (r < 0)
+                return r;
+
+        return ppid == getpid_cached();
+}
+
 bool pid_is_unwaited(pid_t pid) {
         /* Checks whether a PID is still valid at all, including a zombie */
 
@@ -1007,7 +1022,7 @@ _noreturn_ void freeze(void) {
         log_close();
 
         /* Make sure nobody waits for us on a socket anymore */
-        close_all_fds(NULL, 0);
+        (void) close_all_fds(NULL, 0);
 
         sync();
 
@@ -1543,6 +1558,40 @@ int set_oom_score_adjust(int value) {
                                  WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
 }
 
+int cpus_in_affinity_mask(void) {
+        size_t n = 16;
+        int r;
+
+        for (;;) {
+                cpu_set_t *c;
+
+                c = CPU_ALLOC(n);
+                if (!c)
+                        return -ENOMEM;
+
+                if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
+                        int k;
+
+                        k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c);
+                        CPU_FREE(c);
+
+                        if (k <= 0)
+                                return -EINVAL;
+
+                        return k;
+                }
+
+                r = -errno;
+                CPU_FREE(c);
+
+                if (r != -EINVAL)
+                        return r;
+                if (n > SIZE_MAX/2)
+                        return -ENOMEM;
+                n *= 2;
+        }
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h
index 0425042f..3933bee6 100644
--- a/shared/systemd/src/basic/process-util.h
+++ b/shared/systemd/src/basic/process-util.h
@@ -12,6 +12,7 @@
 #include <sys/resource.h>
 #include <sys/types.h>
 
+#include "alloc-util.h"
 #include "format-util.h"
 #include "ioprio.h"
 #include "macro.h"
@@ -68,6 +69,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value);
 
 bool pid_is_alive(pid_t pid);
 bool pid_is_unwaited(pid_t pid);
+int pid_is_my_child(pid_t pid);
 int pid_from_same_root_fs(pid_t pid);
 
 bool is_main_thread(void);
@@ -194,3 +196,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
                 (pid) = 0;                      \
                 _pid_;                          \
         })
+
+int cpus_in_affinity_mask(void);
diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c
index 7c670e59..b8b45958 100644
--- a/shared/systemd/src/basic/random-util.c
+++ b/shared/systemd/src/basic/random-util.c
@@ -25,16 +25,13 @@
 #  include <linux/random.h>
 #endif
 
+#include "alloc-util.h"
 #include "fd-util.h"
 #include "io-util.h"
 #include "missing.h"
 #include "random-util.h"
 #include "time-util.h"
 
-#if HAS_FEATURE_MEMORY_SANITIZER
-#include <sanitizer/msan_interface.h>
-#endif
-
 int rdrand(unsigned long *ret) {
 
 #if defined(__i386__) || defined(__x86_64__)
@@ -60,11 +57,7 @@ int rdrand(unsigned long *ret) {
                      "setc %1"
                      : "=r" (*ret),
                        "=qm" (err));
-
-#if HAS_FEATURE_MEMORY_SANITIZER
-        __msan_unpoison(&err, sizeof(err));
-#endif
-
+        msan_unpoison(&err, sizeof(err));
         if (!err)
                 return -EAGAIN;
 
diff --git a/shared/systemd/src/basic/refcnt.h b/shared/systemd/src/basic/refcnt.h
deleted file mode 100644
index 40f9a84a..00000000
--- a/shared/systemd/src/basic/refcnt.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-#pragma once
-
-/* A type-safe atomic refcounter.
- *
- * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */
-
-typedef struct {
-        volatile unsigned _value;
-} RefCount;
-
-#define REFCNT_GET(r) ((r)._value)
-#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1))
-#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1))
-
-#define REFCNT_INIT ((RefCount) { ._value = 1 })
-
-#define _DEFINE_ATOMIC_REF_FUNC(type, name, scope)                    \
-        scope type *name##_ref(type *p) {                               \
-                if (!p)                                                 \
-                        return NULL;                                    \
-                                                                        \
-                assert_se(REFCNT_INC(p->n_ref) >= 2);                   \
-                return p;                                               \
-        }
-
-#define _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func, scope)       \
-        scope type *name##_unref(type *p) {                             \
-                if (!p)                                                 \
-                        return NULL;                                    \
-                                                                        \
-                if (REFCNT_DEC(p->n_ref) > 0)                           \
-                        return NULL;                                    \
-                                                                        \
-                return free_func(p);                                    \
-        }
-
-#define DEFINE_ATOMIC_REF_FUNC(type, name)    \
-        _DEFINE_ATOMIC_REF_FUNC(type, name,)
-#define DEFINE_PUBLIC_ATOMIC_REF_FUNC(type, name)     \
-        _DEFINE_ATOMIC_REF_FUNC(type, name, _public_)
-
-#define DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func)       \
-        _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func,)
-#define DEFINE_PUBLIC_ATOMIC_UNREF_FUNC(type, name, free_func)        \
-        _DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func, _public_)
-
-#define DEFINE_ATOMIC_REF_UNREF_FUNC(type, name, free_func)   \
-        DEFINE_ATOMIC_REF_FUNC(type, name);                   \
-        DEFINE_ATOMIC_UNREF_FUNC(type, name, free_func);
-
-#define DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(type, name, free_func)   \
-        DEFINE_PUBLIC_ATOMIC_REF_FUNC(type, name);                   \
-        DEFINE_PUBLIC_ATOMIC_UNREF_FUNC(type, name, free_func);
diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c
index 68a62f85..b98b0461 100644
--- a/shared/systemd/src/basic/socket-util.c
+++ b/shared/systemd/src/basic/socket-util.c
@@ -17,12 +17,14 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
 #include "log.h"
 #include "macro.h"
+#include "memory-util.h"
 #include "missing.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -33,7 +35,6 @@
 #include "strv.h"
 #include "user-util.h"
 #include "utf8.h"
-#include "util.h"
 
 #if 0 /* NM_IGNORED */
 #if ENABLE_IDN
@@ -238,23 +239,32 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
 }
 
 int socket_address_parse_netlink(SocketAddress *a, const char *s) {
-        int family;
+        _cleanup_free_ char *word = NULL;
         unsigned group = 0;
-        _cleanup_free_ char *sfamily = NULL;
+        int family, r;
+
         assert(a);
         assert(s);
 
         zero(*a);
         a->type = SOCK_RAW;
 
-        errno = 0;
-        if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
-                return errno > 0 ? -errno : -EINVAL;
+        r = extract_first_word(&s, &word, NULL, 0);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
 
-        family = netlink_family_from_string(sfamily);
+        family = netlink_family_from_string(word);
         if (family < 0)
                 return -EINVAL;
 
+        if (!isempty(s)) {
+                r = safe_atou(s, &group);
+                if (r < 0)
+                        return r;
+        }
+
         a->sockaddr.nl.nl_family = AF_NETLINK;
         a->sockaddr.nl.nl_groups = group;
 
@@ -1233,22 +1243,22 @@ int flush_accept(int fd) {
                                 continue;
 
                         return -errno;
-
-                } else if (r == 0)
+                }
+                if (r == 0)
                         return 0;
 
                 cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
                 if (cfd < 0) {
-                        if (errno == EINTR)
-                                continue;
-
                         if (errno == EAGAIN)
                                 return 0;
 
+                        if (ERRNO_IS_ACCEPT_AGAIN(errno))
+                                continue;
+
                         return -errno;
                 }
 
-                close(cfd);
+                safe_close(cfd);
         }
 }
 
@@ -1351,3 +1361,39 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
         }
 }
 #endif /* NM_IGNORED */
+
+int socket_bind_to_ifname(int fd, const char *ifname) {
+        assert(fd >= 0);
+
+        /* Call with NULL to drop binding */
+
+        if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int socket_bind_to_ifindex(int fd, int ifindex) {
+        char ifname[IFNAMSIZ] = "";
+
+        assert(fd >= 0);
+
+        if (ifindex <= 0) {
+                /* Drop binding */
+                if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0)
+                        return -errno;
+
+                return 0;
+        }
+
+        if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0)
+                return 0;
+        if (errno != ENOPROTOOPT)
+                return -errno;
+
+        /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
+        if (!if_indextoname(ifindex, ifname))
+                return -errno;
+
+        return socket_bind_to_ifname(fd, ifname);
+}
diff --git a/shared/systemd/src/basic/socket-util.h b/shared/systemd/src/basic/socket-util.h
index d2246a8e..15443f1e 100644
--- a/shared/systemd/src/basic/socket-util.h
+++ b/shared/systemd/src/basic/socket-util.h
@@ -200,3 +200,6 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) {
 
         return 0;
 }
+
+int socket_bind_to_ifname(int fd, const char *ifname);
+int socket_bind_to_ifindex(int fd, int ifindex);
diff --git a/shared/systemd/src/basic/sort-util.h b/shared/systemd/src/basic/sort-util.h
new file mode 100644
index 00000000..e029f864
--- /dev/null
+++ b/shared/systemd/src/basic/sort-util.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdlib.h>
+
+#include "macro.h"
+
+void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
+                 __compar_d_fn_t compar, void *arg);
+
+#define typesafe_bsearch_r(k, b, n, func, userdata)                     \
+        ({                                                              \
+                const typeof(b[0]) *_k = k;                             \
+                int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
+                xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \
+        })
+
+/**
+ * Normal bsearch requires base to be nonnull. Here were require
+ * that only if nmemb > 0.
+ */
+static inline void* bsearch_safe(const void *key, const void *base,
+                                 size_t nmemb, size_t size, __compar_fn_t compar) {
+        if (nmemb <= 0)
+                return NULL;
+
+        assert(base);
+        return bsearch(key, base, nmemb, size, compar);
+}
+
+#define typesafe_bsearch(k, b, n, func)                                 \
+        ({                                                              \
+                const typeof(b[0]) *_k = k;                             \
+                int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
+                bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \
+        })
+
+/**
+ * Normal qsort requires base to be nonnull. Here were require
+ * that only if nmemb > 0.
+ */
+static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
+        if (nmemb <= 1)
+                return;
+
+        assert(base);
+        qsort(base, nmemb, size, compar);
+}
+
+/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so
+ * is the prototype for the comparison function */
+#define typesafe_qsort(p, n, func)                                      \
+        ({                                                              \
+                int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
+                qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
+        })
+
+static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
+        if (nmemb <= 1)
+                return;
+
+        assert(base);
+        qsort_r(base, nmemb, size, compar, userdata);
+}
+
+#define typesafe_qsort_r(p, n, func, userdata)                          \
+        ({                                                              \
+                int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
+                qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \
+        })
diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c
index 686adaf1..c9837fa1 100644
--- a/shared/systemd/src/basic/stat-util.c
+++ b/shared/systemd/src/basic/stat-util.c
@@ -226,52 +226,6 @@ int fd_is_network_fs(int fd) {
         return is_network_fs(&s);
 }
 
-int fd_is_network_ns(int fd) {
-        struct statfs s;
-        int r;
-
-        /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice
-         * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle
-         * this somewhat nicely.
-         *
-         * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
-         * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
-
-        if (fstatfs(fd, &s) < 0)
-                return -errno;
-
-        if (!is_fs_type(&s, NSFS_MAGIC)) {
-                /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs
-                 * instead. Handle that in a somewhat smart way. */
-
-                if (is_fs_type(&s, PROC_SUPER_MAGIC)) {
-                        struct statfs t;
-
-                        /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the
-                         * passed fd might refer to a network namespace, but we can't know for sure. In that case,
-                         * return a recognizable error. */
-
-                        if (statfs("/proc/self/ns/net", &t) < 0)
-                                return -errno;
-
-                        if (s.f_type == t.f_type)
-                                return -EUCLEAN; /* It's possible, we simply don't know */
-                }
-
-                return 0; /* No! */
-        }
-
-        r = ioctl(fd, NS_GET_NSTYPE);
-        if (r < 0) {
-                if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
-                        return -EUCLEAN;
-
-                return -errno;
-        }
-
-        return r == CLONE_NEWNET;
-}
-
 int path_is_temporary_fs(const char *path) {
         _cleanup_close_ int fd = -1;
 
diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h
index 74fb7251..7824af35 100644
--- a/shared/systemd/src/basic/stat-util.h
+++ b/shared/systemd/src/basic/stat-util.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include <fcntl.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <sys/stat.h>
@@ -50,8 +51,6 @@ bool is_network_fs(const struct statfs *s) _pure_;
 int fd_is_temporary_fs(int fd);
 int fd_is_network_fs(int fd);
 
-int fd_is_network_ns(int fd);
-
 int path_is_temporary_fs(const char *path);
 
 /* Because statfs.t_type can be int on some architectures, we have to cast
diff --git a/shared/systemd/src/basic/stdio-util.h b/shared/systemd/src/basic/stdio-util.h
index dc67b6e7..c3b9448d 100644
--- a/shared/systemd/src/basic/stdio-util.h
+++ b/shared/systemd/src/basic/stdio-util.h
@@ -7,7 +7,7 @@
 #include <sys/types.h>
 
 #include "macro.h"
-#include "util.h"
+#include "memory-util.h"
 
 #define snprintf_ok(buf, len, fmt, ...) \
         ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len))
diff --git a/shared/systemd/src/basic/string-table.h b/shared/systemd/src/basic/string-table.h
index 228c12ad..42fe4f43 100644
--- a/shared/systemd/src/basic/string-table.h
+++ b/shared/systemd/src/basic/string-table.h
@@ -59,13 +59,13 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
 
 #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \
         scope type name##_from_string(const char *s) {                  \
-                type i;                                                 \
                 unsigned u = 0;                                         \
+                type i;                                                 \
                 if (!s)                                                 \
                         return (type) -1;                               \
-                for (i = 0; i < (type) ELEMENTSOF(name##_table); i++)   \
-                        if (streq_ptr(name##_table[i], s))              \
-                                return i;                               \
+                i = (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
+                if (i >= 0)                                             \
+                        return i;                                       \
                 if (safe_atou(s, &u) >= 0 && u <= max)                  \
                         return (type) u;                                \
                 return (type) -1;                                       \
diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c
index 0e961927..df519976 100644
--- a/shared/systemd/src/basic/string-util.c
+++ b/shared/systemd/src/basic/string-util.c
@@ -12,14 +12,15 @@
 
 #include "alloc-util.h"
 #include "escape.h"
+#include "fileio.h"
 #include "gunicode.h"
 #include "locale-util.h"
 #include "macro.h"
+#include "memory-util.h"
 #include "string-util.h"
 #include "terminal-util.h"
 #include "utf8.h"
 #include "util.h"
-#include "fileio.h"
 
 int strcmp_ptr(const char *a, const char *b) {
 
@@ -681,19 +682,6 @@ char *cellescape(char *buf, size_t len, const char *s) {
 }
 #endif /* NM_IGNORED */
 
-bool nulstr_contains(const char *nulstr, const char *needle) {
-        const char *i;
-
-        if (!nulstr)
-                return false;
-
-        NULSTR_FOREACH(i, nulstr)
-                if (streq(i, needle))
-                        return true;
-
-        return false;
-}
-
 char* strshorten(char *s, size_t l) {
         assert(s);
 
@@ -1056,25 +1044,6 @@ int free_and_strndup(char **p, const char *s, size_t l) {
         return 1;
 }
 
-#if !HAVE_EXPLICIT_BZERO
-/*
- * Pointer to memset is volatile so that compiler must de-reference
- * the pointer and can't assume that it points to any function in
- * particular (such as memset, which it then might further "optimize")
- * This approach is inspired by openssl's crypto/mem_clr.c.
- */
-typedef void *(*memset_t)(void *,int,size_t);
-
-static volatile memset_t memset_func = memset;
-
-void* explicit_bzero_safe(void *p, size_t l) {
-        if (l > 0)
-                memset_func(p, '\0', l);
-
-        return p;
-}
-#endif
-
 char* string_erase(char *x) {
         if (!x)
                 return NULL;
diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h
index 38070abb..b23f4c83 100644
--- a/shared/systemd/src/basic/string-util.h
+++ b/shared/systemd/src/basic/string-util.h
@@ -57,6 +57,16 @@ static inline const char *empty_to_dash(const char *str) {
         return isempty(str) ? "-" : str;
 }
 
+static inline bool empty_or_dash(const char *str) {
+        return !str ||
+                str[0] == 0 ||
+                (str[0] == '-' && str[1] == 0);
+}
+
+static inline const char *empty_or_dash_to_null(const char *p) {
+        return empty_or_dash(p) ? NULL : p;
+}
+
 static inline char *startswith(const char *s, const char *prefix) {
         size_t l;
 
@@ -165,8 +175,6 @@ char *cellescape(char *buf, size_t len, const char *s);
 /* This limit is arbitrary, enough to give some idea what the string contains */
 #define CELLESCAPE_DEFAULT_LENGTH 64
 
-bool nulstr_contains(const char *nulstr, const char *needle);
-
 char* strshorten(char *s, size_t l);
 
 char *strreplace(const char *text, const char *old_string, const char *new_string);
@@ -182,33 +190,12 @@ char *strrep(const char *s, unsigned n);
 int split_pair(const char *s, const char *sep, char **l, char **r);
 
 int free_and_strdup(char **p, const char *s);
-int free_and_strndup(char **p, const char *s, size_t l);
-
-/* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
-static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
-
-        if (needlelen <= 0)
-                return (void*) haystack;
-
-        if (haystacklen < needlelen)
-                return NULL;
-
-        assert(haystack);
-        assert(needle);
-
-        return memmem(haystack, haystacklen, needle, needlelen);
+static inline int free_and_strdup_warn(char **p, const char *s) {
+        if (free_and_strdup(p, s) < 0)
+                return log_oom();
+        return 0;
 }
-
-#if HAVE_EXPLICIT_BZERO
-static inline void* explicit_bzero_safe(void *p, size_t l) {
-        if (l > 0)
-                explicit_bzero(p, l);
-
-        return p;
-}
-#else
-void *explicit_bzero_safe(void *p, size_t l);
-#endif
+int free_and_strndup(char **p, const char *s, size_t l);
 
 char *string_erase(char *x);
 
diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c
index 1615c9ba..8de40b9d 100644
--- a/shared/systemd/src/basic/strv.c
+++ b/shared/systemd/src/basic/strv.c
@@ -13,9 +13,10 @@
 #include "escape.h"
 #include "extract-word.h"
 #include "fileio.h"
+#include "nulstr-util.h"
+#include "sort-util.h"
 #include "string-util.h"
 #include "strv.h"
-#include "util.h"
 
 char *strv_find(char **l, const char *name) {
         char **i;
@@ -651,6 +652,7 @@ char **strv_parse_nulstr(const char *s, size_t l) {
         return v;
 }
 
+#if 0 /* NM_IGNORED */
 char **strv_split_nulstr(const char *s) {
         const char *i;
         char **r = NULL;
@@ -666,6 +668,7 @@ char **strv_split_nulstr(const char *s) {
 
         return r;
 }
+#endif /* NM_IGNORED */
 
 int strv_make_nulstr(char **l, char **p, size_t *q) {
         /* A valid nulstr with two NULs at the end will be created, but
@@ -722,6 +725,7 @@ bool strv_overlap(char **a, char **b) {
         return false;
 }
 
+#if 0 /* NM_IGNORED */
 static int str_compare(char * const *a, char * const *b) {
         return strcmp(*a, *b);
 }
@@ -730,6 +734,7 @@ char **strv_sort(char **l) {
         typesafe_qsort(l, strv_length(l), str_compare);
         return l;
 }
+#endif /* NM_IGNORED */
 
 bool strv_equal(char **a, char **b) {
 
diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h
index 392cab65..aa5f95ab 100644
--- a/shared/systemd/src/basic/strv.h
+++ b/shared/systemd/src/basic/strv.h
@@ -5,12 +5,12 @@
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdio.h>
 
 #include "alloc-util.h"
 #include "extract-word.h"
 #include "macro.h"
 #include "string-util.h"
-#include "util.h"
 
 char *strv_find(char **l, const char *name) _pure_;
 char *strv_find_prefix(char **l, const char *name) _pure_;
diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c
index 9ea0380a..14b17bfc 100644
--- a/shared/systemd/src/basic/time-util.c
+++ b/shared/systemd/src/basic/time-util.c
@@ -1219,7 +1219,7 @@ int get_timezones(char ***ret) {
         n_allocated = 2;
         n_zones = 1;
 
-        f = fopen("/usr/share/zoneinfo/zone.tab", "re");
+        f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
         if (f) {
                 for (;;) {
                         _cleanup_free_ char *line = NULL;
diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c
index e9958c91..f1c6ac1f 100644
--- a/shared/systemd/src/basic/utf8.c
+++ b/shared/systemd/src/basic/utf8.c
@@ -65,12 +65,7 @@ static bool unichar_is_control(char32_t ch) {
 #endif /* NM_IGNORED */
 
 /* count of characters used to encode one unicode char */
-static size_t utf8_encoded_expected_len(const char *str) {
-        uint8_t c;
-
-        assert(str);
-
-        c = (uint8_t) str[0];
+static size_t utf8_encoded_expected_len(uint8_t c) {
         if (c < 0x80)
                 return 1;
         if ((c & 0xe0) == 0xc0)
@@ -94,7 +89,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
 
         assert(str);
 
-        len = utf8_encoded_expected_len(str);
+        len = utf8_encoded_expected_len(str[0]);
 
         switch (len) {
         case 1:
@@ -138,14 +133,14 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
 
         assert(str);
 
-        for (p = str; length;) {
+        for (p = str; length > 0;) {
                 int encoded_len, r;
                 char32_t val;
 
-                encoded_len = utf8_encoded_valid_unichar(p);
-                if (encoded_len < 0 ||
-                    (size_t) encoded_len > length)
+                encoded_len = utf8_encoded_valid_unichar(p, length);
+                if (encoded_len < 0)
                         return false;
+                assert(encoded_len > 0 && (size_t) encoded_len <= length);
 
                 r = utf8_encoded_to_unichar(p, &val);
                 if (r < 0 ||
@@ -170,7 +165,7 @@ char *utf8_is_valid(const char *str) {
         while (*p) {
                 int len;
 
-                len = utf8_encoded_valid_unichar(p);
+                len = utf8_encoded_valid_unichar(p, (size_t) -1);
                 if (len < 0)
                         return NULL;
 
@@ -192,7 +187,7 @@ char *utf8_escape_invalid(const char *str) {
         while (*str) {
                 int len;
 
-                len = utf8_encoded_valid_unichar(str);
+                len = utf8_encoded_valid_unichar(str, (size_t) -1);
                 if (len > 0) {
                         s = mempcpy(s, str, len);
                         str += len;
@@ -220,7 +215,7 @@ char *utf8_escape_non_printable(const char *str) {
         while (*str) {
                 int len;
 
-                len = utf8_encoded_valid_unichar(str);
+                len = utf8_encoded_valid_unichar(str, (size_t) -1);
                 if (len > 0) {
                         if (utf8_is_printable(str, len)) {
                                 s = mempcpy(s, str, len);
@@ -416,7 +411,7 @@ char16_t *utf8_to_utf16(const char *s, size_t length) {
                 char32_t unichar;
                 size_t e;
 
-                e = utf8_encoded_expected_len(s + i);
+                e = utf8_encoded_expected_len(s[i]);
                 if (e <= 1) /* Invalid and single byte characters are copied as they are */
                         goto copy;
 
@@ -469,17 +464,24 @@ static int utf8_unichar_to_encoded_len(char32_t unichar) {
 }
 
 /* validate one encoded unicode char and return its length */
-int utf8_encoded_valid_unichar(const char *str) {
+int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) {
         char32_t unichar;
         size_t len, i;
         int r;
 
         assert(str);
+        assert(length > 0);
 
-        len = utf8_encoded_expected_len(str);
+        /* We read until NUL, at most length bytes. (size_t) -1 may be used to disable the length check. */
+
+        len = utf8_encoded_expected_len(str[0]);
         if (len == 0)
                 return -EINVAL;
 
+        /* Do we have a truncated multi-byte character? */
+        if (len > length)
+                return -EINVAL;
+
         /* ascii is valid */
         if (len == 1)
                 return 1;
@@ -513,7 +515,7 @@ size_t utf8_n_codepoints(const char *str) {
         while (*str != 0) {
                 int k;
 
-                k = utf8_encoded_valid_unichar(str);
+                k = utf8_encoded_valid_unichar(str, (size_t) -1);
                 if (k < 0)
                         return (size_t) -1;
 
diff --git a/shared/systemd/src/basic/utf8.h b/shared/systemd/src/basic/utf8.h
index 62845693..6df70921 100644
--- a/shared/systemd/src/basic/utf8.h
+++ b/shared/systemd/src/basic/utf8.h
@@ -32,7 +32,7 @@ char16_t *utf8_to_utf16(const char *s, size_t length);
 
 size_t char16_strlen(const char16_t *s); /* returns the number of 16bit words in the string (not bytes!) */
 
-int utf8_encoded_valid_unichar(const char *str);
+int utf8_encoded_valid_unichar(const char *str, size_t length);
 int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar);
 
 static inline bool utf16_is_surrogate(char16_t c) {
diff --git a/shared/systemd/src/basic/util.c b/shared/systemd/src/basic/util.c
index 7686ecd2..23aa6b26 100644
--- a/shared/systemd/src/basic/util.c
+++ b/shared/systemd/src/basic/util.c
@@ -21,7 +21,6 @@
 #include "alloc-util.h"
 #include "btrfs-util.h"
 #include "build.h"
-#include "cgroup-util.h"
 #include "def.h"
 #include "device-nodes.h"
 #include "dirent-util.h"
@@ -54,35 +53,6 @@
 int saved_argc = 0;
 char **saved_argv = NULL;
 static int saved_in_initrd = -1;
-#endif /* NM_IGNORED */
-
-size_t page_size(void) {
-        static thread_local size_t pgsz = 0;
-        long r;
-
-        if (_likely_(pgsz > 0))
-                return pgsz;
-
-        r = sysconf(_SC_PAGESIZE);
-        assert(r > 0);
-
-        pgsz = (size_t) r;
-        return pgsz;
-}
-
-#if 0 /* NM_IGNORED */
-bool plymouth_running(void) {
-        return access("/run/plymouth/pid", F_OK) >= 0;
-}
-
-bool display_is_local(const char *display) {
-        assert(display);
-
-        return
-                display[0] == ':' &&
-                display[1] >= '0' &&
-                display[1] <= '9';
-}
 
 bool kexec_loaded(void) {
        _cleanup_free_ char *s = NULL;
@@ -146,53 +116,6 @@ void in_initrd_force(bool value) {
         saved_in_initrd = value;
 }
 
-/* hey glibc, APIs with callbacks without a user pointer are so useless */
-void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
-                 __compar_d_fn_t compar, void *arg) {
-        size_t l, u, idx;
-        const void *p;
-        int comparison;
-
-        assert(!size_multiply_overflow(nmemb, size));
-
-        l = 0;
-        u = nmemb;
-        while (l < u) {
-                idx = (l + u) / 2;
-                p = (const uint8_t*) base + idx * size;
-                comparison = compar(key, p, arg);
-                if (comparison < 0)
-                        u = idx;
-                else if (comparison > 0)
-                        l = idx + 1;
-                else
-                        return (void *)p;
-        }
-        return NULL;
-}
-
-bool memeqzero(const void *data, size_t length) {
-        /* Does the buffer consist entirely of NULs?
-         * Copied from https://github.com/systemd/casync/, copied in turn from
-         * https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92,
-         * which is licensed CC-0.
-         */
-
-        const uint8_t *p = data;
-        size_t i;
-
-        /* Check first 16 bytes manually */
-        for (i = 0; i < 16; i++, length--) {
-                if (length == 0)
-                        return true;
-                if (p[i])
-                        return false;
-        }
-
-        /* Now we know first 16 bytes are NUL, memcmp with self.  */
-        return memcmp(data, p + i, length) == 0;
-}
-
 int on_ac_power(void) {
         bool found_offline = false, found_online = false;
         _cleanup_closedir_ DIR *d = NULL;
@@ -299,268 +222,6 @@ int container_get_leader(const char *machine, pid_t *pid) {
         return 0;
 }
 
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
-        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
-        int rfd = -1;
-
-        assert(pid >= 0);
-
-        if (mntns_fd) {
-                const char *mntns;
-
-                mntns = procfs_file_alloca(pid, "ns/mnt");
-                mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-                if (mntnsfd < 0)
-                        return -errno;
-        }
-
-        if (pidns_fd) {
-                const char *pidns;
-
-                pidns = procfs_file_alloca(pid, "ns/pid");
-                pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-                if (pidnsfd < 0)
-                        return -errno;
-        }
-
-        if (netns_fd) {
-                const char *netns;
-
-                netns = procfs_file_alloca(pid, "ns/net");
-                netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-                if (netnsfd < 0)
-                        return -errno;
-        }
-
-        if (userns_fd) {
-                const char *userns;
-
-                userns = procfs_file_alloca(pid, "ns/user");
-                usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-                if (usernsfd < 0 && errno != ENOENT)
-                        return -errno;
-        }
-
-        if (root_fd) {
-                const char *root;
-
-                root = procfs_file_alloca(pid, "root");
-                rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
-                if (rfd < 0)
-                        return -errno;
-        }
-
-        if (pidns_fd)
-                *pidns_fd = pidnsfd;
-
-        if (mntns_fd)
-                *mntns_fd = mntnsfd;
-
-        if (netns_fd)
-                *netns_fd = netnsfd;
-
-        if (userns_fd)
-                *userns_fd = usernsfd;
-
-        if (root_fd)
-                *root_fd = rfd;
-
-        pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
-
-        return 0;
-}
-
-int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
-        if (userns_fd >= 0) {
-                /* Can't setns to your own userns, since then you could
-                 * escalate from non-root to root in your own namespace, so
-                 * check if namespaces equal before attempting to enter. */
-                _cleanup_free_ char *userns_fd_path = NULL;
-                int r;
-                if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
-                        return -ENOMEM;
-
-                r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
-                if (r < 0)
-                        return r;
-                if (r)
-                        userns_fd = -1;
-        }
-
-        if (pidns_fd >= 0)
-                if (setns(pidns_fd, CLONE_NEWPID) < 0)
-                        return -errno;
-
-        if (mntns_fd >= 0)
-                if (setns(mntns_fd, CLONE_NEWNS) < 0)
-                        return -errno;
-
-        if (netns_fd >= 0)
-                if (setns(netns_fd, CLONE_NEWNET) < 0)
-                        return -errno;
-
-        if (userns_fd >= 0)
-                if (setns(userns_fd, CLONE_NEWUSER) < 0)
-                        return -errno;
-
-        if (root_fd >= 0) {
-                if (fchdir(root_fd) < 0)
-                        return -errno;
-
-                if (chroot(".") < 0)
-                        return -errno;
-        }
-
-        return reset_uid_gid();
-}
-
-uint64_t physical_memory(void) {
-        _cleanup_free_ char *root = NULL, *value = NULL;
-        uint64_t mem, lim;
-        size_t ps;
-        long sc;
-        int r;
-
-        /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
-         * memory.
-         *
-         * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
-         * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
-
-        sc = sysconf(_SC_PHYS_PAGES);
-        assert(sc > 0);
-
-        ps = page_size();
-        mem = (uint64_t) sc * (uint64_t) ps;
-
-        r = cg_get_root_path(&root);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m");
-                return mem;
-        }
-
-        r = cg_all_unified();
-        if (r < 0) {
-                log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m");
-                return mem;
-        }
-        if (r > 0) {
-                r = cg_get_attribute("memory", root, "memory.max", &value);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m");
-                        return mem;
-                }
-
-                if (streq(value, "max"))
-                        return mem;
-        } else {
-                r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m");
-                        return mem;
-                }
-        }
-
-        r = safe_atou64(value, &lim);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value);
-                return mem;
-        }
-        if (lim == UINT64_MAX)
-                return mem;
-
-        /* Make sure the limit is a multiple of our own page size */
-        lim /= ps;
-        lim *= ps;
-
-        return MIN(mem, lim);
-}
-
-uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
-        uint64_t p, m, ps, r;
-
-        assert(max > 0);
-
-        /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
-         * the result is a multiple of the page size (rounds down). */
-
-        ps = page_size();
-        assert(ps > 0);
-
-        p = physical_memory() / ps;
-        assert(p > 0);
-
-        m = p * v;
-        if (m / p != v)
-                return UINT64_MAX;
-
-        m /= max;
-
-        r = m * ps;
-        if (r / ps != m)
-                return UINT64_MAX;
-
-        return r;
-}
-
-uint64_t system_tasks_max(void) {
-
-        uint64_t a = TASKS_MAX, b = TASKS_MAX;
-        _cleanup_free_ char *root = NULL;
-        int r;
-
-        /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
-         * limit:
-         *
-         * a) the maximum tasks value the kernel allows on this architecture
-         * b) the cgroups pids_max attribute for the system
-         * c) the kernel's configured maximum PID value
-         *
-         * And then pick the smallest of the three */
-
-        r = procfs_tasks_get_limit(&a);
-        if (r < 0)
-                log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m");
-
-        r = cg_get_root_path(&root);
-        if (r < 0)
-                log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m");
-        else {
-                _cleanup_free_ char *value = NULL;
-
-                r = cg_get_attribute("pids", root, "pids.max", &value);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m");
-                else if (!streq(value, "max")) {
-                        r = safe_atou64(value, &b);
-                        if (r < 0)
-                                log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m");
-                }
-        }
-
-        return MIN3(TASKS_MAX,
-                    a <= 0 ? TASKS_MAX : a,
-                    b <= 0 ? TASKS_MAX : b);
-}
-
-uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
-        uint64_t t, m;
-
-        assert(max > 0);
-
-        /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
-         * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
-
-        t = system_tasks_max();
-        assert(t > 0);
-
-        m = t * v;
-        if (m / t != v) /* overflow? */
-                return UINT64_MAX;
-
-        return m / max;
-}
-
 int version(void) {
         puts("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n"
              SYSTEMD_FEATURES);
diff --git a/shared/systemd/src/basic/util.h b/shared/systemd/src/basic/util.h
index dc33d660..25e6ab81 100644
--- a/shared/systemd/src/basic/util.h
+++ b/shared/systemd/src/basic/util.h
@@ -1,34 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-#include <alloca.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <locale.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stddef.h>
 #include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/inotify.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "format-util.h"
-#include "macro.h"
-#include "time-util.h"
 
-size_t page_size(void) _pure_;
-#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+#include "macro.h"
 
 static inline const char* yes_no(bool b) {
         return b ? "yes" : "no";
@@ -46,19 +21,14 @@ static inline const char* enable_disable(bool b) {
         return b ? "enable" : "disable";
 }
 
-bool plymouth_running(void);
-
-bool display_is_local(const char *display) _pure_;
-
-#define NULSTR_FOREACH(i, l)                                    \
-        for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
-
-#define NULSTR_FOREACH_PAIR(i, j, l)                             \
-        for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
-
 extern int saved_argc;
 extern char **saved_argv;
 
+static inline void save_argc_argv(int argc, char **argv) {
+        saved_argc = argc;
+        saved_argv = argv;
+}
+
 bool kexec_loaded(void);
 
 int prot_from_flags(int flags) _const_;
@@ -66,138 +36,8 @@ int prot_from_flags(int flags) _const_;
 bool in_initrd(void);
 void in_initrd_force(bool value);
 
-void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
-                 __compar_d_fn_t compar, void *arg);
-
-#define typesafe_bsearch_r(k, b, n, func, userdata)                     \
-        ({                                                              \
-                const typeof(b[0]) *_k = k;                             \
-                int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
-                xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_d_fn_t) _func_, userdata); \
-        })
-
-/**
- * Normal bsearch requires base to be nonnull. Here were require
- * that only if nmemb > 0.
- */
-static inline void* bsearch_safe(const void *key, const void *base,
-                                 size_t nmemb, size_t size, __compar_fn_t compar) {
-        if (nmemb <= 0)
-                return NULL;
-
-        assert(base);
-        return bsearch(key, base, nmemb, size, compar);
-}
-
-#define typesafe_bsearch(k, b, n, func)                                 \
-        ({                                                              \
-                const typeof(b[0]) *_k = k;                             \
-                int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
-                bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (__compar_fn_t) _func_); \
-        })
-
-/**
- * Normal qsort requires base to be nonnull. Here were require
- * that only if nmemb > 0.
- */
-static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
-        if (nmemb <= 1)
-                return;
-
-        assert(base);
-        qsort(base, nmemb, size, compar);
-}
-
-/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so
- * is the prototype for the comparison function */
-#define typesafe_qsort(p, n, func)                                      \
-        ({                                                              \
-                int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
-                qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
-        })
-
-static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
-        if (nmemb <= 1)
-                return;
-
-        assert(base);
-        qsort_r(base, nmemb, size, compar, userdata);
-}
-
-#define typesafe_qsort_r(p, n, func, userdata)                          \
-        ({                                                              \
-                int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
-                qsort_r_safe((p), (n), sizeof((p)[0]), (__compar_d_fn_t) _func_, userdata); \
-        })
-
-/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
-static inline void memcpy_safe(void *dst, const void *src, size_t n) {
-        if (n == 0)
-                return;
-        assert(src);
-        memcpy(dst, src, n);
-}
-
-/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */
-static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
-        if (n == 0)
-                return 0;
-        assert(s1);
-        assert(s2);
-        return memcmp(s1, s2, n);
-}
-
-/* Compare s1 (length n1) with s2 (length n2) in lexicographic order. */
-static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2) {
-        return memcmp_safe(s1, s2, MIN(n1, n2))
-            ?: CMP(n1, n2);
-}
-
 int on_ac_power(void);
 
-#define memzero(x,l)                                            \
-        ({                                                      \
-                size_t _l_ = (l);                               \
-                void *_x_ = (x);                                \
-                _l_ == 0 ? _x_ : memset(_x_, 0, _l_);           \
-        })
-
-#define zero(x) (memzero(&(x), sizeof(x)))
-
-bool memeqzero(const void *data, size_t length);
-
-#define eqzero(x) memeqzero(x, sizeof(x))
-
-static inline void *mempset(void *s, int c, size_t n) {
-        memset(s, c, n);
-        return (uint8_t*)s + n;
-}
-
-static inline void _reset_errno_(int *saved_errno) {
-        if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */
-                return;
-
-        errno = *saved_errno;
-}
-
-#define PROTECT_ERRNO                                                   \
-        _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno
-
-#define UNPROTECT_ERRNO                         \
-        do {                                    \
-                errno = _saved_errno_;          \
-                _saved_errno_ = -1;             \
-        } while (false)
-
-static inline int negative_errno(void) {
-        /* This helper should be used to shut up gcc if you know 'errno' is
-         * negative. Instead of "return -errno;", use "return negative_errno();"
-         * It will suppress bogus gcc warnings in case it assumes 'errno' might
-         * be 0 and thus the caller's error-handling might not be triggered. */
-        assert_return(errno > 0, -EINVAL);
-        return -errno;
-}
-
 static inline unsigned u64log2(uint64_t n) {
 #if __SIZEOF_LONG_LONG__ == 8
         return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0;
@@ -237,15 +77,6 @@ static inline unsigned log2u_round_up(unsigned x) {
 
 int container_get_leader(const char *machine, pid_t *pid);
 
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd);
-int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
-
-uint64_t physical_memory(void);
-uint64_t physical_memory_scale(uint64_t v, uint64_t max);
-
-uint64_t system_tasks_max(void);
-uint64_t system_tasks_max_scale(uint64_t v, uint64_t max);
-
 int version(void);
 
 int str_verscmp(const char *s1, const char *s2);