summary refs log tree commit diff
path: root/shared
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-12-18 18:29:24 +0100
committerMichael Biebl <biebl@debian.org>2019-12-18 18:29:24 +0100
commit28028b26b3371756811e95d894f709f4b1207c00 (patch)
tree6fe7316fd743b51042db47601a8ef8814b3134ac /shared
parente22609983008e1a669196ad64ba3a59ae8c76e0d (diff)
New upstream version 1.22.0 upstream/1.22.0
Diffstat (limited to 'shared')
-rw-r--r--shared/meson.build495
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-connection.c67
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-lease.c12
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-c-probe.c25
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-client.c42
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-incoming.c5
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-private.h25
-rw-r--r--shared/n-dhcp4/src/n-dhcp4-socket.c4
-rw-r--r--shared/n-dhcp4/src/n-dhcp4.h5
-rw-r--r--shared/nm-default.h25
-rw-r--r--shared/nm-glib-aux/nm-c-list.h56
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.c333
-rw-r--r--shared/nm-glib-aux/nm-dbus-aux.h152
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.c21
-rw-r--r--shared/nm-glib-aux/nm-dedup-multi.h21
-rw-r--r--shared/nm-glib-aux/nm-enum-utils.c24
-rw-r--r--shared/nm-glib-aux/nm-enum-utils.h21
-rw-r--r--shared/nm-glib-aux/nm-errno.c21
-rw-r--r--shared/nm-glib-aux/nm-errno.h21
-rw-r--r--shared/nm-glib-aux/nm-glib.h60
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.c72
-rw-r--r--shared/nm-glib-aux/nm-hash-utils.h57
-rw-r--r--shared/nm-glib-aux/nm-io-utils.c179
-rw-r--r--shared/nm-glib-aux/nm-io-utils.h54
-rw-r--r--shared/nm-glib-aux/nm-jansson.h17
-rw-r--r--shared/nm-glib-aux/nm-json-aux.c18
-rw-r--r--shared/nm-glib-aux/nm-json-aux.h18
-rw-r--r--shared/nm-glib-aux/nm-keyfile-aux.c38
-rw-r--r--shared/nm-glib-aux/nm-keyfile-aux.h21
-rw-r--r--shared/nm-glib-aux/nm-logging-base.c38
-rw-r--r--shared/nm-glib-aux/nm-logging-base.h29
-rw-r--r--shared/nm-glib-aux/nm-logging-fwd.h154
-rw-r--r--shared/nm-glib-aux/nm-macros-internal.h71
-rw-r--r--shared/nm-glib-aux/nm-obj.h21
-rw-r--r--shared/nm-glib-aux/nm-random-utils.c21
-rw-r--r--shared/nm-glib-aux/nm-random-utils.h21
-rw-r--r--shared/nm-glib-aux/nm-ref-string.c195
-rw-r--r--shared/nm-glib-aux/nm-ref-string.h71
-rw-r--r--shared/nm-glib-aux/nm-secret-utils.c23
-rw-r--r--shared/nm-glib-aux/nm-secret-utils.h21
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.c768
-rw-r--r--shared/nm-glib-aux/nm-shared-utils.h236
-rw-r--r--shared/nm-glib-aux/nm-time-utils.c85
-rw-r--r--shared/nm-glib-aux/nm-time-utils.h22
-rw-r--r--shared/nm-glib-aux/nm-value-type.h18
-rw-r--r--shared/nm-libnm-core-aux/nm-dispatcher-api.h18
-rw-r--r--shared/nm-libnm-core-aux/nm-libnm-core-aux.c21
-rw-r--r--shared/nm-libnm-core-aux/nm-libnm-core-aux.h21
-rw-r--r--shared/nm-libnm-core-intern/nm-common-macros.h36
-rw-r--r--shared/nm-libnm-core-intern/nm-ethtool-utils.c18
-rw-r--r--shared/nm-libnm-core-intern/nm-ethtool-utils.h18
-rw-r--r--shared/nm-libnm-core-intern/nm-libnm-core-utils.c17
-rw-r--r--shared/nm-libnm-core-intern/nm-libnm-core-utils.h17
-rw-r--r--shared/nm-meta-setting.c18
-rw-r--r--shared/nm-meta-setting.h18
-rw-r--r--shared/nm-std-aux/c-list-util.c21
-rw-r--r--shared/nm-std-aux/c-list-util.h21
-rw-r--r--shared/nm-std-aux/nm-dbus-compat.h19
-rw-r--r--shared/nm-test-libnm-utils.h19
-rw-r--r--shared/nm-test-utils-impl.c207
-rw-r--r--shared/nm-udev-aux/nm-udev-utils.c18
-rw-r--r--shared/nm-udev-aux/nm-udev-utils.h18
-rw-r--r--shared/nm-utils/nm-compat.c21
-rw-r--r--shared/nm-utils/nm-compat.h21
-rw-r--r--shared/nm-utils/nm-test-utils.h55
-rw-r--r--shared/nm-utils/nm-vpn-editor-plugin-call.h18
-rw-r--r--shared/nm-utils/nm-vpn-plugin-macros.h18
-rw-r--r--shared/nm-utils/nm-vpn-plugin-utils.c18
-rw-r--r--shared/nm-utils/nm-vpn-plugin-utils.h18
-rw-r--r--shared/nm-utils/tests/meson.build21
-rw-r--r--shared/nm-utils/tests/test-shared-general.c78
-rw-r--r--shared/nm-version-macros.h25
-rw-r--r--shared/nm-version-macros.h.in21
-rw-r--r--shared/systemd/nm-logging-stub.c18
-rw-r--r--shared/systemd/nm-sd-utils-shared.c34
-rw-r--r--shared/systemd/nm-sd-utils-shared.h20
-rw-r--r--shared/systemd/sd-adapt-shared/dhcp-server-internal.h (renamed from shared/systemd/sd-adapt-shared/btrfs-util.h)0
-rw-r--r--shared/systemd/sd-adapt-shared/missing.h8
-rw-r--r--shared/systemd/sd-adapt-shared/missing_fs.h (renamed from shared/systemd/sd-adapt-shared/cgroup-util.h)0
-rw-r--r--shared/systemd/sd-adapt-shared/missing_magic.h (renamed from shared/systemd/sd-adapt-shared/device-nodes.h)0
-rw-r--r--shared/systemd/sd-adapt-shared/missing_network.h (renamed from shared/systemd/sd-adapt-shared/procfs-util.h)0
-rw-r--r--shared/systemd/sd-adapt-shared/missing_sched.h3
-rw-r--r--shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h16
-rw-r--r--shared/systemd/src/basic/alloc-util.c2
-rw-r--r--shared/systemd/src/basic/env-file.c2
-rw-r--r--shared/systemd/src/basic/env-util.c1
-rw-r--r--shared/systemd/src/basic/errno-util.h15
-rw-r--r--shared/systemd/src/basic/extract-word.c3
-rw-r--r--shared/systemd/src/basic/fd-util.c4
-rw-r--r--shared/systemd/src/basic/fileio.c121
-rw-r--r--shared/systemd/src/basic/fileio.h1
-rw-r--r--shared/systemd/src/basic/format-util.c19
-rw-r--r--shared/systemd/src/basic/format-util.h10
-rw-r--r--shared/systemd/src/basic/fs-util.c120
-rw-r--r--shared/systemd/src/basic/fs-util.h24
-rw-r--r--shared/systemd/src/basic/hash-funcs.c6
-rw-r--r--shared/systemd/src/basic/hash-funcs.h1
-rw-r--r--shared/systemd/src/basic/hashmap.c3
-rw-r--r--shared/systemd/src/basic/hostname-util.c1
-rw-r--r--shared/systemd/src/basic/io-util.c1
-rw-r--r--shared/systemd/src/basic/macro.h15
-rw-r--r--shared/systemd/src/basic/memory-util.h1
-rw-r--r--shared/systemd/src/basic/missing_random.h16
-rw-r--r--shared/systemd/src/basic/parse-util.c4
-rw-r--r--shared/systemd/src/basic/path-util.c9
-rw-r--r--shared/systemd/src/basic/path-util.h4
-rw-r--r--shared/systemd/src/basic/process-util.c46
-rw-r--r--shared/systemd/src/basic/process-util.h1
-rw-r--r--shared/systemd/src/basic/random-util.c36
-rw-r--r--shared/systemd/src/basic/random-util.h6
-rw-r--r--shared/systemd/src/basic/set.h4
-rw-r--r--shared/systemd/src/basic/socket-util.c4
-rw-r--r--shared/systemd/src/basic/stat-util.c8
-rw-r--r--shared/systemd/src/basic/string-table.h1
-rw-r--r--shared/systemd/src/basic/string-util.c26
-rw-r--r--shared/systemd/src/basic/string-util.h17
-rw-r--r--shared/systemd/src/basic/strv.c1
-rw-r--r--shared/systemd/src/basic/strv.h12
-rw-r--r--shared/systemd/src/basic/time-util.c47
-rw-r--r--shared/systemd/src/basic/time-util.h3
-rw-r--r--shared/systemd/src/basic/tmpfile-util.c1
-rw-r--r--shared/systemd/src/basic/utf8.c1
-rw-r--r--shared/systemd/src/basic/util.c27
-rw-r--r--shared/systemd/src/basic/util.h16
-rw-r--r--shared/systemd/src/shared/dns-domain.c1
125 files changed, 3517 insertions, 1765 deletions
diff --git a/shared/meson.build b/shared/meson.build
index af903d3c..5f96d49d 100644
--- a/shared/meson.build
+++ b/shared/meson.build
@@ -1,353 +1,260 @@
 shared_inc = include_directories('.')
 
-###############################################################################
+nm_default_dep = declare_dependency(include_directories: [top_inc, shared_inc])
 
