about summary refs log tree commit diff
path: root/shared
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2019-03-12 15:13:33 +0100
committerSebastien Bacher <seb128@ubuntu.com>2019-03-12 15:13:33 +0100
commitdd428301eb6f02542015121d7b08d9997f137e50 (patch)
tree5530189f63510287d65268fc36025bdbc9414c00 /shared
parentbbae86d3d2997a853ca0365e8eb7a3ca7489ee09 (diff)
New upstream version 1.15.91
Diffstat (limited to 'shared')
-rw-r--r--shared/meson.build110
-rw-r--r--shared/nm-common-macros.h1
-rw-r--r--shared/nm-default.h1
-rw-r--r--shared/nm-ethtool-utils.c2
-rw-r--r--shared/nm-meta-setting.c24
-rw-r--r--shared/nm-meta-setting.h2
-rw-r--r--shared/nm-test-utils-impl.c1
-rw-r--r--shared/nm-utils/nm-c-list.h36
-rw-r--r--shared/nm-utils/nm-dedup-multi.c55
-rw-r--r--shared/nm-utils/nm-errno.c177
-rw-r--r--shared/nm-utils/nm-errno.h138
-rw-r--r--shared/nm-utils/nm-glib.h8
-rw-r--r--shared/nm-utils/nm-hash-utils.c2
-rw-r--r--shared/nm-utils/nm-hash-utils.h3
-rw-r--r--shared/nm-utils/nm-io-utils.c75
-rw-r--r--shared/nm-utils/nm-jansson.h13
-rw-r--r--shared/nm-utils/nm-logging-fwd.h8
-rw-r--r--shared/nm-utils/nm-macros-internal.h107
-rw-r--r--shared/nm-utils/nm-secret-utils.c27
-rw-r--r--shared/nm-utils/nm-secret-utils.h27
-rw-r--r--shared/nm-utils/nm-shared-utils.c376
-rw-r--r--shared/nm-utils/nm-shared-utils.h92
-rw-r--r--shared/nm-utils/nm-test-utils.h8
-rw-r--r--shared/nm-utils/tests/test-shared-general.c46
-rw-r--r--shared/nm-version-macros.h2
-rw-r--r--shared/systemd/nm-logging-stub.c6
-rw-r--r--shared/systemd/sd-adapt-shared/missing.h3
-rw-r--r--shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h19
-rw-r--r--shared/systemd/src/basic/alloc-util.h49
-rw-r--r--shared/systemd/src/basic/env-file.c32
-rw-r--r--shared/systemd/src/basic/env-util.c2
-rw-r--r--shared/systemd/src/basic/env-util.h6
-rw-r--r--shared/systemd/src/basic/extract-word.h12
-rw-r--r--shared/systemd/src/basic/fd-util.c39
-rw-r--r--shared/systemd/src/basic/fileio.c4
-rw-r--r--shared/systemd/src/basic/hashmap.c10
-rw-r--r--shared/systemd/src/basic/hashmap.h1
-rw-r--r--shared/systemd/src/basic/hexdecoct.c7
-rw-r--r--shared/systemd/src/basic/in-addr-util.c45
-rw-r--r--shared/systemd/src/basic/in-addr-util.h24
-rw-r--r--shared/systemd/src/basic/io-util.c16
-rw-r--r--shared/systemd/src/basic/io-util.h2
-rw-r--r--shared/systemd/src/basic/missing_fcntl.h60
-rw-r--r--shared/systemd/src/basic/path-util.h2
-rw-r--r--shared/systemd/src/basic/prioq.c7
-rw-r--r--shared/systemd/src/basic/prioq.h8
-rw-r--r--shared/systemd/src/basic/process-util.c91
-rw-r--r--shared/systemd/src/basic/process-util.h4
-rw-r--r--shared/systemd/src/basic/stat-util.c16
-rw-r--r--shared/systemd/src/basic/stat-util.h5
-rw-r--r--shared/systemd/src/basic/string-util.h3
-rw-r--r--shared/systemd/src/basic/strv.h15
-rw-r--r--shared/systemd/src/basic/time-util.c9
-rw-r--r--shared/systemd/src/basic/time-util.h1
-rw-r--r--shared/systemd/src/basic/tmpfile-util.c1
-rw-r--r--shared/systemd/src/basic/util.h9
56 files changed, 1419 insertions, 430 deletions
diff --git a/shared/meson.build b/shared/meson.build
index 54ef3a2e..a6e94d6b 100644
--- a/shared/meson.build
+++ b/shared/meson.build
@@ -81,37 +81,37 @@ shared_nm_test_utils_impl_c = files('nm-test-utils-impl.c')
 
 shared_nm_utils_nm_vpn_plugin_utils_c = files('nm-utils/nm-vpn-plugin-utils.c')
 