-shared_c_stdaux = static_library(
-    'c-stdaux',
-    c_args: '-std=c11',
-    sources: files('c-stdaux/src/c-stdaux.h'),
+glib_nm_default_dep = declare_dependency(dependencies: [glib_dep, nm_default_dep])
+
+libc_siphash = static_library(
+  'c-siphash',
+  sources: 'c-siphash/src/c-siphash.c',
+  include_directories: include_directories('c-stdaux/src'),
+)
+
+libc_rbtree = static_library(
+  'c-rbtree',
+  sources: 'c-rbtree/src/c-rbtree.c',
+  include_directories: include_directories('c-stdaux/src'),
+  c_args: '-std=c11',
 )
 
-shared_c_stdaux_dep = declare_dependency(
-    include_directories: shared_inc,
+sources = files(
+  'n-acd/src/n-acd.c',
+  'n-acd/src/n-acd-probe.c',
+  'n-acd/src/util/timer.c',
 )
 
-###############################################################################
+if enable_ebpf
+  sources += files('n-acd/src/n-acd-bpf.c')
+else
+  sources += files('n-acd/src/n-acd-bpf-fallback.c')
+endif
 
-shared_c_siphash = static_library(
-    'c-siphash',
-    include_directories: [
-        include_directories('c-stdaux/src'),
-    ],
-    sources: 'c-siphash/src/c-siphash.c',
+incs = include_directories(
+  'c-list/src',
+  'c-rbtree/src',
+  'c-siphash/src',
+  'c-stdaux/src',
 )
 
-shared_c_siphash_dep = declare_dependency(
-    include_directories: shared_inc,
-    link_with: shared_c_siphash,
+c_flags = [
+  '-D_GNU_SOURCE',
+  '-DSO_ATTACH_BPF=50',
+  '-std=c11',
+  '-Wno-pointer-arith',
+  '-Wno-vla',
+]
+
+links = [
+  libc_rbtree,
+  libc_siphash,
+]
+
+libn_acd = static_library(
+  'n-acd',
+  sources: sources,
+  include_directories: incs,
+  c_args: c_flags,
+  link_with: links,
 )
 
-###############################################################################
-
-shared_c_rbtree = static_library(
-    'c-rbtree',
-    c_args: '-std=c11',
-    include_directories: [
-        include_directories('c-stdaux/src'),
-    ],
-    sources: files('c-rbtree/src/c-rbtree.c',
-                   'c-rbtree/src/c-rbtree.h',
-                   'c-rbtree/src/c-rbtree-private.h'),
+libn_acd_dep = declare_dependency(
+  include_directories: shared_inc,
+  link_with: libn_acd,
 )
 
-shared_c_rbtree_dep = declare_dependency(
-    include_directories: shared_inc,
-    link_with: shared_c_rbtree,
+sources = files(
+  'n-dhcp4/src/n-dhcp4-c-connection.c',
+  'n-dhcp4/src/n-dhcp4-c-lease.c',
+  'n-dhcp4/src/n-dhcp4-client.c',
+  'n-dhcp4/src/n-dhcp4-c-probe.c',
+  'n-dhcp4/src/n-dhcp4-incoming.c',
+  'n-dhcp4/src/n-dhcp4-outgoing.c',
+  'n-dhcp4/src/n-dhcp4-socket.c',
+  'n-dhcp4/src/util/packet.c',
+  'n-dhcp4/src/util/socket.c',
 )
 
-###############################################################################
+incs = include_directories(
+  'c-list/src',
+  'c-siphash/src',
+  'c-stdaux/src',
+)
 
-if enable_ebpf
-    shared_n_acd_bpf_files = files('n-acd/src/n-acd-bpf.c')
-else
-    shared_n_acd_bpf_files = files('n-acd/src/n-acd-bpf-fallback.c')
-endif
+c_flags = [
+  '-D_GNU_SOURCE',
+  '-Wno-declaration-after-statement',
+  '-Wno-pointer-arith',
+]
 
-shared_n_acd = static_library(
-    'n-acd',
-    sources: files('n-acd/src/n-acd.c',
-                   'n-acd/src/n-acd.h',
-                   'n-acd/src/n-acd-private.h',
-                   'n-acd/src/n-acd-probe.c',
-                   'n-acd/src/util/timer.c',
-                   'n-acd/src/util/timer.h')
-             + shared_n_acd_bpf_files,
-    c_args: [
-        '-D_GNU_SOURCE',
-        '-DSO_ATTACH_BPF=50',
-        '-std=c11',
-        '-Wno-pointer-arith',
-        '-Wno-vla',
-    ],
-    include_directories: [
-        include_directories('c-stdaux/src'),
-        include_directories('c-siphash/src'),
-        include_directories('c-list/src'),
-        include_directories('c-rbtree/src'),
-    ],
-    dependencies: [
-        shared_c_siphash_dep,
-        shared_c_rbtree_dep,
-    ],
+libn_dhcp4 = static_library(
+  'n-dhcp4',
+  sources: sources,
+  c_args: c_flags,
+  include_directories: incs,
+  link_with: libc_siphash,
 )
 
-shared_n_acd_dep = declare_dependency(
-    include_directories: shared_inc,
-    link_with: shared_n_acd,
+libn_dhcp4_dep = declare_dependency(
+  include_directories: shared_inc,
+  link_with: libn_dhcp4,
 )
 
-###############################################################################
-
-shared_n_dhcp4 = static_library(
-    'n-dhcp4',
-    sources: files('n-dhcp4/src/n-dhcp4-c-connection.c',
-                   'n-dhcp4/src/n-dhcp4-c-lease.c',
-                   'n-dhcp4/src/n-dhcp4-c-probe.c',
-                   'n-dhcp4/src/n-dhcp4-client.c',
-                   'n-dhcp4/src/n-dhcp4-incoming.c',
-                   'n-dhcp4/src/n-dhcp4-outgoing.c',
-                   'n-dhcp4/src/n-dhcp4-private.h',
-                   'n-dhcp4/src/n-dhcp4-socket.c',
-                   'n-dhcp4/src/n-dhcp4.h',
-                   'n-dhcp4/src/util/packet.c',
-                   'n-dhcp4/src/util/packet.h',
-                   'n-dhcp4/src/util/socket.c',
-                   'n-dhcp4/src/util/socket.h'),
-    c_args: [
-        '-D_GNU_SOURCE',
-        '-Wno-declaration-after-statement',
-        '-Wno-pointer-arith',
-    ],
-    include_directories: [
-        include_directories('c-list/src'),
-        include_directories('c-siphash/src'),
-        include_directories('c-stdaux/src'),
-    ],
-    dependencies: [
-        shared_c_siphash_dep,
-    ],
+nm_version_macro_header = configure_file(
+  input: 'nm-version-macros.h.in',
+  output: '@BASENAME@',
+  configuration: data_conf,
 )
 
-shared_n_dhcp4_dep = declare_dependency(
-    include_directories: shared_inc,
-    link_with: shared_n_dhcp4,
-)
+nm_ethtool_utils_source = files('nm-libnm-core-intern/nm-ethtool-utils.c')
 
-###############################################################################
+nm_libnm_core_aux_source = files('nm-libnm-core-aux/nm-libnm-core-aux.c')
 
-version_conf = configuration_data()
-version_conf.set('NM_MAJOR_VERSION', nm_major_version)
-version_conf.set('NM_MINOR_VERSION', nm_minor_version)
-version_conf.set('NM_MICRO_VERSION', nm_micro_version)
+nm_libnm_core_utils_source = files('nm-libnm-core-intern/nm-libnm-core-utils.c')
 
-version_header = configure_file(
-    input: 'nm-version-macros.h.in',
-    output: 'nm-version-macros.h',
-    configuration: version_conf,
-)
+nm_meta_setting_source = files('nm-meta-setting.c')
 
-shared_nm_meta_setting_c = files('nm-meta-setting.c')
+nm_test_utils_impl_source = files('nm-test-utils-impl.c')
 
-shared_nm_test_utils_impl_c = files('nm-test-utils-impl.c')
+nm_vpn_plugin_utils_source = files('nm-utils/nm-vpn-plugin-utils.c')
 
-shared_nm_utils_nm_vpn_plugin_utils_c = files('nm-utils/nm-vpn-plugin-utils.c')
+c_flags = [
+  '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
+  '-DNETWORKMANAGER_COMPILATION=0',
+]
 
-###############################################################################
+libnm_std_aux = static_library(
+  'nm-std-aux',
+  sources: 'nm-std-aux/c-list-util.c',
+  c_args: c_flags,
+)
 
-shared_nm_std_aux = static_library(
-    'nm-std-aux',
-    sources: files('nm-std-aux/c-list-util.c'),
-    c_args: [
-      '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
-      '-DNETWORKMANAGER_COMPILATION=0',
-    ],
-    include_directories: [
-        top_inc,
-        shared_inc,
-    ],
-    dependencies: [
-    ],
+sources = files(
+  'nm-glib-aux/nm-dbus-aux.c',
+  'nm-glib-aux/nm-dedup-multi.c',
+  'nm-glib-aux/nm-enum-utils.c',
+  'nm-glib-aux/nm-errno.c',
+  'nm-glib-aux/nm-hash-utils.c',
+  'nm-glib-aux/nm-io-utils.c',
+  'nm-glib-aux/nm-json-aux.c',
+  'nm-glib-aux/nm-keyfile-aux.c',
+  'nm-glib-aux/nm-logging-base.c',
+  'nm-glib-aux/nm-random-utils.c',
+  'nm-glib-aux/nm-ref-string.c',
+  'nm-glib-aux/nm-secret-utils.c',
+  'nm-glib-aux/nm-shared-utils.c',
+  'nm-glib-aux/nm-time-utils.c',
 )
 
-shared_nm_std_aux_dep = declare_dependency(
-    link_with: shared_nm_std_aux,
-    include_directories: [
-        top_inc,
-        shared_inc,
-    ],
+c_flags = [
+  '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
+  '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB)',
+]
+
+links = [
+  libc_siphash,
+  libnm_std_aux,
+]
+
+libnm_utils_base = static_library(
+  'nm-utils-base',
+  sources: sources,
+  dependencies: glib_nm_default_dep,
+  c_args: c_flags,
+  link_with: links,
 )
 
-###############################################################################
+libnm_utils_base_dep = declare_dependency(
+  dependencies: glib_nm_default_dep,
+  link_with: libnm_utils_base,
+)
 
-shared_nm_glib_aux_c_args = [
-    '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
-    '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_LIB)',
+deps = [
+  glib_nm_default_dep,
+  libudev_dep,
 ]
 
-shared_nm_glib_aux = static_library(
-    'nm-utils-base',
-    sources: files('nm-glib-aux/nm-dbus-aux.c',
-                   'nm-glib-aux/nm-dedup-multi.c',
-                   'nm-glib-aux/nm-enum-utils.c',
-                   'nm-glib-aux/nm-errno.c',
-                   'nm-glib-aux/nm-hash-utils.c',
-                   'nm-glib-aux/nm-io-utils.c',
-                   'nm-glib-aux/nm-json-aux.c',
-                   'nm-glib-aux/nm-keyfile-aux.c',
-                   'nm-glib-aux/nm-random-utils.c',
-                   'nm-glib-aux/nm-secret-utils.c',
-                   'nm-glib-aux/nm-shared-utils.c',
-                   'nm-glib-aux/nm-time-utils.c'),
-    c_args: shared_nm_glib_aux_c_args,
-    include_directories: [
-        top_inc,
-        shared_inc,
-    ],
-    dependencies: [
-        glib_dep,
-        shared_nm_std_aux_dep,
-    ],
+libnm_udev_aux = static_library(
+  'nm-udev-aux',
+  sources: 'nm-udev-aux/nm-udev-utils.c',
+  dependencies: deps,
+  c_args: c_flags,
 )
 
-shared_nm_glib_aux_dep = declare_dependency(
-    link_with: shared_nm_glib_aux,
-    include_directories: [
-        top_inc,
-        shared_inc,
-    ],
-    dependencies: glib_dep,
+libnm_udev_aux_dep = declare_dependency(
+  include_directories: shared_inc,
+  link_with: libnm_udev_aux,
 )
 
-###############################################################################
-
-shared_nm_udev_aux = static_library(
-    'nm-udev-aux',
-    sources: files('nm-udev-aux/nm-udev-utils.c'),
-    c_args: shared_nm_glib_aux_c_args,
-    include_directories: [
-        top_inc,
-        shared_inc,
-    ],
-    dependencies: [
-        glib_dep,
-        shared_nm_glib_aux_dep,
-        libudev_dep,
-    ],
+sources = files(
+  'systemd/src/basic/alloc-util.c',
+  'systemd/src/basic/escape.c',
+  'systemd/src/basic/env-file.c',
+  'systemd/src/basic/env-util.c',
+  'systemd/src/basic/ether-addr-util.c',
+  'systemd/src/basic/extract-word.c',
+  'systemd/src/basic/fd-util.c',
+  'systemd/src/basic/fileio.c',
+  'systemd/src/basic/format-util.c',
+  'systemd/src/basic/fs-util.c',
+  'systemd/src/basic/hash-funcs.c',
+  'systemd/src/basic/hashmap.c',
+  'systemd/src/basic/hexdecoct.c',
+  'systemd/src/basic/hostname-util.c',
+  'systemd/src/basic/in-addr-util.c',
+  'systemd/src/basic/io-util.c',
+  'systemd/src/basic/memory-util.c',
+  'systemd/src/basic/mempool.c',
+  'systemd/src/basic/parse-util.c',
+  'systemd/src/basic/path-util.c',
+  'systemd/src/basic/prioq.c',
+  'systemd/src/basic/process-util.c',
+  'systemd/src/basic/random-util.c',
+  'systemd/src/basic/socket-util.c',
+  'systemd/src/basic/stat-util.c',
+  'systemd/src/basic/string-table.c',
+  'systemd/src/basic/string-util.c',
+  'systemd/src/basic/strv.c',
+  'systemd/src/basic/strxcpyx.c',
+  'systemd/src/basic/time-util.c',
+  'systemd/src/basic/tmpfile-util.c',
+  'systemd/src/basic/utf8.c',
+  'systemd/src/basic/util.c',
+  'systemd/src/shared/dns-domain.c',
+  'systemd/nm-sd-utils-shared.c',
 )
 
-shared_nm_udev_aux_dep = declare_dependency(
-    link_with: shared_nm_udev_aux,
-    include_directories: [
-        top_inc,
-        shared_inc,
-    ],
-    dependencies: [
-        glib_dep,
-        shared_nm_glib_aux_dep,
-        libudev_dep,
-    ],
+incs = include_directories(
+  'systemd/sd-adapt-shared',
+  'systemd/src/basic',
+  'systemd/src/shared',
 )
 
-###############################################################################
+c_flags = [
+  '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
+  '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED',
+]
 
 libnm_systemd_shared = static_library(
   'nm-systemd-shared',
-  sources: files(
-    'systemd/src/basic/alloc-util.c',
-    'systemd/src/basic/escape.c',
-    'systemd/src/basic/env-file.c',
-    'systemd/src/basic/env-util.c',
-    'systemd/src/basic/ether-addr-util.c',
-    'systemd/src/basic/extract-word.c',
-    'systemd/src/basic/fd-util.c',
-    'systemd/src/basic/fileio.c',
-    'systemd/src/basic/format-util.c',
-    'systemd/src/basic/fs-util.c',
-    'systemd/src/basic/hash-funcs.c',
-    'systemd/src/basic/hashmap.c',
-    'systemd/src/basic/hexdecoct.c',
-    'systemd/src/basic/hostname-util.c',
-    'systemd/src/basic/in-addr-util.c',
-    'systemd/src/basic/io-util.c',
-    'systemd/src/basic/memory-util.c',
-    'systemd/src/basic/mempool.c',
-    'systemd/src/basic/parse-util.c',
-    'systemd/src/basic/path-util.c',
-    'systemd/src/basic/prioq.c',
-    'systemd/src/basic/process-util.c',
-    'systemd/src/basic/random-util.c',
-    'systemd/src/basic/socket-util.c',
-    'systemd/src/basic/stat-util.c',
-    'systemd/src/basic/string-table.c',
-    'systemd/src/basic/string-util.c',
-    'systemd/src/basic/strv.c',
-    'systemd/src/basic/strxcpyx.c',
-    'systemd/src/basic/time-util.c',
-    'systemd/src/basic/tmpfile-util.c',
-    'systemd/src/basic/utf8.c',
-    'systemd/src/basic/util.c',
-    'systemd/src/shared/dns-domain.c',
-    'systemd/nm-sd-utils-shared.c',
-  ),
-  include_directories: include_directories(
-    'systemd/sd-adapt-shared',
-    'systemd/src/basic',
-    'systemd/src/shared',
-  ),
-  dependencies: shared_nm_glib_aux_dep,
-  c_args: [
-    '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED',
-    '-DG_LOG_DOMAIN="libnm"',
-  ],
+  sources: sources,
+  include_directories: incs,
+  dependencies: glib_nm_default_dep,
+  c_args: c_flags,
 )
 
 libnm_systemd_shared_dep = declare_dependency(
-  include_directories: include_directories(
-    'systemd/sd-adapt-shared',
-    'systemd/src/basic',
-    'systemd/src/shared',
-  ),
-  dependencies: [
-    shared_nm_glib_aux_dep,
-  ],
-  link_with: [
-    libnm_systemd_shared,
-  ],
+  include_directories: incs,
+  dependencies: glib_dep,
+  link_with: libnm_systemd_shared,
 )
 
 libnm_systemd_logging_stub = static_library(
   'nm-systemd-logging-stub',
-  sources: files(
-    'systemd/nm-logging-stub.c',
-  ),
-  include_directories: include_directories(
-    'systemd/sd-adapt-shared',
-    'systemd/src/basic',
-  ),
-  dependencies: shared_nm_glib_aux_dep,
-  c_args: [
-    '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_SYSTEMD_SHARED',
-    '-DG_LOG_DOMAIN="libnm"',
-  ],
-)
-
-libnm_systemd_shared_no_logging_dep = declare_dependency(
-  dependencies: [
-    libnm_systemd_shared_dep,
-  ],
-  link_with: [
-    libnm_systemd_logging_stub,
-  ],
+  sources: 'systemd/nm-logging-stub.c',
+  dependencies: glib_nm_default_dep,
+  c_args: c_flags,
 )
 
-###############################################################################
-
-exe = executable(
-    'nm-utils/tests/test-shared-general',
-    [ 'nm-utils/tests/test-shared-general.c' ],
-    c_args: [
-        '-DNETWORKMANAGER_COMPILATION_TEST',
-        '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)',
-    ],
-    dependencies: [
-        shared_nm_glib_aux_dep,
-        libnm_systemd_shared_no_logging_dep,
-        shared_c_siphash_dep,
-    ],
-)
-
-test(
-    'shared/nm-utils/tests/test-shared-general',
-    test_script,
-    args: test_args + [exe.full_path()]
-)
+if enable_tests
+  subdir('nm-utils/tests')
+endif
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-connection.c b/shared/n-dhcp4/src/n-dhcp4-c-connection.c
index 5c50dacf..8a2ddb36 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-connection.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-connection.c
@@ -513,6 +513,7 @@ static int n_dhcp4_c_connection_new_message(NDhcp4CConnection *connection,
         n_dhcp4_c_connection_init_header(connection, header);
 
         message->userdata.type = type;
+        message->userdata.message_type = message_type;
 
         /*
          * Note that some implementations expect the MESSAGE_TYPE option to be
@@ -690,6 +691,7 @@ int n_dhcp4_c_connection_select_new(NDhcp4CConnection *connection,
          */
         message->userdata.start_time = offer->userdata.start_time;
         message->userdata.base_time = offer->userdata.base_time;
+        message->userdata.client_addr = client.s_addr;
         n_dhcp4_incoming_get_xid(offer, &xid);
         n_dhcp4_outgoing_set_xid(message, xid);
 
@@ -763,6 +765,7 @@ int n_dhcp4_c_connection_renew_new(NDhcp4CConnection *connection,
         if (r)
                 return r;
 
+        message->userdata.client_addr = connection->client_ip;
         *requestp = message;
         message = NULL;
         return 0;
@@ -796,6 +799,7 @@ int n_dhcp4_c_connection_rebind_new(NDhcp4CConnection *connection,
         if (r)
                 return r;
 
+        message->userdata.client_addr = connection->client_ip;
         *requestp = message;
         message = NULL;
         return 0;
@@ -847,6 +851,7 @@ int n_dhcp4_c_connection_decline_new(NDhcp4CConnection *connection,
                         return r;
         }
 
+        message->userdata.client_addr = client.s_addr;
         *requestp = message;
         message = NULL;
         return 0;
@@ -889,6 +894,7 @@ int n_dhcp4_c_connection_inform_new(NDhcp4CConnection *connection,
         if (r)
                 return r;
 
+        message->userdata.client_addr = connection->client_ip;
         *requestp = message;
         message = NULL;
         return 0;
@@ -955,9 +961,36 @@ int n_dhcp4_c_connection_release_new(NDhcp4CConnection *connection,
         return 0;
 }
 
+static const char *message_type_to_str(uint8_t type) {
+        switch (type) {
+        case N_DHCP4_MESSAGE_DISCOVER:
+                return "DISCOVER";
+        case N_DHCP4_MESSAGE_OFFER:
+                return "OFFER";
+        case N_DHCP4_MESSAGE_REQUEST:
+                return "REQUEST";
+        case N_DHCP4_MESSAGE_DECLINE:
+                return "DECLINE";
+        case N_DHCP4_MESSAGE_ACK:
+                return "ACK";
+        case N_DHCP4_MESSAGE_NAK:
+                return "NACK";
+        case N_DHCP4_MESSAGE_RELEASE:
+                return "RELEASE";
+        case N_DHCP4_MESSAGE_INFORM:
+                return "INFORM";
+        case N_DHCP4_MESSAGE_FORCERENEW:
+                return "FORCERENEW";
+        default:
+                return "UNKNOWN";
+        }
+}
+
 static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection,
                                              NDhcp4Outgoing *request,
                                              uint64_t timestamp) {
+        char server_addr[INET_ADDRSTRLEN];
+        char client_addr[INET_ADDRSTRLEN];
         int r;
 
         /*
@@ -1015,6 +1048,22 @@ static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection,
                 c_assert(0);
         }
 
+        if (request->userdata.client_addr == INADDR_ANY) {
+                n_dhcp4_c_log(connection->client_config, LOG_INFO,
+                              "sent %s to %s",
+                              message_type_to_str(request->userdata.message_type),
+                              inet_ntop(AF_INET, &connection->server_ip,
+                                        server_addr, sizeof(server_addr)));
+        } else {
+                n_dhcp4_c_log(connection->client_config, LOG_INFO,
+                              "sent %s of %s to %s",
+                              message_type_to_str(request->userdata.message_type),
+                              inet_ntop(AF_INET, &request->userdata.client_addr,
+                                        client_addr, sizeof(client_addr)),
+                              inet_ntop(AF_INET, &connection->server_ip,
+                                        server_addr, sizeof(server_addr)));
+        }
+
         ++request->userdata.n_send;
         return 0;
 }
@@ -1066,6 +1115,8 @@ int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection,
 int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
                                      NDhcp4Incoming **messagep) {
         _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
+        char serv_addr[INET_ADDRSTRLEN];
+        char client_addr[INET_ADDRSTRLEN];
         uint8_t type;
         int r;
 
@@ -1118,6 +1169,22 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
         if (r)
                 return r;
 
+        if (type == N_DHCP4_MESSAGE_OFFER || type == N_DHCP4_MESSAGE_ACK) {
+                n_dhcp4_c_log(connection->client_config, LOG_INFO,
+                              "received %s of %s from %s",
+                              message_type_to_str(type),
+                              inet_ntop(AF_INET, &message->message.header.yiaddr,
+                                        client_addr, sizeof(client_addr)),
+                              inet_ntop(AF_INET, &message->message.header.siaddr,
+                                        serv_addr, sizeof(serv_addr)));
+        } else {
+                n_dhcp4_c_log(connection->client_config, LOG_INFO,
+                              "received %s from %s",
+                              message_type_to_str(type),
+                              inet_ntop(AF_INET, &message->message.header.siaddr,
+                                        serv_addr, sizeof(serv_addr)));
+        }
+
         switch (type) {
         case N_DHCP4_MESSAGE_OFFER:
         case N_DHCP4_MESSAGE_ACK:
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-lease.c b/shared/n-dhcp4/src/n-dhcp4-c-lease.c
index c14a9daf..695a112a 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-lease.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-lease.c
@@ -218,6 +218,18 @@ _c_public_ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct
 }
 
 /**
+ * n_dhcp4_client_lease_get_basetime() - get the timestamp when the lease was received.
+ * @lease:                      the lease to operate on
+ * @ns_basetimep:               return argument for the base time in nano seconds
+ *
+ * Gets the timestamp when the lease was received in CLOCK_BOOTTIME. This
+ * is also the base timestamp for the expiration of the lifetime and t1/t2.
+ */
+_c_public_ void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep) {
+        *ns_basetimep = lease->message->userdata.base_time;
+}
+
+/**
  * n_dhcp4_client_lease_get_lifetime() - get the lifetime
  * @lease:                      the lease to operate on
  * @ns_lifetimep:               return argument for the lifetime in nano seconds
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-probe.c b/shared/n-dhcp4/src/n-dhcp4-c-probe.c
index 107c18bb..2bced4cf 100644
--- a/shared/n-dhcp4/src/n-dhcp4-c-probe.c
+++ b/shared/n-dhcp4/src/n-dhcp4-c-probe.c
@@ -800,7 +800,8 @@ static int n_dhcp4_client_probe_transition_lifetime(NDhcp4ClientProbe *probe) {
         return 0;
 }
 
-static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message) {
+static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) {
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take;
         _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL;
         NDhcp4CEventNode *node;
         int r;
@@ -818,7 +819,7 @@ static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp
                 if (r)
                         return r;
 
-                /* message consumed, do not fail */
+                message = NULL; /* consumed */
 
                 n_dhcp4_client_lease_link(lease, probe);
 
@@ -843,7 +844,8 @@ static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp
         return 0;
 }
 
-static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message) {
+static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) {
+        _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take;
         _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL;
         NDhcp4CEventNode *node;
         int r;
@@ -862,7 +864,7 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
                 if (r)
                         return r;
 
-                /* message consumed, do not fail */
+                message = NULL; /* consumed */
 
                 n_dhcp4_client_lease_link(lease, probe);
 
@@ -885,7 +887,7 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
                 if (r)
                         return r;
 
-                /* message consumed, don to fail */
+                message = NULL; /* consumed */
 
                 n_dhcp4_client_lease_link(lease, probe);
 
@@ -1100,16 +1102,17 @@ int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_no
                         r = n_dhcp4_client_probe_transition_lifetime(probe);
                         if (r)
                                 return r;
-                } else if (ns_now >= probe->current_lease->t2) {
+                } else if (ns_now >= probe->current_lease->t2 &&
+                           probe->state != N_DHCP4_CLIENT_PROBE_STATE_REBINDING) {
                         r = n_dhcp4_client_probe_transition_t2(probe, ns_now);
                         if (r)
                                 return r;
-                } else if (ns_now >= probe->current_lease->t1) {
+                } else if (ns_now >= probe->current_lease->t1 &&
+                           probe->state == N_DHCP4_CLIENT_PROBE_STATE_BOUND) {
                         r = n_dhcp4_client_probe_transition_t1(probe, ns_now);
                         if (r)
                                 return r;
                 }
-
                 break;
         default:
                 /* ignore */
@@ -1167,17 +1170,15 @@ int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events)
         switch (type) {
         case N_DHCP4_MESSAGE_OFFER:
                 r = n_dhcp4_client_probe_transition_offer(probe, message);
+                message = NULL; /* consumed */
                 if (r)
                         return r;
-                else
-                        message = NULL; /* consumed */
                 break;
         case N_DHCP4_MESSAGE_ACK:
                 r = n_dhcp4_client_probe_transition_ack(probe, message);
+                message = NULL; /* consumed */
                 if (r)
                         return r;
-                else
-                        message = NULL; /* consumed */
                 break;
         case N_DHCP4_MESSAGE_NAK:
                 r = n_dhcp4_client_probe_transition_nak(probe);
diff --git a/shared/n-dhcp4/src/n-dhcp4-client.c b/shared/n-dhcp4/src/n-dhcp4-client.c
index 5f7794fb..0bfe48ee 100644
--- a/shared/n-dhcp4/src/n-dhcp4-client.c
+++ b/shared/n-dhcp4/src/n-dhcp4-client.c
@@ -94,6 +94,9 @@ int n_dhcp4_client_config_dup(NDhcp4ClientConfig *config, NDhcp4ClientConfig **d
         dup->n_mac = config->n_mac;
         memcpy(dup->broadcast_mac, config->broadcast_mac, sizeof(dup->broadcast_mac));
         dup->n_broadcast_mac = config->n_broadcast_mac;
+        dup->log.level = config->log.level;
+        dup->log.func = config->log.func;
+        dup->log.data = config->log.data;
 
         r = n_dhcp4_client_config_set_client_id(dup,
                                                 config->client_id,
@@ -248,6 +251,15 @@ _c_public_ int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, c
         return 0;
 }
 
+_c_public_ void n_dhcp4_client_config_set_log_level(NDhcp4ClientConfig *config, int level) {
+        config->log.level = level;
+}
+
+_c_public_ void n_dhcp4_client_config_set_log_func(NDhcp4ClientConfig *config, NDhcp4LogFunc func, void *data) {
+        config->log.func = func;
+        config->log.data = data;
+}
+
 /**
  * n_dhcp4_c_event_node_new() - allocate new event
  * @nodep:                      output argument for new event
@@ -376,8 +388,14 @@ _c_public_ int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *co
                 return -errno;
 
         client->fd_timer = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK);
-        if (client->fd_timer < 0)
-                return -errno;
+        if (client->fd_timer < 0) {
+                if (errno != EINVAL)
+                        return -errno;
+                client->fd_timer = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+                if (client->fd_timer < 0)
+                        return -errno;
+                client->timerfd_is_monotonic = true;
+        }
 
         ev.data.u32 = N_DHCP4_CLIENT_EPOLL_TIMER;
         r = epoll_ctl(client->fd_epoll, EPOLL_CTL_ADD, client->fd_timer, &ev);
@@ -487,8 +505,24 @@ void n_dhcp4_client_arm_timer(NDhcp4Client *client) {
                 n_dhcp4_client_probe_get_timeout(client->current_probe, &timeout);
 
         if (timeout != client->scheduled_timeout) {
+                uint64_t scheduled_timeout = timeout;
+                int flags = TFD_TIMER_ABSTIME;
+
+                if (   timeout != 0
+                    && client->timerfd_is_monotonic) {
+                        uint64_t now;
+
+                        /* the timerfd ticks with CLOCK_MONOTONIC. Calculate and set the relative
+                         * timeout. */
+                        now = n_dhcp4_gettime(CLOCK_BOOTTIME);
+                        if (timeout <= now)
+                                timeout = 1;
+                        else
+                                timeout = timeout - now;
+                        flags = 0;
+                }
                 r = timerfd_settime(client->fd_timer,
-                                    TFD_TIMER_ABSTIME,
+                                    flags,
                                     &(struct itimerspec){
                                         .it_value = {
                                                 .tv_sec = timeout / UINT64_C(1000000000),
@@ -498,7 +532,7 @@ void n_dhcp4_client_arm_timer(NDhcp4Client *client) {
                                     NULL);
                 c_assert(r >= 0);
 
-                client->scheduled_timeout = timeout;
+                client->scheduled_timeout = scheduled_timeout;
         }
 }
 
diff --git a/shared/n-dhcp4/src/n-dhcp4-incoming.c b/shared/n-dhcp4/src/n-dhcp4-incoming.c
index 255da458..e7234c0a 100644
--- a/shared/n-dhcp4/src/n-dhcp4-incoming.c
+++ b/shared/n-dhcp4/src/n-dhcp4-incoming.c
@@ -365,10 +365,7 @@ static int n_dhcp4_incoming_query_u32(NDhcp4Incoming *message, uint8_t option, u
 
         memcpy(&be32, data, sizeof(be32));
 
-        if (be32 == (uint32_t)-1)
-                *u32p = 0;
-        else
-                *u32p = ntohl(be32);
+        *u32p = ntohl(be32);
         return 0;
 }
 
diff --git a/shared/n-dhcp4/src/n-dhcp4-private.h b/shared/n-dhcp4/src/n-dhcp4-private.h
index c38ddbfc..1cf5f25e 100644
--- a/shared/n-dhcp4/src/n-dhcp4-private.h
+++ b/shared/n-dhcp4/src/n-dhcp4-private.h
@@ -11,6 +11,7 @@
 #include <stdlib.h>
 #include <time.h>
 #include <unistd.h>
+#include <syslog.h>
 #include "n-dhcp4.h"
 
 typedef struct NDhcp4CConnection NDhcp4CConnection;
@@ -198,6 +199,8 @@ struct NDhcp4Outgoing {
 
         struct {
                 uint8_t type;
+                uint8_t message_type;
+                uint32_t client_addr;
                 uint64_t start_time;
                 uint64_t base_time;
                 uint64_t send_time;
@@ -239,6 +242,11 @@ struct NDhcp4ClientConfig {
         size_t n_broadcast_mac;
         uint8_t *client_id;
         size_t n_client_id;
+        struct {
+                int level;
+                NDhcp4LogFunc func;
+                void *data;
+        } log;
 };
 
 #define N_DHCP4_CLIENT_CONFIG_NULL(_x) {                                        \
@@ -324,6 +332,7 @@ struct NDhcp4Client {
         uint64_t scheduled_timeout;
 
         bool preempted : 1;
+        bool timerfd_is_monotonic : 1;
 };
 
 #define N_DHCP4_CLIENT_NULL(_x) {                                               \
@@ -686,3 +695,19 @@ static inline uint64_t n_dhcp4_gettime(clockid_t clock) {
 
         return ts.tv_sec * 1000ULL * 1000ULL * 1000ULL + ts.tv_nsec;
 }
+
+#define n_dhcp4_c_log(_config, _level, ...)                                    \
+        do {                                                                   \
+                const NDhcp4ClientConfig *__config = _config;                  \
+                                                                               \
+                if (_level <= __config->log.level && __config->log.func) {     \
+                        if (1) {                                               \
+                                _config->log.func(_level,                      \
+                                                  __config->log.data,          \
+                                                  __VA_ARGS__);                \
+                        } else {                                               \
+                                /* To have the compiler check arguments */     \
+                                printf(__VA_ARGS__);                           \
+                        }                                                      \
+                }                                                              \
+        } while (0)
diff --git a/shared/n-dhcp4/src/n-dhcp4-socket.c b/shared/n-dhcp4/src/n-dhcp4-socket.c
index b9ac176f..c7e89772 100644
--- a/shared/n-dhcp4/src/n-dhcp4-socket.c
+++ b/shared/n-dhcp4/src/n-dhcp4-socket.c
@@ -195,6 +195,10 @@ int n_dhcp4_c_socket_udp_new(int *sockfdp,
         if (sockfd < 0)
                 return -errno;
 
+        r = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+        if (r < 0)
+                return -errno;
+
         r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
         if (r < 0)
                 return -errno;
diff --git a/shared/n-dhcp4/src/n-dhcp4.h b/shared/n-dhcp4/src/n-dhcp4.h
index ef75eca7..d3747662 100644
--- a/shared/n-dhcp4/src/n-dhcp4.h
+++ b/shared/n-dhcp4/src/n-dhcp4.h
@@ -29,6 +29,8 @@ typedef struct NDhcp4ServerEvent NDhcp4ServerEvent;
 typedef struct NDhcp4ServerIp NDhcp4ServerIp;
 typedef struct NDhcp4ServerLease NDhcp4ServerLease;
 
+typedef void (*NDhcp4LogFunc)(int level, void *data, const char *fmt, ...);
+
 #define N_DHCP4_CLIENT_START_DELAY_RFC2131 (UINT64_C(9000))
 
 enum {
@@ -111,6 +113,8 @@ void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *config, boo
 void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac);
 void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac);
 int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id);
+void n_dhcp4_client_config_set_log_level(NDhcp4ClientConfig *config, int level);
+void n_dhcp4_client_config_set_log_func(NDhcp4ClientConfig *config, NDhcp4LogFunc func, void *data);
 
 /* client-probe configs */
 
@@ -157,6 +161,7 @@ NDhcp4ClientLease *n_dhcp4_client_lease_unref(NDhcp4ClientLease *lease);
 
 void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct in_addr *yiaddr);
 void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr);
+void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep);
 void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep);
 int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap);
 
diff --git a/shared/nm-default.h b/shared/nm-default.h
index 16a756c2..ace6ede1 100644
--- a/shared/nm-default.h
+++ b/shared/nm-default.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2015 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #ifndef __NM_DEFAULT_H__
@@ -295,6 +280,10 @@ _nm_g_return_if_fail_warning (const char *log_domain,
 #include "nm-logging.h"
 #endif
 
+#if (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE
+#include "nm-libnm-utils.h"
+#endif
+
 #if ((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM) && !((NETWORKMANAGER_COMPILATION) & (NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_PRIVATE | NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL))
 #include "NetworkManager.h"
 #endif
diff --git a/shared/nm-glib-aux/nm-c-list.h b/shared/nm-glib-aux/nm-c-list.h
index 7512730d..256dda74 100644
--- a/shared/nm-glib-aux/nm-c-list.h
+++ b/shared/nm-glib-aux/nm-c-list.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2014 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
  */
 
 #ifndef __NM_C_LIST_H__
@@ -32,6 +17,12 @@
 		_what && c_list_contains (list, &_what->member); \
 	})
 
+/* iterate over the list backwards. */
+#define nm_c_list_for_each_entry_prev(_iter, _list, _m) \
+	for (_iter = c_list_entry ((_list)->prev, __typeof__ (*_iter), _m); \
+	     &(_iter)->_m != (_list); \
+	     _iter = c_list_entry ((_iter)->_m.prev, __typeof__ (*_iter), _m))
+
 /*****************************************************************************/
 
 typedef struct {
@@ -88,8 +79,25 @@ nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn)
 		nm_c_list_elem_free_full (elem, free_fcn);
 }
 
+#define nm_c_list_elem_find_first(head, arg, predicate) \
+	({ \
+		CList *const _head = (head); \
+		NMCListElem *_result = NULL; \
+		NMCListElem *_elem; \
+		\
+		c_list_for_each_entry (_elem, _head, lst) { \
+			void *const arg = _elem->data; \
+			\
+			if (predicate) { \
+				_result = _elem; \
+				break; \
+			} \
+		} \
+		_result; \
+	})
+
 /**
- * nm_c_list_elem_find_first:
+ * nm_c_list_elem_find_first_ptr:
  * @head: the @CList head of a list containing #NMCListElem elements.
  *   Note that the head is not itself part of the list.
  * @needle: the needle pointer.
@@ -100,15 +108,9 @@ nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn)
  * Returns: the found list element or %NULL if not found.
  */
 static inline NMCListElem *
-nm_c_list_elem_find_first (CList *head, gconstpointer needle)
+nm_c_list_elem_find_first_ptr (CList *head, gconstpointer needle)
 {
-	NMCListElem *elem;
-
-	c_list_for_each_entry (elem, head, lst) {
-		if (elem->data == needle)
-			return elem;
-	}
-	return NULL;
+	return nm_c_list_elem_find_first (head, x, x == needle);
 }
 
 /*****************************************************************************/
diff --git a/shared/nm-glib-aux/nm-dbus-aux.c b/shared/nm-glib-aux/nm-dbus-aux.c
index 083c4fee..75b282bc 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.c
+++ b/shared/nm-glib-aux/nm-dbus-aux.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -67,3 +52,315 @@ nm_dbus_connection_call_get_name_owner (GDBusConnection *dbus_connection,
 	                        _nm_dbus_connection_call_get_name_owner_cb,
 	                        nm_utils_user_data_pack (user_data, callback));
 }
+
+/*****************************************************************************/
+
+static void
+_nm_dbus_connection_call_get_all_cb (GObject *source,
+                                     GAsyncResult *res,
+                                     gpointer user_data)
+{
+	gs_unref_variant GVariant *ret = NULL;
+	gs_free_error GError *error = NULL;
+	gpointer orig_user_data;
+	NMDBusConnectionCallDefaultCb callback;
+
+	nm_utils_user_data_unpack (user_data, &orig_user_data, &callback);
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+
+	nm_assert ((!!ret) != (!!error));
+
+	callback (ret, error, orig_user_data);
+}
+
+void
+nm_dbus_connection_call_get_all (GDBusConnection *dbus_connection,
+                                 const char *bus_name,
+                                 const char *object_path,
+                                 const char *interface_name,
+                                 int timeout_msec,
+                                 GCancellable *cancellable,
+                                 NMDBusConnectionCallDefaultCb callback,
+                                 gpointer user_data)
+{
+	nm_assert (callback);
+
+	g_dbus_connection_call (dbus_connection,
+	                        bus_name,
+	                        object_path,
+	                        DBUS_INTERFACE_PROPERTIES,
+	                        "GetAll",
+	                        g_variant_new ("(s)", interface_name),
+	                        G_VARIANT_TYPE ("(a{sv})"),
+	                        G_DBUS_CALL_FLAGS_NONE,
+	                        timeout_msec,
+	                        cancellable,
+	                        _nm_dbus_connection_call_get_all_cb,
+	                        nm_utils_user_data_pack (user_data, callback));
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	NMDBusConnectionSignalObjectMangerCb callback;
+	gpointer user_data;
+	GDestroyNotify user_data_free_func;
+} SubscribeObjectManagerData;
+
+static void
+_subscribe_object_manager_cb (GDBusConnection *connection,
+                              const char *sender_name,
+                              const char *arg_object_path,
+                              const char *interface_name,
+                              const char *signal_name,
+                              GVariant *parameters,
+                              gpointer user_data)
+{
+	const SubscribeObjectManagerData *d = user_data;
+
+	nm_assert (nm_streq0 (interface_name, DBUS_INTERFACE_OBJECT_MANAGER));
+
+	if (nm_streq (signal_name, "InterfacesAdded")) {
+		gs_unref_variant GVariant *interfaces_and_properties = NULL;
+		const char *object_path;
+
+		if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oa{sa{sv}})")))
+			return;
+
+		g_variant_get (parameters,
+		               "(&o@a{sa{sv}})",
+		               &object_path,
+		               &interfaces_and_properties);
+
+		d->callback (object_path, interfaces_and_properties, NULL, d->user_data);
+		return;
+	}
+
+	if (nm_streq (signal_name, "InterfacesRemoved")) {
+		gs_free const char **interfaces = NULL;
+		const char *object_path;
+
+		if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(oas)")))
+			return;
+
+		g_variant_get (parameters,
+		               "(&o^a&s)",
+		               &object_path,
+		               &interfaces);
+
+		d->callback (object_path, NULL, interfaces, d->user_data);
+		return;
+	}
+}
+
+static void
+_subscribe_object_manager_data_free (gpointer ptr)
+{
+	SubscribeObjectManagerData *d = ptr;
+
+	if (d->user_data_free_func)
+		d->user_data_free_func (d->user_data);
+	nm_g_slice_free (d);
+}
+
+guint
+nm_dbus_connection_signal_subscribe_object_manager (GDBusConnection *dbus_connection,
+                                                    const char *service_name,
+                                                    const char *object_path,
+                                                    NMDBusConnectionSignalObjectMangerCb callback,
+                                                    gpointer user_data,
+                                                    GDestroyNotify user_data_free_func)
+{
+	SubscribeObjectManagerData *d;
+
+	g_return_val_if_fail (callback, 0);
+
+	d = g_slice_new (SubscribeObjectManagerData);
+	*d = (SubscribeObjectManagerData) {
+		.callback            = callback,
+		.user_data           = user_data,
+		.user_data_free_func = user_data_free_func,
+	};
+
+	return nm_dbus_connection_signal_subscribe_object_manager_plain (dbus_connection,
+	                                                                 service_name,
+	                                                                 object_path,
+	                                                                 NULL,
+	                                                                 _subscribe_object_manager_cb,
+	                                                                 d,
+	                                                                 _subscribe_object_manager_data_free);
+}
+
+/*****************************************************************************/
+
+static void
+_nm_dbus_connection_call_get_managed_objects_cb (GObject *source,
+                                                 GAsyncResult *res,
+                                                 gpointer user_data)
+{
+	gs_unref_variant GVariant *ret = NULL;
+	gs_unref_variant GVariant *arg = NULL;
+	gs_free_error GError *error = NULL;
+	gpointer orig_user_data;
+	NMDBusConnectionCallDefaultCb callback;
+
+	nm_utils_user_data_unpack (user_data, &orig_user_data, &callback);
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), res, &error);
+
+	nm_assert ((!!ret) != (!!error));
+
+	if (ret) {
+		nm_assert (g_variant_is_of_type (ret, G_VARIANT_TYPE ("(a{oa{sa{sv}}})")));
+		arg = g_variant_get_child_value (ret, 0);
+	}
+
+	callback (arg, error, orig_user_data);
+}
+
+void
+nm_dbus_connection_call_get_managed_objects (GDBusConnection *dbus_connection,
+                                             const char *bus_name,
+                                             const char *object_path,
+                                             GDBusCallFlags flags,
+                                             int timeout_msec,
+                                             GCancellable *cancellable,
+                                             NMDBusConnectionCallDefaultCb callback,
+                                             gpointer user_data)
+{
+	nm_assert (callback);
+
+	g_dbus_connection_call (dbus_connection,
+	                        bus_name,
+	                        object_path,
+	                        DBUS_INTERFACE_OBJECT_MANAGER,
+	                        "GetManagedObjects",
+	                        NULL,
+	                        G_VARIANT_TYPE ("(a{oa{sa{sv}}})"),
+	                        flags,
+	                        timeout_msec,
+	                        cancellable,
+	                        _nm_dbus_connection_call_get_managed_objects_cb,
+	                        nm_utils_user_data_pack (user_data, callback));
+}
+
+/*****************************************************************************/
+
+static void
+_call_finish_cb (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data,
+                 gboolean return_void,
+                 gboolean strip_dbus_error)
+{
+	gs_unref_object GTask *task = user_data;
+	gs_unref_variant GVariant *ret = NULL;
+	GError *error = NULL;
+
+	nm_assert (G_IS_DBUS_CONNECTION (source));
+	nm_assert (G_IS_TASK (user_data));
+
+	ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
+	if (!ret) {
+		if (strip_dbus_error)
+			g_dbus_error_strip_remote_error (error);
+		g_task_return_error (task, error);
+		return;
+	}
+
+	if (!return_void) {
+		nm_assert (!g_variant_is_of_type (ret, G_VARIANT_TYPE ("()")));
+		g_task_return_pointer (task, g_steal_pointer (&ret), (GDestroyNotify) g_variant_unref);
+	} else {
+		nm_assert (g_variant_is_of_type (ret, G_VARIANT_TYPE ("()")));
+		g_task_return_boolean (task, TRUE);
+	}
+}
+
+/**
+ * nm_dbus_connection_call_finish_void_cb:
+ *
+ * A default callback to pass as callback to g_dbus_connection_call().
+ *
+ * - user_data must be a GTask, whose reference will be consumed by the
+ *   callback.
+ * - the return GVariant must be a empty tuple "()".
+ * - the GTask is returned either with error or TRUE boolean.
+ */
+void
+nm_dbus_connection_call_finish_void_cb (GObject *source,
+                                        GAsyncResult *result,
+                                        gpointer user_data)
+{
+	_call_finish_cb (source, result, user_data, TRUE, FALSE);
+}
+
+/**
+ * nm_dbus_connection_call_finish_void_strip_dbus_error_cb:
+ *
+ * Like nm_dbus_connection_call_finish_void_cb(). The difference
+ * is that on error this will first call g_dbus_error_strip_remote_error() on the error.
+ */
+void
+nm_dbus_connection_call_finish_void_strip_dbus_error_cb (GObject *source,
+                                                         GAsyncResult *result,
+                                                         gpointer user_data)
+{
+	_call_finish_cb (source, result, user_data, TRUE, TRUE);
+}
+
+/**
+ * nm_dbus_connection_call_finish_variant_cb:
+ *
+ * A default callback to pass as callback to g_dbus_connection_call().
+ *
+ * - user_data must be a GTask, whose reference will be consumed by the
+ *   callback.
+ * - the return GVariant must not be an empty tuple "()".
+ * - the GTask is returned either with error or with a pointer containing the GVariant.
+ */
+void
+nm_dbus_connection_call_finish_variant_cb (GObject *source,
+                                           GAsyncResult *result,
+                                           gpointer user_data)
+{
+	_call_finish_cb (source, result, user_data, FALSE, FALSE);
+}
+
+/**
+ * nm_dbus_connection_call_finish_variant_strip_dbus_error_cb:
+ *
+ * Like nm_dbus_connection_call_finish_variant_strip_dbus_error_cb(). The difference
+ * is that on error this will first call g_dbus_error_strip_remote_error() on the error.
+ */
+void
+nm_dbus_connection_call_finish_variant_strip_dbus_error_cb (GObject *source,
+                                                            GAsyncResult *result,
+                                                            gpointer user_data)
+{
+	_call_finish_cb (source, result, user_data, FALSE, TRUE);
+}
+
+/*****************************************************************************/
+
+gboolean
+_nm_dbus_error_is (GError *error, ...)
+{
+	gs_free char *dbus_error = NULL;
+	const char *name;
+	va_list ap;
+
+	dbus_error = g_dbus_error_get_remote_error (error);
+	if (!dbus_error)
+		return FALSE;
+
+	va_start (ap, error);
+	while ((name = va_arg (ap, const char *))) {
+		if (nm_streq (dbus_error, name))
+			return TRUE;
+	}
+	va_end (ap);
+
+	return FALSE;
+}
diff --git a/shared/nm-glib-aux/nm-dbus-aux.h b/shared/nm-glib-aux/nm-dbus-aux.h
index 271a7d9c..840e23c2 100644
--- a/shared/nm-glib-aux/nm-dbus-aux.h
+++ b/shared/nm-glib-aux/nm-dbus-aux.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_DBUS_AUX_H__
@@ -42,6 +27,12 @@ nm_clear_g_dbus_connection_signal (GDBusConnection *dbus_connection,
 
 /*****************************************************************************/
 
+typedef void (*NMDBusConnectionCallDefaultCb) (GVariant *result,
+                                               GError *error,
+                                               gpointer user_data);
+
+/*****************************************************************************/
+
 static inline void
 nm_dbus_connection_call_start_service_by_name (GDBusConnection *dbus_connection,
                                                const char *name,
@@ -91,11 +82,126 @@ typedef void (*NMDBusConnectionCallGetNameOwnerCb) (const char *name_owner,
                                                     gpointer user_data);
 
 void nm_dbus_connection_call_get_name_owner (GDBusConnection *dbus_connection,
-                                              const char *service_name,
-                                              int timeout_msec,
-                                              GCancellable *cancellable,
-                                              NMDBusConnectionCallGetNameOwnerCb callback,
-                                              gpointer user_data);
+                                             const char *service_name,
+                                             int timeout_msec,
+                                             GCancellable *cancellable,
+                                             NMDBusConnectionCallGetNameOwnerCb callback,
+                                             gpointer user_data);
+
+static inline guint
+nm_dbus_connection_signal_subscribe_properties_changed (GDBusConnection *dbus_connection,
+                                                        const char *bus_name,
+                                                        const char *object_path,
+                                                        const char *interface_name,
+                                                        GDBusSignalCallback callback,
+                                                        gpointer user_data,
+                                                        GDestroyNotify user_data_free_func)
+
+{
+	nm_assert (bus_name);
+
+	/* it seems that using a non-unique name causes problems that we get signals
+	 * also from unrelated senders. Usually, you are anyway monitoring the name-owner,
+	 * so you should have the unique name at hand.
+	 *
+	 * If not, investigate this, ensure that it works, and lift this restriction. */
+	nm_assert (g_dbus_is_unique_name (bus_name));
+
+	return g_dbus_connection_signal_subscribe (dbus_connection,
+	                                           bus_name,
+	                                           DBUS_INTERFACE_PROPERTIES,
+	                                           "PropertiesChanged",
+	                                           object_path,
+	                                           interface_name,
+	                                           G_DBUS_SIGNAL_FLAGS_NONE,
+	                                           callback,
+	                                           user_data,
+	                                           user_data_free_func);
+}
+
+void nm_dbus_connection_call_get_all (GDBusConnection *dbus_connection,
+                                      const char *bus_name,
+                                      const char *object_path,
+                                      const char *interface_name,
+                                      int timeout_msec,
+                                      GCancellable *cancellable,
+                                      NMDBusConnectionCallDefaultCb callback,
+                                      gpointer user_data);
+
+/*****************************************************************************/
+
+static inline guint
+nm_dbus_connection_signal_subscribe_object_manager_plain (GDBusConnection *dbus_connection,
+                                                          const char *service_name,
+                                                          const char *object_path,
+                                                          const char *signal_name,
+                                                          GDBusSignalCallback callback,
+                                                          gpointer user_data,
+                                                          GDestroyNotify user_data_free_func)
+{
+	return g_dbus_connection_signal_subscribe (dbus_connection,
+	                                           service_name,
+	                                           DBUS_INTERFACE_OBJECT_MANAGER,
+	                                           signal_name,
+	                                           object_path,
+	                                           NULL,
+	                                           G_DBUS_SIGNAL_FLAGS_NONE,
+	                                           callback,
+	                                           user_data,
+	                                           user_data_free_func);
+}
+
+typedef void (*NMDBusConnectionSignalObjectMangerCb) (const char *object_path,
+                                                      GVariant *added_interfaces_and_properties,
+                                                      const char *const*removed_interfaces,
+                                                      gpointer user_data);
+
+guint nm_dbus_connection_signal_subscribe_object_manager (GDBusConnection *dbus_connection,
+                                                          const char *service_name,
+                                                          const char *object_path,
+                                                          NMDBusConnectionSignalObjectMangerCb callback,
+                                                          gpointer user_data,
+                                                          GDestroyNotify user_data_free_func);
+
+void nm_dbus_connection_call_get_managed_objects (GDBusConnection *dbus_connection,
+                                                  const char *bus_name,
+                                                  const char *object_path,
+                                                  GDBusCallFlags flags,
+                                                  int timeout_msec,
+                                                  GCancellable *cancellable,
+                                                  NMDBusConnectionCallDefaultCb callback,
+                                                  gpointer user_data);
+
+/*****************************************************************************/
+
+void nm_dbus_connection_call_finish_void_cb (GObject *source,
+                                             GAsyncResult *result,
+                                             gpointer user_data);
+
+void nm_dbus_connection_call_finish_void_strip_dbus_error_cb (GObject *source,
+                                                              GAsyncResult *result,
+                                                              gpointer user_data);
+
+void nm_dbus_connection_call_finish_variant_cb (GObject *source,
+                                                GAsyncResult *result,
+                                                gpointer user_data);
+
+void nm_dbus_connection_call_finish_variant_strip_dbus_error_cb (GObject *source,
+                                                                 GAsyncResult *result,
+                                                                 gpointer user_data);
+
+/*****************************************************************************/
+
+gboolean _nm_dbus_error_is (GError *error, ...) G_GNUC_NULL_TERMINATED;
+
+#define nm_dbus_error_is(error, ...) \
+	({ \
+		GError *const _error = (error); \
+		\
+		_error && _nm_dbus_error_is (_error, __VA_ARGS__, NULL); \
+	})
+
+#define NM_DBUS_ERROR_NAME_UNKNOWN_METHOD "org.freedesktop.DBus.Error.UnknownMethod"
 
 /*****************************************************************************/
 
diff --git a/shared/nm-glib-aux/nm-dedup-multi.c b/shared/nm-glib-aux/nm-dedup-multi.c
index 345062ca..6e23228e 100644
--- a/shared/nm-glib-aux/nm-dedup-multi.c
+++ b/shared/nm-glib-aux/nm-dedup-multi.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-dedup-multi.h b/shared/nm-glib-aux/nm-dedup-multi.h
index ca15c516..6867ae79 100644
--- a/shared/nm-glib-aux/nm-dedup-multi.h
+++ b/shared/nm-glib-aux/nm-dedup-multi.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_DEDUP_MULTI_H__
diff --git a/shared/nm-glib-aux/nm-enum-utils.c b/shared/nm-glib-aux/nm-enum-utils.c
index b16267a5..e105a4f5 100644
--- a/shared/nm-glib-aux/nm-enum-utils.c
+++ b/shared/nm-glib-aux/nm-enum-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -225,7 +210,7 @@ _nm_utils_enum_from_str_full (GType type,
                               char **err_token,
                               const NMUtilsEnumValueInfo *value_infos)
 {
-	GTypeClass *klass;
+	nm_auto_unref_gtypeclass GTypeClass *klass = NULL;
 	gboolean ret = FALSE;
 	int value = 0;
 	gs_free char *str_clone = NULL;
@@ -317,7 +302,6 @@ _nm_utils_enum_from_str_full (GType type,
 
 	NM_SET_OUT (err_token, !ret && s[0] ? g_strdup (s) : NULL);
 	NM_SET_OUT (out_value, ret ? value : 0);
-	g_type_class_unref (klass);
 	return ret;
 }
 
diff --git a/shared/nm-glib-aux/nm-enum-utils.h b/shared/nm-glib-aux/nm-enum-utils.h
index 20db07cc..d863dabd 100644
--- a/shared/nm-glib-aux/nm-enum-utils.h
+++ b/shared/nm-glib-aux/nm-enum-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_ENUM_UTILS_H__
diff --git a/shared/nm-glib-aux/nm-errno.c b/shared/nm-glib-aux/nm-errno.c
index 30eb9a8e..9133d87d 100644
--- a/shared/nm-glib-aux/nm-errno.c
+++ b/shared/nm-glib-aux/nm-errno.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-errno.h b/shared/nm-glib-aux/nm-errno.h
index d77735a7..1d329b25 100644
--- a/shared/nm-glib-aux/nm-errno.h
+++ b/shared/nm-glib-aux/nm-errno.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_ERRNO_H__
diff --git a/shared/nm-glib-aux/nm-glib.h b/shared/nm-glib-aux/nm-glib.h
index bdb7ea5b..dfb75bf0 100644
--- a/shared/nm-glib-aux/nm-glib.h
+++ b/shared/nm-glib-aux/nm-glib.h
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2008 - 2018 Red Hat, Inc.
+ * Copyright (C) 2008 - 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_GLIB_H__
@@ -569,6 +556,49 @@ _nm_g_value_unset (GValue *value)
 #define g_value_unset _nm_g_value_unset
 #endif
 
+/* G_PID_FORMAT was added only in 2.53.5. Define it ourself.
+ *
+ * If this was about "pid_t", we would check SIZEOF_PID_T, and set
+ * PRIi32/PRIi16, like systemd does. But it's actually about
+ * GPid, which glib typedefs as an "int".
+ *
+ * There is a test_gpid() that check that GPid is really a typedef
+ * for int. */
+#undef G_PID_FORMAT
+#define G_PID_FORMAT "i"
+
+/*****************************************************************************/
+
+#if !GLIB_CHECK_VERSION (2, 57, 2)
+#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f))
+#endif
+
+/*****************************************************************************/
+
+/* Glib implements g_atomic_pointer_compare_and_exchange() as a macro.
+ * For one, to inline the atomic operation and also to perform some type checks
+ * on the arguments.
+ * Depending on compiler and glib version, glib passes the arguments as they
+ * are to __atomic_compare_exchange_n(). Some clang version don't accept const
+ * pointers there. Reimplement the macro to get that right, but with stronger
+ * type checks (as we use typeof()). Had one job. */
+static inline gboolean
+_g_atomic_pointer_compare_and_exchange (volatile void *atomic,
+                                        gconstpointer oldval,
+                                        gconstpointer newval)
+{
+	return g_atomic_pointer_compare_and_exchange ((void **) atomic, (void *) oldval, (void *) newval);
+}
+#undef g_atomic_pointer_compare_and_exchange
+#define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \
+	({ \
+		typeof (atomic) const _atomic = (atomic); \
+		typeof (*_atomic) const _oldval = (oldval); \
+		typeof (*_atomic) const _newval = (newval); \
+		\
+		_g_atomic_pointer_compare_and_exchange (_atomic, _oldval, _newval); \
+	})
+
 /*****************************************************************************/
 
 #endif  /* __NM_GLIB_H__ */
diff --git a/shared/nm-glib-aux/nm-hash-utils.c b/shared/nm-glib-aux/nm-hash-utils.c
index a6158269..cd51bbaf 100644
--- a/shared/nm-glib-aux/nm-hash-utils.c
+++ b/shared/nm-glib-aux/nm-hash-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -201,6 +186,26 @@ nm_pstr_equal (gconstpointer a, gconstpointer b)
 }
 
 guint
+nm_pint_hash (gconstpointer p)
+{
+	const int *s = p;
+
+	if (!s)
+		return nm_hash_static (298377461u);
+	return nm_hash_val (1208815757u, *s);
+}
+
+gboolean
+nm_pint_equals (gconstpointer a, gconstpointer b)
+{
+	const int *s1 = a;
+	const int *s2 = a;
+
+	return    s1 == s2
+	       || (s1 && s2 && *s1 == *s2);
+}
+
+guint
 nm_pdirect_hash (gconstpointer p)
 {
 	const void *const*s = p;
@@ -221,3 +226,34 @@ nm_pdirect_equal (gconstpointer a, gconstpointer b)
 	           && s2
 	           && *s1 == *s2);
 }
+
+guint
+nm_ppdirect_hash (gconstpointer p)
+{
+	const void *const*const*s = p;
+
+	if (!s)
+		return nm_hash_static (396534869u);
+	if (!*s)
+		return nm_hash_static (1476102263u);
+	return nm_direct_hash (**s);
+}
+
+gboolean
+nm_ppdirect_equal (gconstpointer a, gconstpointer b)
+{
+	const void *const*const*s1 = a;
+	const void *const*const*s2 = b;
+
+	if (s1 == s2)
+		return TRUE;
+	if (!s1 || !s2)
+		return FALSE;
+
+	if (*s1 == *s2)
+		return TRUE;
+	if (!*s1 || !*s2)
+		return FALSE;
+
+	return **s1 == **s2;
+}
diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h
index aa0a3d7c..21c5e584 100644
--- a/shared/nm-glib-aux/nm-hash-utils.h
+++ b/shared/nm-glib-aux/nm-hash-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_HASH_UTILS_H__
@@ -293,13 +278,29 @@ gboolean nm_pstr_equal (gconstpointer a, gconstpointer b);
 
 /*****************************************************************************/
 
+/* nm_pint_*() are for hashing keys that are pointers to int values,
+ * that is, "const int *" types. */
+
+guint nm_pint_hash (gconstpointer p);
+gboolean nm_pint_equals (gconstpointer a, gconstpointer b);
+
+/*****************************************************************************/
+
 /* this hashes/compares the pointer value that we point to. Basically,
- * (((const void *const*) a) == ((const void *const*) b)). */
+ * (*((const void *const*) a) == *((const void *const*) b)). */
 
 guint nm_pdirect_hash (gconstpointer p);
 
 gboolean nm_pdirect_equal (gconstpointer a, gconstpointer b);
 
+/* this hashes/compares the direct pointer value by following pointers to
+ * pointers 2 times.
+ * (**((const void *const*const*) a) == **((const void *const*const*) b)). */
+
+guint nm_ppdirect_hash (gconstpointer p);
+
+gboolean nm_ppdirect_equal (gconstpointer a, gconstpointer b);
+
 /*****************************************************************************/
 
 #define NM_HASH_OBFUSCATE_PTR_FMT "%016" G_GINT64_MODIFIER "x"
@@ -311,15 +312,15 @@ gboolean nm_pdirect_equal (gconstpointer a, gconstpointer b);
  *
  * Note that there is a chance that two different pointer values hash to the same obfuscated
  * value. So beware of that when reviewing logs. However, such a collision is very unlikely. */
-#define nm_hash_obfuscate_ptr(static_seed, val) \
-	({ \
-		NMHashState _h; \
-		const void *_val_obf_ptr = (val); \
-		\
-		nm_hash_init (&_h, (static_seed)); \
-		nm_hash_update_val (&_h, _val_obf_ptr); \
-		nm_hash_complete_u64 (&_h); \
-	})
+static inline guint64
+nm_hash_obfuscate_ptr (guint static_seed, gconstpointer val)
+{
+	NMHashState h;
+
+	nm_hash_init (&h, static_seed);
+	nm_hash_update_val (&h, val);
+	return nm_hash_complete_u64 (&h);
+}
 
 /* if you want to log obfuscated pointer for a certain context (like, NMPRuleManager
  * logging user-tags), then you are advised to use nm_hash_obfuscate_ptr() with your
diff --git a/shared/nm-glib-aux/nm-io-utils.c b/shared/nm-glib-aux/nm-io-utils.c
index 23133ec5..ec0cd4c8 100644
--- a/shared/nm-glib-aux/nm-io-utils.c
+++ b/shared/nm-glib-aux/nm-io-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -32,9 +17,9 @@
 
 /*****************************************************************************/
 
-_nm_printf (3, 4)
+_nm_printf (4, 5)
 static int
-_get_contents_error (GError **error, int errsv, const char *format, ...)
+_get_contents_error (GError **error, int errsv, int *out_errsv, const char *format, ...)
 {
 	nm_assert (NM_ERRNO_NATIVE (errsv));
 
@@ -53,13 +38,17 @@ _get_contents_error (GError **error, int errsv, const char *format, ...)
 		             msg,
 		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
 	}
-	return -errsv;
+
+	nm_assert (errsv > 0);
+	NM_SET_OUT (out_errsv, errsv);
+
+	return FALSE;
 }
-#define _get_contents_error_errno(error, ...) \
+#define _get_contents_error_errno(error, out_errsv, ...) \
 	({ \
 		int _errsv = (errno); \
 		\
-		_get_contents_error (error, _errsv, __VA_ARGS__); \
+		_get_contents_error (error, _errsv, out_errsv, __VA_ARGS__); \
 	})
 
 static char *
@@ -110,21 +99,25 @@ _mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len)
  *  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.
+ * @out_errsv: (allow-none) (out): on error, a positive errno. or zero.
+ * @error:
+ *
  *
  * A reimplementation of g_file_get_contents() with a few differences:
  *   - accepts an open fd, instead of a path name. This allows you to
  *     use openat().
  *   - limits the maximum filesize to max_length.
  *
- * Returns: a negative error code on failure.
+ * Returns: TRUE on success.
  */
-int
+gboolean
 nm_utils_fd_get_contents (int fd,
                           gboolean close_fd,
                           gsize max_length,
                           NMUtilsFileGetContentsFlags flags,
                           char **contents,
                           gsize *length,
+                          int *out_errsv,
                           GError **error)
 {
 	nm_auto_close int fd_keeper = close_fd ? fd : -1;
@@ -133,12 +126,14 @@ nm_utils_fd_get_contents (int fd,
 	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);
+	g_return_val_if_fail (fd >= 0, FALSE);
+	g_return_val_if_fail (contents && !*contents, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	NM_SET_OUT (length, 0);
 
 	if (fstat (fd, &stat_buf) < 0)
-		return _get_contents_error_errno (error, "failure during fstat");
+		return _get_contents_error_errno (error, out_errsv, "failure during fstat");
 
 	if (!max_length) {
 		/* default to a very large size, but not extreme */
@@ -151,23 +146,23 @@ nm_utils_fd_get_contents (int fd,
 		ssize_t n_read;
 
 		if (n_stat > max_length - 1)
-			return _get_contents_error (error, EMSGSIZE, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length);
+			return _get_contents_error (error, EMSGSIZE, out_errsv, "file too large (%zu+1 bytes with maximum %zu bytes)", n_stat, max_length);
 
 		str = g_try_malloc (n_stat + 1);
 		if (!str)
-			return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu+1 bytes", n_stat);
+			return _get_contents_error (error, ENOMEM, out_errsv, "failure to allocate buffer of %zu+1 bytes", n_stat);
 
 		n_read = nm_utils_fd_read_loop (fd, str, n_stat, TRUE);
 		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, out_errsv, "error reading %zu bytes from file descriptor", n_stat);
 		}
 		str[n_read] = '\0';
 
 		if (n_read < n_stat) {
 			if (!(str = _mem_realloc (str, do_bzero_mem, n_stat + 1, n_read + 1)))
-				return _get_contents_error (error, ENOMEM, "failure to reallocate buffer with %zu bytes", n_read + 1);
+				return _get_contents_error (error, ENOMEM, out_errsv, "failure to reallocate buffer with %zu bytes", n_read + 1);
 		}
 		NM_SET_OUT (length, n_read);
 	} else {
@@ -181,13 +176,13 @@ nm_utils_fd_get_contents (int fd,
 		else {
 			fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0);
 			if (fd2 < 0)
-				return _get_contents_error_errno (error, "error during dup");
+				return _get_contents_error_errno (error, out_errsv, "error during dup");
 		}
 
 		if (!(f = fdopen (fd2, "r"))) {
 			errsv = errno;
 			nm_close (fd2);
-			return _get_contents_error (error, errsv, "failure during fdopen");
+			return _get_contents_error (error, errsv, out_errsv, "failure during fdopen");
 		}
 
 		n_have = 0;
@@ -201,14 +196,14 @@ nm_utils_fd_get_contents (int fd,
 			if (ferror (f)) {
 				if (do_bzero_mem)
 					nm_explicit_bzero (buf, sizeof (buf));
-				return _get_contents_error (error, errsv, "error during fread");
+				return _get_contents_error (error, errsv, out_errsv, "error during fread");
 			}
 
 			if (   n_have > G_MAXSIZE - 1 - n_read
 			    || n_have + n_read + 1 > max_length) {
 				if (do_bzero_mem)
 					nm_explicit_bzero (buf, sizeof (buf));
-				return _get_contents_error (error, EMSGSIZE, "file stream too large (%zu+1 bytes with maximum %zu bytes)",
+				return _get_contents_error (error, EMSGSIZE, out_errsv, "file stream too large (%zu+1 bytes with maximum %zu bytes)",
 				                            (n_have > G_MAXSIZE - 1 - n_read) ? G_MAXSIZE : n_have + n_read,
 				                            max_length);
 			}
@@ -230,7 +225,7 @@ nm_utils_fd_get_contents (int fd,
 				if (!(str = _mem_realloc (str, do_bzero_mem, old_n_alloc, n_alloc))) {
 					if (do_bzero_mem)
 						nm_explicit_bzero (buf, sizeof (buf));
-					return _get_contents_error (error, ENOMEM, "failure to allocate buffer of %zu bytes", n_alloc);
+					return _get_contents_error (error, ENOMEM, out_errsv, "failure to allocate buffer of %zu bytes", n_alloc);
 				}
 			}
 
@@ -247,7 +242,7 @@ nm_utils_fd_get_contents (int fd,
 			str[n_have] = '\0';
 			if (n_have + 1 < n_alloc) {
 				if (!(str = _mem_realloc (str, do_bzero_mem, n_alloc, n_have + 1)))
-					return _get_contents_error (error, ENOMEM, "failure to truncate buffer to %zu bytes", n_have + 1);
+					return _get_contents_error (error, ENOMEM, out_errsv, "failure to truncate buffer to %zu bytes", n_have + 1);
 			}
 		}
 
@@ -255,7 +250,8 @@ nm_utils_fd_get_contents (int fd,
 	}
 
 	*contents = g_steal_pointer (&str);
-	return 0;
+	NM_SET_OUT (out_errsv, 0);
+	return TRUE;
 }
 
 /**
@@ -270,54 +266,49 @@ nm_utils_fd_get_contents (int fd,
  *   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.
+ * @out_errsv: (allow-none) (out): on error, a positive errno. or zero.
+ * @error:
  *
  * A reimplementation of g_file_get_contents() with a few differences:
  *   - accepts an @dirfd to open @filename relative to that path via openat().
  *   - limits the maximum filesize to max_length.
  *   - uses O_CLOEXEC on internal file descriptor
+ *   - optionally returns the native errno on failure.
  *
- * Returns: a negative error code on failure.
+ * Returns: TRUE on success.
  */
-int
+gboolean
 nm_utils_file_get_contents (int dirfd,
                             const char *filename,
                             gsize max_length,
                             NMUtilsFileGetContentsFlags flags,
                             char **contents,
                             gsize *length,
+                            int *out_errsv,
                             GError **error)
 {
 	int fd;
-	int errsv;
-	char bstrerr[NM_STRERROR_BUFSIZE];
 
-	g_return_val_if_fail (filename && filename[0], -EINVAL);
+	g_return_val_if_fail (filename && filename[0], FALSE);
+	g_return_val_if_fail (contents && !*contents, FALSE);
+
+	NM_SET_OUT (length, 0);
 
 	if (dirfd >= 0) {
 		fd = openat (dirfd, filename, O_RDONLY | O_CLOEXEC);
 		if (fd < 0) {
-			errsv = errno;
-
-			g_set_error (error,
-			             G_FILE_ERROR,
-			             g_file_error_from_errno (errsv),
-			             "Failed to open file \"%s\" with openat: %s",
-			             filename,
-			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-			return -NM_ERRNO_NATIVE (errsv);
+			return _get_contents_error_errno (error,
+			                                  out_errsv,
+			                                  "Failed to open file \"%s\" with openat",
+			                                  filename);
 		}
 	} else {
 		fd = open (filename, O_RDONLY | O_CLOEXEC);
 		if (fd < 0) {
-			errsv = errno;
-
-			g_set_error (error,
-			             G_FILE_ERROR,
-			             g_file_error_from_errno (errsv),
-			             "Failed to open file \"%s\": %s",
-			             filename,
-			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-			return -NM_ERRNO_NATIVE (errsv);
+			return _get_contents_error_errno (error,
+			                                  out_errsv,
+			                                  "Failed to open file \"%s\"",
+			                                  filename);
 		}
 	}
 	return nm_utils_fd_get_contents (fd,
@@ -326,6 +317,7 @@ nm_utils_file_get_contents (int dirfd,
 	                                 flags,
 	                                 contents,
 	                                 length,
+	                                 out_errsv,
 	                                 error);
 }
 
@@ -340,6 +332,7 @@ nm_utils_file_set_contents (const char *filename,
                             const char *contents,
                             gssize length,
                             mode_t mode,
+                            int *out_errsv,
                             GError **error)
 {
 	gs_free char *tmp_name = NULL;
@@ -347,7 +340,6 @@ 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);
@@ -360,33 +352,26 @@ nm_utils_file_set_contents (const char *filename,
 	tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
 	fd = g_mkstemp_full (tmp_name, O_RDWR | O_CLOEXEC, mode);
 	if (fd < 0) {
-		errsv = errno;
-		g_set_error (error,
-		             G_FILE_ERROR,
-		             g_file_error_from_errno (errsv),
-		             "failed to create file %s: %s",
-		             tmp_name,
-		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-		return FALSE;
+		return _get_contents_error_errno (error,
+		                                  out_errsv,
+		                                  "failed to create file %s",
+		                                  tmp_name);
 	}
 
 	while (length > 0) {
 		s = write (fd, contents, length);
 		if (s < 0) {
-			errsv = errno;
+			errsv = NM_ERRNO_NATIVE (errno);
 			if (errsv == EINTR)
 				continue;
 
 			nm_close (fd);
 			unlink (tmp_name);
-
-			g_set_error (error,
-			             G_FILE_ERROR,
-			             g_file_error_from_errno (errsv),
-			             "failed to write to file %s: %s",
-			             tmp_name,
-			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-			return FALSE;
+			return _get_contents_error (error,
+			                            errsv,
+			                            out_errsv,
+			                            "failed to write to file %s",
+			                            tmp_name);
 		}
 
 		g_assert (s <= length);
@@ -404,34 +389,28 @@ nm_utils_file_set_contents (const char *filename,
 	if (   lstat (filename, &statbuf) == 0
 	    && statbuf.st_size > 0) {
 		if (fsync (fd) != 0) {
-			errsv = errno;
-
+			errsv = NM_ERRNO_NATIVE (errno);
 			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,
-			             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-			return FALSE;
+			return _get_contents_error (error,
+			                            errsv,
+			                            out_errsv,
+			                            "failed to fsync %s",
+			                            tmp_name);
 		}
 	}
 
 	nm_close (fd);
 
 	if (rename (tmp_name, filename)) {
-		errsv = errno;
+		errsv = NM_ERRNO_NATIVE (errno);
 		unlink (tmp_name);
-		g_set_error (error,
-		             G_FILE_ERROR,
-		             g_file_error_from_errno (errsv),
-		             "failed to rename %s to %s: %s",
-		             tmp_name,
-		             filename,
-		             nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr)));
-		return FALSE;
+		return _get_contents_error (error,
+		                            errsv,
+		                            out_errsv,
+		                            "failed rename %s to %s",
+		                            tmp_name,
+		                            filename);
 	}
 
 	return TRUE;
diff --git a/shared/nm-glib-aux/nm-io-utils.h b/shared/nm-glib-aux/nm-io-utils.h
index 121fc481..18dc5f74 100644
--- a/shared/nm-glib-aux/nm-io-utils.h
+++ b/shared/nm-glib-aux/nm-io-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_IO_UTILS_H__
@@ -37,26 +22,29 @@ typedef enum {
 	NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET = (1 << 0),
 } NMUtilsFileGetContentsFlags;
 
-int nm_utils_fd_get_contents (int fd,
-                              gboolean close_fd,
-                              gsize max_length,
-                              NMUtilsFileGetContentsFlags flags,
-                              char **contents,
-                              gsize *length,
-                              GError **error);
-
-int nm_utils_file_get_contents (int dirfd,
-                                const char *filename,
-                                gsize max_length,
-                                NMUtilsFileGetContentsFlags flags,
-                                char **contents,
-                                gsize *length,
-                                GError **error);
+gboolean nm_utils_fd_get_contents (int fd,
+                                   gboolean close_fd,
+                                   gsize max_length,
+                                   NMUtilsFileGetContentsFlags flags,
+                                   char **contents,
+                                   gsize *length,
+                                   int *out_errsv,
+                                   GError **error);
+
+gboolean nm_utils_file_get_contents (int dirfd,
+                                     const char *filename,
+                                     gsize max_length,
+                                     NMUtilsFileGetContentsFlags flags,
+                                     char **contents,
+                                     gsize *length,
+                                     int *out_errsv,
+                                     GError **error);
 
 gboolean nm_utils_file_set_contents (const char *filename,
                                      const char *contents,
                                      gssize length,
                                      mode_t mode,
+                                     int *out_errsv,
                                      GError **error);
 
 struct stat;
diff --git a/shared/nm-glib-aux/nm-jansson.h b/shared/nm-glib-aux/nm-jansson.h
index d4642319..0a75cff5 100644
--- a/shared/nm-glib-aux/nm-jansson.h
+++ b/shared/nm-glib-aux/nm-jansson.h
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_JANSSON_H__
diff --git a/shared/nm-glib-aux/nm-json-aux.c b/shared/nm-glib-aux/nm-json-aux.c
index 6f04ef2b..a738ab72 100644
--- a/shared/nm-glib-aux/nm-json-aux.c
+++ b/shared/nm-glib-aux/nm-json-aux.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-json-aux.h b/shared/nm-glib-aux/nm-json-aux.h
index 19d43ce4..ed3be376 100644
--- a/shared/nm-glib-aux/nm-json-aux.h
+++ b/shared/nm-glib-aux/nm-json-aux.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_JSON_AUX_H__
diff --git a/shared/nm-glib-aux/nm-keyfile-aux.c b/shared/nm-glib-aux/nm-keyfile-aux.c
index 989c773f..8b2af7a8 100644
--- a/shared/nm-glib-aux/nm-keyfile-aux.c
+++ b/shared/nm-glib-aux/nm-keyfile-aux.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -186,7 +171,6 @@ nm_key_file_db_destroy (NMKeyFileDB *self)
 void
 nm_key_file_db_start (NMKeyFileDB *self)
 {
-	int r;
 	gs_free char *contents = NULL;
 	gsize contents_len;
 	gs_free_error GError *error = NULL;
@@ -196,14 +180,14 @@ nm_key_file_db_start (NMKeyFileDB *self)
 
 	self->is_started = TRUE;
 
-	r = nm_utils_file_get_contents (-1,
-	                                self->filename,
-	                                20*1024*1024,
-	                                NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
-	                                &contents,
-	                                &contents_len,
-	                                &error);
-	if (r < 0) {
+	if (!nm_utils_file_get_contents (-1,
+	                                 self->filename,
+	                                 20*1024*1024,
+	                                 NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
+	                                 &contents,
+	                                 &contents_len,
+	                                 NULL,
+	                                 &error)) {
 		_LOGD ("failed to read \"%s\": %s", self->filename, error->message);
 		return;
 	}
diff --git a/shared/nm-glib-aux/nm-keyfile-aux.h b/shared/nm-glib-aux/nm-keyfile-aux.h
index 8563f4d1..0f729c72 100644
--- a/shared/nm-glib-aux/nm-keyfile-aux.h
+++ b/shared/nm-glib-aux/nm-keyfile-aux.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_KEYFILE_AUX_H__
diff --git a/shared/nm-glib-aux/nm-logging-base.c b/shared/nm-glib-aux/nm-logging-base.c
new file mode 100644
index 00000000..47b3e993
--- /dev/null
+++ b/shared/nm-glib-aux/nm-logging-base.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#include "nm-default.h"
+
+#include "nm-logging-base.h"
+
+#include <syslog.h>
+
+/*****************************************************************************/
+
+const LogLevelDesc level_desc[_LOGL_N] = {
+	[LOGL_TRACE] = { "TRACE", "<trace>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   },
+	[LOGL_DEBUG] = { "DEBUG", "<debug>", LOG_DEBUG,   G_LOG_LEVEL_DEBUG,   },
+	[LOGL_INFO]  = { "INFO",  "<info>",  LOG_INFO,    G_LOG_LEVEL_INFO,    },
+	[LOGL_WARN]  = { "WARN",  "<warn>",  LOG_WARNING, G_LOG_LEVEL_MESSAGE, },
+	[LOGL_ERR]   = { "ERR",   "<error>", LOG_ERR,     G_LOG_LEVEL_MESSAGE, },
+	[_LOGL_OFF]  = { "OFF",   NULL,      0,           0,                   },
+	[_LOGL_KEEP] = { "KEEP",  NULL,      0,           0,                   },
+};
+
+gboolean
+_nm_log_parse_level (const char  *level,
+                     NMLogLevel  *out_level)
+{
+	int i;
+
+	if (!level)
+		return FALSE;
+
+	for (i = 0; i < (int) G_N_ELEMENTS (level_desc); i++) {
+		if (!g_ascii_strcasecmp (level_desc[i].name, level)) {
+			NM_SET_OUT (out_level, i);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
diff --git a/shared/nm-glib-aux/nm-logging-base.h b/shared/nm-glib-aux/nm-logging-base.h
new file mode 100644
index 00000000..3d964a6e
--- /dev/null
+++ b/shared/nm-glib-aux/nm-logging-base.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#ifndef __NM_LOGGING_BASE_H__
+#define __NM_LOGGING_BASE_H__
+
+#include "nm-logging-fwd.h"
+
+typedef struct {
+	const char *name;
+	const char *level_str;
+
+	/* nm-logging uses syslog internally. Note that the three most-verbose syslog levels
+	 * are LOG_DEBUG, LOG_INFO and LOG_NOTICE. Journal already highlights LOG_NOTICE
+	 * as special.
+	 *
+	 * On the other hand, we have three levels LOGL_TRACE, LOGL_DEBUG and LOGL_INFO,
+	 * which are regular messages not to be highlighted. For that reason, we must map
+	 * LOGL_TRACE and LOGL_DEBUG both to syslog level LOG_DEBUG. */
+	int syslog_level;
+
+	GLogLevelFlags g_log_level;
+} LogLevelDesc;
+
+extern const LogLevelDesc level_desc[_LOGL_N];
+
+gboolean _nm_log_parse_level (const char *level,
+                              NMLogLevel *out_level);
+
+#endif /* __NM_LOGGING_BASE_H__ */
diff --git a/shared/nm-glib-aux/nm-logging-fwd.h b/shared/nm-glib-aux/nm-logging-fwd.h
index c60a20b5..47661788 100644
--- a/shared/nm-glib-aux/nm-logging-fwd.h
+++ b/shared/nm-glib-aux/nm-logging-fwd.h
@@ -1,26 +1,11 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2006 - 2018 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
-#ifndef __NM_LOGGING_DEFINES_H__
-#define __NM_LOGGING_DEFINES_H__
+#ifndef __NM_LOGGING_FWD_H__
+#define __NM_LOGGING_FWD_H__
 
 /* Log domains */
 
@@ -127,6 +112,19 @@ nm_log_level_from_syslog (int syslog_level)
 	}
 }
 
+static inline int
+nm_log_level_to_syslog (NMLogLevel nm_level)
+{
+	switch (nm_level) {
+	case LOGL_ERR:    return 3; /* LOG_ERR */
+	case LOGL_WARN:   return 4; /* LOG_WARN */
+	case LOGL_INFO:   return 5; /* LOG_NOTICE */
+	case LOGL_DEBUG:  return 6; /* LOG_INFO */
+	case LOGL_TRACE:  return 7; /* LOG_DEBUG */
+	default:          return 0; /* LOG_EMERG */
+	}
+}
+
 /*****************************************************************************/
 
 struct timespec;
@@ -137,4 +135,120 @@ extern void _nm_utils_monotonic_timestamp_initialized (const struct timespec *tp
                                                        gint64 offset_sec,
                                                        gboolean is_boottime);
 
-#endif /* __NM_LOGGING_DEFINES_H__ */
+/*****************************************************************************/
+
+#define _LOGL_TRACE LOGL_TRACE
+#define _LOGL_DEBUG LOGL_DEBUG
+#define _LOGL_INFO  LOGL_INFO
+#define _LOGL_WARN  LOGL_WARN
+#define _LOGL_ERR   LOGL_ERR
+
+/* This is the default definition of _NMLOG_ENABLED(). Special implementations
+ * might want to undef this and redefine it. */
+#define _NMLOG_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG_DOMAIN)) )
+
+#define _LOGT(...)          _NMLOG (_LOGL_TRACE, __VA_ARGS__)
+#define _LOGD(...)          _NMLOG (_LOGL_DEBUG, __VA_ARGS__)
+#define _LOGI(...)          _NMLOG (_LOGL_INFO , __VA_ARGS__)
+#define _LOGW(...)          _NMLOG (_LOGL_WARN , __VA_ARGS__)
+#define _LOGE(...)          _NMLOG (_LOGL_ERR  , __VA_ARGS__)
+
+#define _LOGT_ENABLED(...)  _NMLOG_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOGD_ENABLED(...)  _NMLOG_ENABLED (_LOGL_DEBUG, ##__VA_ARGS__)
+#define _LOGI_ENABLED(...)  _NMLOG_ENABLED (_LOGL_INFO , ##__VA_ARGS__)
+#define _LOGW_ENABLED(...)  _NMLOG_ENABLED (_LOGL_WARN , ##__VA_ARGS__)
+#define _LOGE_ENABLED(...)  _NMLOG_ENABLED (_LOGL_ERR  , ##__VA_ARGS__)
+
+#define _LOGT_err(errsv, ...) _NMLOG_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#define _LOGD_err(errsv, ...) _NMLOG_err (errsv, _LOGL_DEBUG, __VA_ARGS__)
+#define _LOGI_err(errsv, ...) _NMLOG_err (errsv, _LOGL_INFO , __VA_ARGS__)
+#define _LOGW_err(errsv, ...) _NMLOG_err (errsv, _LOGL_WARN , __VA_ARGS__)
+#define _LOGE_err(errsv, ...) _NMLOG_err (errsv, _LOGL_ERR  , __VA_ARGS__)
+
+/* _LOGT() and _LOGt() both log with level TRACE, but the latter is disabled by default,
+ * unless building with --with-more-logging. */
+#if NM_MORE_LOGGING
+#define _LOGt_ENABLED(...)    _NMLOG_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOGt(...)            _NMLOG (_LOGL_TRACE, __VA_ARGS__)
+#define _LOGt_err(errsv, ...) _NMLOG_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#else
+/* still call the logging macros to get compile time checks, but they will be optimized out. */
+#define _LOGt_ENABLED(...)    ( FALSE && (_NMLOG_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)) )
+#define _LOGt(...)            G_STMT_START { if (FALSE) { _NMLOG (_LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#define _LOGt_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG_err (errsv, _LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#endif
+
+/*****************************************************************************/
+
+/* Some implementation define a second set of logging macros, for a separate
+ * use. As with the _LOGD() macro family above, the exact implementation
+ * depends on the file that uses them.
+ * Still, it encourages a common pattern to have the common set of macros
+ * like _LOG2D(), _LOG2I(), etc. and have _LOG2t() which by default
+ * is disabled at compile time. */
+
+#define _NMLOG2_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG2_DOMAIN)) )
+
+#define _LOG2T(...)          _NMLOG2 (_LOGL_TRACE, __VA_ARGS__)
+#define _LOG2D(...)          _NMLOG2 (_LOGL_DEBUG, __VA_ARGS__)
+#define _LOG2I(...)          _NMLOG2 (_LOGL_INFO , __VA_ARGS__)
+#define _LOG2W(...)          _NMLOG2 (_LOGL_WARN , __VA_ARGS__)
+#define _LOG2E(...)          _NMLOG2 (_LOGL_ERR  , __VA_ARGS__)
+
+#define _LOG2T_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOG2D_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_DEBUG, ##__VA_ARGS__)
+#define _LOG2I_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_INFO , ##__VA_ARGS__)
+#define _LOG2W_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_WARN , ##__VA_ARGS__)
+#define _LOG2E_ENABLED(...)  _NMLOG2_ENABLED (_LOGL_ERR  , ##__VA_ARGS__)
+
+#define _LOG2T_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#define _LOG2D_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_DEBUG, __VA_ARGS__)
+#define _LOG2I_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_INFO , __VA_ARGS__)
+#define _LOG2W_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_WARN , __VA_ARGS__)
+#define _LOG2E_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_ERR  , __VA_ARGS__)
+
+#if NM_MORE_LOGGING
+#define _LOG2t_ENABLED(...)    _NMLOG2_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOG2t(...)            _NMLOG2 (_LOGL_TRACE, __VA_ARGS__)
+#define _LOG2t_err(errsv, ...) _NMLOG2_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#else
+/* still call the logging macros to get compile time checks, but they will be optimized out. */
+#define _LOG2t_ENABLED(...)    ( FALSE && (_NMLOG2_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)) )
+#define _LOG2t(...)            G_STMT_START { if (FALSE) { _NMLOG2 (_LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#define _LOG2t_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG2_err (errsv, _LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#endif
+
+#define _NMLOG3_ENABLED(level) ( nm_logging_enabled ((level), (_NMLOG3_DOMAIN)) )
+
+#define _LOG3T(...)          _NMLOG3 (_LOGL_TRACE, __VA_ARGS__)
+#define _LOG3D(...)          _NMLOG3 (_LOGL_DEBUG, __VA_ARGS__)
+#define _LOG3I(...)          _NMLOG3 (_LOGL_INFO , __VA_ARGS__)
+#define _LOG3W(...)          _NMLOG3 (_LOGL_WARN , __VA_ARGS__)
+#define _LOG3E(...)          _NMLOG3 (_LOGL_ERR  , __VA_ARGS__)
+
+#define _LOG3T_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOG3D_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_DEBUG, ##__VA_ARGS__)
+#define _LOG3I_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_INFO , ##__VA_ARGS__)
+#define _LOG3W_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_WARN , ##__VA_ARGS__)
+#define _LOG3E_ENABLED(...)  _NMLOG3_ENABLED (_LOGL_ERR  , ##__VA_ARGS__)
+
+#define _LOG3T_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#define _LOG3D_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_DEBUG, __VA_ARGS__)
+#define _LOG3I_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_INFO , __VA_ARGS__)
+#define _LOG3W_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_WARN , __VA_ARGS__)
+#define _LOG3E_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_ERR  , __VA_ARGS__)
+
+#if NM_MORE_LOGGING
+#define _LOG3t_ENABLED(...)    _NMLOG3_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)
+#define _LOG3t(...)            _NMLOG3 (_LOGL_TRACE, __VA_ARGS__)
+#define _LOG3t_err(errsv, ...) _NMLOG3_err (errsv, _LOGL_TRACE, __VA_ARGS__)
+#else
+/* still call the logging macros to get compile time checks, but they will be optimized out. */
+#define _LOG3t_ENABLED(...)    ( FALSE && (_NMLOG3_ENABLED (_LOGL_TRACE, ##__VA_ARGS__)) )
+#define _LOG3t(...)            G_STMT_START { if (FALSE) { _NMLOG3 (_LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#define _LOG3t_err(errsv, ...) G_STMT_START { if (FALSE) { _NMLOG3_err (errsv, _LOGL_TRACE, __VA_ARGS__); } } G_STMT_END
+#endif
+
+/*****************************************************************************/
+
+#endif /* __NM_LOGGING_FWD_H__ */
diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h
index 9502c442..d1ea04c0 100644
--- a/shared/nm-glib-aux/nm-macros-internal.h
+++ b/shared/nm-glib-aux/nm-macros-internal.h
@@ -1,22 +1,7 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2012 Colin Walters <walters@verbum.org>.
- * (C) Copyright 2014 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2012 Colin Walters <walters@verbum.org>.
+ * Copyright (C) 2014 Red Hat, Inc.
  */
 
 #ifndef __NM_MACROS_INTERNAL_H__
@@ -324,6 +309,12 @@ _nm_auto_protect_errno (int *p_saved_errno)
 NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_unref_gsource, g_source_unref);
 #define nm_auto_unref_gsource nm_auto(_nm_auto_unref_gsource)
 
+NM_AUTO_DEFINE_FCN0 (guint, _nm_auto_remove_source, g_source_remove);
+#define nm_auto_remove_source nm_auto(_nm_auto_remove_source)
+
+NM_AUTO_DEFINE_FCN0 (GIOChannel *, _nm_auto_unref_io_channel, g_io_channel_unref)
+#define nm_auto_unref_io_channel nm_auto(_nm_auto_unref_io_channel)
+
 NM_AUTO_DEFINE_FCN0 (GMainLoop *, _nm_auto_unref_gmainloop, g_main_loop_unref);
 #define nm_auto_unref_gmainloop nm_auto(_nm_auto_unref_gmainloop)
 
@@ -644,6 +635,13 @@ NM_G_ERROR_MSG (GError *error)
 #define _NM_ENSURE_TYPE_CONST(type, value) ((const type) (value))
 #endif
 
+#if _NM_CC_SUPPORT_GENERIC && ( !defined (__clang__) || __clang_major__ > 3 )
+#define NM_STRUCT_OFFSET_ENSURE_TYPE(type, container, field) (_Generic ( (&(((container *) NULL)->field))[0] , \
+                                                                        type: G_STRUCT_OFFSET (container, field)))
+#else
+#define NM_STRUCT_OFFSET_ENSURE_TYPE(type, container, field) G_STRUCT_OFFSET (container, field)
+#endif
+
 #if _NM_CC_SUPPORT_GENERIC
 /* these macros cast (value) to
  *  - "const char **"      (for "MC", mutable-const)
@@ -1045,7 +1043,7 @@ _nm_gobject_notify_together_impl (obj_type *obj, guint n, const property_enums_t
 		g_object_thaw_notify ((GObject *) obj); \
 } \
 \
-static inline void \
+_nm_unused static inline void \
 _notify (obj_type *obj, property_enums_type prop) \
 { \
 	_nm_gobject_notify_together_impl (obj, 1, &prop); \
@@ -1270,6 +1268,25 @@ nm_clear_g_cancellable_disconnect (GCancellable *cancellable, gulong *cancellabl
 
 /*****************************************************************************/
 
+static inline const char *
+nm_dbus_path_not_empty (const char *str)
+{
+	nm_assert (!str || str[0] == '/');
+	return !str || (str[0] == '/' && str[1] == '\0')
+	       ? NULL
+	       : str;
+}
+
+/*****************************************************************************/
+
+/* GVariantType is basically a C string. But G_VARIANT_TYPE() is not suitable
+ * to initialize a static variable (because it evaluates a function check that
+ * the string is valid). Add an alternative macro that does the plain cast.
+ *
+ * Here you loose the assertion check that G_VARIANT_TYPE() to ensure the
+ * string is valid. */
+#define NM_G_VARIANT_TYPE(fmt) ((const GVariantType *) (""fmt""))
+
 static inline GVariant *
 nm_g_variant_ref (GVariant *v)
 {
@@ -1989,4 +2006,18 @@ nm_close (int fd)
 
 #define NM_PID_T_INVAL ((pid_t) -1)
 
+/*****************************************************************************/
+
+NM_AUTO_DEFINE_FCN_VOID0 (GMutex *, _nm_auto_unlock_g_mutex, g_mutex_unlock)
+
+#define nm_auto_unlock_g_mutex nm_auto (_nm_auto_unlock_g_mutex)
+
+#define _NM_G_MUTEX_LOCKED(lock, uniq) \
+	nm_auto_unlock_g_mutex GMutex *NM_UNIQ_T(nm_lock, uniq) = (lock)
+
+#define NM_G_MUTEX_LOCKED(lock) \
+	_NM_G_MUTEX_LOCKED (lock, NM_UNIQ)
+
+/*****************************************************************************/
+
 #endif /* __NM_MACROS_INTERNAL_H__ */
diff --git a/shared/nm-glib-aux/nm-obj.h b/shared/nm-glib-aux/nm-obj.h
index 06016bdd..cbfdba63 100644
--- a/shared/nm-glib-aux/nm-obj.h
+++ b/shared/nm-glib-aux/nm-obj.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_OBJ_H__
diff --git a/shared/nm-glib-aux/nm-random-utils.c b/shared/nm-glib-aux/nm-random-utils.c
index f56f8b99..95063c94 100644
--- a/shared/nm-glib-aux/nm-random-utils.c
+++ b/shared/nm-glib-aux/nm-random-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-random-utils.h b/shared/nm-glib-aux/nm-random-utils.h
index 8e134ee9..056af38a 100644
--- a/shared/nm-glib-aux/nm-random-utils.h
+++ b/shared/nm-glib-aux/nm-random-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_RANDOM_UTILS_H__
diff --git a/shared/nm-glib-aux/nm-ref-string.c b/shared/nm-glib-aux/nm-ref-string.c
new file mode 100644
index 00000000..0a0b0d3a
--- /dev/null
+++ b/shared/nm-glib-aux/nm-ref-string.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#include "nm-default.h"
+
+#include "nm-ref-string.h"
+
+/*****************************************************************************/
+
+typedef struct {
+	NMRefString r;
+	volatile int ref_count;
+	char str_data[];
+} RefString;
+
+G_LOCK_DEFINE_STATIC (gl_lock);
+static GHashTable *gl_hash;
+
+/* the first field of NMRefString is a pointer to the NUL terminated string.
+ * This also allows to compare strings with nm_pstr_equal(), although, pointer
+ * equality might be better. */
+G_STATIC_ASSERT (G_STRUCT_OFFSET (NMRefString, str) == 0);
+G_STATIC_ASSERT (G_STRUCT_OFFSET (RefString, r) == 0);
+G_STATIC_ASSERT (G_STRUCT_OFFSET (RefString, r.str) == 0);
+
+/*****************************************************************************/
+
+static guint
+_ref_string_hash (gconstpointer ptr)
+{
+	const RefString *a = ptr;
+	NMHashState h;
+
+	nm_hash_init (&h, 1463435489u);
+	nm_hash_update (&h, a->r.str, a->r.len);
+	return nm_hash_complete (&h);
+}
+
+static gboolean
+_ref_string_equal (gconstpointer pa, gconstpointer pb)
+{
+	const RefString *a = pa;
+	const RefString *b = pb;
+
+	return    a->r.len == b->r.len
+	       && memcmp (a->r.str, b->r.str, a->r.len) == 0;
+}
+
+/*****************************************************************************/
+
+static void
+_ASSERT (const RefString *rstr0)
+{
+#if NM_MORE_ASSERTS
+	int r;
+
+	nm_assert (rstr0);
+
+	G_LOCK (gl_lock);
+	r = g_atomic_int_get (&rstr0->ref_count);
+
+	nm_assert (r > 0);
+	nm_assert (r < G_MAXINT);
+
+	nm_assert (rstr0 == g_hash_table_lookup (gl_hash, rstr0));
+	G_UNLOCK (gl_lock);
+#endif
+}
+
+/**
+ * nm_ref_string_new_len:
+ * @cstr: the string to intern. Must contain @len bytes.
+ *   If @len is zero, @cstr may be %NULL. Note that it is
+ *   accetable that the string contains a NUL character
+ *   within the first @len bytes. That is, the string is
+ *   not treated as a NUL terminated string, but as binary.
+ *   Also, contrary to strncpy(), this will read all the
+ *   first @len bytes. It won't stop at the first NUL.
+ * @len: the length of the string (usually there is no NUL character
+ *   within the first @len bytes, but that would be acceptable as well
+ *   to add binary data).
+ *
+ * Note that the resulting NMRefString instance will always be NUL terminated
+ * (at position @len).
+ *
+ * Note that NMRefString are always interned/deduplicated. If such a string
+ * already exists, the existing instance will be refered and returned.
+ *
+ *
+ * Since all NMRefString are shared and interned, you may use
+ * pointer equality to compare them. Note that if a NMRefString contains
+ * a NUL character (meaning, if
+ *
+ *    strlen (nm_ref_string_get_str (str)) != nm_ref_string_get_len (str)
+ *
+ * ), then pointer in-equality does not mean that the NUL terminated strings
+ * are also unequal. In other words, for strings that contain NUL characters,
+ *
+ *    if (str1 != str2)
+ *       assert (!nm_streq0 (nm_ref_string_get_str (str1), nm_ref_string_get_str (str2)));
+ *
+ * might not hold!
+ *
+ *
+ * NMRefString is thread-safe.
+ *
+ * Returns: (transfer full): the interned string. This is
+ *   never %NULL, but note that %NULL is also a valid NMRefString.
+ *   The result must be unrefed with nm_ref_string_unref().
+ */
+NMRefString *
+nm_ref_string_new_len (const char *cstr, gsize len)
+{
+	RefString *rstr0;
+
+	G_LOCK (gl_lock);
+
+	if (G_UNLIKELY (!gl_hash)) {
+		gl_hash = g_hash_table_new_full (_ref_string_hash, _ref_string_equal, g_free, NULL);
+		rstr0 = NULL;
+	} else {
+		NMRefString rr_lookup = {
+			.len = len,
+			.str = cstr,
+		};
+
+		rstr0 = g_hash_table_lookup (gl_hash, &rr_lookup);
+	}
+
+	if (rstr0) {
+		nm_assert (({
+		               int r = g_atomic_int_get (&rstr0->ref_count);
+
+		               (r >= 0 && r < G_MAXINT);
+		            }));
+		g_atomic_int_inc (&rstr0->ref_count);
+	} else {
+		rstr0 = g_malloc (sizeof (RefString) + 1 + len);
+		rstr0->ref_count = 1;
+		*((gsize *) &rstr0->r.len) = len;
+		*((const char **) &rstr0->r.str) = rstr0->str_data;
+		if (len > 0)
+			memcpy (rstr0->str_data, cstr, len);
+		rstr0->str_data[len] = '\0';
+
+		if (!g_hash_table_add (gl_hash, rstr0))
+			nm_assert_not_reached ();
+	}
+
+	G_UNLOCK (gl_lock);
+
+	return &rstr0->r;
+}
+
+NMRefString *
+nm_ref_string_ref (NMRefString *rstr)
+{
+	RefString *const rstr0 = (RefString *) rstr;
+
+	if (!rstr)
+		return NULL;
+
+	_ASSERT (rstr0);
+
+	g_atomic_int_inc (&rstr0->ref_count);
+	return &rstr0->r;
+}
+
+void
+_nm_ref_string_unref_non_null (NMRefString *rstr)
+{
+	RefString *const rstr0 = (RefString *) rstr;
+
+	_ASSERT (rstr0);
+
+	if (G_LIKELY (!g_atomic_int_dec_and_test (&rstr0->ref_count)))
+		return;
+
+	G_LOCK (gl_lock);
+
+	/* in the fast-path above, we already decremented the ref-count to zero.
+	 * We need recheck that the ref-count is still zero. */
+
+	if (g_atomic_int_get (&rstr0->ref_count) == 0) {
+		if (!g_hash_table_remove (gl_hash, rstr0))
+			nm_assert_not_reached ();
+	} else {
+#if NM_MORE_ASSERTS > 5
+		nm_assert (g_hash_table_lookup (gl_hash, rstr0) == rstr0);
+#endif
+	}
+
+	G_UNLOCK (gl_lock);
+}
+
+/*****************************************************************************/
diff --git a/shared/nm-glib-aux/nm-ref-string.h b/shared/nm-glib-aux/nm-ref-string.h
new file mode 100644
index 00000000..c80b75ea
--- /dev/null
+++ b/shared/nm-glib-aux/nm-ref-string.h
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#ifndef __NM_REF_STRING_H__
+#define __NM_REF_STRING_H__
+
+/*****************************************************************************/
+
+typedef struct _NMRefString {
+	const char *const str;
+	const gsize len;
+} NMRefString;
+
+/*****************************************************************************/
+
+NMRefString *nm_ref_string_new_len (const char *cstr, gsize len);
+
+static inline NMRefString *
+nm_ref_string_new (const char *cstr)
+{
+	return   cstr
+	       ? nm_ref_string_new_len (cstr, strlen (cstr))
+	       : NULL;
+}
+
+NMRefString *nm_ref_string_ref (NMRefString *rstr);
+void _nm_ref_string_unref_non_null (NMRefString *rstr);
+
+static inline void
+nm_ref_string_unref (NMRefString *rstr)
+{
+	if (rstr)
+		_nm_ref_string_unref_non_null (rstr);
+}
+
+NM_AUTO_DEFINE_FCN_VOID0 (NMRefString *, _nm_auto_ref_string, _nm_ref_string_unref_non_null)
+#define nm_auto_ref_string nm_auto(_nm_auto_ref_string)
+
+/*****************************************************************************/
+
+static inline const char *
+nm_ref_string_get_str (NMRefString *rstr)
+{
+	return rstr ? rstr->str : NULL;
+}
+
+static inline gsize
+nm_ref_string_get_len (NMRefString *rstr)
+{
+	return rstr ? rstr->len : 0u;
+}
+
+static inline gboolean
+NM_IS_REF_STRING (const NMRefString *rstr)
+{
+#if NM_MORE_ASSERTS > 10
+	if (rstr) {
+		nm_auto_ref_string NMRefString *r2 = NULL;
+
+		r2 = nm_ref_string_new_len (rstr->str, rstr->len);
+		nm_assert (rstr == r2);
+	}
+#endif
+
+	/* Technically, %NULL is also a valid NMRefString (according to nm_ref_string_new(),
+	 * nm_ref_string_get_str() and nm_ref_string_unref()). However, NM_IS_REF_STRING()
+	 * does not think so. If callers want to allow %NULL, they need to check
+	 * separately. */
+	return !!rstr;
+}
+
+#endif /* __NM_REF_STRING_H__ */
diff --git a/shared/nm-glib-aux/nm-secret-utils.c b/shared/nm-glib-aux/nm-secret-utils.c
index aeb88877..5b0afe46 100644
--- a/shared/nm-glib-aux/nm-secret-utils.c
+++ b/shared/nm-glib-aux/nm-secret-utils.c
@@ -1,22 +1,7 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
- * (C) Copyright 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ * Copyright (C) 2015 - 2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-glib-aux/nm-secret-utils.h b/shared/nm-glib-aux/nm-secret-utils.h
index 0fd1ac8b..1b98b7e9 100644
--- a/shared/nm-glib-aux/nm-secret-utils.h
+++ b/shared/nm-glib-aux/nm-secret-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_SECRET_UTILS_H__
diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c
index 9bfd5ae2..8e1c8b58 100644
--- a/shared/nm-glib-aux/nm-shared-utils.c
+++ b/shared/nm-glib-aux/nm-shared-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2016 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -26,6 +11,7 @@
 #include <poll.h>
 #include <fcntl.h>
 #include <sys/syscall.h>
+#include <glib-unix.h>
 
 #include "nm-errno.h"
 
@@ -555,6 +541,102 @@ _nm_utils_ip4_prefix_to_netmask (guint32 prefix)
 	return prefix < 32 ? ~htonl(0xFFFFFFFF >> prefix) : 0xFFFFFFFF;
 }
 
+gconstpointer
+nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen)
+{
+	g_return_val_if_fail (dst, NULL);
+
+	switch (family) {
+	case AF_INET:
+		g_return_val_if_fail (plen <= 32, NULL);
+
+		if (!src) {
+			/* allow "self-assignment", by specifying %NULL as source. */
+			src = dst;
+		}
+
+		*((guint32 *) dst) = nm_utils_ip4_address_clear_host_address (*((guint32 *) src), plen);
+		break;
+	case AF_INET6:
+		nm_utils_ip6_address_clear_host_address (dst, src, plen);
+		break;
+	default:
+		g_return_val_if_reached (NULL);
+	}
+	return dst;
+}
+
+/* nm_utils_ip4_address_clear_host_address:
+ * @addr: source ip6 address
+ * @plen: prefix length of network
+ *
+ * returns: the input address, with the host address set to 0.
+ */
+in_addr_t
+nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen)
+{
+	return addr & _nm_utils_ip4_prefix_to_netmask (plen);
+}
+
+/* nm_utils_ip6_address_clear_host_address:
+ * @dst: destination output buffer, will contain the network part of the @src address
+ * @src: source ip6 address
+ * @plen: prefix length of network
+ *
+ * Note: this function is self assignment safe, to update @src inplace, set both
+ * @dst and @src to the same destination or set @src NULL.
+ */
+const struct in6_addr *
+nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen)
+{
+	g_return_val_if_fail (plen <= 128, NULL);
+	g_return_val_if_fail (dst, NULL);
+
+	if (!src)
+		src = dst;
+
+	if (plen < 128) {
+		guint nbytes = plen / 8;
+		guint nbits = plen % 8;
+
+		if (nbytes && dst != src)
+			memcpy (dst, src, nbytes);
+		if (nbits) {
+			dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
+			nbytes++;
+		}
+		if (nbytes <= 15)
+			memset (&dst->s6_addr[nbytes], 0, 16 - nbytes);
+	} else if (src != dst)
+		*dst = *src;
+
+	return dst;
+}
+
+int
+nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen)
+{
+	int nbytes;
+	guint8 va, vb, m;
+
+	if (plen >= 128)
+		NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, sizeof (struct in6_addr));
+	else {
+		nbytes = plen / 8;
+		if (nbytes)
+			NM_CMP_DIRECT_MEMCMP (addr_a, addr_b, nbytes);
+
+		plen = plen % 8;
+		if (plen != 0) {
+			m = ~((1 << (8 - plen)) - 1);
+			va = ((((const guint8 *) addr_a))[nbytes]) & m;
+			vb = ((((const guint8 *) addr_b))[nbytes]) & m;
+			NM_CMP_DIRECT (va, vb);
+		}
+	}
+	return 0;
+}
+
 /**
  * _nm_utils_ip4_get_default_prefix:
  * @ip: an IPv4 address (in network byte order)
@@ -601,11 +683,80 @@ nm_utils_ip_is_site_local (int addr_family,
 
 /*****************************************************************************/
 
+static gboolean
+_parse_legacy_addr4 (const char *text, in_addr_t *out_addr)
+{
+	gs_free char *s_free = NULL;
+	struct in_addr a1;
+	guint8 bin[sizeof (a1)];
+	char *s;
+	int i;
+
+	if (inet_aton (text, &a1) != 1)
+		return FALSE;
+
+	/* OK, inet_aton() accepted the format. That's good, because we want
+	 * to accept IPv4 addresses in octal format, like 255.255.000.000.
+	 * That's what "legacy" means here. inet_pton() doesn't accept those.
+	 *
+	 * But inet_aton() also ignores trailing garbage and formats with fewer than
+	 * 4 digits. That is just too crazy and we don't do that. Perform additional checks
+	 * and reject some forms that inet_aton() accepted.
+	 *
+	 * Note that we still should (of course) accept everything that inet_pton()
+	 * accepts. However this code never gets called if inet_pton() succeeds
+	 * (see below, aside the assertion code). */
+
+	if (NM_STRCHAR_ANY (text, ch, (   !(ch >= '0' && ch <= '9')
+	                               && !NM_IN_SET (ch, '.', 'x')))) {
+		/* We only accepts '.', digits, and 'x' for "0x". */
+		return FALSE;
+	}
+
+	s = nm_memdup_maybe_a (300, text, strlen (text) + 1, &s_free);
+
+	for (i = 0; i < G_N_ELEMENTS (bin); i++) {
+		char *current_token = s;
+		gint32 v;
+
+		s = strchr (s, '.');
+		if (s) {
+			s[0] = '\0';
+			s++;
+		}
+
+		if ((i == G_N_ELEMENTS (bin) - 1) != (s == NULL)) {
+			/* Exactly for the last digit, we expect to have no more following token.
+			 * But this isn't the case. Abort. */
+			return FALSE;
+		}
+
+		v = _nm_utils_ascii_str_to_int64 (current_token, 0, 0, 0xFF, -1);
+		if (v == -1) {
+			/* we do accept octal and hex (even with leading "0x"). But something
+			 * about this token is wrong. */
+			return FALSE;
+		}
+
+		bin[i] = v;
+	}
+
+	if (memcmp (bin, &a1, sizeof (bin)) != 0) {
+		/* our parsing did not agree with what inet_aton() gave. Something
+		 * is wrong. Abort. */
+		return FALSE;
+	}
+
+	*out_addr = a1.s_addr;
+	return TRUE;
+}
+
 gboolean
-nm_utils_parse_inaddr_bin (int addr_family,
-                           const char *text,
-                           int *out_addr_family,
-                           gpointer out_addr)
+nm_utils_parse_inaddr_bin_full (int addr_family,
+                                gboolean accept_legacy,
+                                const char *text,
+                                int *out_addr_family,
+                                gpointer out_addr)
 {
 	NMIPAddr addrbin;
 
@@ -617,8 +768,26 @@ nm_utils_parse_inaddr_bin (int addr_family,
 	} else
 		g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), FALSE);
 
-	if (inet_pton (addr_family, text, &addrbin) != 1)
-		return FALSE;
+	if (inet_pton (addr_family, text, &addrbin) != 1) {
+		if (   accept_legacy
+		    && addr_family == AF_INET
+		    && _parse_legacy_addr4 (text, &addrbin.addr4)) {
+			/* The address is in some legacy format which inet_aton() accepts, but not inet_pton().
+			 * Most likely octal digits (leading zeros). We accept the address. */
+		} else
+			return FALSE;
+	}
+
+#if NM_MORE_ASSERTS > 10
+	if (addr_family == AF_INET) {
+		in_addr_t a;
+
+		/* The legacy parser should accept everything that inet_pton() accepts too. Meaning,
+		 * it should strictly parse *more* formats. And of course, parse it the same way. */
+		nm_assert (_parse_legacy_addr4 (text, &a));
+		nm_assert (addrbin.addr4 == a);
+	}
+#endif
 
 	NM_SET_OUT (out_addr_family, addr_family);
 	if (out_addr)
@@ -2302,6 +2471,44 @@ nm_utils_hash_keys_to_array (GHashTable *hash,
 	return keys;
 }
 
+gpointer *
+nm_utils_hash_values_to_array (GHashTable *hash,
+                               GCompareDataFunc compare_func,
+                               gpointer user_data,
+                               guint *out_len)
+{
+	GHashTableIter iter;
+	gpointer value;
+	gpointer *arr;
+	guint i, len;
+
+	if (   !hash
+	    || (len = g_hash_table_size (hash)) == 0u) {
+		NM_SET_OUT (out_len, 0);
+		return NULL;
+	}
+
+	arr = g_new (gpointer, ((gsize) len) + 1);
+	i = 0;
+	g_hash_table_iter_init (&iter, hash);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &value))
+		arr[i++] = value;
+
+	nm_assert (i == len);
+	arr[len] = NULL;
+
+	if (   len > 1
+	    && compare_func) {
+		g_qsort_with_data (arr,
+		                   len,
+		                   sizeof (gpointer),
+		                   compare_func,
+		                   user_data);
+	}
+
+	return arr;
+}
+
 gboolean
 nm_utils_hashtable_same_keys (const GHashTable *a,
                               const GHashTable *b)
@@ -2370,6 +2577,10 @@ nm_utils_strv_make_deep_copied_n (const char **strv, gsize len)
  *   is negative or zero (in which case %NULL will be returned).
  * @len: the length of strings in @str. If negative, strv is assumed
  *   to be a NULL terminated array.
+ * @deep_copied: if %TRUE, clones the individual strings. In that case,
+ *   the returned array must be freed with g_strfreev(). Otherwise, the
+ *   strings themself are not copied. You must take care of who owns the
+ *   strings yourself.
  *
  * Like g_strdupv(), with two differences:
  *
@@ -2384,10 +2595,13 @@ nm_utils_strv_make_deep_copied_n (const char **strv, gsize len)
  * array with g_strfreev(). Allowing that would be error prone.
  *
  * Returns: (transfer full): a clone of the strv array. Always
- *   %NULL terminated.
+ *   %NULL terminated. Depending on @deep_copied, the strings are
+ *   cloned or not.
  */
 char **
-nm_utils_strv_dup (gpointer strv, gssize len)
+nm_utils_strv_dup (gpointer strv,
+                   gssize len,
+                   gboolean deep_copied)
 {
 	gsize i, l;
 	char **v;
@@ -2415,7 +2629,10 @@ nm_utils_strv_dup (gpointer strv, gssize len)
 			g_return_val_if_reached (v);
 		}
 
-		v[i] = g_strdup (src[i]);
+		if (deep_copied)
+			v[i] = g_strdup (src[i]);
+		else
+			v[i] = (char *) src[i];
 	}
 	v[l] = NULL;
 	return v;
@@ -2657,7 +2874,9 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid)
 
 	g_return_val_if_fail (pid > 0, 0);
 
-	nm_sprintf_buf (filename, "/proc/%"G_GUINT64_FORMAT"/stat", (guint64) pid);
+	G_STATIC_ASSERT_EXPR (sizeof (GPid) >= sizeof (pid_t));
+
+	nm_sprintf_buf (filename, "/proc/%"G_PID_FORMAT"/stat", (GPid) pid);
 
 	if (!g_file_get_contents (filename, &contents, &length, NULL))
 		goto fail;
@@ -2845,6 +3064,24 @@ nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b)
 	}
 }
 
+char *
+nm_utils_g_slist_strlist_join (const GSList *a, const char *separator)
+{
+	GString *str = NULL;
+
+	if (!a)
+		return NULL;
+
+	for (; a; a = a->next) {
+		if (!str)
+			str = g_string_new (NULL);
+		else
+			g_string_append (str, separator);
+		g_string_append (str, a->data);
+	}
+	return g_string_free (str, FALSE);
+}
+
 /*****************************************************************************/
 
 gpointer
@@ -3275,3 +3512,478 @@ nm_utils_gvariant_vardict_filter_drop_one (GVariant *src,
 	                                         _gvariant_vardict_filter_drop_one,
 	                                         (gpointer) key);
 }
+
+/*****************************************************************************/
+
+static gboolean
+debug_key_matches (const char *key,
+                   const char *token,
+                   guint        length)
+{
+	/* may not call GLib functions: see note in g_parse_debug_string() */
+	for (; length; length--, key++, token++) {
+		char k = (*key   == '_') ? '-' : g_ascii_tolower (*key  );
+		char t = (*token == '_') ? '-' : g_ascii_tolower (*token);
+
+		if (k != t)
+			return FALSE;
+	}
+
+	return *key == '\0';
+}
+
+/**
+ * nm_utils_parse_debug_string:
+ * @string: the string to parse
+ * @keys: the debug keys
+ * @nkeys: number of entries in @keys
+ *
+ * Similar to g_parse_debug_string(), but does not special
+ * case "help" or "all".
+ *
+ * Returns: the flags
+ */
+guint
+nm_utils_parse_debug_string (const char *string,
+                             const GDebugKey *keys,
+                             guint nkeys)
+{
+	guint i;
+	guint result = 0;
+	const char *q;
+
+	if (string == NULL)
+		return 0;
+
+	while (*string) {
+		q = strpbrk (string, ":;, \t");
+		if (!q)
+			q = string + strlen (string);
+
+		for (i = 0; i < nkeys; i++) {
+			if (debug_key_matches (keys[i].key, string, q - string))
+				result |= keys[i].value;
+		}
+
+		string = q;
+		if (*string)
+			string++;
+	}
+
+	return result;
+}
+
+/*****************************************************************************/
+
+GSource *
+nm_g_idle_source_new (int priority,
+                      GSourceFunc func,
+                      gpointer user_data,
+                      GDestroyNotify destroy_notify)
+{
+	GSource *source;
+
+	source = g_idle_source_new ();
+	if (priority != G_PRIORITY_DEFAULT)
+		g_source_set_priority (source, priority);
+	g_source_set_callback (source, func, user_data, destroy_notify);
+	return source;
+}
+
+GSource *
+nm_g_timeout_source_new (guint timeout_ms,
+                         int priority,
+                         GSourceFunc func,
+                         gpointer user_data,
+                         GDestroyNotify destroy_notify)
+{
+	GSource *source;
+
+	source = g_timeout_source_new (timeout_ms);
+	if (priority != G_PRIORITY_DEFAULT)
+		g_source_set_priority (source, priority);
+	g_source_set_callback (source, func, user_data, destroy_notify);
+	return source;
+}
+
+GSource *
+nm_g_unix_signal_source_new (int signum,
+                             int priority,
+                             GSourceFunc handler,
+                             gpointer user_data,
+                             GDestroyNotify notify)
+{
+	GSource *source;
+
+	source = g_unix_signal_source_new (signum);
+
+	if (priority != G_PRIORITY_DEFAULT)
+		g_source_set_priority (source, priority);
+	g_source_set_callback (source, handler, user_data, notify);
+	return source;
+}
+
+/*****************************************************************************/
+
+#define _CTX_LOG(fmt, ...) \
+	G_STMT_START { \
+		if (FALSE) { \
+			gint64 _ts = g_get_monotonic_time () / 100; \
+			\
+			g_printerr (">>>> [%"G_GINT64_FORMAT".%05"G_GINT64_FORMAT"] [src:%p]: " fmt "\n", \
+			            _ts / 10000, \
+			            _ts % 10000, \
+			            (ctx_src), \
+			            ##__VA_ARGS__); \
+		} \
+	} G_STMT_END
+
+typedef struct {
+	int fd;
+	guint events;
+	guint registered_events;
+	union {
+		int one;
+		int *many;
+	} idx;
+	gpointer tag;
+	bool stale:1;
+	bool has_many_idx:1;
+} PollData;
+
+typedef struct {
+	GSource source;
+	GMainContext *context;
+	GHashTable *fds;
+	GPollFD *fds_arr;
+	int fds_len;
+	int max_priority;
+	bool acquired:1;
+} CtxIntegSource;
+
+static void
+_poll_data_free (gpointer user_data)
+{
+	PollData *poll_data = user_data;
+
+	if (poll_data->has_many_idx)
+		g_free (poll_data->idx.many);
+	nm_g_slice_free (poll_data);
+}
+
+static void
+_ctx_integ_source_reacquire (CtxIntegSource *ctx_src)
+{
+	if (G_LIKELY (   ctx_src->acquired
+	              && g_main_context_is_owner (ctx_src->context)))
+		return;
+
+	/* the parent context now iterates on a different thread.
+	 * We need to release and reacquire the inner context. */
+
+	if (ctx_src->acquired)
+		g_main_context_release (ctx_src->context);
+
+	if (G_UNLIKELY (!g_main_context_acquire (ctx_src->context))) {
+		/* Nobody is supposed to reacquire the context while we use it. This is a bug
+		 * of the user. */
+		ctx_src->acquired = FALSE;
+		g_return_if_reached ();
+	}
+	ctx_src->acquired = TRUE;
+}
+
+static gboolean
+_ctx_integ_source_prepare (GSource *source,
+                           int *out_timeout)
+{
+	CtxIntegSource *ctx_src = ((CtxIntegSource *) source);
+	int max_priority;
+	int timeout = -1;
+	gboolean any_ready;
+	int fds_allocated;
+	int fds_len_old;
+	gs_free GPollFD *fds_arr_old = NULL;
+	GHashTableIter h_iter;
+	PollData *poll_data;
+	gboolean fds_changed;
+	int i;
+
+	_CTX_LOG ("prepare...");
+
+	_ctx_integ_source_reacquire (ctx_src);
+
+	any_ready = g_main_context_prepare (ctx_src->context, &max_priority);
+
+	fds_arr_old = g_steal_pointer (&ctx_src->fds_arr);
+	fds_len_old = ctx_src->fds_len;
+
+	fds_allocated = NM_MAX (1, fds_len_old); /* there is at least the wakeup's FD */
+	ctx_src->fds_arr = g_new (GPollFD, fds_allocated);
+
+	while ((ctx_src->fds_len = g_main_context_query (ctx_src->context,
+	                                                 max_priority,
+	                                                 &timeout,
+	                                                 ctx_src->fds_arr,
+	                                                 fds_allocated)) > fds_allocated) {
+		fds_allocated = ctx_src->fds_len;
+		g_free (ctx_src->fds_arr);
+		ctx_src->fds_arr = g_new (GPollFD, fds_allocated);
+	}
+
+	fds_changed = FALSE;
+	if (fds_len_old != ctx_src->fds_len)
+		fds_changed = TRUE;
+	else {
+		for (i = 0; i < ctx_src->fds_len; i++) {
+			if (   fds_arr_old[i].fd != ctx_src->fds_arr[i].fd
+			    || fds_arr_old[i].events != ctx_src->fds_arr[i].events) {
+				fds_changed = TRUE;
+				break;
+			}
+		}
+	}
+
+	if (G_UNLIKELY (fds_changed)) {
+
+		g_hash_table_iter_init (&h_iter, ctx_src->fds);
+		while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL))
+			poll_data->stale = TRUE;
+
+		for (i = 0; i < ctx_src->fds_len; i++) {
+			const GPollFD *fd = &ctx_src->fds_arr[i];
+
+			poll_data = g_hash_table_lookup (ctx_src->fds, &fd->fd);
+
+			if (G_UNLIKELY (!poll_data)) {
+				poll_data = g_slice_new (PollData);
+				*poll_data = (PollData) {
+					.fd                = fd->fd,
+					.idx.one           = i,
+					.has_many_idx      = FALSE,
+					.events            = fd->events,
+					.registered_events = 0,
+					.tag               = NULL,
+					.stale             = FALSE,
+				};
+				g_hash_table_add (ctx_src->fds, poll_data);
+				nm_assert (poll_data == g_hash_table_lookup (ctx_src->fds, &fd->fd));
+				continue;
+			}
+
+			if (G_LIKELY (poll_data->stale)) {
+				if (poll_data->has_many_idx) {
+					g_free (poll_data->idx.many);
+					poll_data->has_many_idx = FALSE;
+				}
+				poll_data->events = fd->events;
+				poll_data->idx.one = i;
+				poll_data->stale = FALSE;
+				continue;
+			}
+
+			/* How odd. We have duplicate FDs. In fact, currently g_main_context_query() always
+			 * coalesces the FDs and this cannot happen. However, that is not documented behavior,
+			 * so we should not rely on that. So we need to keep a list of indexes... */
+			poll_data->events |= fd->events;
+			if (!poll_data->has_many_idx) {
+				int idx0;
+
+				idx0 = poll_data->idx.one;
+				poll_data->has_many_idx = TRUE;
+				poll_data->idx.many = g_new (int, 4);
+				poll_data->idx.many[0] = 2; /* number allocated */
+				poll_data->idx.many[1] = 2; /* number used */
+				poll_data->idx.many[2] = idx0;
+				poll_data->idx.many[3] = i;
+			} else {
+				if (poll_data->idx.many[0] == poll_data->idx.many[1]) {
+					poll_data->idx.many[0] *= 2;
+					poll_data->idx.many = g_realloc (poll_data->idx.many, sizeof (int) * (2 + poll_data->idx.many[0]));
+				}
+				poll_data->idx.many[2 + poll_data->idx.many[1]] = i;
+				poll_data->idx.many[1]++;
+			}
+
+		}
+
+		g_hash_table_iter_init (&h_iter, ctx_src->fds);
+		while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) {
+			if (poll_data->stale) {
+				nm_assert (poll_data->tag);
+				nm_assert (poll_data->events == poll_data->registered_events);
+				_CTX_LOG ("prepare: remove poll fd=%d, events=0x%x", poll_data->fd, poll_data->events);
+				g_source_remove_unix_fd (&ctx_src->source, poll_data->tag);
+				g_hash_table_iter_remove (&h_iter);
+				continue;
+			}
+			if (!poll_data->tag) {
+				_CTX_LOG ("prepare: add poll fd=%d, events=0x%x", poll_data->fd, poll_data->events);
+				poll_data->registered_events = poll_data->events;
+				poll_data->tag = g_source_add_unix_fd (&ctx_src->source, poll_data->fd, poll_data->registered_events);
+				continue;
+			}
+			if (poll_data->registered_events != poll_data->events) {
+				_CTX_LOG ("prepare: update poll fd=%d, events=0x%x", poll_data->fd, poll_data->events);
+				poll_data->registered_events = poll_data->events;
+				g_source_modify_unix_fd (&ctx_src->source, poll_data->tag, poll_data->registered_events);
+			}
+		}
+	}
+
+	NM_SET_OUT (out_timeout, timeout);
+	ctx_src->max_priority = max_priority;
+
+	_CTX_LOG ("prepare: done, any-ready=%d, timeout=%d, max-priority=%d", any_ready, timeout, max_priority);
+
+	/* we always need to poll, because we have some file descriptors. */
+	return FALSE;
+}
+
+static gboolean
+_ctx_integ_source_check (GSource *source)
+{
+	CtxIntegSource *ctx_src = ((CtxIntegSource *) source);
+	GHashTableIter h_iter;
+	gboolean some_ready;
+	PollData *poll_data;
+
+	nm_assert (ctx_src->context);
+
+	_CTX_LOG ("check");
+
+	_ctx_integ_source_reacquire (ctx_src);
+
+	g_hash_table_iter_init (&h_iter, ctx_src->fds);
+	while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) {
+		guint revents;
+
+		revents = g_source_query_unix_fd (&ctx_src->source, poll_data->tag);
+		if (G_UNLIKELY (poll_data->has_many_idx)) {
+			int num = poll_data->idx.many[1];
+			int *p_idx = &poll_data->idx.many[2];
+
+			for (; num > 0; num--, p_idx++)
+				ctx_src->fds_arr[*p_idx].revents = revents;
+		} else
+			ctx_src->fds_arr[poll_data->idx.one].revents = revents;
+	}
+
+	some_ready = g_main_context_check (ctx_src->context,
+	                                   ctx_src->max_priority,
+	                                   ctx_src->fds_arr,
+	                                   ctx_src->fds_len);
+
+	_CTX_LOG ("check (some-ready=%d)...", some_ready);
+
+	return some_ready;
+}
+
+static gboolean
+_ctx_integ_source_dispatch (GSource *source,
+                            GSourceFunc callback,
+                            gpointer user_data)
+{
+	CtxIntegSource *ctx_src = ((CtxIntegSource *) source);
+
+	nm_assert (ctx_src->context);
+
+	_ctx_integ_source_reacquire (ctx_src);
+
+	_CTX_LOG ("dispatch");
+
+	g_main_context_dispatch (ctx_src->context);
+
+	return G_SOURCE_CONTINUE;
+}
+
+static void
+_ctx_integ_source_finalize (GSource *source)
+{
+	CtxIntegSource *ctx_src = ((CtxIntegSource *) source);
+	GHashTableIter h_iter;
+	PollData *poll_data;
+
+	g_return_if_fail (ctx_src->context);
+
+	_CTX_LOG ("finalize...");
+
+	g_hash_table_iter_init (&h_iter, ctx_src->fds);
+	while (g_hash_table_iter_next (&h_iter, (gpointer *) &poll_data, NULL)) {
+		nm_assert (poll_data->tag);
+		_CTX_LOG ("prepare: remove poll fd=%d, events=0x%x", poll_data->fd, poll_data->events);
+		g_source_remove_unix_fd (&ctx_src->source, poll_data->tag);
+		g_hash_table_iter_remove (&h_iter);
+	}
+
+	nm_clear_pointer (&ctx_src->fds, g_hash_table_unref);
+	nm_clear_g_free (&ctx_src->fds_arr);
+	ctx_src->fds_len = 0;
+
+	if (ctx_src->acquired) {
+		ctx_src->acquired = FALSE;
+		g_main_context_release (ctx_src->context);
+	}
+
+	nm_clear_pointer (&ctx_src->context, g_main_context_unref);
+}
+
+static GSourceFuncs ctx_integ_source_funcs = {
+	.prepare  = _ctx_integ_source_prepare,
+	.check    = _ctx_integ_source_check,
+	.dispatch = _ctx_integ_source_dispatch,
+	.finalize = _ctx_integ_source_finalize,
+};
+
+/**
+ * nm_utils_g_main_context_create_integrate_source:
+ * @inner_context: the inner context that will be integrated to an
+ *   outer #GMainContext.
+ *
+ * By integrating the inner context with an outer context, when iterating the outer
+ * context sources on the inner context will be dispatched. Note that while the
+ * created source exists, the @inner_context will be acquired. The user gets restricted
+ * what to do with the inner context. In particular while the inner context is integrated,
+ * the user should not acquire the inner context again or explicitly iterate it. What
+ * the user of course still can (and wants to) do is attaching new sources to the inner
+ * context.
+ *
+ * Note that GSource has a priority. While each context dispatches events based on
+ * their source's priorities, the outer context dispatches to the inner context
+ * only with one priority (the priority of the created source). That is, the sources
+ * from the two contexts are kept separate and are not sorted by their priorities.
+ *
+ * Returns: a newly created GSource that should be attached to the
+ *   outer context.
+ */
+GSource *
+nm_utils_g_main_context_create_integrate_source (GMainContext *inner_context)
+{
+	CtxIntegSource *ctx_src;
+
+	g_return_val_if_fail (inner_context, NULL);
+
+	if (!g_main_context_acquire (inner_context)) {
+		/* We require to acquire the context while it's integrated. We need to keep it acquired
+		 * for the entire duration.
+		 *
+		 * This is also necessary because g_source_attach() only wakes up the context, if
+		 * the context is currently acquired. */
+		g_return_val_if_reached (NULL);
+	}
+
+	ctx_src = (CtxIntegSource *) g_source_new (&ctx_integ_source_funcs, sizeof (CtxIntegSource));
+
+	g_source_set_name (&ctx_src->source, "ContextIntegrateSource");
+
+	ctx_src->context = g_main_context_ref (inner_context);
+	ctx_src->fds = g_hash_table_new_full (nm_pint_hash, nm_pint_equals, _poll_data_free, NULL);
+	ctx_src->fds_len = 0;
+	ctx_src->fds_arr = NULL;
+	ctx_src->acquired = TRUE;
+	ctx_src->max_priority = G_MAXINT;
+
+	_CTX_LOG ("create new integ-source for %p", inner_context);
+
+	return &ctx_src->source;
+}
diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h
index d9c430d4..3bd1afae 100644
--- a/shared/nm-glib-aux/nm-shared-utils.h
+++ b/shared/nm-glib-aux/nm-shared-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2016 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_SHARED_UTILS_H__
@@ -104,6 +89,16 @@ nm_utils_addr_family_to_size (int addr_family)
 	g_return_val_if_reached (0);
 }
 
+static inline int
+nm_utils_addr_family_from_size (gsize len)
+{
+	switch (len) {
+	case sizeof (in_addr_t):       return AF_INET;
+	case sizeof (struct in6_addr): return AF_INET6;
+	}
+	return AF_UNSPEC;
+}
+
 #define nm_assert_addr_family(addr_family) \
 	nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6))
 
@@ -537,15 +532,29 @@ nm_utils_escaped_tokens_escape_gstr (const char *str,
 guint32 _nm_utils_ip4_prefix_to_netmask (guint32 prefix);
 guint32 _nm_utils_ip4_get_default_prefix (guint32 ip);
 
+gconstpointer          nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen);
+in_addr_t              nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen);
+const struct in6_addr *nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen);
+int nm_utils_ip6_address_same_prefix_cmp (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen);
+
 gboolean nm_utils_ip_is_site_local (int addr_family,
                                     const void *address);
 
 /*****************************************************************************/
 
-gboolean nm_utils_parse_inaddr_bin  (int addr_family,
-                                     const char *text,
-                                     int *out_addr_family,
-                                     gpointer out_addr);
+gboolean nm_utils_parse_inaddr_bin_full (int addr_family,
+                                         gboolean accept_legacy,
+                                         const char *text,
+                                         int *out_addr_family,
+                                         gpointer out_addr);
+static inline gboolean
+nm_utils_parse_inaddr_bin (int addr_family,
+                           const char *text,
+                           int *out_addr_family,
+                           gpointer out_addr)
+{
+	return nm_utils_parse_inaddr_bin_full (addr_family, FALSE, text, out_addr_family, out_addr);
+}
 
 gboolean nm_utils_parse_inaddr (int addr_family,
                                 const char *text,
@@ -652,8 +661,9 @@ _nm_g_slice_free_fcn_define (8)
 _nm_g_slice_free_fcn_define (10)
 _nm_g_slice_free_fcn_define (12)
 _nm_g_slice_free_fcn_define (16)
+_nm_g_slice_free_fcn_define (32)
 
-#define _nm_g_slice_free_fcn1(mem_size) \
+#define nm_g_slice_free_fcn1(mem_size) \
 	({ \
 		void (*_fcn) (gpointer); \
 		\
@@ -666,7 +676,8 @@ _nm_g_slice_free_fcn_define (16)
 		                      || ((mem_size) ==  8) \
 		                      || ((mem_size) == 10) \
 		                      || ((mem_size) == 12) \
-		                      || ((mem_size) == 16)); \
+		                      || ((mem_size) == 16) \
+		                      || ((mem_size) == 32)); \
 		switch ((mem_size)) { \
 		case  1: _fcn = _nm_g_slice_free_fcn_1;  break; \
 		case  2: _fcn = _nm_g_slice_free_fcn_2;  break; \
@@ -675,6 +686,7 @@ _nm_g_slice_free_fcn_define (16)
 		case 10: _fcn = _nm_g_slice_free_fcn_10; break; \
 		case 12: _fcn = _nm_g_slice_free_fcn_12; break; \
 		case 16: _fcn = _nm_g_slice_free_fcn_16; break; \
+		case 32: _fcn = _nm_g_slice_free_fcn_32; break; \
 		default: g_assert_not_reached (); _fcn = NULL; break; \
 		} \
 		_fcn; \
@@ -688,14 +700,38 @@ _nm_g_slice_free_fcn_define (16)
  * Returns: a function pointer with GDestroyNotify signature
  *   for g_slice_free(type,*).
  *
- * Only certain types are implemented. You'll get an assertion
- * using the wrong type. */
-#define nm_g_slice_free_fcn(type) (_nm_g_slice_free_fcn1 (sizeof (type)))
+ * Only certain types are implemented. You'll get a compile time
+ * error for the wrong types. */
+#define nm_g_slice_free_fcn(type) (nm_g_slice_free_fcn1 (sizeof (type)))
 
 #define nm_g_slice_free_fcn_gint64 (nm_g_slice_free_fcn (gint64))
 
 /*****************************************************************************/
 
+static inline void
+nm_g_set_error_take (GError **error, GError *error_take)
+{
+	if (!error_take)
+		g_return_if_reached ();
+	if (!error) {
+		g_error_free (error_take);
+		return;
+	}
+	if (*error) {
+		g_error_free (error_take);
+		g_return_if_reached ();
+	}
+	*error = error_take;
+}
+
+#define nm_g_set_error_take_lazy(error, error_take_lazy) \
+	G_STMT_START { \
+		GError **_error = (error); \
+		\
+		if (_error) \
+			nm_g_set_error_take (_error, (error_take_lazy)); \
+	} G_STMT_END
+
 /**
  * NMUtilsError:
  * @NM_UTILS_ERROR_UNKNOWN: unknown or unclassified error
@@ -750,6 +786,17 @@ GQuark nm_utils_error_quark (void);
 void nm_utils_error_set_cancelled (GError **error,
                                    gboolean is_disposing,
                                    const char *instance_name);
+
+static inline GError *
+nm_utils_error_new_cancelled (gboolean is_disposing,
+                              const char *instance_name)
+{
+	GError *error = NULL;
+
+	nm_utils_error_set_cancelled (&error, is_disposing, instance_name);
+	return error;
+}
+
 gboolean nm_utils_error_is_cancelled (GError *error,
                                       gboolean consider_is_disposing);
 
@@ -792,6 +839,11 @@ nm_utils_error_set_literal (GError **error, int error_code, const char *literal)
 		                                   sizeof (_bstrerr))); \
 	} G_STMT_END
 
+#define nm_utils_error_new(error_code, ...) \
+	(   (NM_NARG (__VA_ARGS__) == 1) \
+	 ? g_error_new_literal (NM_UTILS_ERROR, (error_code), _NM_UTILS_MACRO_FIRST (__VA_ARGS__)) \
+	 : g_error_new         (NM_UTILS_ERROR, (error_code), __VA_ARGS__))
+
 /*****************************************************************************/
 
 gboolean nm_g_object_set_property (GObject *object,
@@ -906,6 +958,82 @@ nm_g_variant_unref_floating (GVariant *var)
 		g_variant_unref (var);
 }
 
+static inline void
+nm_g_source_destroy_and_unref (GSource *source)
+{
+	g_source_destroy (source);
+	g_source_unref (source);
+}
+
+#define nm_clear_g_source_inst(ptr) (nm_clear_pointer ((ptr), nm_g_source_destroy_and_unref))
+
+NM_AUTO_DEFINE_FCN0 (GSource *, _nm_auto_destroy_and_unref_gsource, nm_g_source_destroy_and_unref);
+#define nm_auto_destroy_and_unref_gsource nm_auto(_nm_auto_destroy_and_unref_gsource)
+
+NM_AUTO_DEFINE_FCN0 (GMainContext *, _nm_auto_pop_gmaincontext, g_main_context_pop_thread_default)
+#define nm_auto_pop_gmaincontext nm_auto (_nm_auto_pop_gmaincontext)
+
+GSource *nm_g_idle_source_new (int priority,
+                               GSourceFunc func,
+                               gpointer user_data,
+                               GDestroyNotify destroy_notify);
+
+GSource *nm_g_timeout_source_new (guint timeout_ms,
+                                  int priority,
+                                  GSourceFunc func,
+                                  gpointer user_data,
+                                  GDestroyNotify destroy_notify);
+
+GSource *nm_g_unix_signal_source_new (int signum,
+                                      int priority,
+                                      GSourceFunc handler,
+                                      gpointer user_data,
+                                      GDestroyNotify notify);
+
+static inline GSource *
+nm_g_source_attach (GSource *source,
+                    GMainContext *context)
+{
+	g_source_attach (source, context);
+	return source;
+}
+
+NM_AUTO_DEFINE_FCN0 (GMainContext *, _nm_auto_unref_gmaincontext, g_main_context_unref)
+#define nm_auto_unref_gmaincontext nm_auto (_nm_auto_unref_gmaincontext)
+
+static inline GMainContext *
+nm_g_main_context_push_thread_default (GMainContext *context)
+{
+	/* This function is to work together with nm_auto_pop_gmaincontext. */
+	if (G_UNLIKELY (!context))
+		context = g_main_context_default ();
+	g_main_context_push_thread_default (context);
+	return context;
+}
+
+static inline GMainContext *
+nm_g_main_context_push_thread_default_if_necessary (GMainContext *context)
+{
+	GMainContext *cur_context;
+
+	cur_context = g_main_context_get_thread_default ();
+	if (cur_context == context)
+		return NULL;
+
+	if (G_UNLIKELY (!cur_context)) {
+		cur_context = g_main_context_default ();
+		if (cur_context == context)
+			return NULL;
+	} else if (G_UNLIKELY (!context)) {
+		context = g_main_context_default ();
+		if (cur_context == context)
+			return NULL;
+	}
+
+	g_main_context_push_thread_default (context);
+	return context;
+}
+
 /*****************************************************************************/
 
 static inline int
@@ -966,6 +1094,11 @@ gpointer *nm_utils_hash_keys_to_array (GHashTable *hash,
                                        gpointer user_data,
                                        guint *out_len);
 
+gpointer *nm_utils_hash_values_to_array (GHashTable *hash,
+                                         GCompareDataFunc compare_func,
+                                         gpointer user_data,
+                                         guint *out_len);
+
 static inline const char **
 nm_utils_strdict_get_keys (const GHashTable *hash,
                            gboolean sorted,
@@ -990,7 +1123,9 @@ nm_utils_strv_make_deep_copied_nonnull (const char **strv)
 	return nm_utils_strv_make_deep_copied (strv) ?: g_new0 (char *, 1);
 }
 
-char **nm_utils_strv_dup (gpointer strv, gssize len);
+char **nm_utils_strv_dup (gpointer strv,
+                          gssize len,
+                          gboolean deep_copied);
 
 /*****************************************************************************/
 
@@ -999,6 +1134,8 @@ GSList *nm_utils_g_slist_find_str (const GSList *list,
 
 int nm_utils_g_slist_strlist_cmp (const GSList *a, const GSList *b);
 
+char *nm_utils_g_slist_strlist_join (const GSList *a, const char *separator);
+
 /*****************************************************************************/
 
 gssize nm_utils_ptrarray_find_binary_search (gconstpointer *list,
@@ -1160,6 +1297,15 @@ nm_utils_dbus_normalize_object_path (const char *path)
 
 guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
 
+static inline gboolean
+nm_utils_process_state_is_dead (char pstate)
+{
+	/* "/proc/[pid]/stat" returns a state as the 3rd fields (see `man 5 proc`).
+	 * Some of these states indicate the the process is effectively dead (or a zombie).
+	 */
+	return NM_IN_SET (pstate, 'Z', 'x', 'X');
+}
+
 /*****************************************************************************/
 
 gpointer _nm_utils_user_data_pack (int nargs, gconstpointer *args);
@@ -1183,6 +1329,10 @@ void nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback,
 
 /*****************************************************************************/
 
+GSource *nm_utils_g_main_context_create_integrate_source (GMainContext *internal);
+
+/*****************************************************************************/
+
 static inline void
 nm_strv_ptrarray_add_string_take (GPtrArray *cmd,
                                   char *str)
@@ -1252,4 +1402,34 @@ guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr,
                                    gsize required_len,
                                    gsize *out_len);
 
+/*****************************************************************************/
+
+static inline GTask *
+nm_g_task_new (gpointer source_object,
+               GCancellable *cancellable,
+               gpointer source_tag,
+               GAsyncReadyCallback callback,
+               gpointer callback_data)
+{
+	GTask *task;
+
+	task = g_task_new (source_object, cancellable, callback, callback_data);
+	if (source_tag)
+		g_task_set_source_tag (task, source_tag);
+	return task;
+}
+
+static inline gboolean
+nm_g_task_is_valid (gpointer task,
+                    gpointer source_object,
+                    gpointer source_tag)
+{
+	return    g_task_is_valid (task, source_object)
+	       && g_task_get_source_tag (task) == source_tag;
+}
+
+guint nm_utils_parse_debug_string (const char *string,
+                                   const GDebugKey *keys,
+                                   guint nkeys);
+
 #endif /* __NM_SHARED_UTILS_H__ */
diff --git a/shared/nm-glib-aux/nm-time-utils.c b/shared/nm-glib-aux/nm-time-utils.c
index 7735f29d..20d663bc 100644
--- a/shared/nm-glib-aux/nm-time-utils.c
+++ b/shared/nm-glib-aux/nm-time-utils.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -234,12 +219,17 @@ nm_utils_get_monotonic_timestamp_s (void)
  * @timestamp_ns_per_tick: How many nanoseconds make one unit of @timestamp? E.g. if
  *   @timestamp is in unit seconds, pass %NM_UTILS_NS_PER_SECOND; if @timestamp is
  *   in nanoseconds, pass 1; if @timestamp is in milliseconds, pass %NM_UTILS_NS_PER_SECOND/1000.
+ *   This must be a multiple of 10, and between 1 and %NM_UTILS_NS_PER_SECOND.
  *
  * Returns: the monotonic-timestamp as CLOCK_BOOTTIME, as returned by clock_gettime().
  *   The unit is the same as the passed in @timestamp based on @timestamp_ns_per_tick.
  *   E.g. if you passed @timestamp in as seconds, it will return boottime in seconds.
- *   If @timestamp is non-positive, it returns -1. Note that a (valid) monotonic-timestamp
- *   is always positive.
+ *
+ *   Note that valid monotonic-timestamps are always positive numbers (counting roughly since
+ *   the application is running). However, it might make sense to calculate a timestamp from
+ *   before the application was running, hence negative @timestamp is allowed. The result
+ *   in that case might also be a negative timestamp (in CLOCK_BOOTTIME), which would indicate
+ *   that the timestamp lies in the past before the machine was booted.
  *
  * On older kernels that don't support CLOCK_BOOTTIME, the returned time is instead CLOCK_MONOTONIC.
  **/
@@ -256,9 +246,6 @@ nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_
 	                          timestamp_ns_per_tick % 10 == 0),
 	                      -1);
 