-shared_files_time_utils = files('nm-utils/nm-time-utils.c')
-
-shared_files_libnm_core = 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-udev-utils.c
-'''.split())
-
-shared_files_clients_common = files('''
-    nm-utils/nm-enum-utils.c
-    nm-utils/nm-hash-utils.c
-    nm-utils/nm-random-utils.c
-    nm-utils/nm-shared-utils.c
-'''.split())
-
-shared_files_libnm_util = files('''
-    nm-utils/nm-shared-utils.c
-'''.split())
-
-shared_files_libnm_glib = files('''
-    nm-utils/nm-udev-utils.c
-'''.split())
-
-shared_dep = declare_dependency(
+###############################################################################
+
+shared_nm_utils_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(
+    '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,
+    include_directories: [
+        top_inc,
+        shared_inc,
+    ],
+    dependencies: [
+        glib_dep,
+    ],
+)
+
+shared_nm_utils_base_dep = declare_dependency(
+    link_with: shared_nm_utils_base,
     include_directories: [
         top_inc,
         shared_inc,
@@ -119,26 +119,44 @@ shared_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,
+    include_directories: [
+        top_inc,
+        shared_inc,
+    ],
+    dependencies: [
+        glib_dep,
+        shared_nm_utils_base_dep,
+        libudev_dep,
+    ],
+)
+
+shared_nm_utils_udev_dep = declare_dependency(
+    link_with: shared_nm_utils_udev,
+    include_directories: [
+        top_inc,
+        shared_inc,
+    ],
+    dependencies: [
+        glib_dep,
+        shared_nm_utils_base_dep,
+        libudev_dep,
+    ],
+)
+
 ###############################################################################
 
 test_shared_general = executable(
   'nm-utils/tests/test-shared-general',
-  [ 'nm-utils/tests/test-shared-general.c',
-    'nm-utils/c-list-util.c',
-    'nm-utils/nm-dedup-multi.c',
-    'nm-utils/nm-enum-utils.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',
-  ],
+  [ '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_dep,
+  dependencies: shared_nm_utils_base_dep,
   link_with: shared_c_siphash,
 )
 test(
@@ -188,7 +206,7 @@ libnm_systemd_shared = static_library(
     'systemd/sd-adapt-shared',
     'systemd/src/basic',
   ),
-  dependencies: shared_dep,
+  dependencies: shared_nm_utils_base_dep,
   c_args: [
     '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED',
     '-DG_LOG_DOMAIN="libnm"',
@@ -201,7 +219,7 @@ libnm_systemd_shared_dep = declare_dependency(
     'systemd/src/basic',
   ),
   dependencies: [
-    shared_dep,
+    shared_nm_utils_base_dep,
   ],
   link_with: [
     libnm_systemd_shared,
@@ -217,7 +235,7 @@ libnm_systemd_logging_stub = static_library(
     'systemd/sd-adapt-shared',
     'systemd/src/basic',
   ),
-  dependencies: shared_dep,
+  dependencies: shared_nm_utils_base_dep,
   c_args: [
     '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED',
     '-DG_LOG_DOMAIN="libnm"',
diff --git a/shared/nm-common-macros.h b/shared/nm-common-macros.h
index 2edb9728..f5aa3a1e 100644
--- a/shared/nm-common-macros.h
+++ b/shared/nm-common-macros.h
@@ -40,6 +40,7 @@
 #define NM_AUTH_PERMISSION_CHECKPOINT_ROLLBACK        "org.freedesktop.NetworkManager.checkpoint-rollback"
 #define NM_AUTH_PERMISSION_ENABLE_DISABLE_STATISTICS  "org.freedesktop.NetworkManager.enable-disable-statistics"
 #define NM_AUTH_PERMISSION_ENABLE_DISABLE_CONNECTIVITY_CHECK "org.freedesktop.NetworkManager.enable-disable-connectivity-check"
+#define NM_AUTH_PERMISSION_WIFI_SCAN                  "org.freedesktop.NetworkManager.wifi.scan"
 
 #define NM_CLONED_MAC_PRESERVE                          "preserve"
 #define NM_CLONED_MAC_PERMANENT                         "permanent"
diff --git a/shared/nm-default.h b/shared/nm-default.h
index c006f33d..26d6476a 100644
--- a/shared/nm-default.h
+++ b/shared/nm-default.h
@@ -292,6 +292,7 @@ _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"
 
 #if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_UTIL
 /* no hash-utils in legacy code. */
diff --git a/shared/nm-ethtool-utils.c b/shared/nm-ethtool-utils.c
index d50695ae..3313274a 100644
--- a/shared/nm-ethtool-utils.c
+++ b/shared/nm-ethtool-utils.c
@@ -90,7 +90,7 @@ const NMEthtoolData *const nm_ethtool_data[_NM_ETHTOOL_ID_NUM + 1] = {
 	[_NM_ETHTOOL_ID_NUM] = NULL,
 };
 
-const guint8 const _by_name[_NM_ETHTOOL_ID_NUM] = {
+static const guint8 _by_name[_NM_ETHTOOL_ID_NUM] = {
 	/* sorted by optname. */
 	NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD,
 	NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD,
diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c
index 3e79747f..e666e0b2 100644
--- a/shared/nm-meta-setting.c
+++ b/shared/nm-meta-setting.c
@@ -28,8 +28,8 @@
 #include "nm-setting-adsl.h"
 #include "nm-setting-bluetooth.h"
 #include "nm-setting-bond.h"
-#include "nm-setting-bridge.h"
 #include "nm-setting-bridge-port.h"
+#include "nm-setting-bridge.h"
 #include "nm-setting-cdma.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-dcb.h"
@@ -38,10 +38,10 @@
 #include "nm-setting-generic.h"
 #include "nm-setting-gsm.h"
 #include "nm-setting-infiniband.h"
-#include "nm-setting-ip4-config.h"
-#include "nm-setting-ip6-config.h"
 #include "nm-setting-ip-config.h"
 #include "nm-setting-ip-tunnel.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
 #include "nm-setting-macsec.h"
 #include "nm-setting-macvlan.h"
 #include "nm-setting-match.h"
@@ -55,17 +55,19 @@
 #include "nm-setting-proxy.h"
 #include "nm-setting-serial.h"
 #include "nm-setting-tc-config.h"
-#include "nm-setting-team.h"
 #include "nm-setting-team-port.h"
+#include "nm-setting-team.h"
 #include "nm-setting-tun.h"
 #include "nm-setting-user.h"
 #include "nm-setting-vlan.h"
 #include "nm-setting-vpn.h"
 #include "nm-setting-vxlan.h"
+#include "nm-setting-wifi-p2p.h"
 #include "nm-setting-wimax.h"
 #include "nm-setting-wired.h"
-#include "nm-setting-wireless.h"
+#include "nm-setting-wireguard.h"
 #include "nm-setting-wireless-security.h"
+#include "nm-setting-wireless.h"
 #include "nm-setting-wpan.h"
 
 /*****************************************************************************/
@@ -383,6 +385,12 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
 		.setting_name =             NM_SETTING_VXLAN_SETTING_NAME,
 		.get_setting_gtype =        nm_setting_vxlan_get_type,
 	},
+	[NM_META_SETTING_TYPE_WIFI_P2P] = {
+		.meta_type =                NM_META_SETTING_TYPE_WIFI_P2P,
+		.setting_priority =         NM_SETTING_PRIORITY_HW_BASE,
+		.setting_name =             NM_SETTING_WIFI_P2P_SETTING_NAME,
+		.get_setting_gtype =        nm_setting_wifi_p2p_get_type,
+	},
 	[NM_META_SETTING_TYPE_WIMAX] = {
 		.meta_type =                NM_META_SETTING_TYPE_WIMAX,
 		.setting_priority =         NM_SETTING_PRIORITY_HW_BASE,
@@ -395,6 +403,12 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
 		.setting_name =             NM_SETTING_WIRED_SETTING_NAME,
 		.get_setting_gtype =        nm_setting_wired_get_type,
 	},
+	[NM_META_SETTING_TYPE_WIREGUARD] = {
+		.meta_type =                NM_META_SETTING_TYPE_WIREGUARD,
+		.setting_priority =         NM_SETTING_PRIORITY_HW_BASE,
+		.setting_name =             NM_SETTING_WIREGUARD_SETTING_NAME,
+		.get_setting_gtype =        nm_setting_wireguard_get_type,
+	},
 	[NM_META_SETTING_TYPE_WIRELESS] = {
 		.meta_type =                NM_META_SETTING_TYPE_WIRELESS,
 		.setting_priority =         NM_SETTING_PRIORITY_HW_BASE,
diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h
index 26c29bea..18727a16 100644
--- a/shared/nm-meta-setting.h
+++ b/shared/nm-meta-setting.h
@@ -145,7 +145,9 @@ typedef enum {
 	NM_META_SETTING_TYPE_VLAN,
 	NM_META_SETTING_TYPE_VPN,
 	NM_META_SETTING_TYPE_VXLAN,
+	NM_META_SETTING_TYPE_WIFI_P2P,
 	NM_META_SETTING_TYPE_WIMAX,
+	NM_META_SETTING_TYPE_WIREGUARD,
 	NM_META_SETTING_TYPE_WPAN,
 
 	NM_META_SETTING_TYPE_UNKNOWN,
diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c
index 1da9014e..509b235a 100644
--- a/shared/nm-test-utils-impl.c
+++ b/shared/nm-test-utils-impl.c
@@ -20,7 +20,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
 #include <sys/wait.h>
 
 #include "NetworkManager.h"
diff --git a/shared/nm-utils/nm-c-list.h b/shared/nm-utils/nm-c-list.h
index b43d1441..5c73f574 100644
--- a/shared/nm-utils/nm-c-list.h
+++ b/shared/nm-utils/nm-c-list.h
@@ -78,4 +78,40 @@ nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn)
 	}
 }
 
+/*****************************************************************************/
+
+static inline gboolean
+nm_c_list_move_before (CList *lst, CList *elem)
+{
+	nm_assert (lst);
+	nm_assert (elem);
+	nm_assert (c_list_contains (lst, elem));
+
+	if (   lst != elem
+	    && lst->prev != elem) {
+		c_list_unlink_stale (elem);
+		c_list_link_before (lst, elem);
+		return TRUE;
+	}
+	return FALSE;
+}
+#define nm_c_list_move_tail(lst, elem) nm_c_list_move_before (lst, elem)
+
+static inline gboolean
+nm_c_list_move_after (CList *lst, CList *elem)
+{
+	nm_assert (lst);
+	nm_assert (elem);
+	nm_assert (c_list_contains (lst, elem));
+
+	if (   lst != elem
+	    && lst->next != elem) {
+		c_list_unlink_stale (elem);
+		c_list_link_after (lst, elem);
+		return TRUE;
+	}
+	return FALSE;
+}
+#define nm_c_list_move_front(lst, elem) nm_c_list_move_after (lst, elem)
+
 #endif /* __NM_C_LIST_H__ */
diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c
index 852c207c..5bdc3e3c 100644
--- a/shared/nm-utils/nm-dedup-multi.c
+++ b/shared/nm-utils/nm-dedup-multi.c
@@ -24,6 +24,7 @@
 #include "nm-dedup-multi.h"
 
 #include "nm-hash-utils.h"
+#include "nm-c-list.h"
 
 /*****************************************************************************/
 
@@ -260,44 +261,27 @@ _add (NMDedupMultiIndex *self,
 		nm_dedup_multi_entry_set_dirty (entry, FALSE);
 
 		nm_assert (!head_existing || entry->head == head_existing);
-
-		if (entry_order) {
-			nm_assert (entry_order->head == entry->head);
-			nm_assert (c_list_contains (&entry->lst_entries, &entry_order->lst_entries));
-			nm_assert (c_list_contains (&entry_order->lst_entries, &entry->lst_entries));
-		}
+		nm_assert (!entry_order || entry_order->head == entry->head);
+		nm_assert (!entry_order || c_list_contains (&entry->lst_entries, &entry_order->lst_entries));
+		nm_assert (!entry_order || c_list_contains (&entry_order->lst_entries, &entry->lst_entries));
 
 		switch (mode) {
 		case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE:
 			if (entry_order) {
-				if (   entry_order != entry
-				    && entry->lst_entries.next != &entry_order->lst_entries) {
-					c_list_unlink_stale (&entry->lst_entries);
-					c_list_link_before ((CList *) &entry_order->lst_entries, &entry->lst_entries);
+				if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, &entry->lst_entries))
 					changed = TRUE;
-				}
 			} else {
-				if (entry->lst_entries.prev != &entry->head->lst_entries_head) {
-					c_list_unlink_stale (&entry->lst_entries);
-					c_list_link_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries);
+				if (nm_c_list_move_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries))
 					changed = TRUE;
-				}
 			}
 			break;
 		case NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE:
 			if (entry_order) {
-				if (   entry_order != entry
-				    && entry->lst_entries.prev != &entry_order->lst_entries) {
-					c_list_unlink_stale (&entry->lst_entries);
-					c_list_link_after ((CList *) &entry_order->lst_entries, &entry->lst_entries);
+				if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, &entry->lst_entries))
 					changed = TRUE;
-				}
 			} else {
-				if (entry->lst_entries.next != &entry->head->lst_entries_head) {
-					c_list_unlink_stale (&entry->lst_entries);
-					c_list_link_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries);
+				if (nm_c_list_move_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries))
 					changed = TRUE;
-				}
 			}
 			break;
 		case NM_DEDUP_MULTI_IDX_MODE_PREPEND:
@@ -1022,33 +1006,20 @@ nm_dedup_multi_entry_reorder (const NMDedupMultiEntry *entry,
 	if (!entry_order) {
 		const NMDedupMultiHeadEntry *head_entry = entry->head;
 
-		nm_assert (c_list_contains (&head_entry->lst_entries_head, &entry->lst_entries));
 		if (order_after) {
-			if (head_entry->lst_entries_head.prev != &entry->lst_entries) {
-				c_list_unlink_stale ((CList *) &entry->lst_entries);
-				c_list_link_tail ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries);
+			if (nm_c_list_move_tail ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries))
 				return TRUE;
-			}
 		} else {
-			if (head_entry->lst_entries_head.next != &entry->lst_entries) {
-				c_list_unlink_stale ((CList *) &entry->lst_entries);
-				c_list_link_front ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries);
+			if (nm_c_list_move_front ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries))
 				return TRUE;
-			}
 		}
-	} else if (entry != entry_order) {
+	} else {
 		if (order_after) {
-			if (entry_order->lst_entries.next != &entry->lst_entries) {
-				c_list_unlink_stale ((CList *) &entry->lst_entries);
-				c_list_link_after ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries);
+			if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries))
 				return TRUE;
-			}
 		} else {
-			if (entry_order->lst_entries.prev != &entry->lst_entries) {
-				c_list_unlink_stale ((CList *) &entry->lst_entries);
-				c_list_link_before ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries);
+			if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries))
 				return TRUE;
-			}
 		}
 	}
 
diff --git a/shared/nm-utils/nm-errno.c b/shared/nm-utils/nm-errno.c
index c87f0b48..30eb9a8e 100644
--- a/shared/nm-utils/nm-errno.c
+++ b/shared/nm-utils/nm-errno.c
@@ -22,33 +22,61 @@
 
 #include "nm-errno.h"
 
+#include <pthread.h>
+
 /*****************************************************************************/
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int,
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror,
+#if 0
+	enum _NMErrno,
+#else
+	int,
+#endif
 	NM_UTILS_LOOKUP_DEFAULT (NULL),
 
-	NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC,          "NME_UNSPEC"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_BUG,             "NME_BUG"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO,    "NME_NATIVE_ERRNO"),
-
-	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE,     "NME_NL_ATTRSIZE"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK,     "NME_NL_BAD_SOCK"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR,    "NME_NL_DUMP_INTR"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC,    "NME_NL_MSG_TRUNC"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"),
-
-	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND,    "not-found"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS,       "exists"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE,   "wrong-type"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE,    "not-slave"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE,  "no-firmware"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP,    "not-supported"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK,      "netlink"),
-	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU, "cant-set-mtu"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_SUCCESS,      "NME_ERRNO_SUCCESS"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_OUT_OF_RANGE, "NME_ERRNO_OUT_OF_RANGE"),
+
+	NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC,             "NME_UNSPEC"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_BUG,                "NME_BUG"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO,       "NME_NATIVE_ERRNO"),
+
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE,        "NME_NL_ATTRSIZE"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK,        "NME_NL_BAD_SOCK"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR,       "NME_NL_DUMP_INTR"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW,    "NME_NL_MSG_OVERFLOW"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT,    "NME_NL_MSG_TOOSHORT"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC,       "NME_NL_MSG_TRUNC"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH,    "NME_NL_SEQ_MISMATCH"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_NL_NOADDR,          "NME_NL_NOADDR"),
+
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND,       "not-found"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS,          "exists"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE,      "wrong-type"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE,       "not-slave"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE,     "no-firmware"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP,       "not-supported"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK,         "netlink"),
+	NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU,    "cant-set-mtu"),
+
+	NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_MININT),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_RESERVED_LAST_PLUS_1),
 );
 
+/**
+ * nm_strerror():
+ * @nmerr: the NetworkManager specific errno to be converted
+ *   to string.
+ *
+ * NetworkManager specific error numbers reserve a range in "errno.h" with
+ * our own defines. For numbers that don't fall into this range, the numbers
+ * are identical to the common error numbers.
+ *
+ * Idential to strerror(), g_strerror(), nm_strerror_native() for error numbers
+ * that are not in the reserved range of NetworkManager specific errors.
+ *
+ * Returns: (transfer none): the string representation of the error number.
+ */
 const char *
 nm_strerror (int nmerr)
 {
@@ -61,5 +89,110 @@ nm_strerror (int nmerr)
 		if (s)
 			return s;
 	}
-	return g_strerror (nmerr);
+	return nm_strerror_native (nmerr);
+}
+
+/*****************************************************************************/
+
+/**
+ * nm_strerror_native_r:
+ * @errsv: the errno to convert to string.
+ * @buf: the output buffer where to write the string to.
+ * @buf_size: the length of buffer.
+ *
+ * This is like strerror_r(), with one difference: depending on the
+ * locale, the returned string is guaranteed to be valid UTF-8.
+ * Also, there is some confusion as to whether to use glibc's
+ * strerror_r() or the POXIX/XSI variant. This is abstracted
+ * by the function.
+ *
+ * Note that the returned buffer may also be a statically allocated
+ * buffer, and not the input buffer @buf. Consequently, the returned
+ * string may be longer than @buf_size.
+ *
+ * Returns: (transfer none): a NUL terminated error message. This is either a static
+ *   string (that is never freed), or the provided @buf argumnt.
+ */
+const char *
+nm_strerror_native_r (int errsv, char *buf, gsize buf_size)
+{
+	char *buf2;
+
+	nm_assert (buf);
+	nm_assert (buf_size > 0);
+
+#if (_POSIX_C_SOURCE >= 200112L) && !  _GNU_SOURCE
+	/* XSI-compliant */
+	{
+		int errno_saved = errno;
+
+		if (strerror_r (errsv, buf, buf_size) != 0) {
+			g_snprintf (buf, buf_size, "Unspecified errno %d", errsv);
+			errno = errno_saved;
+		}
+		buf2 = buf;
+	}
+#else
+	/* GNU-specific */
+	buf2 = strerror_r (errsv, buf, buf_size);
+#endif
+
+	/* like g_strerror(), ensure that the error message is UTF-8. */
+	if (   !g_get_charset (NULL)
+	    && !g_utf8_validate (buf2, -1, NULL)) {
+		gs_free char *msg = NULL;
+
+		msg = g_locale_to_utf8 (buf2, -1, NULL, NULL, NULL);
+		if (msg) {
+			g_strlcpy (buf, msg, buf_size);
+			buf2 = buf;
+		}
+	}
+
+	return buf2;
+}
+
+/**
+ * nm_strerror_native:
+ * @errsv: the errno integer from <errno.h>
+ *
+ * Like strerror(), but strerror() is not thread-safe and not guaranteed
+ * to be UTF-8.
+ *
+ * g_strerror() is a thread-safe variant of strerror(), however it caches
+ * all returned strings in a dictionary. That means, using this on untrusted
+ * error numbers can result in this cache to grow without limits.
+ *
+ * Instead, return a tread-local buffer. This way, it's thread-safe.
+ *
+ * There is a downside to this: subsequent calls of nm_strerror_native()
+ * overwrite the error message.
+ *
+ * Returns: (transfer none): the text representation of the error number.
+ */
+const char *
+nm_strerror_native (int errsv)
+{
+	static _nm_thread_local char *buf_static = NULL;
+	char *buf;
+
+	buf = buf_static;
+	if (G_UNLIKELY (!buf)) {
+		int errno_saved = errno;
+		pthread_key_t key;
+
+		buf = g_malloc (NM_STRERROR_BUFSIZE);
+		buf_static = buf;
+
+		if (   pthread_key_create (&key, g_free) != 0
+		    || pthread_setspecific (key, buf) != 0) {
+			/* Failure. We will leak the buffer when the thread exits.
+			 *
+			 * Nothing we can do about it really. For Debug builds we fail with an assertion. */
+			nm_assert_not_reached ();
+		}
+		errno = errno_saved;
+	}
+
+	return nm_strerror_native_r (errsv, buf, NM_STRERROR_BUFSIZE);
 }
diff --git a/shared/nm-utils/nm-errno.h b/shared/nm-utils/nm-errno.h
index c3008f1f..d77735a7 100644
--- a/shared/nm-utils/nm-errno.h
+++ b/shared/nm-utils/nm-errno.h
@@ -25,11 +25,23 @@
 
 /*****************************************************************************/
 
-enum {
+enum _NMErrno {
 	_NM_ERRNO_MININT         = G_MININT,
 	_NM_ERRNO_MAXINT         = G_MAXINT,
 	_NM_ERRNO_RESERVED_FIRST = 100000,
 
+
+	/* when we cannot represent a number as positive number, we resort to this
+	 * number. Basically, the values G_MININT, -NME_ERRNO_SUCCESS, NME_ERRNO_SUCCESS
+	 * and G_MAXINT all map to the same value. */
+	NME_ERRNO_OUT_OF_RANGE   = G_MAXINT,
+
+	/* Indicate that the original errno was zero. Zero denotes *no error*, but we know something
+	 * went wrong and we want to report some error. This is a placeholder to mean, something
+	 * was wrong, but errno was zero. */
+	NME_ERRNO_SUCCESS        = G_MAXINT - 1,
+
+
 	/* an unspecified error. */
 	NME_UNSPEC = _NM_ERRNO_RESERVED_FIRST,
 
@@ -68,68 +80,106 @@ enum {
 
 /*****************************************************************************/
 
+/* When we receive an errno from a system function, we can safely assume
+ * that the error number is not negative. We rely on that, and possibly just
+ * "return -errsv;" to signal an error. We also rely on that, because libc
+ * is our trusted base: meaning, if it cannot even succeed at setting errno
+ * according to specification, all bets are off.
+ *
+ * This macro returns the input argument, and asserts that the error variable
+ * is positive.
+ *
+ * In a sense, the macro is related to nm_errno_native() function, but the difference
+ * is that this macro asserts that @errsv is positive, while nm_errno_native() coerces
+ * negative values to be non-negative. */
+#define NM_ERRNO_NATIVE(errsv) \
+	({ \
+		const int _errsv_x = (errsv); \
+		\
+		nm_assert (_errsv_x > 0); \
+		_errsv_x; \
+	})
+
+/* Normalize native errno.
+ *
+ * Our API may return native error codes (<errno.h>) as negative values. This function
+ * takes such an errno, and normalizes it to their positive value.
+ *
+ * The special values G_MININT and zero are coerced to NME_ERRNO_OUT_OF_RANGE and NME_ERRNO_SUCCESS
+ * respectively.
+ * Other values are coerced to their inverse.
+ * Other positive values are returned unchanged.
+ *
+ * Basically, this normalizes errsv to be positive (taking care of two pathological cases).
+ */
 static inline int
 nm_errno_native (int errsv)
 {
-	/* several API returns negative errno values as errors. Normalize
-	 * negative values to positive values.
-	 *
-	 * As a special case, map G_MININT to G_MAXINT. If you care about the
-	 * distinction, then check for G_MININT before.
-	 *
-	 * Basically, this normalizes a plain errno to be non-negative. */
-	return errsv >= 0
-	       ? errsv
-	       : ((errsv == G_MININT) ? G_MAXINT : -errsv);
+	switch (errsv) {
+	case 0:                  return NME_ERRNO_SUCCESS;
+	case G_MININT:           return NME_ERRNO_OUT_OF_RANGE;
+	default:
+		return errsv >= 0 ? errsv : -errsv;
+	}
 }
 
+/* Normalizes an nm-error to be positive.
+ *
+ * Various API returns negative error codes, and this function converts the negative
+ * value to its positive.
+ *
+ * Note that @nmerr is on the domain of NetworkManager specific error numbers,
+ * which is not the same as the native error numbers (errsv from <errno.h>). But
+ * as far as normalizing goes, nm_errno() does exactly the same remapping as
+ * nm_errno_native(). */
 static inline int
 nm_errno (int nmerr)
 {
-	/* Normalizes an nm-error to be positive. Various API returns negative
-	 * error codes, and this function converts the negative value to its
-	 * positive.
-	 *
-	 * It's very similar to nm_errno_native(), but not exactly. The difference is that
-	 * nm_errno_native() is for plain errno, while nm_errno() is for nm-error numbers.
-	 * Yes, nm-error number are ~almost~ the same as errno, except that a particular
-	 * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) is reserved. The difference
-	 * between the two functions is only how G_MININT is mapped.
-	 *
-	 * See also nm_errno_from_native() below. */
-	return nmerr >= 0
-	       ? nmerr
-	       : ((nmerr == G_MININT) ? NME_BUG : -nmerr);
+	return nm_errno_native (nmerr);
 }
 
+/* this maps a native errno to a (always non-negative) nm-error number.
+ *
+ * Note that nm-error numbers are embedded into the range of regular
+ * errno. The only difference is, that nm-error numbers reserve a
+ * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their
+ * own purpose.
+ *
+ * That means, converting an errno to nm-error number means in
+ * most cases just returning itself.
+ * Only pathological cases need special handling:
+ *
+ *  - 0 is mapped to NME_ERRNO_SUCCESS;
+ *  - G_MININT is mapped to NME_ERRNO_OUT_OF_RANGE;
+ *  - values in the range of (+/-) [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST]
+ *    are mapped to NME_NATIVE_ERRNO
+ *  - all other values are their (positive) absolute value.
+ */
 static inline int
 nm_errno_from_native (int errsv)
 {
-	/* this maps a native errno to a (always non-negative) nm-error number.
-	 *
-	 * Note that nm-error numbers are embedded into the range of regular
-	 * errno. The only difference is, that nm-error numbers reserve a
-	 * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their
-	 * own purpose.
-	 *
-	 * That means, converting an errno to nm-error number means in
-	 * most cases just returning itself (negative values are normalized
-	 * to be positive). Only values G_MININT and [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST]
-	 * are coerced to the special value NME_NATIVE_ERRNO, as they cannot
-	 * otherwise be represented in nm-error number domain. */
-	if (errsv < 0) {
-		return   G_UNLIKELY (errsv == G_MININT)
+	switch (errsv) {
+	case 0:                  return NME_ERRNO_SUCCESS;
+	case G_MININT:           return NME_ERRNO_OUT_OF_RANGE;
+	default:
+		if (errsv < 0)
+			errsv = -errsv;
+		return   G_UNLIKELY (   errsv >= _NM_ERRNO_RESERVED_FIRST
+		                     && errsv <= _NM_ERRNO_RESERVED_LAST)
 		       ? NME_NATIVE_ERRNO
-		       : -errsv;
+		       : errsv;
 	}
-	return   G_UNLIKELY (   errsv >= _NM_ERRNO_RESERVED_FIRST
-	                     && errsv <= _NM_ERRNO_RESERVED_LAST)
-	       ? NME_NATIVE_ERRNO
-	       : errsv;
 }
 
 const char *nm_strerror (int nmerr);
 
 /*****************************************************************************/
 
+#define NM_STRERROR_BUFSIZE 1024
+
+const char *nm_strerror_native_r (int errsv, char *buf, gsize buf_size);
+const char *nm_strerror_native (int errsv);
+
+/*****************************************************************************/
+
 #endif /* __NM_ERRNO_H__ */
diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h
index b7534edd..e941e067 100644
--- a/shared/nm-utils/nm-glib.h
+++ b/shared/nm-utils/nm-glib.h
@@ -424,11 +424,13 @@ g_steal_pointer (gpointer pp)
 
 	return ref;
 }
+#endif
 
-/* type safety */
-#define g_steal_pointer(pp) \
-  (0 ? (*(pp)) : (g_steal_pointer) (pp))
+#ifdef g_steal_pointer
+#undef g_steal_pointer
 #endif
+#define g_steal_pointer(pp) \
+	((typeof (*(pp))) g_steal_pointer (pp))
 
 /*****************************************************************************/
 
diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c
index 80387c71..6e728e6b 100644
--- a/shared/nm-utils/nm-hash-utils.c
+++ b/shared/nm-utils/nm-hash-utils.c
@@ -71,7 +71,7 @@ again:
 		 * the first guint has only the entropy that nm_utils_random_bytes()
 		 * generated for the first 4 bytes and relies on a good random generator.
 		 *
-		 * The first int is especially intersting for nm_hash_static() below, and we
+		 * The first int is especially interesting for nm_hash_static() below, and we
 		 * want to have it all the entropy of t_arr. */
 		c_siphash_init (&siph_state, t_arr.v8);
 		c_siphash_append (&siph_state, (const guint8 *) &t_arr, sizeof (t_arr));
diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h
index cf71a7e9..1a1e44f5 100644
--- a/shared/nm-utils/nm-hash-utils.h
+++ b/shared/nm-utils/nm-hash-utils.h
@@ -122,6 +122,9 @@ nm_hash_update (NMHashState *state, const void *ptr, gsize n)
 		nm_hash_update ((state), &_val, sizeof (_val)); \
 	} G_STMT_END
 
+#define nm_hash_update_valp(state, val) \
+	nm_hash_update ((state), (val), sizeof (*(val))) \
+
 static inline void
 nm_hash_update_bool (NMHashState *state, bool val)
 {
diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-utils/nm-io-utils.c
index 06f756c4..51312748 100644
--- a/shared/nm-utils/nm-io-utils.c
+++ b/shared/nm-utils/nm-io-utils.c
@@ -29,6 +29,7 @@
 
 #include "nm-shared-utils.h"
 #include "nm-secret-utils.h"
+#include "nm-errno.h"
 
 /*****************************************************************************/
 
@@ -36,14 +37,12 @@ _nm_printf (3, 4)
 static int
 _get_contents_error (GError **error, int errsv, const char *format, ...)
 {
-	if (errsv < 0)
-		errsv = -errsv;
-	else if (!errsv)
-		errsv = errno;
+	nm_assert (NM_ERRNO_NATIVE (errsv));
 
 	if (error) {
-		char *msg;
+		gs_free char *msg = NULL;
 		va_list args;
+		char bstrerr[NM_STRERROR_BUFSIZE];
 
 		va_start (args, format);
 		msg = g_strdup_vprintf (format, args);
@@ -52,11 +51,17 @@ _get_contents_error (GError **error, int errsv, const char *format, ...)
 		             G_FILE_ERROR,
 		             g_file_error_from_errno (errsv),
 		             "%s: %s",
-		             msg, g_strerror (errsv));
-		g_free (msg);
+		             msg,
+		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
 	}
 	return -errsv;
 }
+#define _get_contents_error_errno(error, ...) \
+	({ \
+		int _errsv = (errno); \
+		\
+		_get_contents_error (error, _errsv, __VA_ARGS__); \
+	})
 
 static char *
 _mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len)
@@ -127,13 +132,14 @@ nm_utils_fd_get_contents (int fd,
 	struct stat stat_buf;
 	gs_free char *str = NULL;
 	const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET);
+	int errsv;
 
 	g_return_val_if_fail (fd >= 0, -EINVAL);
 	g_return_val_if_fail (contents, -EINVAL);
 	g_return_val_if_fail (!error || !*error, -EINVAL);
 
 	if (fstat (fd, &stat_buf) < 0)
-		return _get_contents_error (error, 0, "failure during fstat");
+		return _get_contents_error_errno (error, "failure during fstat");
 
 	if (!max_length) {
 		/* default to a very large size, but not extreme */
@@ -156,7 +162,7 @@ nm_utils_fd_get_contents (int fd,
 		if (n_read < 0) {
 			if (do_bzero_mem)
 				nm_explicit_bzero (str, n_stat);
-			return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat);
+			return _get_contents_error (error, -n_read, "error reading %zu bytes from file descriptor", n_stat);
 		}
 		str[n_read] = '\0';
 
@@ -176,19 +182,19 @@ nm_utils_fd_get_contents (int fd,
 		else {
 			fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0);
 			if (fd2 < 0)
-				return _get_contents_error (error, 0, "error during dup");
+				return _get_contents_error_errno (error, "error during dup");
 		}
 
 		if (!(f = fdopen (fd2, "r"))) {
+			errsv = errno;
 			nm_close (fd2);
-			return _get_contents_error (error, 0, "failure during fdopen");
+			return _get_contents_error (error, errsv, "failure during fdopen");
 		}
 
 		n_have = 0;
 		n_alloc = 0;
 
 		while (!feof (f)) {
-			int errsv;
 			gsize n_read;
 
 			n_read = fread (buf, 1, sizeof (buf), f);
@@ -262,8 +268,8 @@ nm_utils_fd_get_contents (int fd,
  * @flags: %NMUtilsFileGetContentsFlags for reading the file.
  * @contents: the output buffer with the file read. It is always
  *   NUL terminated. The buffer is at most @max_length long, including
- *  the NUL byte. That is, it reads only files up to a length of
- *  @max_length - 1 bytes.
+ *   the NUL byte. That is, it reads only files up to a length of
+ *   @max_length - 1 bytes.
  * @length: optional output argument of the read file size.
  *
  * A reimplementation of g_file_get_contents() with a few differences:
@@ -284,6 +290,7 @@ nm_utils_file_get_contents (int dirfd,
 {
 	int fd;
 	int errsv;
+	char bstrerr[NM_STRERROR_BUFSIZE];
 
 	g_return_val_if_fail (filename && filename[0], -EINVAL);
 
@@ -297,8 +304,8 @@ nm_utils_file_get_contents (int dirfd,
 			             g_file_error_from_errno (errsv),
 			             "Failed to open file \"%s\" with openat: %s",
 			             filename,
-			             g_strerror (errsv));
-			return -errsv;
+			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
+			return -NM_ERRNO_NATIVE (errsv);
 		}
 	} else {
 		fd = open (filename, O_RDONLY | O_CLOEXEC);
@@ -310,8 +317,8 @@ nm_utils_file_get_contents (int dirfd,
 			             g_file_error_from_errno (errsv),
 			             "Failed to open file \"%s\": %s",
 			             filename,
-			             g_strerror (errsv));
-			return -errsv;
+			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
+			return -NM_ERRNO_NATIVE (errsv);
 		}
 	}
 	return nm_utils_fd_get_contents (fd,
@@ -341,6 +348,7 @@ nm_utils_file_set_contents (const char *filename,
 	int errsv;
 	gssize s;
 	int fd;
+	char bstrerr[NM_STRERROR_BUFSIZE];
 
 	g_return_val_if_fail (filename, FALSE);
 	g_return_val_if_fail (contents || !length, FALSE);
@@ -359,7 +367,7 @@ nm_utils_file_set_contents (const char *filename,
 		             g_file_error_from_errno (errsv),
 		             "failed to create file %s: %s",
 		             tmp_name,
-		             g_strerror (errsv));
+		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
 		return FALSE;
 	}
 
@@ -378,7 +386,7 @@ nm_utils_file_set_contents (const char *filename,
 			             g_file_error_from_errno (errsv),
 			             "failed to write to file %s: %s",
 			             tmp_name,
-			             g_strerror (errsv));
+			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
 			return FALSE;
 		}
 
@@ -395,20 +403,21 @@ nm_utils_file_set_contents (const char *filename,
 	 * guarantee the data is written to the disk before the metadata.)
 	 */
 	if (   lstat (filename, &statbuf) == 0
-	    && statbuf.st_size > 0
-	    && fsync (fd) != 0) {
-		errsv = errno;
+	    && statbuf.st_size > 0) {
+		if (fsync (fd) != 0) {
+			errsv = errno;
 
-		nm_close (fd);
-		unlink (tmp_name);
+			nm_close (fd);
+			unlink (tmp_name);
 
-		g_set_error (error,
-		             G_FILE_ERROR,
-		             g_file_error_from_errno (errsv),
-		             "failed to fsync %s: %s",
-		             tmp_name,
-		             g_strerror (errsv));
-		return FALSE;
+			g_set_error (error,
+			             G_FILE_ERROR,
+			             g_file_error_from_errno (errsv),
+			             "failed to fsync %s: %s",
+			             tmp_name,
+			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
+			return FALSE;
+		}
 	}
 
 	nm_close (fd);
@@ -422,7 +431,7 @@ nm_utils_file_set_contents (const char *filename,
 		             "failed to rename %s to %s: %s",
 		             tmp_name,
 		             filename,
-		             g_strerror (errsv));
+		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
 		return FALSE;
 	}
 
diff --git a/shared/nm-utils/nm-jansson.h b/shared/nm-utils/nm-jansson.h
index b00c75c6..5a73231f 100644
--- a/shared/nm-utils/nm-jansson.h
+++ b/shared/nm-utils/nm-jansson.h
@@ -34,13 +34,16 @@
 /* Added in Jansson v2.8 */
 #ifndef json_object_foreach_safe
 #define json_object_foreach_safe(object, n, key, value)     \
-    for(key = json_object_iter_key(json_object_iter(object)), \
-            n = json_object_iter_next(object, json_object_key_to_iter(key)); \
-        key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
-        key = json_object_iter_key(n), \
-            n = json_object_iter_next(object, json_object_key_to_iter(key)))
+    for (key = json_object_iter_key(json_object_iter(object)), \
+             n = json_object_iter_next(object, json_object_key_to_iter(key)); \
+         key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
+         key = json_object_iter_key(n), \
+             n = json_object_iter_next(object, json_object_key_to_iter(key)))
 #endif
 
+NM_AUTO_DEFINE_FCN0 (json_t *, _nm_auto_decref_json, json_decref)
+#define nm_auto_decref_json nm_auto(_nm_auto_decref_json)
+
 #endif /* WITH_JANSON */
 
 #endif  /* __NM_JANSSON_H__ */
diff --git a/shared/nm-utils/nm-logging-fwd.h b/shared/nm-utils/nm-logging-fwd.h
index 303d5951..900dfff8 100644
--- a/shared/nm-utils/nm-logging-fwd.h
+++ b/shared/nm-utils/nm-logging-fwd.h
@@ -94,18 +94,20 @@ typedef enum  { /*< skip >*/
 	_LOGL_N, /* the number of logging levels including "OFF" */
 } NMLogLevel;
 
-gboolean _nm_log_enabled (NMLogLevel level,
-                          NMLogDomain domain);
+gboolean _nm_log_enabled_impl (gboolean mt_require_locking,
+                               NMLogLevel level,
+                               NMLogDomain domain);
 
 void _nm_log_impl (const char *file,
                    guint line,
                    const char *func,
+                   gboolean mt_require_locking,
                    NMLogLevel level,
                    NMLogDomain domain,
                    int error,
                    const char *ifname,
                    const char *con_uuid,
                    const char *fmt,
-                   ...) _nm_printf (9, 10);
+                   ...) _nm_printf (10, 11);
 
 #endif /* __NM_LOGGING_DEFINES_H__ */
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index 1f3970fe..42299c96 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -45,6 +45,18 @@
 #define nm_auto(fcn)         __attribute__ ((__cleanup__(fcn)))
 
 
+/* This is required to make LTO working.
+ *
+ * See https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/76#note_112694
+ *     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48200#c28
+ */
+#ifndef __clang__
+#define _nm_externally_visible __attribute__ ((__externally_visible__))
+#else
+#define _nm_externally_visible
+#endif
+
+
 #if __GNUC__ >= 7
 #define _nm_fallthrough      __attribute__ ((__fallthrough__))
 #else
@@ -67,6 +79,28 @@
 
 /*****************************************************************************/
 
+/* most of our code is single-threaded with a mainloop. Hence, we usually don't need
+ * any thread-safety. Sometimes, we do need thread-safety (nm-logging), but we can
+ * avoid locking if we are on the main-thread by:
+ *
+ *   - modifications of shared data is done infrequently and only from the
+ *     main-thread (nm_logging_setup())
+ *   - read-only access is done frequently (nm_logging_enabled())
+ *     - from the main-thread, we can do that without locking (because
+ *       all modifications are also done on the main thread.
+ *     - from other threads, we need locking. But this is expected to be
+ *       done infrequently too. Important is the lock-free fast-path on the
+ *       main-thread.
+ *
+ * By defining NM_THREAD_SAFE_ON_MAIN_THREAD you indicate that this code runs
+ * on the main-thread. It is by default defined to "1". If you have code that
+ * is also used on another thread, redefine the define to 0 (to opt in into
+ * the slow-path).
+ */
+#define NM_THREAD_SAFE_ON_MAIN_THREAD 1
+
+/*****************************************************************************/
+
 #define NM_AUTO_DEFINE_FCN_VOID(CastType, name, func) \
 static inline void name (void *v) \
 { \
@@ -415,7 +449,7 @@ NM_G_ERROR_MSG (GError *error)
 /*****************************************************************************/
 
 /* macro to return strlen() of a compile time string. */
-#define NM_STRLEN(str)     ( sizeof ("" str) - 1 )
+#define NM_STRLEN(str)     ( sizeof (""str"") - 1 )
 
 /* returns the length of a NULL terminated array of pointers,
  * like g_strv_length() does. The difference is:
@@ -826,12 +860,33 @@ fcn (void) \
 
 /*****************************************************************************/
 
-#define nm_streq(s1, s2)  (strcmp (s1, s2) == 0)
-#define nm_streq0(s1, s2) (g_strcmp0 (s1, s2) == 0)
+static inline gboolean
+nm_streq (const char *s1, const char *s2)
+{
+	return strcmp (s1, s2) == 0;
+}
+
+static inline gboolean
+nm_streq0 (const char *s1, const char *s2)
+{
+	return    (s1 == s2)
+	       || (s1 && s2 && strcmp (s1, s2) == 0);
+}
 
 #define NM_STR_HAS_PREFIX(str, prefix) \
 	(strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0)
 
+#define NM_STR_HAS_SUFFIX(str, suffix) \
+	({ \
+		const char *_str = (str); \
+		gsize _l = strlen (_str); \
+		\
+		(   (_l >= NM_STRLEN (suffix)) \
+		 && (memcmp (&_str[_l - NM_STRLEN (suffix)], \
+		             ""suffix"", \
+		             NM_STRLEN (suffix)) == 0)); \
+	})
+
 /*****************************************************************************/
 
 static inline GString *
@@ -1127,6 +1182,28 @@ nm_clear_g_cancellable (GCancellable **cancellable)
 	return FALSE;
 }
 
+/* If @cancellable_id is not 0, clear it and call g_cancellable_disconnect().
+ * @cancellable may be %NULL, if there is nothing to disconnect.
+ *
+ * It's like nm_clear_g_signal_handler(), except that it uses g_cancellable_disconnect()
+ * instead of g_signal_handler_disconnect().
+ *
+ * Note the warning in glib documentation about dead-lock and what g_cancellable_disconnect()
+ * actually does. */
+static inline gboolean
+nm_clear_g_cancellable_disconnect (GCancellable *cancellable, gulong *cancellable_id)
+{
+	gulong id;
+
+	if (   cancellable_id
+	    && (id = *cancellable_id) != 0) {
+		*cancellable_id = 0;
+		g_cancellable_disconnect (cancellable, id);
+		return TRUE;
+	}
+	return FALSE;
+}
+
 /*****************************************************************************/
 
 static inline GVariant *
@@ -1243,17 +1320,17 @@ fcn_name (lookup_type val) \
 
 /*****************************************************************************/
 
-#define _NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, ORIG_FUNC, VERSIONED_FUNC, ARGS_TYPED, ARGS) \
-RETURN_TYPE VERSIONED_FUNC ARGS_TYPED; \
-RETURN_TYPE VERSIONED_FUNC ARGS_TYPED \
+#define _NM_BACKPORT_SYMBOL_IMPL(version, return_type, orig_func, versioned_func, args_typed, args) \
+return_type versioned_func args_typed; \
+_nm_externally_visible return_type versioned_func args_typed \
 { \
-    return ORIG_FUNC ARGS; \
+    return orig_func args; \
 } \
-RETURN_TYPE ORIG_FUNC ARGS_TYPED; \
-__asm__(".symver "G_STRINGIFY(VERSIONED_FUNC)", "G_STRINGIFY(ORIG_FUNC)"@"G_STRINGIFY(VERSION))
+return_type orig_func args_typed; \
+__asm__(".symver "G_STRINGIFY(versioned_func)", "G_STRINGIFY(orig_func)"@"G_STRINGIFY(version))
 
-#define NM_BACKPORT_SYMBOL(VERSION, RETURN_TYPE, FUNC, ARGS_TYPED, ARGS) \
-_NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, FUNC, _##FUNC##_##VERSION, ARGS_TYPED, ARGS)
+#define NM_BACKPORT_SYMBOL(version, return_type, func, args_typed, args) \
+_NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_typed, args)
 
 /*****************************************************************************/
 
@@ -1371,6 +1448,14 @@ nm_strcmp_p (gconstpointer a, gconstpointer b)
 		 : _b); \
 	})
 
+/* evaluates to (void) if _A or _B are not constant or of different types */
+#define NM_CONST_MAX(_A, _B) \
+	(__builtin_choose_expr ((   __builtin_constant_p (_A) \
+	                         && __builtin_constant_p (_B) \
+	                         && __builtin_types_compatible_p (typeof (_A), typeof (_B))), \
+	                        ((_A) > (_B)) ? (_A) : (_B),                            \
+	                        ((void)  0)))
+
 /*****************************************************************************/
 
 static inline guint
diff --git a/shared/nm-utils/nm-secret-utils.c b/shared/nm-utils/nm-secret-utils.c
index 65f99c65..ec5cc6b1 100644
--- a/shared/nm-utils/nm-secret-utils.c
+++ b/shared/nm-utils/nm-secret-utils.c
@@ -17,6 +17,7 @@
  * Boston, MA 02110-1301 USA.
  *
  * (C) Copyright 2018 Red Hat, Inc.
+ * (C) Copyright 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
  */
 
 #include "nm-default.h"
@@ -132,3 +133,29 @@ nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len)
 	                                   _secret_buf_free,
 	                                   secret);
 }
+
+/*****************************************************************************/
+
+/**
+ * nm_utils_memeqzero_secret:
+ * @data: the data pointer to check (may be %NULL if @length is zero).
+ * @length: the number of bytes to check.
+ *
+ * Checks that all bytes are zero. This always takes the same amount
+ * of time to prevent timing attacks.
+ *
+ * Returns: whether all bytes are zero.
+ */
+gboolean
+nm_utils_memeqzero_secret (gconstpointer data, gsize length)
+{
+	const guint8 *const key = data;
+	volatile guint8 acc = 0;
+	gsize i;
+
+	for (i = 0; i < length; i++) {
+		acc |= key[i];
+		asm volatile("" : "=r"(acc) : "0"(acc));
+	}
+	return 1 & ((acc - 1) >> 8);
+}
diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-utils/nm-secret-utils.h
index 9df31afe..034ef7bd 100644
--- a/shared/nm-utils/nm-secret-utils.h
+++ b/shared/nm-utils/nm-secret-utils.h
@@ -76,6 +76,19 @@ typedef struct {
 } NMSecretPtr;
 
 static inline void
+nm_secret_ptr_bzero (NMSecretPtr *secret)
+{
+	if (secret) {
+		if (secret->len > 0) {
+			if (secret->ptr)
+				nm_explicit_bzero (secret->ptr, secret->len);
+		}
+	}
+}
+
+#define nm_auto_bzero_secret_ptr nm_auto(nm_secret_ptr_bzero)
+
+static inline void
 nm_secret_ptr_clear (NMSecretPtr *secret)
 {
 	if (secret) {
@@ -90,12 +103,24 @@ nm_secret_ptr_clear (NMSecretPtr *secret)
 
 #define nm_auto_clear_secret_ptr nm_auto(nm_secret_ptr_clear)
 
+#define NM_SECRET_PTR_INIT() \
+	((const NMSecretPtr) { \
+		.len = 0, \
+		.ptr = NULL, \
+	})
+
 #define NM_SECRET_PTR_STATIC(_len) \
 	((const NMSecretPtr) { \
 		.len = _len, \
 		.ptr = ((guint8 [_len]) { }), \
 	})
 
+#define NM_SECRET_PTR_ARRAY(_arr) \
+	((const NMSecretPtr) { \
+		.len = G_N_ELEMENTS (_arr) * sizeof ((_arr)[0]), \
+		.ptr = &((_arr)[0]), \
+	})
+
 static inline void
 nm_secret_ptr_clear_static (const NMSecretPtr *secret)
 {
@@ -148,4 +173,6 @@ GBytes *nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len);
 
 /*****************************************************************************/
 
+gboolean nm_utils_memeqzero_secret (gconstpointer data, gsize length);
+
 #endif /* __NM_SECRET_UTILS_H__ */
diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c
index c9ef89e9..6a43c670 100644
--- a/shared/nm-utils/nm-shared-utils.c
+++ b/shared/nm-utils/nm-shared-utils.c
@@ -23,10 +23,12 @@
 
 #include "nm-shared-utils.h"
 
-#include <errno.h>
 #include <arpa/inet.h>
 #include <poll.h>
 #include <fcntl.h>
+#include <sys/syscall.h>
+
+#include "nm-errno.h"
 
 /*****************************************************************************/
 
@@ -34,7 +36,116 @@ const void *const _NM_PTRARRAY_EMPTY[1] = { NULL };
 
 /*****************************************************************************/
 
-const NMIPAddr nm_ip_addr_zero = { 0 };
+const NMIPAddr nm_ip_addr_zero = { };
+
+/* this initializes a struct in_addr/in6_addr and allows for untrusted
+ * arguments (like unsuitable @addr_family or @src_len). It's almost safe
+ * in the sense that it verifies input arguments strictly. Also, it
+ * uses memcpy() to access @src, so alignment is not an issue.
+ *
+ * Only potential pitfalls:
+ *
+ * - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the
+ *   caller allows for that), the caller MUST provide @out_addr_family.
+ * - when setting @dst to an IPv4 address, the trailing bytes are not touched.
+ *   Meaning, if @dst is an NMIPAddr union, only the first bytes will be set.
+ *   If that matter to you, clear @dst before. */
+gboolean
+nm_ip_addr_set_from_untrusted (int addr_family,
+                               gpointer dst,
+                               gconstpointer src,
+                               gsize src_len,
+                               int *out_addr_family)
+{
+	nm_assert (dst);
+
+	switch (addr_family) {
+	case AF_UNSPEC:
+		if (!out_addr_family) {
+			/* when the callers allow undefined @addr_family, they must provide
+			 * an @out_addr_family argument. */
+			nm_assert_not_reached ();
+			return FALSE;
+		}
+		switch (src_len) {
+		case sizeof (struct in_addr):  addr_family = AF_INET;  break;
+		case sizeof (struct in6_addr): addr_family = AF_INET6; break;
+		default:
+			return FALSE;
+		}
+		break;
+	case AF_INET:
+		if (src_len != sizeof (struct in_addr))
+			return FALSE;
+		break;
+	case AF_INET6:
+		if (src_len != sizeof (struct in6_addr))
+			return FALSE;
+		break;
+	default:
+		/* when the callers allow undefined @addr_family, they must provide
+		 * an @out_addr_family argument. */
+		nm_assert (out_addr_family);
+		return FALSE;
+	}
+
+	nm_assert (src);
+
+	memcpy (dst, src, src_len);
+	NM_SET_OUT (out_addr_family, addr_family);
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+pid_t
+nm_utils_gettid (void)
+{
+	return (pid_t) syscall (SYS_gettid);
+}
+
+/* Used for asserting that this function is called on the main-thread.
+ * The main-thread is determined by remembering the thread-id
+ * of when the function was called the first time.
+ *
+ * When forking, the thread-id is again reset upon first call. */
+gboolean
+_nm_assert_on_main_thread (void)
+{
+	G_LOCK_DEFINE_STATIC (lock);
+	static pid_t seen_tid;
+	static pid_t seen_pid;
+	pid_t tid;
+	pid_t pid;
+	gboolean success = FALSE;
+
+	tid = nm_utils_gettid ();
+	nm_assert (tid != 0);
+
+	G_LOCK (lock);
+
+	if (G_LIKELY (tid == seen_tid)) {
+		/* we don't care about false positives (when the process forked, and the thread-id
+		 * is accidentally re-used) . It's for assertions only. */
+		success = TRUE;
+	} else {
+		pid = getpid ();
+		nm_assert (pid != 0);
+
+		if (   seen_tid == 0
+			|| seen_pid != pid) {
+			/* either this is the first time we call the function, or the process
+			 * forked. In both cases, remember the thread-id. */
+			seen_tid = tid;
+			seen_pid = pid;
+			success = TRUE;
+		}
+	}
+
+	G_UNLOCK (lock);
+
+	return success;
+}
 
 /*****************************************************************************/
 
@@ -561,6 +672,8 @@ nm_utils_parse_inaddr_prefix_bin (int addr_family,
 		return FALSE;
 
 	if (slash) {
+		/* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't
+		 * do that. */
 		prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10,
 		                                       0,
 		                                       addr_family == AF_INET ? 32 : 128,
@@ -1360,6 +1473,53 @@ nm_g_object_class_find_property_from_gtype (GType gtype,
 
 /*****************************************************************************/
 
+/**
+ * nm_g_type_find_implementing_class_for_property:
+ * @gtype: the GObject type which has a property @pname
+ * @pname: the name of the property to look up
+ *
+ * This is only a helper function for printf debugging. It's not
+ * used in actual code. Hence, the function just asserts that
+ * @pname and @gtype arguments are suitable. It cannot fail.
+ *
+ * Returns: the most ancestor type of @gtype, that
+ *   implements the property @pname. It means, it
+ *   searches the type hierarchy to find the type
+ *   that added @pname.
+ */
+GType
+nm_g_type_find_implementing_class_for_property (GType gtype,
+                                                const char *pname)
+{
+	nm_auto_unref_gtypeclass GObjectClass *klass = NULL;
+	GParamSpec *pspec;
+
+	g_return_val_if_fail (pname, G_TYPE_INVALID);
+
+	klass = g_type_class_ref (gtype);
+	g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), G_TYPE_INVALID);
+
+	pspec = g_object_class_find_property (klass, pname);
+	g_return_val_if_fail (pspec, G_TYPE_INVALID);
+
+	gtype = G_TYPE_FROM_CLASS (klass);
+
+	while (TRUE) {
+		nm_auto_unref_gtypeclass GObjectClass *k = NULL;
+
+		k = g_type_class_ref (g_type_parent (gtype));
+
+		g_return_val_if_fail (G_IS_OBJECT_CLASS (k), G_TYPE_INVALID);
+
+		if (g_object_class_find_property (k, pname) != pspec)
+			return gtype;
+
+		gtype = G_TYPE_FROM_CLASS (k);
+	}
+}
+
+/*****************************************************************************/
+
 static void
 _str_append_escape (GString *s, char ch)
 {
@@ -1691,7 +1851,7 @@ nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns)
 
 	r = ppoll (&pollfd, 1, pts, NULL);
 	if (r < 0)
-		return -errno;
+		return -NM_ERRNO_NATIVE (errno);
 	if (r == 0)
 		return 0;
 	return pollfd.revents;
@@ -1718,10 +1878,12 @@ nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll)
 
 		k = read (fd, p, nbytes);
 		if (k < 0) {
-			if (errno == EINTR)
+			int errsv = errno;
+
+			if (errsv == EINTR)
 				continue;
 
-			if (errno == EAGAIN && do_poll) {
+			if (errsv == EAGAIN && do_poll) {
 
 				/* We knowingly ignore any return value here,
 				 * and expect that any error/EOF is reported
@@ -1731,7 +1893,7 @@ nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll)
 				continue;
 			}
 
-			return n > 0 ? n : -errno;
+			return n > 0 ? n : -NM_ERRNO_NATIVE (errsv);
 		}
 
 		if (k == 0)
@@ -2375,3 +2537,205 @@ nm_utils_memeqzero (gconstpointer data, gsize length)
 	/* Now we know that's zero, memcmp with self. */
 	return memcmp (data, p, length) == 0;
 }
+
+/**
+ * nm_utils_bin2hexstr_full:
+ * @addr: pointer of @length bytes. If @length is zero, this may
+ *   also be %NULL.
+ * @length: number of bytes in @addr. May also be zero, in which
+ *   case this will return an empty string.
+ * @delimiter: either '\0', otherwise the output string will have the
+ *   given delimiter character between each two hex numbers.
+ * @upper_case: if TRUE, use upper case ASCII characters for hex.
+ * @out: if %NULL, the function will allocate a new buffer of
+ *   either (@length*2+1) or (@length*3) bytes, depending on whether
+ *   a @delimiter is specified. In that case, the allocated buffer will
+ *   be returned and must be freed by the caller.
+ *   If not %NULL, the buffer must already be preallocated and contain
+ *   at least (@length*2+1) or (@length*3) bytes, depending on the delimiter.
+ *
+ * Returns: the binary value converted to a hex string. If @out is given,
+ *   this always returns @out. If @out is %NULL, a newly allocated string
+ *   is returned.
+ */
+char *
+nm_utils_bin2hexstr_full (gconstpointer addr,
+                          gsize length,
+                          char delimiter,
+                          gboolean upper_case,
+                          char *out)
+{
+	const guint8 *in = addr;
+	const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef";
+	char *out0;
+
+	if (out)
+		out0 = out;
+	else {
+		out0 = out = g_new (char, delimiter == '\0'
+		                          ? length * 2 + 1
+		                          : length * 3);
+	}
+
+	/* @out must contain at least @length*3 bytes if @delimiter is set,
+	 * otherwise, @length*2+1. */
+
+	if (length > 0) {
+		nm_assert (in);
+		for (;;) {
+			const guint8 v = *in++;
+
+			*out++ = LOOKUP[v >> 4];
+			*out++ = LOOKUP[v & 0x0F];
+			length--;
+			if (!length)
+				break;
+			if (delimiter)
+				*out++ = delimiter;
+		}
+	}
+
+	*out = '\0';
+	return out0;
+}
+
+guint8 *
+nm_utils_hexstr2bin_full (const char *hexstr,
+                          gboolean allow_0x_prefix,
+                          gboolean delimiter_required,
+                          const char *delimiter_candidates,
+                          gsize required_len,
+                          guint8 *buffer,
+                          gsize buffer_len,
+                          gsize *out_len)
+{
+	const char *in = hexstr;
+	guint8 *out = buffer;
+	gboolean delimiter_has = TRUE;
+	guint8 delimiter = '\0';
+	gsize len;
+
+	nm_assert (hexstr);
+	nm_assert (buffer);
+	nm_assert (required_len > 0 || out_len);
+
+	if (   allow_0x_prefix
+	    && in[0] == '0'
+	    && in[1] == 'x')
+		in += 2;
+
+	while (TRUE) {
+		const guint8 d1 = in[0];
+		guint8 d2;
+		int i1, i2;
+
+		i1 = nm_utils_hexchar_to_int (d1);
+		if (i1 < 0)
+			goto fail;
+
+		/* If there's no leading zero (ie "aa:b:cc") then fake it */
+		d2 = in[1];
+		if (   d2
+		    && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) {
+			*out++ = (i1 << 4) + i2;
+			d2 = in[2];
+			if (!d2)
+				break;
+			in += 2;
+		} else {
+			/* Fake leading zero */
+			*out++ = i1;
+			if (!d2) {
+				if (!delimiter_has) {
+					/* when using no delimiter, there must be pairs of hex chars */
+					goto fail;
+				}
+				break;
+			}
+			in += 1;
+		}
+
+		if (--buffer_len == 0)
+			goto fail;
+
+		if (delimiter_has) {
+			if (d2 != delimiter) {
+				if (delimiter)
+					goto fail;
+				if (delimiter_candidates) {
+					while (delimiter_candidates[0]) {
+						if (delimiter_candidates++[0] == d2)
+							delimiter = d2;
+					}
+				}
+				if (!delimiter) {
+					if (delimiter_required)
+						goto fail;
+					delimiter_has = FALSE;
+					continue;
+				}
+			}
+			in++;
+		}
+	}
+
+	len = out - buffer;
+	if (   required_len == 0
+	    || len == required_len) {
+		NM_SET_OUT (out_len, len);
+		return buffer;
+	}
+
+fail:
+	NM_SET_OUT (out_len, 0);
+	return NULL;
+}
+
+guint8 *
+nm_utils_hexstr2bin_alloc (const char *hexstr,
+                           gboolean allow_0x_prefix,
+                           gboolean delimiter_required,
+                           const char *delimiter_candidates,
+                           gsize required_len,
+                           gsize *out_len)
+{
+	guint8 *buffer;
+	gsize buffer_len, len;
+
+	g_return_val_if_fail (hexstr, NULL);
+
+	nm_assert (required_len > 0 || out_len);
+
+	if (   allow_0x_prefix
+	    && hexstr[0] == '0'
+	    && hexstr[1] == 'x')
+		hexstr += 2;
+
+	if (!hexstr[0])
+		goto fail;
+
+	if (required_len > 0)
+		buffer_len = required_len;
+	else
+		buffer_len = strlen (hexstr) / 2 + 3;
+
+	buffer = g_malloc (buffer_len);
+
+	if (nm_utils_hexstr2bin_full (hexstr,
+	                              FALSE,
+	                              delimiter_required,
+	                              delimiter_candidates,
+	                              required_len,
+	                              buffer,
+	                              buffer_len,
+	                              &len)) {
+		NM_SET_OUT (out_len, len);
+		return buffer;
+	}
+
+	g_free (buffer);
+
+fail:
+	NM_SET_OUT (out_len, 0);
+	return NULL;
+}
diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h
index e28a5fb2..65e34959 100644
--- a/shared/nm-utils/nm-shared-utils.h
+++ b/shared/nm-utils/nm-shared-utils.h
@@ -26,6 +26,18 @@
 
 /*****************************************************************************/
 
+pid_t nm_utils_gettid (void);
+
+gboolean _nm_assert_on_main_thread (void);
+
+#if NM_MORE_ASSERTS > 5
+#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { nm_assert (_nm_assert_on_main_thread ()); } G_STMT_END
+#else
+#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START {                                         ; } G_STMT_END
+#endif
+
+/*****************************************************************************/
+
 static inline gboolean
 _NM_INT_NOT_NEGATIVE (gssize val)
 {
@@ -128,6 +140,18 @@ nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src)
 	          : sizeof (struct in6_addr));
 }
 
+gboolean nm_ip_addr_set_from_untrusted (int addr_family,
+                                        gpointer dst,
+                                        gconstpointer src,
+                                        gsize src_len,
+                                        int *out_addr_family);
+
+static inline gboolean
+nm_ip4_addr_is_localhost (in_addr_t addr4)
+{
+	return (addr4 & htonl (0xFF000000u)) == htonl (0x7F000000u);
+}
+
 /*****************************************************************************/
 
 #define NM_CMP_RETURN(c) \
@@ -305,7 +329,7 @@ _nm_strndup_a_step (char *s, const char *str, gsize len)
  *
  * 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 suble error potential, because the caller needs to ensure to
+ * 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. */
@@ -688,20 +712,26 @@ nm_utils_error_set_literal (GError **error, int error_code, const char *literal)
 	g_set_error ((error), NM_UTILS_ERROR, error_code, __VA_ARGS__)
 
 #define nm_utils_error_set_errno(error, errsv, fmt, ...) \
-	g_set_error ((error), \
-	             NM_UTILS_ERROR, \
-	             NM_UTILS_ERROR_UNKNOWN, \
-	             fmt, \
-	             ##__VA_ARGS__, \
-	             g_strerror (({ \
-	                            const int _errsv = (errsv); \
-	                            \
-	                            (  _errsv >= 0 \
-	                             ? _errsv \
-	                             : (  (_errsv == G_MININT) \
-	                                ? G_MAXINT \
-	                                : -errsv)); \
-	                          })))
+	G_STMT_START { \
+		char _bstrerr[NM_STRERROR_BUFSIZE]; \
+		\
+		g_set_error ((error), \
+		             NM_UTILS_ERROR, \
+		             NM_UTILS_ERROR_UNKNOWN, \
+		             fmt, \
+		             ##__VA_ARGS__, \
+		             nm_strerror_native_r (({ \
+		                                      const int _errsv = (errsv); \
+		                                      \
+		                                      (  _errsv >= 0 \
+		                                       ? _errsv \
+		                                       : (  G_UNLIKELY (_errsv == G_MININT) \
+		                                          ? G_MAXINT \
+		                                          : -errsv)); \
+		                                   }), \
+		                                   _bstrerr, \
+		                                   sizeof (_bstrerr))); \
+	} G_STMT_END
 
 /*****************************************************************************/
 
@@ -777,6 +807,11 @@ GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype,
 
 /*****************************************************************************/
 
+GType nm_g_type_find_implementing_class_for_property (GType gtype,
+                                                      const char *pname);
+
+/*****************************************************************************/
+
 typedef enum {
 	NM_UTILS_STR_UTF8_SAFE_FLAG_NONE                = 0,
 	NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL         = 0x0001,
@@ -1093,4 +1128,31 @@ nm_strv_ptrarray_take_gstring (GPtrArray *cmd,
 
 int nm_utils_getpagesize (void);
 
+/*****************************************************************************/
+
+char *nm_utils_bin2hexstr_full (gconstpointer addr,
+                                gsize length,
+                                char delimiter,
+                                gboolean upper_case,
+                                char *out);
+
+guint8 *nm_utils_hexstr2bin_full (const char *hexstr,
+                                  gboolean allow_0x_prefix,
+                                  gboolean delimiter_required,
+                                  const char *delimiter_candidates,
+                                  gsize required_len,
+                                  guint8 *buffer,
+                                  gsize buffer_len,
+                                  gsize *out_len);
+
+#define nm_utils_hexstr2bin_buf(hexstr, allow_0x_prefix, delimiter_required, delimiter_candidates, buffer) \
+    nm_utils_hexstr2bin_full ((hexstr), (allow_0x_prefix), (delimiter_required), (delimiter_candidates), G_N_ELEMENTS (buffer), (buffer), G_N_ELEMENTS (buffer), NULL)
+
+guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr,
+                                   gboolean allow_0x_prefix,
+                                   gboolean delimiter_required,
+                                   const char *delimiter_candidates,
+                                   gsize required_len,
+                                   gsize *out_len);
+
 #endif /* __NM_SHARED_UTILS_H__ */
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index c235d93d..c5ea5e3f 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -1060,7 +1060,7 @@ nmtst_reexec_sudo (void)
 	execvp (__nmtst_internal.sudo_cmd, argv);
 
 	errsv = errno;
-	g_error (">> exec %s failed: %d - %s", __nmtst_internal.sudo_cmd, errsv, strerror (errsv));
+	g_error (">> exec %s failed: %d - %s", __nmtst_internal.sudo_cmd, errsv, nm_strerror_native (errsv));
 }
 
 /*****************************************************************************/
@@ -1214,7 +1214,7 @@ nmtst_inet_from_string (int addr_family, const char *str)
 static inline const char *
 nmtst_inet_to_string (int addr_family, gconstpointer addr)
 {
-	static char buf[MAX (INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
+	static char buf[NM_CONST_MAX (INET6_ADDRSTRLEN, INET_ADDRSTRLEN)];
 
 	g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
 	g_assert (addr);
@@ -1370,7 +1370,7 @@ nmtst_file_unlink_if_exists (const char *name)
 	if (unlink (name) != 0) {
 		errsv = errno;
 		if (errsv != ENOENT)
-			g_error ("nmtst_file_unlink_if_exists(%s): failed with %s", name, strerror (errsv));
+			g_error ("nmtst_file_unlink_if_exists(%s): failed with %s", name, nm_strerror_native (errsv));
 	}
 }
 
@@ -1383,7 +1383,7 @@ nmtst_file_unlink (const char *name)
 
 	if (unlink (name) != 0) {
 		errsv = errno;
-		g_error ("nmtst_file_unlink(%s): failed with %s", name, strerror (errsv));
+		g_error ("nmtst_file_unlink(%s): failed with %s", name, nm_strerror_native (errsv));
 	}
 }
 
diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c
index 7d22e56d..d53b21d9 100644
--- a/shared/nm-utils/tests/test-shared-general.c
+++ b/shared/nm-utils/tests/test-shared-general.c
@@ -23,6 +23,7 @@
 
 #include "nm-utils/nm-time-utils.h"
 #include "nm-utils/nm-random-utils.h"
+#include "nm-utils/unaligned.h"
 
 #include "nm-utils/nm-test-utils.h"
 
@@ -204,6 +205,49 @@ test_nm_strndup_a (void)
 
 /*****************************************************************************/
 
+static void
+test_nm_ip4_addr_is_localhost (void)
+{
+	g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.0.0.0")));
+	g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.0.0.1")));
+	g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.5.0.1")));
+	g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("126.5.0.1")));
+	g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("128.5.0.1")));
+	g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("129.5.0.1")));
+}
+
+/*****************************************************************************/
+
+static void
+test_unaligned (void)
+{
+	int shift;
+
+	for (shift = 0; shift <= 32; shift++) {
+		guint8 buf[100] = { };
+		guint8 val = 0;
+
+		while (val == 0)
+			val = nmtst_get_rand_int () % 256;
+
+		buf[shift] = val;
+
+		g_assert_cmpint (unaligned_read_le64 (&buf[shift]), ==, (guint64) val);
+		g_assert_cmpint (unaligned_read_be64 (&buf[shift]), ==, ((guint64) val) << 56);
+		g_assert_cmpint (unaligned_read_ne64 (&buf[shift]), !=, 0);
+
+		g_assert_cmpint (unaligned_read_le32 (&buf[shift]), ==, (guint32) val);
+		g_assert_cmpint (unaligned_read_be32 (&buf[shift]), ==, ((guint32) val) << 24);
+		g_assert_cmpint (unaligned_read_ne32 (&buf[shift]), !=, 0);
+
+		g_assert_cmpint (unaligned_read_le16 (&buf[shift]), ==, (guint16) val);
+		g_assert_cmpint (unaligned_read_be16 (&buf[shift]), ==, ((guint16) val) << 8);
+		g_assert_cmpint (unaligned_read_ne16 (&buf[shift]), !=, 0);
+	}
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
@@ -215,6 +259,8 @@ int main (int argc, char **argv)
 	g_test_add_func ("/general/test_nm_make_strv", test_make_strv);
 	g_test_add_func ("/general/test_nm_strdup_int", test_nm_strdup_int);
 	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);
 
 	return g_test_run ();
 }
diff --git a/shared/nm-version-macros.h b/shared/nm-version-macros.h
index b04baabb..ffe13c4e 100644
--- a/shared/nm-version-macros.h
+++ b/shared/nm-version-macros.h
@@ -45,7 +45,7 @@
  * Evaluates to the micro version number of NetworkManager which this source
  * compiled against.
  */
-#define NM_MICRO_VERSION (2)
+#define NM_MICRO_VERSION (91)
 
 /**
  * NM_CHECK_VERSION:
diff --git a/shared/systemd/nm-logging-stub.c b/shared/systemd/nm-logging-stub.c
index 80d43607..5be69b4b 100644
--- a/shared/systemd/nm-logging-stub.c
+++ b/shared/systemd/nm-logging-stub.c
@@ -24,8 +24,9 @@
 /*****************************************************************************/
 
 gboolean
-_nm_log_enabled (NMLogLevel level,
-                 NMLogDomain domain)
+_nm_log_enabled_impl (gboolean mt_require_locking,
+                      NMLogLevel level,
+                      NMLogDomain domain)
 {
 	return FALSE;
 }
@@ -34,6 +35,7 @@ void
 _nm_log_impl (const char *file,
               guint line,
               const char *func,
+              gboolean mt_require_locking,
               NMLogLevel level,
               NMLogDomain domain,
               int error,
diff --git a/shared/systemd/sd-adapt-shared/missing.h b/shared/systemd/sd-adapt-shared/missing.h
index 637892c2..2ee34b6a 100644
--- a/shared/systemd/sd-adapt-shared/missing.h
+++ b/shared/systemd/sd-adapt-shared/missing.h
@@ -1,3 +1,6 @@
 #pragma once
 
 /* dummy header */
+
+#include "missing_fcntl.h"
+#include "missing_type.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 905e45c0..06008ce8 100644
--- a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h
+++ b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h
@@ -16,8 +16,8 @@
  * Copyright (C) 2014 - 2018 Red Hat, Inc.
  */
 
-#ifndef __NM_SD_ADAPT_BASIC_H__
-#define __NM_SD_ADAPT_BASIC_H__
+#ifndef __NM_SD_ADAPT_SHARED_H__
+#define __NM_SD_ADAPT_SHARED_H__
 
 #include "nm-default.h"
 
@@ -27,11 +27,16 @@
 
 /*****************************************************************************/
 
+/* strerror() is not thread-safe. Patch systemd-sources via a define. */
+#define strerror(errsv) nm_strerror_native (errsv)
+
+/*****************************************************************************/
+
 static inline NMLogLevel
 _slog_level_to_nm (int slevel)
 {
-    switch (LOG_PRI (slevel)) {
-    case LOG_DEBUG:   return LOGL_DEBUG;
+	switch (LOG_PRI (slevel)) {
+	case LOG_DEBUG:   return LOGL_DEBUG;
 	case LOG_WARNING: return LOGL_WARN;
 	case LOG_CRIT:
 	case LOG_ERR:     return LOGL_ERR;
@@ -54,10 +59,10 @@ _nm_log_get_max_level_realm (void)
 	const int _nm_e = (error); \
 	const NMLogLevel _nm_l = _slog_level_to_nm ((level)); \
 	\
-	if (_nm_log_enabled (_nm_l, LOGD_SYSTEMD)) { \
+	if (_nm_log_enabled_impl (!(NM_THREAD_SAFE_ON_MAIN_THREAD), _nm_l, LOGD_SYSTEMD)) { \
 		const char *_nm_location = strrchr ((""file), '/'); \
 		\
-		_nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_SYSTEMD, _nm_e, NULL, NULL, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \
+		_nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), !(NM_THREAD_SAFE_ON_MAIN_THREAD), _nm_l, LOGD_SYSTEMD, _nm_e, NULL, NULL, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \
 	} \
 	(_nm_e > 0 ? -_nm_e : _nm_e); \
 })
@@ -130,4 +135,4 @@ static inline pid_t gettid(void) {
 
 /*****************************************************************************/
 
-#endif /* __NM_SD_ADAPT_BASIC_H__ */
+#endif /* __NM_SD_ADAPT_SHARED_H__ */
diff --git a/shared/systemd/src/basic/alloc-util.h b/shared/systemd/src/basic/alloc-util.h
index ff7a4679..893a1238 100644
--- a/shared/systemd/src/basic/alloc-util.h
+++ b/shared/systemd/src/basic/alloc-util.h
@@ -10,20 +10,28 @@
 
 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
+ * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */
+#define ALLOCA_MAX (4U*1024U*1024U)
+
 #define new(t, n) ((t*) malloc_multiply(sizeof(t), (n)))
 
 #define new0(t, n) ((t*) calloc((n) ?: 1, sizeof(t)))
 
-#define newa(t, n)                                              \
-        ({                                                      \
-                assert(!size_multiply_overflow(sizeof(t), n));  \
-                (t*) alloca(sizeof(t)*(n));                     \
+#define newa(t, n)                                                      \
+        ({                                                              \
+                size_t _n_ = n;                                         \
+                assert(!size_multiply_overflow(sizeof(t), _n_));        \
+                assert(sizeof(t)*_n_ <= ALLOCA_MAX);                    \
+                (t*) alloca(sizeof(t)*_n_);                             \
         })
 
-#define newa0(t, n)                                             \
-        ({                                                      \
-                assert(!size_multiply_overflow(sizeof(t), n));  \
-                (t*) alloca0(sizeof(t)*(n));                    \
+#define newa0(t, n)                                                     \
+        ({                                                              \
+                size_t _n_ = n;                                         \
+                assert(!size_multiply_overflow(sizeof(t), _n_));        \
+                assert(sizeof(t)*_n_ <= ALLOCA_MAX);                    \
+                (t*) alloca0(sizeof(t)*_n_);                            \
         })
 
 #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n)))
@@ -51,16 +59,20 @@ void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
 #define memdupa(p, l)                           \
         ({                                      \
                 void *_q_;                      \
-                _q_ = alloca(l);                \
-                memcpy(_q_, p, l);              \
+                size_t _l_ = l;                 \
+                assert(_l_ <= ALLOCA_MAX);      \
+                _q_ = alloca(_l_);              \
+                memcpy(_q_, p, _l_);            \
         })
 
 #define memdupa_suffix0(p, l)                   \
         ({                                      \
                 void *_q_;                      \
-                _q_ = alloca(l + 1);            \
-                ((uint8_t*) _q_)[l] = 0;        \
-                memcpy(_q_, p, l);              \
+                size_t _l_ = l;                 \
+                assert(_l_ <= ALLOCA_MAX);      \
+                _q_ = alloca(_l_ + 1);          \
+                ((uint8_t*) _q_)[_l_] = 0;      \
+                memcpy(_q_, p, _l_);            \
         })
 
 static inline void freep(void *p) {
@@ -116,6 +128,7 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
         ({                                              \
                 char *_new_;                            \
                 size_t _len_ = n;                       \
+                assert(_len_ <= ALLOCA_MAX);            \
                 _new_ = alloca(_len_);                  \
                 (void *) memset(_new_, 0, _len_);       \
         })
@@ -125,16 +138,18 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size);
         ({                                                              \
                 void *_ptr_;                                            \
                 size_t _mask_ = (align) - 1;                            \
-                _ptr_ = alloca((size) + _mask_);                        \
+                size_t _size_ = size;                                   \
+                assert(_size_ <= ALLOCA_MAX);                           \
+                _ptr_ = alloca(_size_ + _mask_);                        \
                 (void*)(((uintptr_t)_ptr_ + _mask_) & ~_mask_);         \
         })
 
 #define alloca0_align(size, align)                                      \
         ({                                                              \
                 void *_new_;                                            \
-                size_t _size_ = (size);                                 \
-                _new_ = alloca_align(_size_, (align));                  \
-                (void*)memset(_new_, 0, _size_);                        \
+                size_t _xsize_ = (size);                                \
+                _new_ = alloca_align(_xsize_, (align));                 \
+                (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
diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c
index beafd8c2..4babe753 100644
--- a/shared/systemd/src/basic/env-file.c
+++ b/shared/systemd/src/basic/env-file.c
@@ -37,7 +37,6 @@ static int parse_env_file_internal(
                 VALUE,
                 VALUE_ESCAPE,
                 SINGLE_QUOTE_VALUE,
-                SINGLE_QUOTE_VALUE_ESCAPE,
                 DOUBLE_QUOTE_VALUE,
                 DOUBLE_QUOTE_VALUE_ESCAPE,
                 COMMENT,
@@ -115,7 +114,7 @@ static int parse_env_file_internal(
 
                         } else if (c == '\'')
                                 state = SINGLE_QUOTE_VALUE;
-                        else if (c == '\"')
+                        else if (c == '"')
                                 state = DOUBLE_QUOTE_VALUE;
                         else if (c == '\\')
                                 state = VALUE_ESCAPE;
@@ -188,8 +187,6 @@ static int parse_env_file_internal(
                 case SINGLE_QUOTE_VALUE:
                         if (c == '\'')
                                 state = PRE_VALUE;
-                        else if (c == '\\')
-                                state = SINGLE_QUOTE_VALUE_ESCAPE;
                         else {
                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
                                         return -ENOMEM;
@@ -199,19 +196,8 @@ static int parse_env_file_internal(
 
                         break;
 
-                case SINGLE_QUOTE_VALUE_ESCAPE:
-                        state = SINGLE_QUOTE_VALUE;
-
-                        if (!strchr(NEWLINE, c)) {
-                                if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
-                                        return -ENOMEM;
-
-                                value[n_value++] = c;
-                        }
-                        break;
-
                 case DOUBLE_QUOTE_VALUE:
-                        if (c == '\"')
+                        if (c == '"')
                                 state = PRE_VALUE;
                         else if (c == '\\')
                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
@@ -227,12 +213,17 @@ static int parse_env_file_internal(
                 case DOUBLE_QUOTE_VALUE_ESCAPE:
                         state = DOUBLE_QUOTE_VALUE;
 
-                        if (!strchr(NEWLINE, c)) {
+                        if (c == '"') {
                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
                                         return -ENOMEM;
-
+                                value[n_value++] = '"';
+                        } else if (!strchr(NEWLINE, c)) {
+                                if (!GREEDY_REALLOC(value, value_alloc, n_value+3))
+                                        return -ENOMEM;
+                                value[n_value++] = '\\';
                                 value[n_value++] = c;
                         }
+
                         break;
 
                 case COMMENT:
@@ -255,7 +246,6 @@ static int parse_env_file_internal(
                    VALUE,
                    VALUE_ESCAPE,
                    SINGLE_QUOTE_VALUE,
-                   SINGLE_QUOTE_VALUE_ESCAPE,
                    DOUBLE_QUOTE_VALUE,
                    DOUBLE_QUOTE_VALUE_ESCAPE)) {
 
@@ -530,7 +520,7 @@ static void write_env_var(FILE *f, const char *v) {
         fwrite_unlocked(v, 1, p-v, f);
 
         if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
-                fputc_unlocked('\"', f);
+                fputc_unlocked('"', f);
 
                 for (; *p; p++) {
                         if (strchr(SHELL_NEED_ESCAPE, *p))
@@ -539,7 +529,7 @@ static void write_env_var(FILE *f, const char *v) {
                         fputc_unlocked(*p, f);
                 }
 
-                fputc_unlocked('\"', f);
+                fputc_unlocked('"', f);
         } else
                 fputs_unlocked(p, f);
 
diff --git a/shared/systemd/src/basic/env-util.c b/shared/systemd/src/basic/env-util.c
index fd9eedc5..dc10362d 100644
--- a/shared/systemd/src/basic/env-util.c
+++ b/shared/systemd/src/basic/env-util.c
@@ -342,7 +342,6 @@ char **strv_env_unset(char **l, const char *p) {
 }
 
 char **strv_env_unset_many(char **l, ...) {
-
         char **f, **t;
 
         if (!l)
@@ -411,7 +410,6 @@ int strv_env_replace(char ***l, char *p) {
 }
 
 char **strv_env_set(char **x, const char *p) {
-
         _cleanup_strv_free_ char **ret = NULL;
         size_t n, m;
         char **k;
diff --git a/shared/systemd/src/basic/env-util.h b/shared/systemd/src/basic/env-util.h
index 4d21ea6b..d54f9965 100644
--- a/shared/systemd/src/basic/env-util.h
+++ b/shared/systemd/src/basic/env-util.h
@@ -13,9 +13,9 @@ bool env_value_is_valid(const char *e);
 bool env_assignment_is_valid(const char *e);
 
 enum {
-        REPLACE_ENV_USE_ENVIRONMENT = 1u,
-        REPLACE_ENV_ALLOW_BRACELESS = 2u,
-        REPLACE_ENV_ALLOW_EXTENDED  = 4u,
+        REPLACE_ENV_USE_ENVIRONMENT = 1 << 0,
+        REPLACE_ENV_ALLOW_BRACELESS = 1 << 1,
+        REPLACE_ENV_ALLOW_EXTENDED  = 1 << 2,
 };
 
 char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
diff --git a/shared/systemd/src/basic/extract-word.h b/shared/systemd/src/basic/extract-word.h
index 8c63b7c3..705ebbe9 100644
--- a/shared/systemd/src/basic/extract-word.h
+++ b/shared/systemd/src/basic/extract-word.h
@@ -4,12 +4,12 @@
 #include "macro.h"
 
 typedef enum ExtractFlags {
-        EXTRACT_RELAX                    = 1,
-        EXTRACT_CUNESCAPE                = 2,
-        EXTRACT_CUNESCAPE_RELAX          = 4,
-        EXTRACT_QUOTES                   = 8,
-        EXTRACT_DONT_COALESCE_SEPARATORS = 16,
-        EXTRACT_RETAIN_ESCAPE            = 32,
+        EXTRACT_RELAX                    = 1 << 0,
+        EXTRACT_CUNESCAPE                = 1 << 1,
+        EXTRACT_CUNESCAPE_RELAX          = 1 << 2,
+        EXTRACT_QUOTES                   = 1 << 3,
+        EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 4,
+        EXTRACT_RETAIN_ESCAPE            = 1 << 5,
 } ExtractFlags;
 
 int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c
index 8ca1711b..0cc0c6b5 100644
--- a/shared/systemd/src/basic/fd-util.c
+++ b/shared/systemd/src/basic/fd-util.c
@@ -192,6 +192,27 @@ _pure_ static bool fd_in_set(int fd, const int fdset[], size_t n_fdset) {
         return false;
 }
 
+static int get_max_fd(void) {
+        struct rlimit rl;
+        rlim_t m;
+
+        /* Return the highest possible fd, based RLIMIT_NOFILE, but enforcing FD_SETSIZE-1 as lower boundary
+         * and INT_MAX as upper boundary. */
+
+        if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+                return -errno;
+
+        m = MAX(rl.rlim_cur, rl.rlim_max);
+        if (m < FD_SETSIZE) /* Let's always cover at least 1024 fds */
+                return FD_SETSIZE-1;
+
+        if (m == RLIM_INFINITY || m > INT_MAX) /* Saturate on overflow. After all fds are "int", hence can
+                                                * never be above INT_MAX */
+                return INT_MAX;
+
+        return (int) (m - 1);
+}
+
 int close_all_fds(const int except[], size_t n_except) {
         _cleanup_closedir_ DIR *d = NULL;
         struct dirent *de;
@@ -201,20 +222,14 @@ int close_all_fds(const int except[], size_t n_except) {
 
         d = opendir("/proc/self/fd");
         if (!d) {
-                struct rlimit rl;
                 int fd, max_fd;
 
-                /* When /proc isn't available (for example in chroots) the fallback is brute forcing through the fd
-                 * table */
-
-                assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
-
-                if (rl.rlim_max == 0)
-                        return -EINVAL;
+                /* When /proc isn't available (for example in chroots) the fallback is brute forcing through
+                 * the fd table */
 
-                /* Let's take special care if the resource limit is set to unlimited, or actually larger than the range
-                 * of 'int'. Let's avoid implicit overflows. */
-                max_fd = (rl.rlim_max == RLIM_INFINITY || rl.rlim_max > INT_MAX) ? INT_MAX : (int) (rl.rlim_max - 1);
+                max_fd = get_max_fd();
+                if (max_fd < 0)
+                        return max_fd;
 
                 for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) {
                         int q;
@@ -365,7 +380,7 @@ int fd_get_path(int fd, char **ret) {
         r = readlink_malloc(procfs_path, ret);
         if (r == -ENOENT) {
                 /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make
-                 * things debuggable and distuingish the two. */
+                 * things debuggable and distinguish the two. */
 
                 if (access("/proc/self/fd/", F_OK) < 0)
                         /* /proc is not available or not set up properly, we're most likely in some chroot
diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c
index b172507b..ee66190f 100644
--- a/shared/systemd/src/basic/fileio.c
+++ b/shared/systemd/src/basic/fileio.c
@@ -215,7 +215,6 @@ int write_string_filef(
 
 int read_one_line_file(const char *fn, char **line) {
         _cleanup_fclose_ FILE *f = NULL;
-        int r;
 
         assert(fn);
         assert(line);
@@ -226,8 +225,7 @@ int read_one_line_file(const char *fn, char **line) {
 
         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
-        r = read_line(f, LONG_LINE_MAX, line);
-        return r < 0 ? r : 0;
+        return read_line(f, LONG_LINE_MAX, line);
 }
 
 int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c
index 09303c6f..c0655831 100644
--- a/shared/systemd/src/basic/hashmap.c
+++ b/shared/systemd/src/basic/hashmap.c
@@ -347,7 +347,7 @@ static unsigned base_bucket_hash(HashmapBase *h, const void *p) {
 }
 #define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p)
 
-static inline void base_set_dirty(HashmapBase *h) {
+static void base_set_dirty(HashmapBase *h) {
         h->dirty = true;
 }
 #define hashmap_set_dirty(h) base_set_dirty(HASHMAP_BASE(h))
@@ -890,7 +890,8 @@ void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_f
                  * themselves from our hash table a second time, the entry is already gone. */
 
                 while (internal_hashmap_size(h) > 0) {
-                        void *v, *k;
+                        void *k = NULL;
+                        void *v;
 
                         v = internal_hashmap_first_key_and_value(h, true, &k);
 
@@ -1517,8 +1518,11 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r
         unsigned idx;
 
         idx = find_first_entry(h);
-        if (idx == IDX_NIL)
+        if (idx == IDX_NIL) {
+                if (ret_key)
+                        *ret_key = NULL;
                 return NULL;
+        }
 
         e = bucket_at(h, idx);
         key = (void*) e->key;
diff --git a/shared/systemd/src/basic/hashmap.h b/shared/systemd/src/basic/hashmap.h
index 5bf807a7..e16a9f9e 100644
--- a/shared/systemd/src/basic/hashmap.h
+++ b/shared/systemd/src/basic/hashmap.h
@@ -326,7 +326,6 @@ static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void
         return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
 }
 
-
 static inline void *hashmap_steal_first(Hashmap *h) {
         return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
 }
diff --git a/shared/systemd/src/basic/hexdecoct.c b/shared/systemd/src/basic/hexdecoct.c
index ffd9a8f6..7c66cc62 100644
--- a/shared/systemd/src/basic/hexdecoct.c
+++ b/shared/systemd/src/basic/hexdecoct.c
@@ -606,10 +606,11 @@ static int base64_append_width(
         lines = DIV_ROUND_UP(len, width);
 
         slen = strlen_ptr(sep);
-        if (lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1))
+        if (plen >= SSIZE_MAX - 1 - slen ||
+            lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1))
                 return -ENOMEM;
 
-        t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
+        t = realloc(*prefix, (ssize_t) plen + 1 + slen + (indent + width + 1) * lines);
         if (!t)
                 return -ENOMEM;
 
@@ -644,7 +645,7 @@ int base64_append(
                 return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
         else
                 /* leave plen on the left, keep last column free */
-                return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
+                return base64_append_width(prefix, plen, " ", plen, p, l, width - plen - 1);
 }
 #endif /* NM_IGNORED */
 
diff --git a/shared/systemd/src/basic/in-addr-util.c b/shared/systemd/src/basic/in-addr-util.c
index d2cc5606..5ced3501 100644
--- a/shared/systemd/src/basic/in-addr-util.c
+++ b/shared/systemd/src/basic/in-addr-util.c
@@ -70,6 +70,14 @@ bool in4_addr_is_localhost(const struct in_addr *a) {
         return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
 }
 
+bool in4_addr_is_non_local(const struct in_addr *a) {
+        /* Whether the address is not null and not localhost.
+         *
+         * As such, it is suitable to configure as DNS/NTP server from DHCP. */
+        return !in4_addr_is_null(a) &&
+               !in4_addr_is_localhost(a);
+}
+
 int in_addr_is_localhost(int family, const union in_addr_union *u) {
         assert(u);
 
@@ -500,9 +508,8 @@ int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) {
         return 0;
 }
 
-int in_addr_prefix_from_string_internal(
+int in_addr_prefix_from_string(
                 const char *p,
-                bool use_default_prefixlen,
                 int family,
                 union in_addr_union *ret_prefix,
                 unsigned char *ret_prefixlen) {
@@ -536,13 +543,6 @@ int in_addr_prefix_from_string_internal(
                 r = in_addr_parse_prefixlen(family, e+1, &k);
                 if (r < 0)
                         return r;
-        } else if (use_default_prefixlen) {
-                if (family == AF_INET) {
-                        r = in4_addr_default_prefixlen(&buffer.in, &k);
-                        if (r < 0)
-                                return r;
-                } else
-                        k = 0;
         } else
                 k = FAMILY_ADDRESS_SIZE(family) * 8;
 
@@ -556,7 +556,7 @@ int in_addr_prefix_from_string_internal(
 
 int in_addr_prefix_from_string_auto_internal(
                 const char *p,
-                bool use_default_prefixlen,
+                InAddrPrefixLenMode mode,
                 int *ret_family,
                 union in_addr_union *ret_prefix,
                 unsigned char *ret_prefixlen) {
@@ -587,15 +587,24 @@ int in_addr_prefix_from_string_auto_internal(
                 r = in_addr_parse_prefixlen(family, e+1, &k);
                 if (r < 0)
                         return r;
-        } else if (use_default_prefixlen) {
-                if (family == AF_INET) {
-                        r = in4_addr_default_prefixlen(&buffer.in, &k);
-                        if (r < 0)
-                                return r;
-                } else
-                        k = 0;
         } else
-                k = FAMILY_ADDRESS_SIZE(family) * 8;
+                switch (mode) {
+                case PREFIXLEN_FULL:
+                        k = FAMILY_ADDRESS_SIZE(family) * 8;
+                        break;
+                case PREFIXLEN_REFUSE:
+                        return -ENOANO; /* To distinguish this error from others. */
+                case PREFIXLEN_LEGACY:
+                        if (family == AF_INET) {
+                                r = in4_addr_default_prefixlen(&buffer.in, &k);
+                                if (r < 0)
+                                        return r;
+                        } else
+                                k = 0;
+                        break;
+                default:
+                        assert_not_reached("Invalid prefixlen mode");
+                }
 
         if (ret_family)
                 *ret_family = family;
diff --git a/shared/systemd/src/basic/in-addr-util.h b/shared/systemd/src/basic/in-addr-util.h
index 5de85cc4..c2156712 100644
--- a/shared/systemd/src/basic/in-addr-util.h
+++ b/shared/systemd/src/basic/in-addr-util.h
@@ -30,6 +30,8 @@ int in_addr_is_link_local(int family, const union in_addr_union *u);
 bool in4_addr_is_localhost(const struct in_addr *a);
 int in_addr_is_localhost(int family, const union in_addr_union *u);
 
+bool in4_addr_is_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);
@@ -45,19 +47,17 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
 int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
 int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
 int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
-int in_addr_prefix_from_string_internal(const char *p, bool use_default_prefixlen, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
-int in_addr_prefix_from_string_auto_internal(const char *p, bool use_default_prefixlen, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
-static inline int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
-        return in_addr_prefix_from_string_internal(p, false, family, ret_prefix, ret_prefixlen);
-}
+int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
+
+typedef enum InAddrPrefixLenMode {
+        PREFIXLEN_FULL,   /* Default to prefixlen of address size, 32 for IPv4 or 128 for IPv6, if not specified. */
+        PREFIXLEN_REFUSE, /* Fail with -ENOANO if prefixlen is not specified. */
+        PREFIXLEN_LEGACY, /* Default to legacy default prefixlen calculation from address if not specified. */
+} InAddrPrefixLenMode;
+
+int in_addr_prefix_from_string_auto_internal(const char *p, InAddrPrefixLenMode mode, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);
 static inline int in_addr_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
-        return in_addr_prefix_from_string_auto_internal(p, false, ret_family, ret_prefix, ret_prefixlen);
-}
-static inline int in_addr_default_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
-        return in_addr_prefix_from_string_internal(p, true, family, ret_prefix, ret_prefixlen);
-}
-static inline int in_addr_default_prefix_from_string_auto(const char *p, int *ret_family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen) {
-        return in_addr_prefix_from_string_auto_internal(p, true, ret_family, ret_prefix, ret_prefixlen);
+        return in_addr_prefix_from_string_auto_internal(p, PREFIXLEN_FULL, ret_family, ret_prefix, ret_prefixlen);
 }
 
 static inline size_t FAMILY_ADDRESS_SIZE(int family) {
diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c
index f7c25915..3f47eff5 100644
--- a/shared/systemd/src/basic/io-util.c
+++ b/shared/systemd/src/basic/io-util.c
@@ -10,8 +10,10 @@
 #include <unistd.h>
 
 #include "io-util.h"
+#include "string-util.h"
 #include "time-util.h"
 
+#if 0 /* NM_IGNORED */
 int flush_fd(int fd) {
         struct pollfd pollfd = {
                 .fd = fd,
@@ -55,6 +57,7 @@ int flush_fd(int fd) {
                 count += (int) l;
         }
 }
+#endif /* NM_IGNORED */
 
 ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
         uint8_t *p = buf;
@@ -115,6 +118,7 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
         return 0;
 }
 
+#if 0 /* NM_IGNORED */
 int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
         const uint8_t *p = buf;
 
@@ -173,6 +177,7 @@ int pipe_eof(int fd) {
 
         return pollfd.revents & POLLHUP;
 }
+#endif /* NM_IGNORED */
 
 int fd_wait_for_event(int fd, int event, usec_t t) {
 
@@ -193,6 +198,7 @@ int fd_wait_for_event(int fd, int event, usec_t t) {
         return pollfd.revents;
 }
 
+#if 0 /* NM_IGNORED */
 static size_t nul_length(const uint8_t *p, size_t sz) {
         size_t n = 0;
 
@@ -254,3 +260,13 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
 
         return q - (const uint8_t*) p;
 }
+
+char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
+        char *x;
+
+        x = strappend(field, value);
+        if (x)
+                iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
+        return x;
+}
+#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/io-util.h b/shared/systemd/src/basic/io-util.h
index ed189b58..792a64ad 100644
--- a/shared/systemd/src/basic/io-util.h
+++ b/shared/systemd/src/basic/io-util.h
@@ -71,3 +71,5 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
 #define IOVEC_MAKE(base, len) (struct iovec) IOVEC_INIT(base, len)
 #define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string))
 #define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)
+
+char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
diff --git a/shared/systemd/src/basic/missing_fcntl.h b/shared/systemd/src/basic/missing_fcntl.h
new file mode 100644
index 00000000..5d1c6352
--- /dev/null
+++ b/shared/systemd/src/basic/missing_fcntl.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <fcntl.h>
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_SETPIPE_SZ
+#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
+#endif
+
+#ifndef F_GETPIPE_SZ
+#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+
+#define F_SEAL_SEAL     0x0001  /* prevent further seals from being set */
+#define F_SEAL_SHRINK   0x0002  /* prevent file from shrinking */
+#define F_SEAL_GROW     0x0004  /* prevent file from growing */
+#define F_SEAL_WRITE    0x0008  /* prevent writes */
+#endif
+
+#ifndef F_OFD_GETLK
+#define F_OFD_GETLK     36
+#define F_OFD_SETLK     37
+#define F_OFD_SETLKW    38
+#endif
+
+#ifndef MAX_HANDLE_SZ
+#define MAX_HANDLE_SZ 128
+#endif
+
+/* The precise definition of __O_TMPFILE is arch specific; use the
+ * values defined by the kernel (note: some are hexa, some are octal,
+ * duplicated as-is from the kernel definitions):
+ * - alpha, parisc, sparc: each has a specific value;
+ * - others: they use the "generic" value.
+ */
+
+#ifndef __O_TMPFILE
+#if defined(__alpha__)
+#define __O_TMPFILE     0100000000
+#elif defined(__parisc__) || defined(__hppa__)
+#define __O_TMPFILE     0400000000
+#elif defined(__sparc__) || defined(__sparc64__)
+#define __O_TMPFILE     0x2000000
+#else
+#define __O_TMPFILE     020000000
+#endif
+#endif
+
+/* a horrid kludge trying to make sure that this will fail on old kernels */
+#ifndef O_TMPFILE
+#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#endif
diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h
index 56ebd59c..5204adaa 100644
--- a/shared/systemd/src/basic/path-util.h
+++ b/shared/systemd/src/basic/path-util.h
@@ -133,7 +133,7 @@ char *prefix_root(const char *root, const char *path);
                         _ret = _path;                                   \
                 else {                                                  \
                         _l = strlen(_root) + 1 + strlen(_path) + 1;     \
-                        _n = alloca(_l);                                \
+                        _n = newa(char, _l);                            \
                         _p = stpcpy(_n, _root);                         \
                         while (_p > _n && _p[-1] == '/')                \
                                 _p--;                                   \
diff --git a/shared/systemd/src/basic/prioq.c b/shared/systemd/src/basic/prioq.c
index b218f37d..dc048cc7 100644
--- a/shared/systemd/src/basic/prioq.c
+++ b/shared/systemd/src/basic/prioq.c
@@ -261,15 +261,14 @@ int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) {
         return 1;
 }
 
-void *prioq_peek(Prioq *q) {
-
+void *prioq_peek_by_index(Prioq *q, unsigned idx) {
         if (!q)
                 return NULL;
 
-        if (q->n_items <= 0)
+        if (idx >= q->n_items)
                 return NULL;
 
-        return q->items[0].data;
+        return q->items[idx].data;
 }
 
 void *prioq_pop(Prioq *q) {
diff --git a/shared/systemd/src/basic/prioq.h b/shared/systemd/src/basic/prioq.h
index bba5c7ca..1fb57bfa 100644
--- a/shared/systemd/src/basic/prioq.h
+++ b/shared/systemd/src/basic/prioq.h
@@ -19,8 +19,14 @@ int prioq_put(Prioq *q, void *data, unsigned *idx);
 int prioq_remove(Prioq *q, void *data, unsigned *idx);
 int prioq_reshuffle(Prioq *q, void *data, unsigned *idx);
 
-void *prioq_peek(Prioq *q) _pure_;
+void *prioq_peek_by_index(Prioq *q, unsigned idx) _pure_;
+static inline void *prioq_peek(Prioq *q) {
+        return prioq_peek_by_index(q, 0);
+}
 void *prioq_pop(Prioq *q);
 
+#define PRIOQ_FOREACH_ITEM(q, p)                                \
+        for (unsigned _i = 0; (p = prioq_peek_by_index(q, _i)); _i++)
+
 unsigned prioq_size(Prioq *q) _pure_;
 bool prioq_isempty(Prioq *q) _pure_;
diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c
index be7911a3..b0afb5c8 100644
--- a/shared/systemd/src/basic/process-util.c
+++ b/shared/systemd/src/basic/process-util.c
@@ -107,7 +107,8 @@ int get_process_comm(pid_t pid, char **ret) {
 int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
         _cleanup_fclose_ FILE *f = NULL;
         bool space = false;
-        char *k, *ans = NULL;
+        char *k;
+        _cleanup_free_ char *ans = NULL;
         const char *p;
         int c;
 
@@ -134,6 +135,13 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
 
         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
+        if (max_length == 0) {
+                /* This is supposed to be a safety guard against runaway command lines. */
+                long l = sysconf(_SC_ARG_MAX);
+                assert(l > 0);
+                max_length = l;
+        }
+
         if (max_length == 1) {
 
                 /* If there's only room for one byte, return the empty string */
@@ -141,35 +149,9 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                 if (!ans)
                         return -ENOMEM;
 
-                *line = ans;
+                *line = TAKE_PTR(ans);
                 return 0;
 
-        } else if (max_length == 0) {
-                size_t len = 0, allocated = 0;
-
-                while ((c = getc(f)) != EOF) {
-
-                        if (!GREEDY_REALLOC(ans, allocated, len+3)) {
-                                free(ans);
-                                return -ENOMEM;
-                        }
-
-                        if (isprint(c)) {
-                                if (space) {
-                                        ans[len++] = ' ';
-                                        space = false;
-                                }
-
-                                ans[len++] = c;
-                        } else if (len > 0)
-                                space = true;
-               }
-
-                if (len > 0)
-                        ans[len] = '\0';
-                else
-                        ans = mfree(ans);
-
         } else {
                 bool dotdotdot = false;
                 size_t left;
@@ -232,7 +214,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                 _cleanup_free_ char *t = NULL;
                 int h;
 
-                free(ans);
+                ans = mfree(ans);
 
                 if (!comm_fallback)
                         return -ENOENT;
@@ -241,37 +223,42 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
                 if (h < 0)
                         return h;
 
-                if (max_length == 0)
-                        ans = strjoin("[", t, "]");
-                else {
-                        size_t l;
-
-                        l = strlen(t);
+                size_t l = strlen(t);
 
-                        if (l + 3 <= max_length)
-                                ans = strjoin("[", t, "]");
-                        else if (max_length <= 6) {
+                if (l + 3 <= max_length) {
+                        ans = strjoin("[", t, "]");
+                        if (!ans)
+                                return -ENOMEM;
 
-                                ans = new(char, max_length);
-                                if (!ans)
-                                        return -ENOMEM;
+                } else if (max_length <= 6) {
+                        ans = new(char, max_length);
+                        if (!ans)
+                                return -ENOMEM;
 
-                                memcpy(ans, "[...]", max_length-1);
-                                ans[max_length-1] = 0;
-                        } else {
-                                t[max_length - 6] = 0;
+                        memcpy(ans, "[...]", max_length-1);
+                        ans[max_length-1] = 0;
+                } else {
+                        t[max_length - 6] = 0;
 
-                                /* Chop off final spaces */
-                                delete_trailing_chars(t, WHITESPACE);
+                        /* Chop off final spaces */
+                        delete_trailing_chars(t, WHITESPACE);
 
-                                ans = strjoin("[", t, "...]");
-                        }
+                        ans = strjoin("[", t, "...]");
+                        if (!ans)
+                                return -ENOMEM;
                 }
-                if (!ans)
-                        return -ENOMEM;
+
+                *line = TAKE_PTR(ans);
+                return 0;
         }
 
-        *line = ans;
+        k = realloc(ans, strlen(ans) + 1);
+        if (!k)
+                return -ENOMEM;
+
+        ans = NULL;
+        *line = k;
+
         return 0;
 }
 
diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h
index 0fad4e1e..0425042f 100644
--- a/shared/systemd/src/basic/process-util.h
+++ b/shared/systemd/src/basic/process-util.h
@@ -24,8 +24,8 @@
                 if (_pid_ == 0) {                                       \
                         _r_ = ("/proc/self/" field);                    \
                 } else {                                                \
-                        _r_ = alloca(STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
-                        sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_);                       \
+                        _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
+                        sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
                 }                                                       \
                 _r_;                                                    \
         })
diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c
index 8843aba8..686adaf1 100644
--- a/shared/systemd/src/basic/stat-util.c
+++ b/shared/systemd/src/basic/stat-util.c
@@ -70,13 +70,22 @@ int is_device_node(const char *path) {
         return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
 }
 
-int dir_is_empty(const char *path) {
-        _cleanup_closedir_ DIR *d;
+int dir_is_empty_at(int dir_fd, const char *path) {
+        _cleanup_close_ int fd = -1;
+        _cleanup_closedir_ DIR *d = NULL;
         struct dirent *de;
 
-        d = opendir(path);
+        if (path)
+                fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+        else
+                fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+        if (fd < 0)
+                return -errno;
+
+        d = fdopendir(fd);
         if (!d)
                 return -errno;
+        fd = -1;
 
         FOREACH_DIRENT(de, d, return -errno)
                 return 0;
@@ -343,7 +352,6 @@ int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
                 return -ENOMEM;
 
         return 0;
-
 }
 
 int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
diff --git a/shared/systemd/src/basic/stat-util.h b/shared/systemd/src/basic/stat-util.h
index 0a08e642..74fb7251 100644
--- a/shared/systemd/src/basic/stat-util.h
+++ b/shared/systemd/src/basic/stat-util.h
@@ -15,7 +15,10 @@ int is_dir(const char *path, bool follow);
 int is_dir_fd(int fd);
 int is_device_node(const char *path);
 
-int dir_is_empty(const char *path);
+int dir_is_empty_at(int dir_fd, const char *path);
+static inline int dir_is_empty(const char *path) {
+        return dir_is_empty_at(AT_FDCWD, path);
+}
 
 static inline int dir_is_populated(const char *path) {
         int r;
diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h
index a5b5a16a..38070abb 100644
--- a/shared/systemd/src/basic/string-util.h
+++ b/shared/systemd/src/basic/string-util.h
@@ -6,6 +6,7 @@
 #include <stddef.h>
 #include <string.h>
 
+#include "alloc-util.h"
 #include "macro.h"
 
 /* What is interpreted as whitespace? */
@@ -111,7 +112,7 @@ char *strjoin_real(const char *x, ...) _sentinel_;
                 size_t _i_;                                           \
                 for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
                         _len_ += strlen(_appendees_[_i_]);              \
-                _p_ = _d_ = alloca(_len_ + 1);                          \
+                _p_ = _d_ = newa(char, _len_ + 1);                      \
                 for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_) && _appendees_[_i_]; _i_++) \
                         _p_ = stpcpy(_p_, _appendees_[_i_]);            \
                 *_p_ = 0;                                               \
diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h
index aa4cd4aa..392cab65 100644
--- a/shared/systemd/src/basic/strv.h
+++ b/shared/systemd/src/basic/strv.h
@@ -156,17 +156,10 @@ void strv_print(char **l);
                 _found;                                         \
         })
 
-#define FOREACH_STRING(x, ...)                               \
-        for (char **_l = ({                                  \
-                char **_ll = STRV_MAKE(__VA_ARGS__);         \
-                x = _ll ? _ll[0] : NULL;                     \
-                _ll;                                         \
-        });                                                  \
-        _l && *_l;                                           \
-        x = ({                                               \
-                _l ++;                                       \
-                _l[0];                                       \
-        }))
+#define FOREACH_STRING(x, y, ...)                                       \
+        for (char **_l = STRV_MAKE(({ x = y; }), ##__VA_ARGS__);        \
+             x;                                                         \
+             x = *(++_l))
 
 char **strv_reverse(char **l);
 char **strv_shell_escape(char **l, const char *bad);
diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c
index 4e609a64..9ea0380a 100644
--- a/shared/systemd/src/basic/time-util.c
+++ b/shared/systemd/src/basic/time-util.c
@@ -1036,6 +1036,15 @@ int parse_sec_fix_0(const char *t, usec_t *ret) {
         return r;
 }
 
+int parse_sec_def_infinity(const char *t, usec_t *ret) {
+        t += strspn(t, WHITESPACE);
+        if (isempty(t)) {
+                *ret = USEC_INFINITY;
+                return 0;
+        }
+        return parse_sec(t, ret);
+}
+
 static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) {
         static const struct {
                 const char *suffix;
diff --git a/shared/systemd/src/basic/time-util.h b/shared/systemd/src/basic/time-util.h
index 53163050..a238f691 100644
--- a/shared/systemd/src/basic/time-util.h
+++ b/shared/systemd/src/basic/time-util.h
@@ -112,6 +112,7 @@ int parse_timestamp(const char *t, usec_t *usec);
 
 int parse_sec(const char *t, usec_t *usec);
 int parse_sec_fix_0(const char *t, usec_t *usec);
+int parse_sec_def_infinity(const char *t, usec_t *usec);
 int parse_time(const char *t, usec_t *usec, usec_t default_unit);
 int parse_nsec(const char *t, nsec_t *nsec);
 
diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c
index 06e325f0..019121cb 100644
--- a/shared/systemd/src/basic/tmpfile-util.c
+++ b/shared/systemd/src/basic/tmpfile-util.c
@@ -10,6 +10,7 @@
 #include "hexdecoct.h"
 #include "macro.h"
 #include "memfd-util.h"
+#include "missing_fcntl.h"
 #include "missing_syscall.h"
 #include "path-util.h"
 #include "process-util.h"
diff --git a/shared/systemd/src/basic/util.h b/shared/systemd/src/basic/util.h
index f009d37d..dc33d660 100644
--- a/shared/systemd/src/basic/util.h
+++ b/shared/systemd/src/basic/util.h
@@ -174,12 +174,21 @@ static inline void *mempset(void *s, int c, size_t 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();"