-	/* Check that the timestamp is in a valid range. */
-	g_return_val_if_fail (timestamp >= 0, -1);
-
 	/* if the caller didn't yet ever fetch a monotonic-timestamp, he cannot pass any meaningful
 	 * value (because he has no idea what these timestamps would be). That would be a bug. */
 	nm_assert (g_atomic_pointer_get (&p_global_state));
@@ -270,12 +257,60 @@ nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ns_
 	/* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */
 	offset = p->offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick);
 
-	/* check for overflow. */
-	g_return_val_if_fail (offset > 0 || timestamp < G_MAXINT64 + offset, G_MAXINT64);
+	nm_assert (offset <= 0 && offset > G_MININT64);
+
+	/* check for overflow (note that offset is non-positive). */
+	g_return_val_if_fail (timestamp < G_MAXINT64 + offset, G_MAXINT64);
 
 	return timestamp - offset;
 }
 
+/**
+ * nm_utils_monotonic_timestamp_from_boottime:
+ * @boottime: the timestamp from CLOCK_BOOTTIME (or CLOCK_MONOTONIC, if
+ *   kernel does not support CLOCK_BOOTTIME and monotonic timestamps are based
+ *   on CLOCK_MONOTONIC).
+ * @timestamp_ns_per_tick: the scale in which @boottime is. If @boottime is in
+ *   nano seconds, this should be 1. If it is in milli seconds, this should be
+ *   %NM_UTILS_NS_PER_SECOND/1000, etc.
+ *
+ * Returns: the same timestamp in monotonic timestamp scale.
+ *
+ * Note that commonly monotonic timestamps are positive. But they may not
+ * be positive in this case. That's when boottime is taken from a time before
+ * the monotonic timestamps started counting. So, that means a zero or negative
+ * value is still a valid timestamp.
+ *
+ * This is the inverse of nm_utils_monotonic_timestamp_as_boottime().
+ */
+gint64
+nm_utils_monotonic_timestamp_from_boottime (guint64 boottime, gint64 timestamp_ns_per_tick)
+{
+	const GlobalState *p;
+	gint64 offset;
+
+	/* only support ns-per-tick being a multiple of 10. */
+	g_return_val_if_fail (timestamp_ns_per_tick == 1
+	                      || (timestamp_ns_per_tick > 0 &&
+	                          timestamp_ns_per_tick <= NM_UTILS_NS_PER_SECOND &&
+	                          timestamp_ns_per_tick % 10 == 0),
+	                      -1);
+
+	p = _t_get_global_state ();
+
+	nm_assert (p->offset_sec <= 0);
+
+	/* calculate the offset of monotonic-timestamp to boottime. offset_s is <= 1. */
+	offset = p->offset_sec * (NM_UTILS_NS_PER_SECOND / timestamp_ns_per_tick);
+
+	nm_assert (offset <= 0 && offset > G_MININT64);
+
+	/* check for overflow (note that offset is non-positive). */
+	g_return_val_if_fail (boottime < G_MAXINT64, G_MAXINT64);
+
+	return (gint64) boottime + offset;
+}
+
 gint64
 nm_utils_clock_gettime_ns (clockid_t clockid)
 {
diff --git a/shared/nm-glib-aux/nm-time-utils.h b/shared/nm-glib-aux/nm-time-utils.h
index 52d6637d..8bf41b96 100644
--- a/shared/nm-glib-aux/nm-time-utils.h
+++ b/shared/nm-glib-aux/nm-time-utils.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2018 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_TIME_UTILS_H__
@@ -42,6 +27,7 @@ gint64 nm_utils_get_monotonic_timestamp_us (void);
 gint64 nm_utils_get_monotonic_timestamp_ms (void);
 gint32 nm_utils_get_monotonic_timestamp_s (void);
 gint64 nm_utils_monotonic_timestamp_as_boottime (gint64 timestamp, gint64 timestamp_ticks_per_ns);
+gint64 nm_utils_monotonic_timestamp_from_boottime (guint64 boottime, gint64 timestamp_ns_per_tick);
 
 static inline gint64
 nm_utils_get_monotonic_timestamp_ns_cached (gint64 *cache_now)
diff --git a/shared/nm-glib-aux/nm-value-type.h b/shared/nm-glib-aux/nm-value-type.h
index b4d6898f..6fe6f072 100644
--- a/shared/nm-glib-aux/nm-value-type.h
+++ b/shared/nm-glib-aux/nm-value-type.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2019 Red Hat, Inc.
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_VALUE_TYPE_H__
diff --git a/shared/nm-libnm-core-aux/nm-dispatcher-api.h b/shared/nm-libnm-core-aux/nm-dispatcher-api.h
index 0ee0f0a8..96f512e0 100644
--- a/shared/nm-libnm-core-aux/nm-dispatcher-api.h
+++ b/shared/nm-libnm-core-aux/nm-dispatcher-api.h
@@ -1,19 +1,5 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2008 - 2012 Red Hat, Inc.
  */
 
diff --git a/shared/nm-libnm-core-aux/nm-libnm-core-aux.c b/shared/nm-libnm-core-aux/nm-libnm-core-aux.c
index a04256b7..f827726f 100644
--- a/shared/nm-libnm-core-aux/nm-libnm-core-aux.c
+++ b/shared/nm-libnm-core-aux/nm-libnm-core-aux.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-libnm-core-aux/nm-libnm-core-aux.h b/shared/nm-libnm-core-aux/nm-libnm-core-aux.h
index d8960ad5..8a835205 100644
--- a/shared/nm-libnm-core-aux/nm-libnm-core-aux.h
+++ b/shared/nm-libnm-core-aux/nm-libnm-core-aux.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2019 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
  */
 
 #ifndef __NM_LIBNM_CORE_AUX_H__
diff --git a/shared/nm-libnm-core-intern/nm-common-macros.h b/shared/nm-libnm-core-intern/nm-common-macros.h
index 8352e405..5f011efd 100644
--- a/shared/nm-libnm-core-intern/nm-common-macros.h
+++ b/shared/nm-libnm-core-intern/nm-common-macros.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2016 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_COMMON_MACROS_H__
@@ -56,6 +41,19 @@ NM_CLONED_MAC_IS_SPECIAL (const char *str)
 	                     NM_CLONED_MAC_STABLE);
 }
 
-/*****************************************************************************/
+#define NM_IAID_MAC                "mac"
+#define NM_IAID_PERM_MAC           "perm-mac"
+#define NM_IAID_IFNAME             "ifname"
+#define NM_IAID_STABLE             "stable"
+
+static inline gboolean
+NM_IAID_IS_SPECIAL (const char *str)
+{
+	return NM_IN_STRSET (str,
+	                     NM_IAID_MAC,
+	                     NM_IAID_PERM_MAC,
+	                     NM_IAID_IFNAME,
+	                     NM_IAID_STABLE);
+}
 
 #endif /* __NM_COMMON_MACROS_H__ */
diff --git a/shared/nm-libnm-core-intern/nm-ethtool-utils.c b/shared/nm-libnm-core-intern/nm-ethtool-utils.c
index 635d77c5..2bc182cf 100644
--- a/shared/nm-libnm-core-intern/nm-ethtool-utils.c
+++ b/shared/nm-libnm-core-intern/nm-ethtool-utils.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-libnm-core-intern/nm-ethtool-utils.h b/shared/nm-libnm-core-intern/nm-ethtool-utils.h
index 71ad9860..463d8e8f 100644
--- a/shared/nm-libnm-core-intern/nm-ethtool-utils.h
+++ b/shared/nm-libnm-core-intern/nm-ethtool-utils.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_ETHTOOL_UTILS_H__
diff --git a/shared/nm-libnm-core-intern/nm-libnm-core-utils.c b/shared/nm-libnm-core-intern/nm-libnm-core-utils.c
index d1e5f754..9bf0b120 100644
--- a/shared/nm-libnm-core-intern/nm-libnm-core-utils.c
+++ b/shared/nm-libnm-core-intern/nm-libnm-core-utils.c
@@ -1,19 +1,4 @@
-/*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- */
+// SPDX-License-Identifier: LGPL-2.1+
 
 #include "nm-default.h"
 
diff --git a/shared/nm-libnm-core-intern/nm-libnm-core-utils.h b/shared/nm-libnm-core-intern/nm-libnm-core-utils.h
index 35d6c5ad..42e9fc64 100644
--- a/shared/nm-libnm-core-intern/nm-libnm-core-utils.h
+++ b/shared/nm-libnm-core-intern/nm-libnm-core-utils.h
@@ -1,19 +1,4 @@
-/*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- */
+// SPDX-License-Identifier: LGPL-2.1+
 
 #ifndef __NM_LIBNM_SHARED_UTILS_H__
 #define __NM_LIBNM_SHARED_UTILS_H__
diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c
index d50739ba..af5aa842 100644
--- a/shared/nm-meta-setting.c
+++ b/shared/nm-meta-setting.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2017 - 2018 Red Hat, Inc.
+ * Copyright (C) 2017 - 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h
index 157a715f..3976bfe1 100644
--- a/shared/nm-meta-setting.h
+++ b/shared/nm-meta-setting.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2017 - 2018 Red Hat, Inc.
+ * Copyright (C) 2017 - 2018 Red Hat, Inc.
  */
 
 #ifndef __NM_META_SETTING_H__
diff --git a/shared/nm-std-aux/c-list-util.c b/shared/nm-std-aux/c-list-util.c
index eb545b2d..a6741c4c 100644
--- a/shared/nm-std-aux/c-list-util.c
+++ b/shared/nm-std-aux/c-list-util.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "c-list-util.h"
diff --git a/shared/nm-std-aux/c-list-util.h b/shared/nm-std-aux/c-list-util.h
index a1fe7169..d1aaa0b1 100644
--- a/shared/nm-std-aux/c-list-util.h
+++ b/shared/nm-std-aux/c-list-util.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __C_LIST_UTIL_H__
diff --git a/shared/nm-std-aux/nm-dbus-compat.h b/shared/nm-std-aux/nm-dbus-compat.h
index 8528bd07..bd010f45 100644
--- a/shared/nm-std-aux/nm-dbus-compat.h
+++ b/shared/nm-std-aux/nm-dbus-compat.h
@@ -1,19 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2015 Red Hat, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
  */
 
 #ifndef __NM_DBUS_COMPAT_H__
@@ -44,6 +31,8 @@
 /** The interface supported by most dbus peers */
 #define DBUS_INTERFACE_PEER           "org.freedesktop.DBus.Peer"
 
+#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
+
 /** This is a special interface whose methods can only be invoked
  * by the local implementation (messages from remote apps aren't
  * allowed to specify this interface).
diff --git a/shared/nm-test-libnm-utils.h b/shared/nm-test-libnm-utils.h
index 4c921f4c..63e3b4c7 100644
--- a/shared/nm-test-libnm-utils.h
+++ b/shared/nm-test-libnm-utils.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2014 - 2015 Red Hat, Inc.
+ * Copyright (C) 2014 - 2015 Red Hat, Inc.
  */
 
 #include "NetworkManager.h"
@@ -81,3 +67,4 @@ void nmtstc_service_update_connection_variant (NMTstcServiceInfo *sinfo,
                                                GVariant *connection,
                                                gboolean verify_connection);
 
+NMClient *nmtstc_client_new (gboolean allow_iterate_main_context);
diff --git a/shared/nm-test-utils-impl.c b/shared/nm-test-utils-impl.c
index eca037ba..0805d0b9 100644
--- a/shared/nm-test-utils-impl.c
+++ b/shared/nm-test-utils-impl.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright 2010 - 2015 Red Hat, Inc.
- *
+ * Copyright (C) 2010 - 2015 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -420,3 +406,192 @@ nmtstc_service_update_connection_variant (NMTstcServiceInfo *sinfo,
 	g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE ("()")));
 	g_variant_unref (result);
 }
+
+/*****************************************************************************/
+
+typedef struct {
+	GMainLoop *loop;
+	NMClient *client;
+} NMTstcClientNewData;
+
+static void
+_nmtstc_client_new_cb (GObject *source_object,
+                       GAsyncResult *res,
+                       gpointer user_data)
+{
+	NMTstcClientNewData *d = user_data;
+	gs_free_error GError *error = NULL;
+
+	g_assert (!d->client);
+
+	d->client = nm_client_new_finish (res,
+	                                  nmtst_get_rand_bool () ? &error : NULL);
+
+	nmtst_assert_success (NM_IS_CLIENT (d->client), error);
+
+	g_main_loop_quit (d->loop);
+}
+
+static NMClient *
+_nmtstc_client_new (gboolean sync)
+{
+	gs_free_error GError *error = NULL;
+	NMClient *client;
+
+	/* Create a NMClient instance synchronously, and arbitrarily use either
+	 * the sync or async constructor.
+	 *
+	 * Note that the sync and async construct differ in one important aspect:
+	 * the async constructor iterates the current g_main_context_get_thread_default(),
+	 * while the sync constructor does not! Aside from that, both should behave
+	 * pretty much the same way. */
+
+	if (sync) {
+		nm_auto_destroy_and_unref_gsource GSource *source = NULL;
+
+		if (nmtst_get_rand_bool ()) {
+			/* the current main context must not be iterated! */
+			source = g_idle_source_new ();
+			g_source_set_callback (source, nmtst_g_source_assert_not_called, NULL, NULL);
+			g_source_attach (source, g_main_context_get_thread_default ());
+		}
+
+		if (nmtst_get_rand_bool ()) {
+			gboolean success;
+
+			client = g_object_new (NM_TYPE_CLIENT, NULL);
+			g_assert (NM_IS_CLIENT (client));
+
+			success = g_initable_init (G_INITABLE (client),
+			                           NULL,
+			                           nmtst_get_rand_bool () ? &error : NULL);
+			nmtst_assert_success (success, error);
+		} else {
+			client = nm_client_new (NULL,
+			                        nmtst_get_rand_bool () ? &error : NULL);
+		}
+	} else {
+		nm_auto_unref_gmainloop GMainLoop *loop = NULL;
+		NMTstcClientNewData d = { .loop = NULL, };
+
+		loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
+
+		d.loop = loop;
+		nm_client_new_async (NULL,
+		                     _nmtstc_client_new_cb,
+		                     &d);
+		g_main_loop_run (loop);
+		g_assert (NM_IS_CLIENT (d.client));
+		client = d.client;
+	}
+
+	nmtst_assert_success (NM_IS_CLIENT (client), error);
+	return client;
+}
+
+typedef struct {
+	GMainLoop *loop;
+	NMClient *client;
+	bool sync;
+} NewSyncInsideDispatchedData;
+
+static gboolean
+_nmtstc_client_new_inside_loop_do (gpointer user_data)
+{
+	NewSyncInsideDispatchedData *d = user_data;
+
+	g_assert (d->loop);
+	g_assert (!d->client);
+
+	d->client = nmtstc_client_new (d->sync);
+	g_main_loop_quit (d->loop);
+	return G_SOURCE_CONTINUE;
+}
+
+static NMClient *
+_nmtstc_client_new_inside_loop (gboolean sync)
+{
+	GMainContext *context = g_main_context_get_thread_default ();
+	nm_auto_unref_gmainloop GMainLoop *loop = g_main_loop_new (context, FALSE);
+	NewSyncInsideDispatchedData d = {
+		.sync = sync,
+		.loop = loop,
+	};
+	nm_auto_destroy_and_unref_gsource GSource *source = NULL;
+
+	source = g_idle_source_new ();
+	g_source_set_callback (source, _nmtstc_client_new_inside_loop_do, &d, NULL);
+	g_source_attach (source, context);
+
+	g_main_loop_run (loop);
+	g_assert (NM_IS_CLIENT (d.client));
+	return d.client;
+}
+
+static NMClient *
+_nmtstc_client_new_extra_context (void)
+{
+	GMainContext *inner_context;
+	NMClient *client;
+	GSource *source;
+	guint key_idx;
+
+	inner_context = g_main_context_new ();
+	g_main_context_push_thread_default (inner_context);
+
+	client = nmtstc_client_new (TRUE);
+
+	source = nm_utils_g_main_context_create_integrate_source (inner_context);
+
+	g_main_context_pop_thread_default (inner_context);
+	g_main_context_unref (inner_context);
+
+	g_source_attach (source, g_main_context_get_thread_default ());
+
+	for (key_idx = 0; TRUE; key_idx++) {
+		char s[100];
+
+		/* nmtstc_client_new() may call _nmtstc_client_new_extra_context() repeatedly. We
+		 * need to attach the source to a previously unused key. */
+		nm_sprintf_buf (s, "nm-test-extra-context-%u", key_idx);
+		if (!g_object_get_data (G_OBJECT (client), s)) {
+			g_object_set_data_full (G_OBJECT (client),
+			                        s,
+			                        source,
+			                        (GDestroyNotify) nm_g_source_destroy_and_unref);
+			break;
+		}
+	}
+
+	return client;
+}
+
+NMClient *
+nmtstc_client_new (gboolean allow_iterate_main_context)
+{
+	gboolean inside_loop;
+	gboolean sync;
+
+	if (nmtst_get_rand_uint32 () % 5 == 0)
+		return _nmtstc_client_new_extra_context ();
+
+	if (!allow_iterate_main_context) {
+		sync = TRUE;
+		inside_loop = FALSE;
+	} else {
+		/* The caller allows to iterate the main context. That that point,
+		 * we can both use the synchronous and the asynchronous initialization,
+		 * both should yield the same result. Choose one randomly. */
+		sync = nmtst_get_rand_bool ();
+		inside_loop = ((nmtst_get_rand_uint32 () % 3) == 0);
+	}
+
+	if (inside_loop) {
+		/* Create the client on an idle handler of the current context.
+		 * In practice, it should make no difference, which this check
+		 * tries to prove. */
+		return _nmtstc_client_new_inside_loop (sync);
+	}
+
+	return _nmtstc_client_new (sync);
+}
diff --git a/shared/nm-udev-aux/nm-udev-utils.c b/shared/nm-udev-aux/nm-udev-utils.c
index e9dfd8d0..1b0927df 100644
--- a/shared/nm-udev-aux/nm-udev-utils.c
+++ b/shared/nm-udev-aux/nm-udev-utils.c
@@ -1,19 +1,5 @@
-/* nm-udev-utils.c - udev utils functions
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
diff --git a/shared/nm-udev-aux/nm-udev-utils.h b/shared/nm-udev-aux/nm-udev-utils.h
index 0e5895ef..92fce699 100644
--- a/shared/nm-udev-aux/nm-udev-utils.h
+++ b/shared/nm-udev-aux/nm-udev-utils.h
@@ -1,19 +1,5 @@
-/* nm-udev-utils.h - udev utils functions
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2017 Red Hat, Inc.
  */
 
diff --git a/shared/nm-utils/nm-compat.c b/shared/nm-utils/nm-compat.c
index ea3e5392..895d9550 100644
--- a/shared/nm-utils/nm-compat.c
+++ b/shared/nm-utils/nm-compat.c
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-utils/nm-compat.h b/shared/nm-utils/nm-compat.h
index a8e3ee97..55f764c7 100644
--- a/shared/nm-utils/nm-compat.h
+++ b/shared/nm-utils/nm-compat.h
@@ -1,21 +1,6 @@
-/* NetworkManager -- Network link manager
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * (C) Copyright 2017 Red Hat, Inc.
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NM_COMPAT_H__
diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h
index d7a8788d..f0ea1179 100644
--- a/shared/nm-utils/nm-test-utils.h
+++ b/shared/nm-utils/nm-test-utils.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2014 Red Hat, Inc.
+ * Copyright (C) 2014 Red Hat, Inc.
  */
 
 #ifndef __NM_TEST_UTILS_H__
@@ -1024,6 +1010,34 @@ _nmtst_main_loop_quit_on_notify (GObject *object, GParamSpec *pspec, gpointer us
 }
 #define nmtst_main_loop_quit_on_notify ((GCallback) _nmtst_main_loop_quit_on_notify)
 
+static inline gboolean
+_nmtst_main_context_iterate_until_timeout (gpointer user_data)
+{
+	gboolean *p_had_pointer = user_data;
+
+	g_assert (!*p_had_pointer);
+	*p_had_pointer = TRUE;
+	return G_SOURCE_CONTINUE;
+}
+
+#define nmtst_main_context_iterate_until(context, timeout_ms, condition) \
+	G_STMT_START { \
+		nm_auto_destroy_and_unref_gsource GSource *_source = NULL; \
+		GMainContext *_context = (context); \
+		gboolean _had_timeout = FALSE; \
+		\
+		_source = g_timeout_source_new (timeout_ms); \
+		g_source_set_callback (_source, _nmtst_main_context_iterate_until_timeout, &_had_timeout, NULL); \
+		g_source_attach (_source, _context); \
+		\
+		while (TRUE) { \
+			if (condition) \
+				break; \
+			g_main_context_iteration (_context, TRUE); \
+			g_assert (!_had_timeout && #condition); \
+		} \
+	} G_STMT_END
+
 /*****************************************************************************/
 
 static inline const char *
@@ -2297,4 +2311,13 @@ nmtst_keyfile_get_num_keys (GKeyFile *keyfile,
 
 /*****************************************************************************/
 
+static inline gboolean
+nmtst_g_source_assert_not_called (gpointer user_data)
+{
+	g_assert_not_reached ();
+	return G_SOURCE_CONTINUE;
+}
+
+/*****************************************************************************/
+
 #endif /* __NM_TEST_UTILS_H__ */
diff --git a/shared/nm-utils/nm-vpn-editor-plugin-call.h b/shared/nm-utils/nm-vpn-editor-plugin-call.h
index ea5e27b0..ab473083 100644
--- a/shared/nm-utils/nm-vpn-editor-plugin-call.h
+++ b/shared/nm-utils/nm-vpn-editor-plugin-call.h
@@ -1,19 +1,5 @@
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2016 Red Hat, Inc.
  */
 
diff --git a/shared/nm-utils/nm-vpn-plugin-macros.h b/shared/nm-utils/nm-vpn-plugin-macros.h
index 97260f5f..f154147f 100644
--- a/shared/nm-utils/nm-vpn-plugin-macros.h
+++ b/shared/nm-utils/nm-vpn-plugin-macros.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2016 Red Hat, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_VPN_PLUGIN_MACROS_H__
diff --git a/shared/nm-utils/nm-vpn-plugin-utils.c b/shared/nm-utils/nm-vpn-plugin-utils.c
index afc05bdf..8616ae5f 100644
--- a/shared/nm-utils/nm-vpn-plugin-utils.c
+++ b/shared/nm-utils/nm-vpn-plugin-utils.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2016,2018 Red Hat, Inc.
+ * Copyright (C) 2016, 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/nm-utils/nm-vpn-plugin-utils.h b/shared/nm-utils/nm-vpn-plugin-utils.h
index 961e0187..2a31b677 100644
--- a/shared/nm-utils/nm-vpn-plugin-utils.h
+++ b/shared/nm-utils/nm-vpn-plugin-utils.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2016 Red Hat, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
  */
 
 #ifndef __NM_VPN_PLUGIN_UTILS_H__
diff --git a/shared/nm-utils/tests/meson.build b/shared/nm-utils/tests/meson.build
new file mode 100644
index 00000000..1ee5efff
--- /dev/null
+++ b/shared/nm-utils/tests/meson.build
@@ -0,0 +1,21 @@
+test_unit = 'test-shared-general'
+
+c_flags = [
+  '-DNETWORKMANAGER_COMPILATION_TEST',
+  '-DNETWORKMANAGER_COMPILATION=(NM_NETWORKMANAGER_COMPILATION_GLIB|NM_NETWORKMANAGER_COMPILATION_WITH_GLIB_I18N_PROG)',
+]
+
+exe = executable(
+  test_unit,
+  test_unit + '.c',
+  c_args: c_flags,
+  dependencies: libnm_utils_base_dep,
+  link_with: libnm_systemd_logging_stub,
+)
+
+test(
+  'shared/nm-utils/' + test_unit,
+  test_script,
+  args: test_args + [exe.full_path()],
+  timeout: default_test_timeout,
+)
diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c
index 24affba7..34da19b3 100644
--- a/shared/nm-utils/tests/test-shared-general.c
+++ b/shared/nm-utils/tests/test-shared-general.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #define NM_TEST_UTILS_NO_LIBNM 1
@@ -24,12 +10,38 @@
 #include "nm-std-aux/unaligned.h"
 #include "nm-glib-aux/nm-random-utils.h"
 #include "nm-glib-aux/nm-time-utils.h"
+#include "nm-glib-aux/nm-ref-string.h"
 
 #include "nm-utils/nm-test-utils.h"
 
 /*****************************************************************************/
 
 static void
+test_gpid (void)
+{
+	const int *int_ptr;
+	GPid pid = 42;
+
+	/* We redefine G_PID_FORMAT, because it's only available since glib 2.53.5.
+	 *
+	 * Also, this is the format for GPid, which for glib is always a typedef
+	 * for "int". Add a check for that here.
+	 *
+	 * G_PID_FORMAT is not about pid_t, which might be a smaller int, and which we would
+	 * check with SIZEOF_PID_T. */
+	G_STATIC_ASSERT (sizeof (GPid) == sizeof (int));
+
+	g_assert_cmpstr (""G_PID_FORMAT, ==, "i");
+
+	/* check that it's really "int". We will get a compiler warning, if that's not
+	 * the case. */
+	int_ptr = &pid;
+	g_assert_cmpint (*int_ptr, ==, 42);
+}
+
+/*****************************************************************************/
+
+static void
 test_monotonic_timestamp (void)
 {
 	g_assert (nm_utils_get_monotonic_timestamp_s () > 0);
@@ -510,12 +522,45 @@ test_nm_utils_bin2hexstr (void)
 
 /*****************************************************************************/
 
+static void
+test_nm_ref_string (void)
+{
+	nm_auto_ref_string NMRefString *s1 = NULL;
+	NMRefString *s2;
+
+	s1 = nm_ref_string_new ("hallo");
+	g_assert (s1);
+	g_assert_cmpstr (s1->str, ==, "hallo");
+	g_assert_cmpint (s1->len, ==, strlen ("hallo"));
+
+	s2 = nm_ref_string_new ("hallo");
+	g_assert (s2 == s1);
+	nm_ref_string_unref (s2);
+
+	s2 = nm_ref_string_new (NULL);
+	g_assert (!s2);
+	nm_ref_string_unref (s2);
+
+#define STR_WITH_NUL "hallo\0test\0"
+	s2 = nm_ref_string_new_len (STR_WITH_NUL, NM_STRLEN (STR_WITH_NUL));
+	g_assert (s2);
+	g_assert_cmpstr (s2->str, ==, "hallo");
+	g_assert_cmpint (s2->len, ==, NM_STRLEN (STR_WITH_NUL));
+	g_assert_cmpint (s2->len, >, strlen (s2->str));
+	g_assert_cmpmem (s2->str, s2->len, STR_WITH_NUL, NM_STRLEN (STR_WITH_NUL));
+	g_assert (s2->str[s2->len] == '\0');
+	nm_ref_string_unref (s2);
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
 {
 	nmtst_init (&argc, &argv, TRUE);
 
+	g_test_add_func ("/general/test_gpid", test_gpid);
 	g_test_add_func ("/general/test_monotonic_timestamp", test_monotonic_timestamp);
 	g_test_add_func ("/general/test_nmhash", test_nmhash);
 	g_test_add_func ("/general/test_nm_make_strv", test_make_strv);
@@ -526,6 +571,7 @@ int main (int argc, char **argv)
 	g_test_add_func ("/general/test_strv_cmp", test_strv_cmp);
 	g_test_add_func ("/general/test_strstrip_avoid_copy", test_strstrip_avoid_copy);
 	g_test_add_func ("/general/test_nm_utils_bin2hexstr", test_nm_utils_bin2hexstr);
+	g_test_add_func ("/general/test_nm_ref_string", test_nm_ref_string);
 
 	return g_test_run ();
 }
diff --git a/shared/nm-version-macros.h b/shared/nm-version-macros.h
index f1f44713..a5c79c1e 100644
--- a/shared/nm-version-macros.h
+++ b/shared/nm-version-macros.h
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2011, 2015 Red Hat, Inc.
+ * Copyright (C) 2011, 2015 Red Hat, Inc.
  */
 
 #ifndef __NM_VERSION_MACROS_H__
@@ -36,7 +22,7 @@
  * Evaluates to the minor version number of NetworkManager which this source
  * is compiled against.
  */
-#define NM_MINOR_VERSION (20)
+#define NM_MINOR_VERSION (22)
 
 /**
  * NM_MICRO_VERSION:
@@ -44,7 +30,7 @@
  * Evaluates to the micro version number of NetworkManager which this source
  * compiled against.
  */
-#define NM_MICRO_VERSION (8)
+#define NM_MICRO_VERSION (0)
 
 /**
  * NM_CHECK_VERSION:
@@ -76,8 +62,7 @@
 #define NM_VERSION_1_16   (NM_ENCODE_VERSION (1, 16, 0))
 #define NM_VERSION_1_18   (NM_ENCODE_VERSION (1, 18, 0))
 #define NM_VERSION_1_20   (NM_ENCODE_VERSION (1, 20, 0))
-#define NM_VERSION_1_20_2 (NM_ENCODE_VERSION (1, 20, 2))
-#define NM_VERSION_1_20_6 (NM_ENCODE_VERSION (1, 20, 6))
+#define NM_VERSION_1_22   (NM_ENCODE_VERSION (1, 22, 0))
 
 /* For releases, NM_API_VERSION is equal to NM_VERSION.
  *
diff --git a/shared/nm-version-macros.h.in b/shared/nm-version-macros.h.in
index c65a1f88..961febb0 100644
--- a/shared/nm-version-macros.h.in
+++ b/shared/nm-version-macros.h.in
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2011, 2015 Red Hat, Inc.
+ * Copyright (C) 2011, 2015 Red Hat, Inc.
  */
 
 #ifndef __NM_VERSION_MACROS_H__
@@ -76,8 +62,7 @@
 #define NM_VERSION_1_16   (NM_ENCODE_VERSION (1, 16, 0))
 #define NM_VERSION_1_18   (NM_ENCODE_VERSION (1, 18, 0))
 #define NM_VERSION_1_20   (NM_ENCODE_VERSION (1, 20, 0))
-#define NM_VERSION_1_20_2 (NM_ENCODE_VERSION (1, 20, 2))
-#define NM_VERSION_1_20_6 (NM_ENCODE_VERSION (1, 20, 6))
+#define NM_VERSION_1_22   (NM_ENCODE_VERSION (1, 22, 0))
 
 /* For releases, NM_API_VERSION is equal to NM_VERSION.
  *
diff --git a/shared/systemd/nm-logging-stub.c b/shared/systemd/nm-logging-stub.c
index 95ff51e5..64a96daa 100644
--- a/shared/systemd/nm-logging-stub.c
+++ b/shared/systemd/nm-logging-stub.c
@@ -1,20 +1,6 @@
+// SPDX-License-Identifier: LGPL-2.1+
 /*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
- * Copyright 2018 Red Hat, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  */
 
 #include "nm-default.h"
diff --git a/shared/systemd/nm-sd-utils-shared.c b/shared/systemd/nm-sd-utils-shared.c
index 7206fa52..85fd6a35 100644
--- a/shared/systemd/nm-sd-utils-shared.c
+++ b/shared/systemd/nm-sd-utils-shared.c
@@ -1,18 +1,5 @@
-/* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
@@ -22,9 +9,10 @@
 
 #include "nm-sd-adapt-shared.h"
 
-#include "path-util.h"
-#include "hexdecoct.h"
 #include "dns-domain.h"
+#include "hexdecoct.h"
+#include "hostname-util.h"
+#include "path-util.h"
 
 /*****************************************************************************/
 
@@ -52,7 +40,7 @@ nm_sd_utils_path_startswith (const char *path, const char *prefix)
 
 /*****************************************************************************/
 
-gboolean
+int
 nm_sd_utils_unbase64char (char ch, gboolean accept_padding_equal)
 {
 	if (   ch == '='
@@ -96,3 +84,13 @@ int nm_sd_dns_name_to_wire_format (const char *domain,
 {
 	return dns_name_to_wire_format (domain, buffer, len, canonical);
 }
+
+int nm_sd_dns_name_is_valid (const char *s)
+{
+	return dns_name_is_valid (s);
+}
+
+gboolean nm_sd_hostname_is_valid (const char *s, bool allow_trailing_dot)
+{
+	return hostname_is_valid (s, allow_trailing_dot);
+}
diff --git a/shared/systemd/nm-sd-utils-shared.h b/shared/systemd/nm-sd-utils-shared.h
index 48a8cf37..3495a31d 100644
--- a/shared/systemd/nm-sd-utils-shared.h
+++ b/shared/systemd/nm-sd-utils-shared.h
@@ -1,18 +1,5 @@
-/* This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: LGPL-2.1+
+/*
  * Copyright (C) 2018 Red Hat, Inc.
  */
 
@@ -44,4 +31,7 @@ int nm_sd_dns_name_to_wire_format (const char *domain,
                                    size_t len,
                                    gboolean canonical);
 
+int nm_sd_dns_name_is_valid (const char *s);
+gboolean nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot);
+
 #endif /* __NM_SD_UTILS_SHARED_H__ */
diff --git a/shared/systemd/sd-adapt-shared/btrfs-util.h b/shared/systemd/sd-adapt-shared/dhcp-server-internal.h
index 637892c2..637892c2 100644
--- a/shared/systemd/sd-adapt-shared/btrfs-util.h
+++ b/shared/systemd/sd-adapt-shared/dhcp-server-internal.h
diff --git a/shared/systemd/sd-adapt-shared/missing.h b/shared/systemd/sd-adapt-shared/missing.h
deleted file mode 100644
index d0b460a7..00000000
--- a/shared/systemd/sd-adapt-shared/missing.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-/* dummy header */
-
-#include "missing_fcntl.h"
-#include "missing_socket.h"
-#include "missing_stat.h"
-#include "missing_type.h"
diff --git a/shared/systemd/sd-adapt-shared/cgroup-util.h b/shared/systemd/sd-adapt-shared/missing_fs.h
index 637892c2..637892c2 100644
--- a/shared/systemd/sd-adapt-shared/cgroup-util.h
+++ b/shared/systemd/sd-adapt-shared/missing_fs.h
diff --git a/shared/systemd/sd-adapt-shared/device-nodes.h b/shared/systemd/sd-adapt-shared/missing_magic.h
index 637892c2..637892c2 100644
--- a/shared/systemd/sd-adapt-shared/device-nodes.h
+++ b/shared/systemd/sd-adapt-shared/missing_magic.h
diff --git a/shared/systemd/sd-adapt-shared/procfs-util.h b/shared/systemd/sd-adapt-shared/missing_network.h
index 637892c2..637892c2 100644
--- a/shared/systemd/sd-adapt-shared/procfs-util.h
+++ b/shared/systemd/sd-adapt-shared/missing_network.h
diff --git a/shared/systemd/sd-adapt-shared/missing_sched.h b/shared/systemd/sd-adapt-shared/missing_sched.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/shared/systemd/sd-adapt-shared/missing_sched.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
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 8dd93341..490cae92 100644
--- a/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h
+++ b/shared/systemd/sd-adapt-shared/nm-sd-adapt-shared.h
@@ -1,17 +1,5 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+// SPDX-License-Identifier: GPL-2.0+
+/*
  * Copyright (C) 2014 - 2018 Red Hat, Inc.
  */
 
diff --git a/shared/systemd/src/basic/alloc-util.c b/shared/systemd/src/basic/alloc-util.c
index c97d8700..e355b60f 100644
--- a/shared/systemd/src/basic/alloc-util.c
+++ b/shared/systemd/src/basic/alloc-util.c
@@ -80,7 +80,7 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
                          * take possession of the extra space. This should be cheap, since libc doesn't have to move
                          * the memory for this. */
 
-                        qq = realloc(q, bn * size);
+                        qq = reallocarray(q, bn, size);
                         if (_likely_(qq)) {
                                 *p = qq;
                                 *allocated = bn;
diff --git a/shared/systemd/src/basic/env-file.c b/shared/systemd/src/basic/env-file.c
index 5860b050..30ee135a 100644
--- a/shared/systemd/src/basic/env-file.c
+++ b/shared/systemd/src/basic/env-file.c
@@ -488,6 +488,8 @@ static int merge_env_file_push(
 
         free_and_replace(value, expanded_value);
 
+        log_debug("%s:%u: setting %s=%s", filename, line, key, value);
+
         return load_env_file_push(filename, line, key, value, env, n_pushed);
 }
 
diff --git a/shared/systemd/src/basic/env-util.c b/shared/systemd/src/basic/env-util.c
index cd9d3176..d16890d9 100644
--- a/shared/systemd/src/basic/env-util.c
+++ b/shared/systemd/src/basic/env-util.c
@@ -6,7 +6,6 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <stdlib.h>
-#include <string.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
diff --git a/shared/systemd/src/basic/errno-util.h b/shared/systemd/src/basic/errno-util.h
index 6053cde6..8f1be6c0 100644
--- a/shared/systemd/src/basic/errno-util.h
+++ b/shared/systemd/src/basic/errno-util.h
@@ -86,3 +86,18 @@ static inline bool ERRNO_IS_RESOURCE(int r) {
                       ENFILE,
                       ENOMEM);
 }
+
+/* Three different errors for "operation/system call/ioctl not supported" */
+static inline bool ERRNO_IS_NOT_SUPPORTED(int r) {
+        return IN_SET(abs(r),
+                      EOPNOTSUPP,
+                      ENOTTY,
+                      ENOSYS);
+}
+
+/* Two different errors for access problems */
+static inline bool ERRNO_IS_PRIVILEGE(int r) {
+        return IN_SET(abs(r),
+                      EACCES,
+                      EPERM);
+}
diff --git a/shared/systemd/src/basic/extract-word.c b/shared/systemd/src/basic/extract-word.c
index 15cbaafb..2da25b03 100644
--- a/shared/systemd/src/basic/extract-word.c
+++ b/shared/systemd/src/basic/extract-word.c
@@ -8,7 +8,6 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <string.h>
 #include <syslog.h>
 
 #include "alloc-util.h"
@@ -30,8 +29,6 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
 
         assert(p);
         assert(ret);
-        /* Those two don't make sense together. */
-        assert(!FLAGS_SET(flags, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE));
 
         /* Bail early if called after last value or with no input */
         if (!*p)
diff --git a/shared/systemd/src/basic/fd-util.c b/shared/systemd/src/basic/fd-util.c
index 941053bf..ea2c1612 100644
--- a/shared/systemd/src/basic/fd-util.c
+++ b/shared/systemd/src/basic/fd-util.c
@@ -5,7 +5,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/resource.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -18,7 +17,8 @@
 #include "io-util.h"
 #include "macro.h"
 #include "memfd-util.h"
-#include "missing.h"
+#include "missing_fcntl.h"
+#include "missing_syscall.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
diff --git a/shared/systemd/src/basic/fileio.c b/shared/systemd/src/basic/fileio.c
index bd2afbbe..3bffa1c5 100644
--- a/shared/systemd/src/basic/fileio.c
+++ b/shared/systemd/src/basic/fileio.c
@@ -10,7 +10,6 @@
 #include <stdint.h>
 #include <stdio_ext.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -22,7 +21,6 @@
 #include "hexdecoct.h"
 #include "log.h"
 #include "macro.h"
-#include "missing.h"
 #include "mkdir.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -314,6 +312,113 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
 }
 #endif /* NM_IGNORED */
 
+int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
+        _cleanup_free_ char *buf = NULL;
+        _cleanup_close_ int fd = -1;
+        struct stat st;
+        size_t n, size;
+        int n_retries;
+        char *p;
+
+        assert(ret_contents);
+
+        /* Virtual filesystems such as sysfs or procfs use kernfs, and kernfs can work
+         * with two sorts of virtual files. One sort uses "seq_file", and the results of
+         * the first read are buffered for the second read. The other sort uses "raw"
+         * reads which always go direct to the device. In the latter case, the content of
+         * the virtual file must be retrieved with a single read otherwise a second read
+         * might get the new value instead of finding EOF immediately. That's the reason
+         * why the usage of fread(3) is prohibited in this case as it always performs a
+         * second call to read(2) looking for EOF. See issue 13585. */
+
+        fd = open(filename, O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        /* Start size for files in /proc which usually report a file size of 0. */
+        size = LINE_MAX / 2;
+
+        /* Limit the number of attempts to read the number of bytes returned by fstat(). */
+        n_retries = 3;
+
+        for (;;) {
+                if (n_retries <= 0)
+                        return -EIO;
+
+                if (fstat(fd, &st) < 0)
+                        return -errno;
+
+                if (!S_ISREG(st.st_mode))
+                        return -EBADF;
+
+                /* Be prepared for files from /proc which generally report a file size of 0. */
+                if (st.st_size > 0) {
+                        size = st.st_size;
+                        n_retries--;
+                } else
+                        size = size * 2;
+
+                if (size > READ_FULL_BYTES_MAX)
+                        return -E2BIG;
+
+                p = realloc(buf, size + 1);
+                if (!p)
+                        return -ENOMEM;
+                buf = TAKE_PTR(p);
+
+                for (;;) {
+                        ssize_t k;
+
+                        /* Read one more byte so we can detect whether the content of the
+                         * file has already changed or the guessed size for files from /proc
+                         * wasn't large enough . */
+                        k = read(fd, buf, size + 1);
+                        if (k >= 0) {
+                                n = k;
+                                break;
+                        }
+
+                        if (errno != -EINTR)
+                                return -errno;
+                }
+
+                /* Consider a short read as EOF */
+                if (n <= size)
+                        break;
+
+                /* Hmm... either we read too few bytes from /proc or less likely the content
+                 * of the file might have been changed (and is now bigger) while we were
+                 * processing, let's try again either with a bigger guessed size or the new
+                 * file size. */
+
+                if (lseek(fd, 0, SEEK_SET) < 0)
+                        return -errno;
+        }
+
+        if (n < size) {
+                p = realloc(buf, n + 1);
+                if (!p)
+                        return -ENOMEM;
+                buf = TAKE_PTR(p);
+        }
+
+        if (!ret_size) {
+                /* Safety check: if the caller doesn't want to know the size of what we
+                 * just read it will rely on the trailing NUL byte. But if there's an
+                 * embedded NUL byte, then we should refuse operation as otherwise
+                 * there'd be ambiguity about what we just read. */
+
+                if (memchr(buf, 0, n))
+                        return -EBADMSG;
+        } else
+                *ret_size = n;
+
+        buf[n] = 0;
+        *ret_contents = TAKE_PTR(buf);
+
+        return 0;
+}
+
 int read_full_stream_full(
                 FILE *f,
                 const char *filename,
@@ -346,9 +451,9 @@ int read_full_stream_full(
                         if (st.st_size > READ_FULL_BYTES_MAX)
                                 return -E2BIG;
 
-                        /* Start with the right file size, but be prepared for files from /proc which generally report a file
-                         * size of 0. Note that we increase the size to read here by one, so that the first read attempt
-                         * already makes us notice the EOF. */
+                        /* Start with the right file size. Note that we increase the size
+                         * to read here by one, so that the first read attempt already
+                         * makes us notice the EOF. */
                         if (st.st_size > 0)
                                 n_next = st.st_size + 1;
 
@@ -508,7 +613,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin
         assert(pattern);
         assert(field);
 
-        r = read_full_file(filename, &status, NULL);
+        r = read_full_virtual_file(filename, &status, NULL);
         if (r < 0)
                 return r;
 
@@ -942,10 +1047,10 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c
 
         if (unit)
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "%s has %04o mode that is too permissive, please adjust the access mode.",
+                           "%s has %04o mode that is too permissive, please adjust the ownership and access mode.",
                            filename, st->st_mode & 07777);
         else
-                log_warning("%s has %04o mode that is too permissive, please adjust the access mode.",
+                log_warning("%s has %04o mode that is too permissive, please adjust the ownership and access mode.",
                             filename, st->st_mode & 07777);
         return 0;
 }
diff --git a/shared/systemd/src/basic/fileio.h b/shared/systemd/src/basic/fileio.h
index 05f6c89d..31bfef33 100644
--- a/shared/systemd/src/basic/fileio.h
+++ b/shared/systemd/src/basic/fileio.h
@@ -56,6 +56,7 @@ int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **co
 static inline int read_full_file(const char *filename, char **contents, size_t *size) {
         return read_full_file_full(filename, 0, contents, size);
 }
+int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size);
 int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size);
 static inline int read_full_stream(FILE *f, char **contents, size_t *size) {
         return read_full_stream_full(f, NULL, 0, contents, size);
diff --git a/shared/systemd/src/basic/format-util.c b/shared/systemd/src/basic/format-util.c
index 7a3e735b..62477f53 100644
--- a/shared/systemd/src/basic/format-util.c
+++ b/shared/systemd/src/basic/format-util.c
@@ -2,15 +2,26 @@
 
 #include "nm-sd-adapt-shared.h"
 
-#include <stdio.h>
-
 #include "format-util.h"
 #include "memory-util.h"
+#include "stdio-util.h"
 
-char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
+assert_cc(DECIMAL_STR_MAX(int) + 1 <= IF_NAMESIZE + 1);
+char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag) {
         /* Buffer is always cleared */
         memzero(buf, IF_NAMESIZE + 1);
-        return if_indextoname(ifindex, buf);
+        if (if_indextoname(ifindex, buf))
+                return buf;
+
+        if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX))
+                return NULL;
+
+        if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT))
+                snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex);
+        else
+                snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex);
+
+        return buf;
 }
 
 char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
diff --git a/shared/systemd/src/basic/format-util.h b/shared/systemd/src/basic/format-util.h
index e0d184a5..59622508 100644
--- a/shared/systemd/src/basic/format-util.h
+++ b/shared/systemd/src/basic/format-util.h
@@ -68,7 +68,15 @@
 #  error Unknown ino_t size
 #endif
 
-char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]);
+typedef enum {
+        FORMAT_IFNAME_IFINDEX              = 1 << 0,
+        FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX,
+} FormatIfnameFlag;
+
+char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag);
+static inline char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
+        return format_ifname_full(ifindex, buf, 0);
+}
 
 typedef enum {
         FORMAT_BYTES_USE_IEC     = 1 << 0,
diff --git a/shared/systemd/src/basic/fs-util.c b/shared/systemd/src/basic/fs-util.c
index 56385fa2..e30f40fb 100644
--- a/shared/systemd/src/basic/fs-util.c
+++ b/shared/systemd/src/basic/fs-util.c
@@ -4,13 +4,9 @@
 
 #include <errno.h>
 #include <stddef.h>
-#include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
 #include <linux/falloc.h>
 #include <linux/magic.h>
-#include <time.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -20,7 +16,9 @@
 #include "locale-util.h"
 #include "log.h"
 #include "macro.h"
-#include "missing.h"
+#include "missing_fcntl.h"
+#include "missing_fs.h"
+#include "missing_syscall.h"
 #include "mkdir.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -669,6 +667,18 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
 }
 
 #if 0 /* NM_IGNORED */
+int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
+
+        if (inotify_add_watch(fd, pathname, mask) < 0) {
+                if (errno == ENOSPC)
+                        return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname);
+
+                return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname);
+        }
+
+        return 0;
+}
+
 static bool unsafe_transition(const struct stat *a, const struct stat *b) {
         /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
          * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
@@ -707,7 +717,7 @@ static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
                                  n1, path);
 }
 
-int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
+int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) {
         _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
         _cleanup_close_ int fd = -1;
         unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
@@ -719,10 +729,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
         assert(path);
 
         /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
-        if (FLAGS_SET(flags, CHASE_NONEXISTENT | CHASE_OPEN))
+        if ((flags & CHASE_NONEXISTENT) && ret_fd)
                 return -EINVAL;
 
-        if (FLAGS_SET(flags, CHASE_STEP | CHASE_OPEN))
+        if ((flags & CHASE_STEP) && ret_fd)
                 return -EINVAL;
 
         if (isempty(path))
@@ -741,24 +751,24 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
          * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
          * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
          * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
-         * at a minimum.
+         * to a minimum.
          *
          * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
          * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
          * function what to do when encountering a symlink with an absolute path as directory: prefix it by the
          * specified path.
          *
-         * There are three ways to invoke this function:
+         * There are five ways to invoke this function:
          *
-         * 1. Without CHASE_STEP or CHASE_OPEN: in this case the path is resolved and the normalized path is returned
-         *    in `ret`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set 0 is returned if the file
-         *    doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set >= 0 is returned if the destination was
-         *    found, -ENOENT if it doesn't.
+         * 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is
+         *    returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0
+         *    is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is
+         *    returned if the destination was found, -ENOENT if it wasn't.
          *
-         * 2. With CHASE_OPEN: in this case the destination is opened after chasing it as O_PATH and this file
+         * 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file
          *    descriptor is returned as return value. This is useful to open files relative to some root
          *    directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using
-         *    fd_reopen() or such) before it can be used for reading/writing. CHASE_OPEN may not be combined with
+         *    fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with
          *    CHASE_NONEXISTENT.
          *
          * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first
@@ -769,26 +779,26 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
          *
          * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
          *    unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
-         *    CHASE_WARN is also set a warning describing the unsafe transition is emitted.
+         *    CHASE_WARN is also set, a warning describing the unsafe transition is emitted.
          *
-         * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, the path normalization is
-         *    aborted and -EREMOTE is returned. If CHASE_WARN is also set a warning showing the path of the mount point
-         *    is emitted.
-         *
-         * */
+         * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization
+         *    is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of
+         *    the mount point is emitted.
+         */
 
         /* A root directory of "/" or "" is identical to none */
         if (empty_or_root(original_root))
                 original_root = NULL;
 
-        if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) {
-                /* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set
+        if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) {
+                /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set
                  * and doesn't care about any of the other special features we provide either. */
                 r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
                 if (r < 0)
                         return -errno;
 
-                return r;
+                *ret_fd = r;
+                return 0;
         }
 
         if (original_root) {
@@ -797,7 +807,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                         return r;
 
                 if (flags & CHASE_PREFIX_ROOT) {
-
                         /* We don't support relative paths in combination with a root directory */
                         if (!path_is_absolute(path))
                                 return -EINVAL;
@@ -942,7 +951,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                 if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
                         char *joined;
-
                         _cleanup_free_ char *destination = NULL;
 
                         /* This is a symlink, in this case read the destination. But let's make sure we don't follow
@@ -1028,15 +1036,15 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                         return -ENOMEM;
         }
 
-        if (ret)
-                *ret = TAKE_PTR(done);
+        if (ret_path)
+                *ret_path = TAKE_PTR(done);
 
-        if (flags & CHASE_OPEN) {
-                /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
-                 * opening /proc/self/fd/xyz. */
+        if (ret_fd) {
+                /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a
+                 * proper fd by opening /proc/self/fd/xyz. */
 
                 assert(fd >= 0);
-                return TAKE_FD(fd);
+                *ret_fd = TAKE_FD(fd);
         }
 
         if (flags & CHASE_STEP)
@@ -1045,14 +1053,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
         return exists;
 
 chased_one:
-        if (ret) {
+        if (ret_path) {
                 char *c;
 
                 c = strjoin(strempty(done), todo);
                 if (!c)
                         return -ENOMEM;
 
-                *ret = c;
+                *ret_path = c;
         }
 
         return 0;
@@ -1081,9 +1089,10 @@ int chase_symlinks_and_open(
                 return r;
         }
 
-        path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
-        if (path_fd < 0)
-                return path_fd;
+        r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
+        if (r < 0)
+                return r;
+        assert(path_fd >= 0);
 
         r = fd_reopen(path_fd, open_flags);
         if (r < 0)
@@ -1106,6 +1115,7 @@ int chase_symlinks_and_opendir(
         _cleanup_close_ int path_fd = -1;
         _cleanup_free_ char *p = NULL;
         DIR *d;
+        int r;
 
         if (!ret_dir)
                 return -EINVAL;
@@ -1122,9 +1132,10 @@ int chase_symlinks_and_opendir(
                 return 0;
         }
 
-        path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
-        if (path_fd < 0)
-                return path_fd;
+        r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
+        if (r < 0)
+                return r;
+        assert(path_fd >= 0);
 
         xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
         d = opendir(procfs_path);
@@ -1143,10 +1154,12 @@ int chase_symlinks_and_stat(
                 const char *root,
                 unsigned chase_flags,
                 char **ret_path,
-                struct stat *ret_stat) {
+                struct stat *ret_stat,
+                int *ret_fd) {
 
         _cleanup_close_ int path_fd = -1;
         _cleanup_free_ char *p = NULL;
+        int r;
 
         assert(path);
         assert(ret_stat);
@@ -1162,18 +1175,18 @@ int chase_symlinks_and_stat(
                 return 1;
         }
 
-        path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
-        if (path_fd < 0)
-                return path_fd;
+        r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd);
+        if (r < 0)
+                return r;
+        assert(path_fd >= 0);
 
         if (fstat(path_fd, ret_stat) < 0)
                 return -errno;
 
         if (ret_path)
                 *ret_path = TAKE_PTR(p);
-
-        if (chase_flags & CHASE_OPEN)
-                return TAKE_FD(path_fd);
+        if (ret_fd)
+                *ret_fd = TAKE_FD(path_fd);
 
         return 1;
 }
@@ -1307,6 +1320,17 @@ int fsync_directory_of_file(int fd) {
         return 0;
 }
 
+int fsync_full(int fd) {
+        int r, q;
+
+        /* Sync both the file and the directory */
+
+        r = fsync(fd) < 0 ? -errno : 0;
+        q = fsync_directory_of_file(fd);
+
+        return r < 0 ? r : q;
+}
+
 int fsync_path_at(int at_fd, const char *path) {
         _cleanup_close_ int opened_fd = -1;
         int fd;
diff --git a/shared/systemd/src/basic/fs-util.h b/shared/systemd/src/basic/fs-util.h
index c5527cc4..78d68be9 100644
--- a/shared/systemd/src/basic/fs-util.h
+++ b/shared/systemd/src/basic/fs-util.h
@@ -72,27 +72,28 @@ union inotify_event_buffer {
 };
 
 int inotify_add_watch_fd(int fd, int what, uint32_t mask);
+int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask);
 
 enum {
-        CHASE_PREFIX_ROOT = 1 << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
-        CHASE_NONEXISTENT = 1 << 1, /* If set, it's OK if the path doesn't actually exist. */
-        CHASE_NO_AUTOFS   = 1 << 2, /* If set, return -EREMOTE if autofs mount point found */
-        CHASE_SAFE        = 1 << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
-        CHASE_OPEN        = 1 << 4, /* If set, return an O_PATH object to the final component */
-        CHASE_TRAIL_SLASH = 1 << 5, /* If set, any trailing slash will be preserved */
-        CHASE_STEP        = 1 << 6, /* If set, just execute a single step of the normalization */
-        CHASE_NOFOLLOW    = 1 << 7, /* Only valid with CHASE_OPEN: when the path's right-most component refers to symlink return O_PATH fd of the symlink, rather than following it. */
-        CHASE_WARN        = 1 << 8, /* Emit an appropriate warning when an error is encountered */
+        CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
+        CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
+        CHASE_NO_AUTOFS   = 1 << 2, /* Return -EREMOTE if autofs mount point found */
+        CHASE_SAFE        = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */
+        CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
+        CHASE_STEP        = 1 << 5, /* Just execute a single step of the normalization */
+        CHASE_NOFOLLOW    = 1 << 6, /* Do not follow the path's right-most compontent. With ret_fd, when the path's
+                                     * right-most component refers to symlink, return O_PATH fd of the symlink. */
+        CHASE_WARN        = 1 << 7, /* Emit an appropriate warning when an error is encountered */
 };
 
 /* How many iterations to execute before returning -ELOOP */
 #define CHASE_SYMLINKS_MAX 32
 
-int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
+int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd);
 
 int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path);
 int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir);
-int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat);
+int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
 
 /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
 static inline void rmdir_and_free(char *p) {
@@ -114,6 +115,7 @@ void unlink_tempfilep(char (*p)[]);
 int unlinkat_deallocate(int fd, const char *name, int flags);
 
 int fsync_directory_of_file(int fd);
+int fsync_full(int fd);
 int fsync_path_at(int at_fd, const char *path);
 
 int syncfs_path(int atfd, const char *path);
diff --git a/shared/systemd/src/basic/hash-funcs.c b/shared/systemd/src/basic/hash-funcs.c
index 03695098..1b0d1292 100644
--- a/shared/systemd/src/basic/hash-funcs.c
+++ b/shared/systemd/src/basic/hash-funcs.c
@@ -55,11 +55,7 @@ void path_hash_func(const char *q, struct siphash *state) {
         }
 }
 
-int path_compare_func(const char *a, const char *b) {
-        return path_compare(a, b);
-}
-
-DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare_func);
+DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare);
 #endif /* NM_IGNORED */
 
 void trivial_hash_func(const void *p, struct siphash *state) {
diff --git a/shared/systemd/src/basic/hash-funcs.h b/shared/systemd/src/basic/hash-funcs.h
index 0d2d4283..7bb5d1cd 100644
--- a/shared/systemd/src/basic/hash-funcs.h
+++ b/shared/systemd/src/basic/hash-funcs.h
@@ -79,7 +79,6 @@ extern const struct hash_ops string_hash_ops;
 extern const struct hash_ops string_hash_ops_free_free;
 
 void path_hash_func(const char *p, struct siphash *state);
-int path_compare_func(const char *a, const char *b) _pure_;
 extern const struct hash_ops path_hash_ops;
 
 /* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
diff --git a/shared/systemd/src/basic/hashmap.c b/shared/systemd/src/basic/hashmap.c
index b1ae08cd..1aa00947 100644
--- a/shared/systemd/src/basic/hashmap.c
+++ b/shared/systemd/src/basic/hashmap.c
@@ -5,7 +5,6 @@
 #include <errno.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include "alloc-util.h"
 #include "fileio.h"
@@ -13,7 +12,7 @@
 #include "macro.h"
 #include "memory-util.h"
 #include "mempool.h"
-#include "missing.h"
+#include "missing_syscall.h"
 #include "process-util.h"
 #include "random-util.h"
 #include "set.h"
diff --git a/shared/systemd/src/basic/hostname-util.c b/shared/systemd/src/basic/hostname-util.c
index 60a94b96..00a92cb7 100644
--- a/shared/systemd/src/basic/hostname-util.c
+++ b/shared/systemd/src/basic/hostname-util.c
@@ -5,7 +5,6 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdio.h>
-#include <string.h>
 #include <sys/utsname.h>
 #include <unistd.h>
 
diff --git a/shared/systemd/src/basic/io-util.c b/shared/systemd/src/basic/io-util.c
index 9669c463..4f57f044 100644
--- a/shared/systemd/src/basic/io-util.c
+++ b/shared/systemd/src/basic/io-util.c
@@ -6,7 +6,6 @@
 #include <limits.h>
 #include <poll.h>
 #include <stdio.h>
-#include <time.h>
 #include <unistd.h>
 
 #include "io-util.h"
diff --git a/shared/systemd/src/basic/macro.h b/shared/systemd/src/basic/macro.h
index 43c51326..fc733366 100644
--- a/shared/systemd/src/basic/macro.h
+++ b/shared/systemd/src/basic/macro.h
@@ -316,17 +316,18 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
 
 extern void __coverity_panic__(void);
 
-static inline int __coverity_check__(int condition) {
+static inline void __coverity_check__(int condition) {
+        if (!condition)
+                __coverity_panic__();
+}
+
+static inline int __coverity_check_and_return__(int condition) {
         return condition;
 }
 
-#define assert_message_se(expr, message)                                \
-        do {                                                            \
-                if (__coverity_check__(!(expr)))                        \
-                        __coverity_panic__();                           \
-        } while (false)
+#define assert_message_se(expr, message) __coverity_check__(!!(expr))
 
-#define assert_log(expr, message) __coverity_check__(!!(expr))
+#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
 
 #else  /* ! __COVERITY__ */
 
diff --git a/shared/systemd/src/basic/memory-util.h b/shared/systemd/src/basic/memory-util.h
index 9cb8ac3c..46a6907a 100644
--- a/shared/systemd/src/basic/memory-util.h
+++ b/shared/systemd/src/basic/memory-util.h
@@ -11,6 +11,7 @@
 
 size_t page_size(void) _pure_;
 #define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+#define PAGE_ALIGN_DOWN(l) (l & ~(page_size() - 1))
 
 /* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
 static inline void memcpy_safe(void *dst, const void *src, size_t n) {
diff --git a/shared/systemd/src/basic/missing_random.h b/shared/systemd/src/basic/missing_random.h
new file mode 100644
index 00000000..2e76031b
--- /dev/null
+++ b/shared/systemd/src/basic/missing_random.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if USE_SYS_RANDOM_H
+#  include <sys/random.h>
+#else
+#  include <linux/random.h>
+#endif
+
+#ifndef GRND_NONBLOCK
+#define GRND_NONBLOCK 0x0001
+#endif
+
+#ifndef GRND_RANDOM
+#define GRND_RANDOM 0x0002
+#endif
diff --git a/shared/systemd/src/basic/parse-util.c b/shared/systemd/src/basic/parse-util.c
index 76ef6e09..96cc43a2 100644
--- a/shared/systemd/src/basic/parse-util.c
+++ b/shared/systemd/src/basic/parse-util.c
@@ -5,11 +5,9 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <linux/oom.h>
-#include <locale.h>
 #include <net/if.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/socket.h>
 
 #include "alloc-util.h"
@@ -17,7 +15,7 @@
 #include "extract-word.h"
 #include "locale-util.h"
 #include "macro.h"
-#include "missing.h"
+#include "missing_network.h"
 #include "parse-util.h"
 #include "process-util.h"
 #include "stat-util.h"
diff --git a/shared/systemd/src/basic/path-util.c b/shared/systemd/src/basic/path-util.c
index e39656bc..5bcc35e5 100644
--- a/shared/systemd/src/basic/path-util.c
+++ b/shared/systemd/src/basic/path-util.c
@@ -6,8 +6,6 @@
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
 #include <unistd.h>
 
 /* When we include libgen.h because we need dirname() we immediately
@@ -22,7 +20,6 @@
 #include "glob-util.h"
 #include "log.h"
 #include "macro.h"
-#include "missing.h"
 #include "nulstr-util.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -276,7 +273,7 @@ char **path_strv_resolve(char **l, const char *root) {
                 } else
                         t = *s;
 
-                r = chase_symlinks(t, root, 0, &u);
+                r = chase_symlinks(t, root, 0, &u, NULL);
                 if (r == -ENOENT) {
                         if (root) {
                                 u = TAKE_PTR(orig);
@@ -658,7 +655,9 @@ int find_binary(const char *name, char **ret) {
                         return 0;
                 }
 
-                last_error = -errno;
+                /* PATH entries which we don't have access to are ignored, as per tradition. */
+                if (errno != EACCES)
+                        last_error = -errno;
         }
 
         return last_error;
diff --git a/shared/systemd/src/basic/path-util.h b/shared/systemd/src/basic/path-util.h
index cd6216bb..88aef2f3 100644
--- a/shared/systemd/src/basic/path-util.h
+++ b/shared/systemd/src/basic/path-util.h
@@ -40,6 +40,10 @@
 #endif
 #endif /* NM_IGNORED */
 
+#ifndef DEFAULT_USER_PATH
+#  define DEFAULT_USER_PATH DEFAULT_PATH
+#endif
+
 bool is_path(const char *p) _pure_;
 int path_split_and_make_absolute(const char *p, char ***ret);
 bool path_is_absolute(const char *p) _pure_;
diff --git a/shared/systemd/src/basic/process-util.c b/shared/systemd/src/basic/process-util.c
index 317815fe..14561670 100644
--- a/shared/systemd/src/basic/process-util.c
+++ b/shared/systemd/src/basic/process-util.c
@@ -6,11 +6,9 @@
 #include <errno.h>
 #include <limits.h>
 #include <linux/oom.h>
-#include <signal.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/mman.h>
 #include <sys/mount.h>
 #include <sys/personality.h>
@@ -27,8 +25,8 @@
 
 #include "alloc-util.h"
 #include "architecture.h"
-#include "escape.h"
 #include "env-util.h"
+#include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -37,8 +35,10 @@
 #include "log.h"
 #include "macro.h"
 #include "memory-util.h"
-#include "missing.h"
+#include "missing_sched.h"
+#include "missing_syscall.h"
 #include "namespace-util.h"
+#include "path-util.h"
 #include "process-util.h"
 #include "raw-clone.h"
 #include "rlimit-util.h"
@@ -58,13 +58,17 @@
 #define COMM_MAX_LEN 128
 
 static int get_process_state(pid_t pid) {
+        _cleanup_free_ char *line = NULL;
         const char *p;
         char state;
         int r;
-        _cleanup_free_ char *line = NULL;
 
         assert(pid >= 0);
 
+        /* Shortcut: if we are enquired about our own state, we are obviously running */
+        if (pid == 0 || pid == getpid_cached())
+                return (unsigned char) 'R';
+
         p = procfs_file_alloca(pid, "stat");
 
         r = read_one_line_file(p, &line);
@@ -87,24 +91,35 @@ static int get_process_state(pid_t pid) {
 
 int get_process_comm(pid_t pid, char **ret) {
         _cleanup_free_ char *escaped = NULL, *comm = NULL;
-        const char *p;
         int r;
 
         assert(ret);
         assert(pid >= 0);
 
+        if (pid == 0 || pid == getpid_cached()) {
+                comm = new0(char, TASK_COMM_LEN + 1); /* Must fit in 16 byte according to prctl(2) */
+                if (!comm)
+                        return -ENOMEM;
+
+                if (prctl(PR_GET_NAME, comm) < 0)
+                        return -errno;
+        } else {
+                const char *p;
+
+                p = procfs_file_alloca(pid, "comm");
+
+                /* Note that process names of kernel threads can be much longer than TASK_COMM_LEN */
+                r = read_one_line_file(p, &comm);
+                if (r == -ENOENT)
+                        return -ESRCH;
+                if (r < 0)
+                        return r;
+        }
+
         escaped = new(char, COMM_MAX_LEN);
         if (!escaped)
                 return -ENOMEM;
 
-        p = procfs_file_alloca(pid, "comm");
-
-        r = read_one_line_file(p, &comm);
-        if (r == -ENOENT)
-                return -ESRCH;
-        if (r < 0)
-                return r;
-
         /* Escape unprintable characters, just in case, but don't grow the string beyond the underlying size */
         cellescape(escaped, COMM_MAX_LEN, comm);
 
@@ -507,6 +522,9 @@ int get_process_cwd(pid_t pid, char **cwd) {
 
         assert(pid >= 0);
 
+        if (pid == 0 || pid == getpid_cached())
+                return safe_getcwd(cwd);
+
         p = procfs_file_alloca(pid, "cwd");
 
         return get_process_link_contents(p, cwd);
diff --git a/shared/systemd/src/basic/process-util.h b/shared/systemd/src/basic/process-util.h
index 20f663e2..66853c6e 100644
--- a/shared/systemd/src/basic/process-util.h
+++ b/shared/systemd/src/basic/process-util.h
@@ -1,7 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-#include <alloca.h>
 #include <errno.h>
 #include <sched.h>
 #include <signal.h>
diff --git a/shared/systemd/src/basic/random-util.c b/shared/systemd/src/basic/random-util.c
index c70871bf..86917ca3 100644
--- a/shared/systemd/src/basic/random-util.c
+++ b/shared/systemd/src/basic/random-util.c
@@ -19,16 +19,13 @@
 #  include <sys/auxv.h>
 #endif
 
-#if USE_SYS_RANDOM_H
-#  include <sys/random.h>
-#else
-#  include <linux/random.h>
-#endif
-
 #include "alloc-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "io-util.h"
-#include "missing.h"
+#include "missing_random.h"
+#include "missing_syscall.h"
+#include "parse-util.h"
 #include "random-util.h"
 #include "siphash24.h"
 #include "time-util.h"
@@ -398,3 +395,28 @@ void random_bytes(void *p, size_t n) {
         /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
         pseudo_random_bytes(p, n);
 }
+
+#if 0 /* NM_IGNORED */
+size_t random_pool_size(void) {
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        /* Read pool size, if possible */
+        r = read_one_line_file("/proc/sys/kernel/random/poolsize", &s);
+        if (r < 0)
+                log_debug_errno(r, "Failed to read pool size from kernel: %m");
+        else {
+                unsigned sz;
+
+                r = safe_atou(s, &sz);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse pool size: %s", s);
+                else
+                        /* poolsize is in bits on 2.6, but we want bytes */
+                        return CLAMP(sz / 8, RANDOM_POOL_SIZE_MIN, RANDOM_POOL_SIZE_MAX);
+        }
+
+        /* Use the minimum as default, if we can't retrieve the correct value */
+        return RANDOM_POOL_SIZE_MIN;
+}
+#endif /* NM_IGNORED */
diff --git a/shared/systemd/src/basic/random-util.h b/shared/systemd/src/basic/random-util.h
index 148b6c78..facc11b9 100644
--- a/shared/systemd/src/basic/random-util.h
+++ b/shared/systemd/src/basic/random-util.h
@@ -31,3 +31,9 @@ static inline uint32_t random_u32(void) {
 }
 
 int rdrand(unsigned long *ret);
+
+/* Some limits on the pool sizes when we deal with the kernel random pool */
+#define RANDOM_POOL_SIZE_MIN 512U
+#define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
+
+size_t random_pool_size(void);
diff --git a/shared/systemd/src/basic/set.h b/shared/systemd/src/basic/set.h
index 2bb26c68..5f195617 100644
--- a/shared/systemd/src/basic/set.h
+++ b/shared/systemd/src/basic/set.h
@@ -102,8 +102,8 @@ static inline void *set_steal_first(Set *s) {
 /* no set_steal_first_key */
 /* no set_first_key */
 
-static inline void *set_first(Set *s) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), false, NULL);
+static inline void *set_first(const Set *s) {
+        return internal_hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL);
 }
 
 /* no set_next */
diff --git a/shared/systemd/src/basic/socket-util.c b/shared/systemd/src/basic/socket-util.c
index b822ed03..cded4545 100644
--- a/shared/systemd/src/basic/socket-util.c
+++ b/shared/systemd/src/basic/socket-util.c
@@ -13,7 +13,7 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+#include <sys/ioctl.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -25,7 +25,7 @@
 #include "log.h"
 #include "macro.h"
 #include "memory-util.h"
-#include "missing.h"
+#include "missing_socket.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
diff --git a/shared/systemd/src/basic/stat-util.c b/shared/systemd/src/basic/stat-util.c
index c9837fa1..071050f2 100644
--- a/shared/systemd/src/basic/stat-util.c
+++ b/shared/systemd/src/basic/stat-util.c
@@ -2,12 +2,9 @@
 
 #include "nm-sd-adapt-shared.h"
 
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <linux/magic.h>
 #include <sched.h>
-#include <sys/stat.h>
 #include <sys/statvfs.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -17,7 +14,8 @@
 #include "fd-util.h"
 #include "fs-util.h"
 #include "macro.h"
-#include "missing.h"
+#include "missing_fs.h"
+#include "missing_magic.h"
 #include "parse-util.h"
 #include "stat-util.h"
 #include "string-util.h"
@@ -340,7 +338,7 @@ int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
         if (r < 0)
                 return r;
 
-        return chase_symlinks(p, NULL, 0, ret);
+        return chase_symlinks(p, NULL, 0, ret, NULL);
 }
 
 int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
diff --git a/shared/systemd/src/basic/string-table.h b/shared/systemd/src/basic/string-table.h
index 42fe4f43..2d3cf814 100644
--- a/shared/systemd/src/basic/string-table.h
+++ b/shared/systemd/src/basic/string-table.h
@@ -5,7 +5,6 @@
 #include <errno.h>
 #include <stddef.h>
 #include <stdio.h>
-#include <string.h>
 #include <sys/types.h>
 
 #include "macro.h"
diff --git a/shared/systemd/src/basic/string-util.c b/shared/systemd/src/basic/string-util.c
index 2d34603e..3d2feb18 100644
--- a/shared/systemd/src/basic/string-util.c
+++ b/shared/systemd/src/basic/string-util.c
@@ -7,7 +7,6 @@
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include "alloc-util.h"
 #include "escape.h"
@@ -750,7 +749,7 @@ static void advance_offsets(
 }
 
 char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
-        const char *i, *begin = NULL;
+        const char *begin = NULL;
         enum {
                 STATE_OTHER,
                 STATE_ESCAPE,
@@ -758,7 +757,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                 STATE_CSO,
         } state = STATE_OTHER;
         char *obuf = NULL;
-        size_t osz = 0, isz, shift[2] = {};
+        size_t osz = 0, isz, shift[2] = {}, n_carriage_returns = 0;
         FILE *f;
 
         assert(ibuf);
@@ -769,6 +768,8 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
          * 1. Replaces TABs by 8 spaces
          * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
          * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
+         * 4. Strip trailing \r characters (since they would "move the cursor", but have no
+         *    other effect).
          *
          * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as
          * are any other special characters. Truncated ANSI sequences are left-as is too. This call is
@@ -784,14 +785,24 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
         if (!f)
                 return NULL;
 
-        for (i = *ibuf; i < *ibuf + isz + 1; i++) {
+        for (const char *i = *ibuf; i < *ibuf + isz + 1; i++) {
 
                 switch (state) {
 
                 case STATE_OTHER:
                         if (i >= *ibuf + isz) /* EOT */
                                 break;
-                        else if (*i == '\x1B')
+
+                        if (*i == '\r') {
+                                n_carriage_returns++;
+                                break;
+                        } else if (*i == '\n')
+                                /* Ignore carriage returns before new line */
+                                n_carriage_returns = 0;
+                        for (; n_carriage_returns > 0; n_carriage_returns--)
+                                fputc('\r', f);
+
+                        if (*i == '\x1B')
                                 state = STATE_ESCAPE;
                         else if (*i == '\t') {
                                 fputs("        ", f);
@@ -802,6 +813,8 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                         break;
 
                 case STATE_ESCAPE:
+                        assert(n_carriage_returns == 0);
+
                         if (i >= *ibuf + isz) { /* EOT */
                                 fputc('\x1B', f);
                                 advance_offsets(i - *ibuf, highlight, shift, 1);
@@ -822,6 +835,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                         break;
 
                 case STATE_CSI:
+                        assert(n_carriage_returns == 0);
 
                         if (i >= *ibuf + isz || /* EOT … */
                             !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */
@@ -836,6 +850,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                         break;
 
                 case STATE_CSO:
+                        assert(n_carriage_returns == 0);
 
                         if (i >= *ibuf + isz || /* EOT … */
                             (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
@@ -855,7 +870,6 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
                 fclose(f);
                 return mfree(obuf);
         }
-
         fclose(f);
 
         free_and_replace(*ibuf, obuf);
diff --git a/shared/systemd/src/basic/string-util.h b/shared/systemd/src/basic/string-util.h
index 76767afc..04cc82b3 100644
--- a/shared/systemd/src/basic/string-util.h
+++ b/shared/systemd/src/basic/string-util.h
@@ -1,7 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-#include <alloca.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <string.h>
@@ -45,6 +44,22 @@ static inline const char *strna(const char *s) {
         return s ?: "n/a";
 }
 
+static inline const char* yes_no(bool b) {
+        return b ? "yes" : "no";
+}
+
+static inline const char* true_false(bool b) {
+        return b ? "true" : "false";
+}
+
+static inline const char* one_zero(bool b) {
+        return b ? "1" : "0";
+}
+
+static inline const char* enable_disable(bool b) {
+        return b ? "enable" : "disable";
+}
+
 static inline bool isempty(const char *p) {
         return !p || !p[0];
 }
diff --git a/shared/systemd/src/basic/strv.c b/shared/systemd/src/basic/strv.c
index ba23178a..aa467132 100644
--- a/shared/systemd/src/basic/strv.c
+++ b/shared/systemd/src/basic/strv.c
@@ -7,7 +7,6 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include "alloc-util.h"
 #include "escape.h"
diff --git a/shared/systemd/src/basic/strv.h b/shared/systemd/src/basic/strv.h
index e80964ac..fbfa96a5 100644
--- a/shared/systemd/src/basic/strv.h
+++ b/shared/systemd/src/basic/strv.h
@@ -157,6 +157,18 @@ void strv_print(char **l);
                 _found;                                         \
         })
 
+#define ENDSWITH_SET(p, ...)                                    \
+        ({                                                      \
+                const char *_p = (p);                           \
+                char  *_found = NULL, **_i;                     \
+                STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) {      \
+                        _found = endswith(_p, *_i);             \
+                        if (_found)                             \
+                                break;                          \
+                }                                               \
+                _found;                                         \
+        })
+
 #define FOREACH_STRING(x, y, ...)                                       \
         for (char **_l = STRV_MAKE(({ x = y; }), ##__VA_ARGS__);        \
              x;                                                         \
diff --git a/shared/systemd/src/basic/time-util.c b/shared/systemd/src/basic/time-util.c
index aa790023..4411127a 100644
--- a/shared/systemd/src/basic/time-util.c
+++ b/shared/systemd/src/basic/time-util.c
@@ -6,9 +6,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/mman.h>
-#include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/timerfd.h>
 #include <sys/timex.h>
@@ -839,8 +837,12 @@ int parse_timestamp(const char *t, usec_t *usec) {
         }
         if (r == 0) {
                 bool with_tz = true;
+                char *colon_tz;
 
-                if (setenv("TZ", tz, 1) != 0) {
+                /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
+                colon_tz = strjoina(":", tz);
+
+                if (setenv("TZ", colon_tz, 1) != 0) {
                         shared->return_value = negative_errno();
                         _exit(EXIT_FAILURE);
                 }
@@ -1196,7 +1198,10 @@ bool ntp_synced(void) {
         if (adjtimex(&txc) < 0)
                 return false;
 
-        if (txc.status & STA_UNSYNC)
+        /* Consider the system clock synchronized if the reported maximum error is smaller than the maximum
+         * value (16 seconds). Ignore the STA_UNSYNC flag as it may have been set to prevent the kernel from
+         * touching the RTC. */
+        if (txc.maxerror >= 16000000)
                 return false;
 
         return true;
@@ -1262,6 +1267,7 @@ int get_timezones(char ***ret) {
                 }
 
                 strv_sort(zones);
+                strv_uniq(zones);
 
         } else if (errno != ENOENT)
                 return -errno;
@@ -1282,6 +1288,10 @@ bool timezone_is_valid(const char *name, int log_level) {
         if (isempty(name))
                 return false;
 
+        /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
+        if (streq(name, "UTC"))
+                return true;
+
         if (name[0] == '/')
                 return false;
 
@@ -1388,13 +1398,22 @@ bool clock_supported(clockid_t clock) {
 }
 
 #if 0 /* NM_IGNORED */
-int get_timezone(char **tz) {
+int get_timezone(char **ret) {
         _cleanup_free_ char *t = NULL;
         const char *e;
         char *z;
         int r;
 
         r = readlink_malloc("/etc/localtime", &t);
+        if (r == -ENOENT) {
+                /* If the symlink does not exist, assume "UTC", like glibc does*/
+                z = strdup("UTC");
+                if (!z)
+                        return -ENOMEM;
+
+                *ret = z;
+                return 0;
+        }
         if (r < 0)
                 return r; /* returns EINVAL if not a symlink */
 
@@ -1409,7 +1428,7 @@ int get_timezone(char **tz) {
         if (!z)
                 return -ENOMEM;
 
-        *tz = z;
+        *ret = z;
         return 0;
 }
 
@@ -1421,8 +1440,8 @@ struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
         return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
 }
 
-unsigned long usec_to_jiffies(usec_t u) {
-        static thread_local unsigned long hz = 0;
+static uint32_t sysconf_clock_ticks_cached(void) {
+        static thread_local uint32_t hz = 0;
         long r;
 
         if (hz == 0) {
@@ -1432,7 +1451,17 @@ unsigned long usec_to_jiffies(usec_t u) {
                 hz = r;
         }
 
-        return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
+        return hz;
+}
+
+uint32_t usec_to_jiffies(usec_t u) {
+        uint32_t hz = sysconf_clock_ticks_cached();
+        return DIV_ROUND_UP(u, USEC_PER_SEC / hz);
+}
+
+usec_t jiffies_to_usec(uint32_t j) {
+        uint32_t hz = sysconf_clock_ticks_cached();
+        return DIV_ROUND_UP(j * USEC_PER_SEC, hz);
 }
 
 usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
diff --git a/shared/systemd/src/basic/time-util.h b/shared/systemd/src/basic/time-util.h
index e3a529d9..4c371257 100644
--- a/shared/systemd/src/basic/time-util.h
+++ b/shared/systemd/src/basic/time-util.h
@@ -136,7 +136,8 @@ int get_timezone(char **timezone);
 time_t mktime_or_timegm(struct tm *tm, bool utc);
 struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
 
-unsigned long usec_to_jiffies(usec_t usec);
+uint32_t usec_to_jiffies(usec_t usec);
+usec_t jiffies_to_usec(uint32_t jiffies);
 
 bool in_utc_timezone(void);
 
diff --git a/shared/systemd/src/basic/tmpfile-util.c b/shared/systemd/src/basic/tmpfile-util.c
index c02ce3df..d8a689e0 100644
--- a/shared/systemd/src/basic/tmpfile-util.c
+++ b/shared/systemd/src/basic/tmpfile-util.c
@@ -2,7 +2,6 @@
 
 #include "nm-sd-adapt-shared.h"
 
-#include <stdio.h>
 #include <sys/mman.h>
 
 #include "alloc-util.h"
diff --git a/shared/systemd/src/basic/utf8.c b/shared/systemd/src/basic/utf8.c
index 3c51fa1f..ba28e129 100644
--- a/shared/systemd/src/basic/utf8.c
+++ b/shared/systemd/src/basic/utf8.c
@@ -28,7 +28,6 @@
 #include <errno.h>
 #include <stdbool.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include "alloc-util.h"
 #include "gunicode.h"
diff --git a/shared/systemd/src/basic/util.c b/shared/systemd/src/basic/util.c
index 23aa6b26..8a3f95dc 100644
--- a/shared/systemd/src/basic/util.c
+++ b/shared/systemd/src/basic/util.c
@@ -2,50 +2,23 @@
 
 #include "nm-sd-adapt-shared.h"
 
-#include <alloca.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <sched.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 #include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/statfs.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <unistd.h>
 
 #include "alloc-util.h"
-#include "btrfs-util.h"
 #include "build.h"
-#include "def.h"
-#include "device-nodes.h"
 #include "dirent-util.h"
 #include "env-file.h"
 #include "env-util.h"
 #include "fd-util.h"
 #include "fileio.h"
-#include "format-util.h"
-#include "hashmap.h"
 #include "hostname-util.h"
 #include "log.h"
 #include "macro.h"
-#include "missing.h"
 #include "parse-util.h"
-#include "path-util.h"
-#include "process-util.h"
-#include "procfs-util.h"
-#include "set.h"
-#include "signal-util.h"
 #include "stat-util.h"
 #include "string-util.h"
-#include "strv.h"
-#include "time-util.h"
-#include "umask-util.h"
-#include "user-util.h"
 #include "util.h"
 #include "virt.h"
 
diff --git a/shared/systemd/src/basic/util.h b/shared/systemd/src/basic/util.h
index 25e6ab81..6fc7480f 100644
--- a/shared/systemd/src/basic/util.h
+++ b/shared/systemd/src/basic/util.h
@@ -5,22 +5,6 @@
 
 #include "macro.h"
 
-static inline const char* yes_no(bool b) {
-        return b ? "yes" : "no";
-}
-
-static inline const char* true_false(bool b) {
-        return b ? "true" : "false";
-}
-
-static inline const char* one_zero(bool b) {
-        return b ? "1" : "0";
-}
-
-static inline const char* enable_disable(bool b) {
-        return b ? "enable" : "disable";
-}
-
 extern int saved_argc;
 extern char **saved_argv;
 
diff --git a/shared/systemd/src/shared/dns-domain.c b/shared/systemd/src/shared/dns-domain.c
index 92543ebe..9bba7359 100644
--- a/shared/systemd/src/shared/dns-domain.c
+++ b/shared/systemd/src/shared/dns-domain.c
@@ -14,7 +14,6 @@
 #include <endian.h>
 #include <netinet/in.h>
 #include <stdio.h>
-#include <string.h>
 #include <sys/socket.h>
 
 #include "alloc-util.h"