summary refs log tree commit diff
path: root/src/libnm-systemd-core
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2026-02-22 00:39:58 +0100
committerMichael Biebl <biebl@debian.org>2026-02-22 00:39:58 +0100
commit067fb576988f685e83ac8b0ae690334aff547c85 (patch)
tree204c7c8a0300691c53627cd3418cf54671ada1a8 /src/libnm-systemd-core
parent6de29285e533f4fec22a219013f3687edb6b7399 (diff)
New upstream version 1.56.0 upstream/1.56.0
Diffstat (limited to 'src/libnm-systemd-core')
-rw-r--r--src/libnm-systemd-core/meson.build1
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h2
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h2
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c2
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c5
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h94
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c35
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h10
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c156
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h17
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c436
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c158
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h9
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c128
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c1
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c13
-rw-r--r--src/libnm-systemd-core/src/systemd/_sd-common.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-device.h3
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h3
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h3
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h3
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ndisc.h3
22 files changed, 769 insertions, 317 deletions
diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build
index 15e1c8a6..00d8a77b 100644
--- a/src/libnm-systemd-core/meson.build
+++ b/src/libnm-systemd-core/meson.build
@@ -12,6 +12,7 @@ libnm_systemd_core = static_library(
     'src/libsystemd-network/sd-dhcp6-lease.c',
     'src/libsystemd/sd-device/device-private.c',
     'src/libsystemd/sd-device/sd-device.c',
+    'src/libsystemd/sd-device/device-util.c',
     'src/libsystemd/sd-event/event-util.c',
     'src/libsystemd/sd-event/sd-event.c',
     'src/libsystemd/sd-id128/id128-util.c',
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
index ecd62ea8..2ef8dc86 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
@@ -8,8 +8,8 @@
 #include <net/ethernet.h>
 #include <netinet/in.h>
 
-#include "sd-event.h"
 #include "sd-dhcp6-client.h"
+#include "sd-event.h"
 
 #include "dhcp-duid-internal.h"
 #include "dhcp6-client-internal.h"
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
index 60cd84f2..62c38588 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
@@ -8,10 +8,10 @@
 #include <inttypes.h>
 
 #include "sd-dhcp6-lease.h"
-#include "dns-resolver-internal.h"
 
 #include "dhcp6-option.h"
 #include "dhcp6-protocol.h"
+#include "dns-resolver-internal.h"
 #include "macro.h"
 #include "set.h"
 #include "time-util.h"
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
index 03732692..1c695a87 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
@@ -6,13 +6,13 @@
 #include "nm-sd-adapt-core.h"
 
 #include <errno.h>
+#include <linux/if_packet.h>
 #include <netinet/in.h>
 #include <netinet/ip6.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <linux/if_packet.h>
 
 #include "dhcp6-internal.h"
 #include "dhcp6-protocol.h"
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
index 5655c0d7..a7cd6a24 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
@@ -6,11 +6,11 @@
 #include "nm-sd-adapt-core.h"
 
 #include <errno.h>
-#include <sys/ioctl.h>
 #ifdef __GLIBC__
 #include <linux/if_arp.h>
 #endif
 #include <linux/if_infiniband.h>
+#include <sys/ioctl.h>
 
 #include "sd-dhcp6-client.h"
 
@@ -1295,7 +1295,8 @@ static int client_receive_message(
 
         sd_dhcp6_client *client = ASSERT_PTR(userdata);
         DHCP6_CLIENT_DONT_DESTROY(client);
-        /* This needs to be initialized with zero. See #20741. */
+        /* This needs to be initialized with zero. See #20741.
+         * The issue is fixed on glibc-2.35 (8fba672472ae0055387e9315fc2eddfa6775ca79). */
         CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
         struct iovec iov;
         union sockaddr_union sa = {};
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h
index a465eb25..cbd89849 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h
@@ -13,6 +13,43 @@
 struct sd_device {
         unsigned n_ref;
 
+        /* syspath */
+        char *syspath;
+        const char *devpath;
+        const char *sysnum;
+        char *sysname;
+
+        /* only set when device is passed through netlink */
+        sd_device_action_t action;
+        uint64_t seqnum;
+
+        /* basic kernel properties */
+        char *subsystem;
+        char *driver_subsystem; /* only set for the 'drivers' subsystem */
+        char *driver;
+        char *devtype;
+
+        /* device node properties */
+        char *devname;
+        dev_t devnum;
+        mode_t devmode;
+        uid_t devuid;
+        gid_t devgid;
+
+        /* block device properties */
+        uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */
+
+        /* network interface properties */
+        int ifindex;
+
+        /* determined by devnnum, ifindex, subsystem, and sysname */
+        char *device_id;
+
+        /* sysfs attributes */
+        Hashmap *sysattr_values; /* cached sysattr values */
+        Set *sysattrs; /* names of sysattrs */
+        Iterator sysattrs_iterator;
+
         /* The database version indicates the supported features by the udev database.
          * This is saved and parsed in V field.
          *
@@ -21,68 +58,38 @@ struct sd_device {
          */
         unsigned database_version;
 
-        sd_device *parent;
+        /* when device is initialized by udevd */
+        usec_t usec_initialized;
 
-        OrderedHashmap *properties;
+        /* properties */
+        OrderedHashmap *properties; /* all properties set from uevent and by udevd */
         Iterator properties_iterator;
         uint64_t properties_generation; /* changes whenever the properties are changed */
         uint64_t properties_iterator_generation; /* generation when iteration was started */
+        OrderedHashmap *properties_db; /* the subset of the properties that should be written to the db */
+        char **properties_strv; /* the properties hashmap as a strv */
+        char *properties_nulstr; /* the same as a nulstr */
+        size_t properties_nulstr_len;
 
-        /* the subset of the properties that should be written to the db */
-        OrderedHashmap *properties_db;
-
-        Hashmap *sysattr_values; /* cached sysattr values */
-
-        Set *sysattrs; /* names of sysattrs */
-        Iterator sysattrs_iterator;
-
+        /* TAG keyword */
         Set *all_tags, *current_tags;
         Iterator all_tags_iterator, current_tags_iterator;
         uint64_t all_tags_iterator_generation, current_tags_iterator_generation; /* generation when iteration was started */
         uint64_t tags_generation; /* changes whenever the tags are changed */
 
+        /* SYMLINK keyword */
         Set *devlinks;
         Iterator devlinks_iterator;
         uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
         uint64_t devlinks_iterator_generation; /* generation when iteration was started */
         int devlink_priority;
 
+        /* parent and child devices */
+        sd_device *parent;
         Hashmap *children;
         Iterator children_iterator;
         bool children_enumerated;
 
-        int ifindex;
-        char *devtype;
-        char *devname;
-        dev_t devnum;
-
-        char **properties_strv; /* the properties hashmap as a strv */
-        char *properties_nulstr; /* the same as a nulstr */
-        size_t properties_nulstr_len;
-
-        char *syspath;
-        const char *devpath;
-        const char *sysnum;
-        char *sysname;
-
-        char *subsystem;
-        char *driver_subsystem; /* only set for the 'drivers' subsystem */
-        char *driver;
-
-        char *device_id;
-
-        usec_t usec_initialized;
-
-        mode_t devmode;
-        uid_t devuid;
-        gid_t devgid;
-
-        uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */
-
-        /* only set when device is passed through netlink */
-        sd_device_action_t action;
-        uint64_t seqnum;
-
         bool parent_set:1; /* no need to try to reload parent */
         bool sysattrs_read:1; /* don't try to re-read sysattrs once read */
         bool property_tags_outdated:1; /* need to update TAGS= or CURRENT_TAGS= property */
@@ -92,7 +99,6 @@ struct sd_device {
         bool driver_set:1; /* don't reread driver */
         bool uevent_loaded:1; /* don't reread uevent */
         bool db_loaded; /* don't reread db */
-
         bool is_initialized:1;
         bool sealed:1; /* don't read more information from uevent/db */
         bool db_persist:1; /* don't clean up the db when switching from initrd to real root */
@@ -106,6 +112,8 @@ static inline int device_add_property_internal(sd_device *device, const char *ke
 
 int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
 int device_set_ifindex(sd_device *device, const char *ifindex);
+int device_set_devuid(sd_device *device, const char *uid);
+int device_set_devgid(sd_device *device, const char *gid);
 int device_set_devmode(sd_device *device, const char *devmode);
 int device_set_devname(sd_device *device, const char *devname);
 int device_set_devtype(sd_device *device, const char *devtype);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
index 6b17e772..aa73184d 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
@@ -119,6 +119,10 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret) {
 
         assert(device);
 
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
         r = device_read_db(device);
         if (r < 0)
                 return r;
@@ -137,6 +141,10 @@ int device_get_devnode_uid(sd_device *device, uid_t *ret) {
 
         assert(device);
 
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
         r = device_read_db(device);
         if (r < 0)
                 return r;
@@ -150,7 +158,9 @@ int device_get_devnode_uid(sd_device *device, uid_t *ret) {
         return 0;
 }
 
-static int device_set_devuid(sd_device *device, const char *uid) {
+#endif  /* NM_IGNORED */
+
+int device_set_devuid(sd_device *device, const char *uid) {
         uid_t u;
         int r;
 
@@ -175,6 +185,10 @@ int device_get_devnode_gid(sd_device *device, gid_t *ret) {
 
         assert(device);
 
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
         r = device_read_db(device);
         if (r < 0)
                 return r;
@@ -188,7 +202,7 @@ int device_get_devnode_gid(sd_device *device, gid_t *ret) {
         return 0;
 }
 
-static int device_set_devgid(sd_device *device, const char *gid) {
+int device_set_devgid(sd_device *device, const char *gid) {
         gid_t g;
         int r;
 
@@ -208,6 +222,8 @@ static int device_set_devgid(sd_device *device, const char *gid) {
         return 0;
 }
 
+#if 0  /* NM_IGNORED */
+
 int device_set_action(sd_device *device, sd_device_action_t a) {
         int r;
 
@@ -432,10 +448,11 @@ static int device_verify(sd_device *device) {
                 return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
                                               "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
 
-        if (streq(device->subsystem, "drivers")) {
+        if (device_in_subsystem(device, "drivers")) {
                 r = device_set_drivers_subsystem(device);
                 if (r < 0)
-                        return r;
+                        return log_device_debug_errno(device, r,
+                                                      "sd-device: Failed to set driver subsystem: %m");
         }
 
         device->sealed = true;
@@ -682,8 +699,8 @@ int device_clone_with_db(sd_device *device, sd_device **ret) {
 void device_cleanup_tags(sd_device *device) {
         assert(device);
 
-        device->all_tags = set_free_free(device->all_tags);
-        device->current_tags = set_free_free(device->current_tags);
+        device->all_tags = set_free(device->all_tags);
+        device->current_tags = set_free(device->current_tags);
         device->property_tags_outdated = true;
         device->tags_generation++;
 }
@@ -691,7 +708,7 @@ void device_cleanup_tags(sd_device *device) {
 void device_cleanup_devlinks(sd_device *device) {
         assert(device);
 
-        set_free_free(device->devlinks);
+        set_free(device->devlinks);
         device->devlinks = NULL;
         device->property_devlinks_outdated = true;
         device->devlinks_generation++;
@@ -955,8 +972,4 @@ static const char* const device_action_table[_SD_DEVICE_ACTION_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(device_action, sd_device_action_t);
-
-void dump_device_action_table(void) {
-        DUMP_STRING_TABLE(device_action, sd_device_action_t, _SD_DEVICE_ACTION_MAX);
-}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
index eab54203..e07ca602 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
@@ -9,6 +9,7 @@
 
 #include "sd-device.h"
 
+#include "chase.h"
 #include "macro.h"
 
 int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum);
@@ -17,8 +18,10 @@ int device_new_from_strv(sd_device **ret, char **strv);
 
 int device_opendir(sd_device *device, const char *subdir, DIR **ret);
 
+int device_get_sysnum_unsigned(sd_device *device, unsigned *ret);
 int device_get_property_bool(sd_device *device, const char *key);
 int device_get_property_int(sd_device *device, const char *key, int *ret);
+int device_get_ifname(sd_device *device, const char **ret);
 int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
 int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value);
 static inline int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
@@ -31,9 +34,11 @@ int device_get_devnode_mode(sd_device *device, mode_t *ret);
 int device_get_devnode_uid(sd_device *device, uid_t *ret);
 int device_get_devnode_gid(sd_device *device, gid_t *ret);
 
+#if 0  /* NM_IGNORED */
+int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **ret_resolved, int *ret_fd);
 void device_clear_sysattr_cache(sd_device *device);
-int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
-int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
+int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error);
+#endif  /* NM_IGNORED */
 
 void device_seal(sd_device *device);
 void device_set_is_initialized(sd_device *device);
@@ -76,4 +81,3 @@ int device_read_uevent_file(sd_device *device);
 int device_set_action(sd_device *device, sd_device_action_t a);
 sd_device_action_t device_action_from_string(const char *s) _pure_;
 const char* device_action_to_string(sd_device_action_t a) _const_;
-void dump_device_action_table(void);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c
new file mode 100644
index 00000000..e289523f
--- /dev/null
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.c
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nm-sd-adapt-shared.h"
+
+#include "device-private.h"
+#include "device-util.h"
+#include "devnum-util.h"
+#include "fd-util.h"
+#include "string-util.h"
+#include "strv.h"
+
+#if 0  /* NM_IGNORED */
+int devname_from_devnum(mode_t mode, dev_t devnum, char **ret) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        const char *devname;
+        int r;
+
+        assert(ret);
+
+        if (devnum_is_zero(devnum))
+                return device_path_make_inaccessible(mode, ret);
+
+        r = device_new_from_mode_and_devnum(&dev, mode, devnum);
+        if (r < 0)
+                return r;
+
+        r = sd_device_get_devname(dev, &devname);
+        if (r < 0)
+                return r;
+
+        return strdup_to(ret, devname);
+}
+
+int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret_devname) {
+        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        int r;
+
+        r = device_new_from_mode_and_devnum(&dev, mode, devnum);
+        if (r < 0)
+                return r;
+
+        fd = sd_device_open(dev, flags);
+        if (fd < 0)
+                return fd;
+
+        if (ret_devname) {
+                const char *devname;
+
+                r = sd_device_get_devname(dev, &devname);
+                if (r < 0)
+                        return r;
+
+                r = strdup_to(ret_devname, devname);
+                if (r < 0)
+                        return r;
+        }
+
+        return TAKE_FD(fd);
+}
+
+static int add_string_field(
+                sd_device *device,
+                const char *field,
+                int (*func)(sd_device *dev, const char **s),
+                char ***strv) {
+
+        const char *s;
+        int r;
+
+        assert(device);
+        assert(field);
+        assert(func);
+        assert(strv);
+
+        r = func(device, &s);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"%s\" property, ignoring: %m", field);
+        if (r >= 0)
+                (void) strv_extend_assignment(strv, field, s);
+
+        return 0;
+}
+
+char** device_make_log_fields(sd_device *device) {
+        _cleanup_strv_free_ char **strv = NULL;
+        dev_t devnum;
+        int ifindex;
+        sd_device_action_t action;
+        uint64_t seqnum, diskseq;
+        int r;
+
+        assert(device);
+
+        (void) add_string_field(device, "SYSPATH", sd_device_get_syspath, &strv);
+        (void) add_string_field(device, "SUBSYSTEM", sd_device_get_subsystem, &strv);
+        (void) add_string_field(device, "DEVTYPE", sd_device_get_devtype, &strv);
+        (void) add_string_field(device, "DRIVER", sd_device_get_driver, &strv);
+        (void) add_string_field(device, "DEVPATH", sd_device_get_devpath, &strv);
+        (void) add_string_field(device, "DEVNAME", sd_device_get_devname, &strv);
+        (void) add_string_field(device, "SYSNAME", sd_device_get_sysname, &strv);
+        (void) add_string_field(device, "SYSNUM", sd_device_get_sysnum, &strv);
+
+        r = sd_device_get_devnum(device, &devnum);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"DEVNUM\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "DEVNUM="DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(devnum));
+
+        r = sd_device_get_ifindex(device, &ifindex);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"IFINDEX\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "IFINDEX=%i", ifindex);
+
+        r = sd_device_get_action(device, &action);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"ACTION\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "ACTION=%s", device_action_to_string(action));
+
+        r = sd_device_get_seqnum(device, &seqnum);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"SEQNUM\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "SEQNUM=%"PRIu64, seqnum);
+
+        r = sd_device_get_diskseq(device, &diskseq);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get device \"DISKSEQ\" property, ignoring: %m");
+        if (r >= 0)
+                (void) strv_extendf(&strv, "DISKSEQ=%"PRIu64, diskseq);
+
+        return TAKE_PTR(strv);
+}
+#endif  /* NM_IGNORED */
+
+bool device_in_subsystem(sd_device *device, const char *subsystem) {
+        const char *s = NULL;
+
+        assert(device);
+
+        (void) sd_device_get_subsystem(device, &s);
+        return streq_ptr(s, subsystem);
+}
+
+#if 0 /* NM_IGNORED */
+bool device_is_devtype(sd_device *device, const char *devtype) {
+        const char *s = NULL;
+
+        assert(device);
+
+        (void) sd_device_get_devtype(device, &s);
+        return streq_ptr(s, devtype);
+}
+#endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
index b17993d5..070e564a 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
@@ -110,6 +110,19 @@ bool device_is_devtype(sd_device *device, const char *devtype);
 static inline bool device_property_can_set(const char *property) {
         return property &&
                 !STR_IN_SET(property,
-                            "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
-                            "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS");
+                            /* basic properties set by kernel, only in netlink event */
+                            "ACTION", "SEQNUM", "SYNTH_UUID",
+                            /* basic properties set by kernel, both in netlink event and uevent file */
+                            "DEVPATH", "DEVPATH_OLD", "SUBSYSTEM", "DEVTYPE", "DRIVER", "MODALIAS",
+                            /* device node */
+                            "DEVNAME", "DEVMODE", "DEVUID", "DEVGID", "MAJOR", "MINOR",
+                            /* block device */
+                            "DISKSEQ", "PARTN",
+                            /* network interface (INTERFACE_OLD is set by udevd) */
+                            "IFINDEX", "INTERFACE", "INTERFACE_OLD",
+                            /* basic properties set by udevd */
+                            "DEVLINKS", "TAGS", "CURRENT_TAGS", "USEC_INITIALIZED", "UDEV_DATABASE_VERSION") &&
+                /* Similar to SYNTH_UUID, but set based on KEY=VALUE arguments passed by userspace.
+                 * See kernel's f36776fafbaa0094390dd4e7e3e29805e0b82730 (v4.13) */
+                !startswith(property, "SYNTH_ARG_");
 }
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
index 480223d0..fce184df 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
@@ -375,7 +375,7 @@ _public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
         assert_return(ret, -EINVAL);
         assert_return(ifindex > 0, -EINVAL);
 
-        r = rtnl_get_ifname_full(NULL, ifindex, &ifname, NULL);
+        r = rtnl_get_ifname(NULL, ifindex, &ifname);
         if (r < 0)
                 return r;
 
@@ -516,7 +516,7 @@ _public_ int sd_device_new_from_subsystem_sysname(
                         if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
                                 r = device_new_from_path_join(&device, subsystem, subsys, "drivers", "/sys/bus/", subsys, "/drivers", NULL);
                         else
-                                r = device_new_from_path_join(&device, subsystem, subsys, sep, "/sys/bus/", subsys, "/drivers/", sep);
+                                r = device_new_from_path_join(&device, subsystem, subsys, sysname + (sep - name), "/sys/bus/", subsys, "/drivers/", sep);
                         if (r < 0)
                                 return r;
                 }
@@ -776,16 +776,24 @@ static int handle_uevent_line(
         assert(major);
         assert(minor);
 
+        if (streq(key, "SUBSYSTEM"))
+                return device_set_subsystem(device, value);
         if (streq(key, "DEVTYPE"))
                 return device_set_devtype(device, value);
         if (streq(key, "IFINDEX"))
                 return device_set_ifindex(device, value);
         if (streq(key, "DEVNAME"))
                 return device_set_devname(device, value);
+        if (streq(key, "DEVUID"))
+                return device_set_devuid(device, value);
+        if (streq(key, "DEVGID"))
+                return device_set_devgid(device, value);
         if (streq(key, "DEVMODE"))
                 return device_set_devmode(device, value);
         if (streq(key, "DISKSEQ"))
                 return device_set_diskseq(device, value);
+        if (streq(key, "DRIVER"))
+                return device_set_driver(device, value);
         if (streq(key, "MAJOR"))
                 *major = value;
         else if (streq(key, "MINOR"))
@@ -800,7 +808,7 @@ int device_read_uevent_file(sd_device *device) {
         _cleanup_free_ char *uevent = NULL;
         const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL;
         char *path;
-        size_t uevent_len = 0;
+        size_t uevent_len;
         int r;
 
         enum {
@@ -882,6 +890,13 @@ int device_read_uevent_file(sd_device *device) {
                         log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, strna(minor), path);
         }
 
+        if (device_in_subsystem(device, "drivers")) {
+                r = device_set_drivers_subsystem(device);
+                if (r < 0)
+                        log_device_debug_errno(device, r,
+                                               "sd-device: Failed to set driver subsystem, ignoring: %m");
+        }
+
         return 0;
 }
 
@@ -904,6 +919,21 @@ _public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
 }
 
 #if 0 /* NM_IGNORED */
+int device_get_ifname(sd_device *device, const char **ret) {
+        int r;
+
+        assert_return(device, -EINVAL);
+
+        /* First, check if the device is a network interface. */
+        r = sd_device_get_ifindex(device, NULL);
+        if (r < 0)
+                return r;
+
+        /* The sysname and ifname may be different, as '!' in sysname are replaced with '/'.
+         * For network interfaces, we can use INTERFACE property. */
+        return sd_device_get_property_value(device, "INTERFACE", ret);
+}
+
 _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
         int r;
 
@@ -1228,6 +1258,10 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
 
         assert_return(device, -EINVAL);
 
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
         if (!device->subsystem_set) {
                 _cleanup_free_ char *subsystem = NULL;
                 const char *syspath;
@@ -1371,6 +1405,10 @@ int device_set_driver(sd_device *device, const char *driver) {
 _public_ int sd_device_get_driver(sd_device *device, const char **ret) {
         assert_return(device, -EINVAL);
 
+        r = device_read_uevent_file(device);
+        if (r < 0)
+                return r;
+
         if (!device->driver_set) {
                 _cleanup_free_ char *driver = NULL;
                 const char *syspath;
@@ -1497,6 +1535,26 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
         return 0;
 }
 
+int device_get_sysnum_unsigned(sd_device *device, unsigned *ret) {
+        int r;
+
+        assert(device);
+
+        const char *s;
+        r = sd_device_get_sysnum(device, &s);
+        if (r < 0)
+                return r;
+
+        unsigned n;
+        r = safe_atou_full(s, SAFE_ATO_REFUSE_PLUS_MINUS | SAFE_ATO_REFUSE_LEADING_WHITESPACE | 10, &n);
+        if (r < 0)
+                return r;
+
+        if (ret)
+                *ret = n;
+        return 0;
+}
+
 _public_ int sd_device_get_action(sd_device *device, sd_device_action_t *ret) {
         assert_return(device, -EINVAL);
 
@@ -1726,18 +1784,13 @@ _public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
 
         if (!device->device_id) {
                 _cleanup_free_ char *id = NULL;
-                const char *subsystem;
                 dev_t devnum;
                 int ifindex, r;
 
-                r = sd_device_get_subsystem(device, &subsystem);
-                if (r < 0)
-                        return r;
-
                 if (sd_device_get_devnum(device, &devnum) >= 0) {
                         /* use dev_t — b259:131072, c254:0 */
                         if (asprintf(&id, "%c" DEVNUM_FORMAT_STR,
-                                     streq(subsystem, "block") ? 'b' : 'c',
+                                     device_in_subsystem(device, "block") ? 'b' : 'c',
                                      DEVNUM_FORMAT_VAL(devnum)) < 0)
                                 return -ENOMEM;
                 } else if (sd_device_get_ifindex(device, &ifindex) >= 0) {
@@ -1755,13 +1808,18 @@ _public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
                         if (r == O_DIRECTORY)
                                 return -EINVAL;
 
-                        if (streq(subsystem, "drivers")) {
+                        if (device_in_subsystem(device, "drivers"))
                                 /* the 'drivers' pseudo-subsystem is special, and needs the real
                                  * subsystem encoded as well */
-                                assert(device->driver_subsystem);
-                                id = strjoin("+drivers:", device->driver_subsystem, ":", sysname);
-                        } else
+                                id = strjoin("+drivers:", ASSERT_PTR(device->driver_subsystem), ":", sysname);
+                        else {
+                                const char *subsystem;
+                                r = sd_device_get_subsystem(device, &subsystem);
+                                if (r < 0)
+                                        return r;
+
                                 id = strjoin("+", subsystem, ":", sysname);
+                        }
                         if (!id)
                                 return -ENOMEM;
                 }
@@ -2355,134 +2413,224 @@ void device_clear_sysattr_cache(sd_device *device) {
         device->sysattr_values = hashmap_free(device->sysattr_values);
 }
 
-int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
-        _unused_ _cleanup_free_ char *old_value = NULL;
-        _cleanup_free_ char *new_key = NULL;
+typedef struct SysAttrCacheEntry {
+        char *key;
+        char *value;
+        int error;
+} SysAttrCacheEntry;
+
+static SysAttrCacheEntry* sysattr_cache_entry_free(SysAttrCacheEntry *p) {
+        if (!p)
+                return NULL;
+
+        free(p->key);
+        free(p->value);
+        return mfree(p);
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                sysattr_cache_hash_ops,
+                char, path_hash_func, path_compare,
+                SysAttrCacheEntry, sysattr_cache_entry_free);
+
+static int device_cache_sysattr_value_full(sd_device *device, char *key, char *value, int error, bool ignore_uevent) {
         int r;
 
         assert(device);
         assert(key);
+        assert(value || error > 0);
 
-        /* This takes the reference of the input value. The input value may be NULL.
-         * This replaces the value if it already exists. */
+        /* This takes the reference of the input arguments when cached, hence the caller must not free them
+         * when a positive return value is returned. The input value may be NULL. This replaces an already
+         * existing entry. */
 
-        /* First, remove the old cache entry. So, we do not need to clear cache on error. */
-        old_value = hashmap_remove2(device->sysattr_values, key, (void **) &new_key);
-        if (!new_key) {
-                new_key = strdup(key);
-                if (!new_key)
-                        return -ENOMEM;
-        }
+        if (ignore_uevent && streq(last_path_component(key), "uevent"))
+                return 0; /* not cached */
 
-        r = hashmap_ensure_put(&device->sysattr_values, &path_hash_ops_free_free, new_key, value);
+        /* Remove the old cache entry. So, we do not need to clear cache on error. */
+        sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, key));
+
+        /* We use ENOANO as a recognizable error code when we have not read the attribute. */
+        if (error == ENOANO)
+                error = ESTALE;
+
+        _cleanup_free_ SysAttrCacheEntry *entry = new(SysAttrCacheEntry, 1);
+        if (!entry)
+                return -ENOMEM;
+
+        *entry = (SysAttrCacheEntry) {
+                .key = key,
+                .value = value,
+                .error = error,
+        };
+
+        r = hashmap_ensure_put(&device->sysattr_values, &sysattr_cache_hash_ops, entry->key, entry);
         if (r < 0)
                 return r;
 
-        TAKE_PTR(new_key);
+        TAKE_PTR(entry);
+        return 1; /* cached */
+}
 
-        return 0;
+int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error) {
+        return device_cache_sysattr_value_full(device, key, value, error, /* ignore_uevent = */ true);
 }
 
-int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
-        const char *k = NULL, *value;
+static int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
+        SysAttrCacheEntry *entry;
 
         assert(device);
         assert(key);
 
-        value = hashmap_get2(device->sysattr_values, key, (void **) &k);
-        if (!k)
-                return -ESTALE; /* We have not read the attribute. */
-        if (!value)
-                return -ENOENT; /* We have looked up the attribute before and it did not exist. */
+        entry = hashmap_get(device->sysattr_values, key);
+        if (!entry)
+                return -ENOANO; /* We have not read the attribute. */
+        if (!entry->value) {
+                /* We have looked up the attribute before and failed. Return the cached error code. */
+                assert(entry->error > 0);
+                return -entry->error;
+        }
         if (ret_value)
-                *ret_value = value;
+                *ret_value = entry->value;
         return 0;
 }
 
-/* We cache all sysattr lookups. If an attribute does not exist, it is stored
- * with a NULL value in the cache, otherwise the returned string is stored */
-_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
-        _cleanup_free_ char *value = NULL, *path = NULL;
-        const char *syspath;
-        struct stat statbuf;
+int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **ret_resolved, int *ret_fd) {
         int r;
 
-        assert_return(device, -EINVAL);
-        assert_return(sysattr, -EINVAL);
+        assert(device);
+        assert(path);
 
-        /* look for possibly already cached result */
-        r = device_get_cached_sysattr_value(device, sysattr, ret_value);
-        if (r != -ESTALE)
+        const char *syspath;
+        r = sd_device_get_syspath(device, &syspath);
+        if (r < 0)
                 return r;
 
-        r = sd_device_get_syspath(device, &syspath);
+        /* Here, CHASE_PREFIX_ROOT is borrowed. If the flag is set or the specified path is relative, then
+         * the path will be prefixed with the syspath. Note, we do not pass CHASE_PREFIX_ROOT flag with
+         * syspath as root to chase(), but we manually concatenate the specified path with syspath before
+         * calling chase(). Otherwise, we cannot set/get attributes of parent or sibling devices. */
+        _cleanup_free_ char *prefixed = NULL;
+        if (FLAGS_SET(flags, CHASE_PREFIX_ROOT) || !path_is_absolute(path)) {
+                prefixed = path_join(syspath, path);
+                if (!prefixed)
+                        return -ENOMEM;
+                path = prefixed;
+                flags &= ~CHASE_PREFIX_ROOT;
+        }
+
+        _cleanup_free_ char *resolved = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        r = chase(path, /* root = */ NULL, CHASE_NO_AUTOFS | flags, &resolved, ret_fd ? &fd : NULL);
         if (r < 0)
                 return r;
 
-        path = path_join(syspath, sysattr);
-        if (!path)
-                return -ENOMEM;
+        /* Refuse to reading/writing files outside of sysfs. */
+        if (!path_startswith(resolved, "/sys/"))
+                return -EINVAL;
 
-        if (lstat(path, &statbuf) < 0) {
-                int k;
+        if (ret_resolved) {
+                /* Always return relative path. */
+                r = path_make_relative(syspath, resolved, ret_resolved);
+                if (r < 0)
+                        return r;
+        }
 
-                r = -errno;
+        if (ret_fd)
+                *ret_fd = TAKE_FD(fd);
 
-                /* remember that we could not access the sysattr */
-                k = device_cache_sysattr_value(device, sysattr, NULL);
-                if (k < 0)
-                        log_device_debug_errno(device, k,
-                                               "sd-device: failed to cache attribute '%s' with NULL, ignoring: %m",
-                                               sysattr);
+        return 0;
+}
+
+_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
+        _cleanup_free_ char *resolved = NULL, *value = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        int r;
 
+        assert_return(device, -EINVAL);
+        assert_return(sysattr, -EINVAL);
+
+        /* Look for possibly already cached result. */
+        r = device_get_cached_sysattr_value(device, sysattr, ret_value);
+        if (r != -ENOANO)
                 return r;
-        } else if (S_ISLNK(statbuf.st_mode)) {
-                /* Some core links return only the last element of the target path,
-                 * these are just values, the paths should not be exposed. */
-                if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
-                        r = readlink_value(path, &value);
-                        if (r < 0)
-                                return r;
-                } else
-                        return -EINVAL;
-        } else if (S_ISDIR(statbuf.st_mode))
-                /* skip directories */
-                return -EISDIR;
-        else if (!(statbuf.st_mode & S_IRUSR))
-                /* skip non-readable files */
-                return -EPERM;
-        else {
-                size_t size;
-
-                /* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to
-                 * also get the size of the result. See issue #20025. */
-                r = read_full_virtual_file(path, &value, &size);
+
+        /* Special cases: read the symlink and return the last component of the value. Some core links return
+         * only the last element of the target path, these are just values, the paths should not be exposed. */
+        if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) {
+                _cleanup_free_ char *prefixed = NULL;
+                const char *syspath;
+
+                r = sd_device_get_syspath(device, &syspath);
                 if (r < 0)
                         return r;
 
-                /* drop trailing newlines */
-                while (size > 0 && strchr(NEWLINE, value[--size]))
-                        value[size] = '\0';
+                prefixed = path_join(syspath, sysattr);
+                if (!prefixed)
+                        return -ENOMEM;
+
+                r = readlink_value(prefixed, &value);
+                if (r != -EINVAL) /* -EINVAL means the path is not a symlink. */
+                        goto cache_result;
         }
 
-        /* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching
-         * sysattr value is critical unlike the other places. */
-        r = device_cache_sysattr_value(device, sysattr, value);
-        if (r < 0) {
-                log_device_debug_errno(device, r,
-                                       "sd-device: failed to cache attribute '%s' with '%s'%s: %m",
-                                       sysattr, value, ret_value ? "" : ", ignoring");
-                if (ret_value)
-                        return r;
+        r = device_chase(device, sysattr, CHASE_PREFIX_ROOT, &resolved, &fd);
+        if (r < 0)
+                goto cache_result;
 
-                return 0;
+        /* Look for cached result again with the resolved path. */
+        r = device_get_cached_sysattr_value(device, resolved, ret_value);
+        if (r != -ENOANO)
+                return r;
+
+        /* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to also get the
+         * size of the result. See issue #20025. */
+        size_t size;
+        r = read_virtual_file_fd(fd, SIZE_MAX, &value, &size);
+        if (r < 0)
+                goto cache_result;
+
+        delete_trailing_chars(value, NEWLINE);
+        r = 0;
+
+cache_result:
+        if (r == -ENOMEM)
+                return r; /* Do not cache -ENOMEM, as the failure may be transient. */
+
+        if (!resolved) {
+                /* If we have not or could not chase the path, assume 'sysattr' is normalized. */
+                resolved = strdup(sysattr);
+                if (!resolved)
+                        return RET_GATHER(r, -ENOMEM);
         }
 
-        if (ret_value)
+        int k = device_cache_sysattr_value_full(device, resolved, value, -r, /* ignore_uevent = */ false);
+        if (k < 0) {
+                if (r < 0)
+                        log_device_debug_errno(device, k,
+                                               "sd-device: failed to cache error code (%i) in reading attribute '%s', ignoring: %m",
+                                               -r, resolved);
+                else {
+                        /* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching
+                         * sysattr value is critical unlike the other places. */
+                        log_device_debug_errno(device, k,
+                                               "sd-device: failed to cache attribute '%s' with '%s'%s: %m",
+                                               resolved, value, ret_value ? "" : ", ignoring");
+                        if (ret_value)
+                                return k;
+                }
+
+                return r;
+        }
+        assert(k > 0);
+
+        if (ret_value && r >= 0)
                 *ret_value = value;
 
+        /* device_cache_sysattr_value_full() takes 'resolved' and 'value' on success. */
+        TAKE_PTR(resolved);
         TAKE_PTR(value);
-        return 0;
+        return r;
 }
 
 int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) {
@@ -2556,19 +2704,22 @@ int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
         return parse_boolean(value);
 }
 
-static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) {
-        _cleanup_free_ char *key = NULL;
+static int device_remove_cached_sysattr_value(sd_device *device, const char *sysattr) {
+        int r;
 
         assert(device);
-        assert(_key);
+        assert(sysattr);
 
-        free(hashmap_remove2(device->sysattr_values, _key, (void **) &key));
+        _cleanup_free_ char *resolved = NULL;
+        r = device_chase(device, sysattr, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &resolved, /* ret_fd = */ NULL);
+        if (r < 0)
+                return r;
+
+        sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, resolved));
+        return 0;
 }
 
-_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
-        _cleanup_free_ char *value = NULL, *path = NULL;
-        const char *syspath;
-        size_t len;
+_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value) {
         int r;
 
         assert_return(device, -EINVAL);
@@ -2576,52 +2727,43 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
 
         /* Set the attribute and save it in the cache. */
 
-        if (!_value) {
+        if (!value)
                 /* If input value is NULL, then clear cache and not write anything. */
-                device_remove_cached_sysattr_value(device, sysattr);
-                return 0;
-        }
+                return device_remove_cached_sysattr_value(device, sysattr);
 
-        r = sd_device_get_syspath(device, &syspath);
-        if (r < 0)
+        _cleanup_free_ char *resolved = NULL;
+        _cleanup_close_ int fd = -EBADF;
+        r = device_chase(device, sysattr, CHASE_PREFIX_ROOT, &resolved, &fd);
+        if (r < 0) {
+                /* On failure, clear cache entry, hopefully, 'sysattr' is normalized. */
+                sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, sysattr));
                 return r;
+        }
 
-        path = path_join(syspath, sysattr);
-        if (!path)
+        /* value length is limited to 4k */
+        _cleanup_free_ char *copied = strndup(value, 4096);
+        if (!copied)
                 return -ENOMEM;
 
-        len = strlen(_value);
-
         /* drop trailing newlines */
-        while (len > 0 && strchr(NEWLINE, _value[len - 1]))
-                len--;
+        delete_trailing_chars(copied, NEWLINE);
 
-        /* value length is limited to 4k */
-        if (len > 4096)
-                return -EINVAL;
-
-        value = strndup(_value, len);
-        if (!value)
-                return -ENOMEM;
-
-        r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
+        r = write_string_file_fd(fd, copied, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_AVOID_NEWLINE);
         if (r < 0) {
                 /* On failure, clear cache entry, as we do not know how it fails. */
-                device_remove_cached_sysattr_value(device, sysattr);
+                sysattr_cache_entry_free(hashmap_remove(device->sysattr_values, resolved));
                 return r;
         }
 
-        /* Do not cache action string written into uevent file. */
-        if (streq(sysattr, "uevent"))
-                return 0;
-
-        r = device_cache_sysattr_value(device, sysattr, value);
+        r = device_cache_sysattr_value(device, resolved, copied, 0);
         if (r < 0)
                 log_device_debug_errno(device, r,
-                                       "sd-device: failed to cache attribute '%s' with '%s', ignoring: %m",
-                                       sysattr, value);
-        else
-                TAKE_PTR(value);
+                                       "sd-device: failed to cache written attribute '%s' with '%s', ignoring: %m",
+                                       resolved, copied);
+        else if (r > 0) {
+                TAKE_PTR(resolved);
+                TAKE_PTR(copied);
+        }
 
         return 0;
 }
@@ -2634,10 +2776,8 @@ _public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr
         assert_return(device, -EINVAL);
         assert_return(sysattr, -EINVAL);
 
-        if (!format) {
-                device_remove_cached_sysattr_value(device, sysattr);
-                return 0;
-        }
+        if (!format)
+                return device_remove_cached_sysattr_value(device, sysattr);
 
         va_start(ap, format);
         r = vasprintf(&value, format, ap);
@@ -2650,16 +2790,7 @@ _public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr
 }
 
 _public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) {
-        const char *s;
-
-        assert_return(device, -EINVAL);
-
-        s = device_action_to_string(action);
-        if (!s)
-                return -EINVAL;
-
-        /* This uses the simple no-UUID interface of kernel < 4.13 */
-        return sd_device_set_sysattr_value(device, "uevent", s);
+        return sd_device_trigger_with_uuid(device, action, NULL);
 }
 
 _public_ int sd_device_trigger_with_uuid(
@@ -2673,10 +2804,6 @@ _public_ int sd_device_trigger_with_uuid(
 
         assert_return(device, -EINVAL);
 
-        /* If no one wants to know the UUID, use the simple interface from pre-4.13 times */
-        if (!ret_uuid)
-                return sd_device_trigger(device, action);
-
         s = device_action_to_string(action);
         if (!s)
                 return -EINVAL;
@@ -2691,7 +2818,8 @@ _public_ int sd_device_trigger_with_uuid(
         if (r < 0)
                 return r;
 
-        *ret_uuid = u;
+        if (ret_uuid)
+                *ret_uuid = u;
         return 0;
 }
 
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
index ac986e48..f8c374b9 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
@@ -4,12 +4,21 @@
 
 #include <errno.h>
 
+#include "errno-util.h"
 #include "event-source.h"
 #include "event-util.h"
 #include "fd-util.h"
 #include "log.h"
 #include "string-util.h"
 
+#define SI_FLAG_FORWARD  (INT32_C(1) << 30)
+#define SI_FLAG_POSITIVE (INT32_C(1) << 29)
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                event_source_hash_ops,
+                void, trivial_hash_func, trivial_compare_func,
+                sd_event_source, sd_event_source_disable_unref);
+
 int event_reset_time(
                 sd_event *e,
                 sd_event_source **s,
@@ -157,19 +166,73 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
 
 int event_add_child_pidref(
                 sd_event *e,
-                sd_event_source **s,
+                sd_event_source **ret,
                 const PidRef *pid,
                 int options,
                 sd_event_child_handler_t callback,
                 void *userdata) {
 
+        int r;
+
+        assert(e);
+
         if (!pidref_is_set(pid))
                 return -ESRCH;
 
-        if (pid->fd >= 0)
-                return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
+        if (pidref_is_remote(pid))
+                return -EREMOTE;
+
+        if (pid->fd < 0)
+                return sd_event_add_child(e, ret, pid->pid, options, callback, userdata);
+
+        _cleanup_close_ int copy_fd = fcntl(pid->fd, F_DUPFD_CLOEXEC, 3);
+        if (copy_fd < 0)
+                return -errno;
 
-        return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
+        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+        r = sd_event_add_child_pidfd(e, &s, copy_fd, options, callback, userdata);
+        if (r < 0)
+                return r;
+
+        r = sd_event_source_set_child_pidfd_own(s, true);
+        if (r < 0)
+                return r;
+
+        TAKE_FD(copy_fd);
+
+        if (ret)
+                *ret = TAKE_PTR(s);
+        else {
+                r = sd_event_source_set_floating(s, true);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int event_source_get_child_pidref(sd_event_source *s, PidRef *ret) {
+        int r;
+
+        assert(s);
+        assert(ret);
+
+        pid_t pid;
+        r = sd_event_source_get_child_pid(s, &pid);
+        if (r < 0)
+                return r;
+
+        int pidfd = sd_event_source_get_child_pidfd(s);
+        if (pidfd < 0)
+                return pidfd;
+
+        /* Note, we don't actually duplicate the fd here, i.e. we do not pass ownership of this PidRef to the caller */
+        *ret = (PidRef) {
+                .pid = pid,
+                .fd = pidfd,
+        };
+
+        return 0;
 }
 
 dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
@@ -180,4 +243,91 @@ dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
         assert_se(sd_event_now(e, CLOCK_MONOTONIC, &ts->monotonic) >= 0);
         return ts;
 }
+
+void event_source_unref_many(sd_event_source **array, size_t n) {
+        FOREACH_ARRAY(v, array, n)
+                sd_event_source_unref(*v);
+
+        free(array);
+}
+
+static int event_forward_signal_callback(sd_event_source *s, const struct signalfd_siginfo *ssi, void *userdata) {
+        sd_event_source *child = ASSERT_PTR(userdata);
+
+        assert(ssi);
+
+        siginfo_t si = {
+                .si_signo = ssi->ssi_signo,
+                /* We include some extra information to indicate the signal was forwarded and originally a positive
+                 * value since we can only set negative values ourselves as positive values are prohibited by the
+                 * kernel. */
+                .si_code = (ssi->ssi_code & (SI_FLAG_FORWARD|SI_FLAG_POSITIVE)) ? INT_MIN :
+                           (ssi->ssi_code >= 0 ? (-ssi->ssi_code - 1) | SI_FLAG_POSITIVE | SI_FLAG_FORWARD : ssi->ssi_code | SI_FLAG_FORWARD),
+                .si_errno = ssi->ssi_errno,
+        };
+
+        /* The following fields are implemented as macros, hence we cannot use compound initialization for them. */
+        si.si_pid = ssi->ssi_pid;
+        si.si_uid = ssi->ssi_uid;
+        si.si_int = ssi->ssi_int;
+        si.si_ptr = UINT64_TO_PTR(ssi->ssi_ptr);
+
+        return sd_event_source_send_child_signal(child, ssi->ssi_signo, &si, /* flags = */ 0);
+}
+
+static void event_forward_signal_destroy(void *userdata) {
+        sd_event_source *child = ASSERT_PTR(userdata);
+        sd_event_source_unref(child);
+}
+
+int event_forward_signals(
+                sd_event *e,
+                sd_event_source *child,
+                const int *signals,
+                size_t n_signals,
+                sd_event_source ***ret_sources,
+                size_t *ret_n_sources) {
+
+        sd_event_source **sources = NULL;
+        size_t n_sources = 0;
+        int r;
+
+        CLEANUP_ARRAY(sources, n_sources, event_source_unref_many);
+
+        assert(e);
+        assert(child);
+        assert(child->type == SOURCE_CHILD);
+        assert(signals || n_signals == 0);
+        assert(ret_sources);
+        assert(ret_n_sources);
+
+        if (n_signals == 0) {
+                *ret_sources = NULL;
+                *ret_n_sources = 0;
+                return 0;
+        }
+
+        sources = new0(sd_event_source*, n_signals);
+        if (!sources)
+                return -ENOMEM;
+
+        FOREACH_ARRAY(sig, signals, n_signals) {
+                _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+                r = sd_event_add_signal(e, &s, *sig | SD_EVENT_SIGNAL_PROCMASK, event_forward_signal_callback, child);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_destroy_callback(s, event_forward_signal_destroy);
+                if (r < 0)
+                        return r;
+
+                sd_event_source_ref(child);
+                sources[n_sources++] = TAKE_PTR(s);
+        }
+
+        *ret_sources = TAKE_PTR(sources);
+        *ret_n_sources = n_sources;
+
+        return 0;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
index c0db014f..692184db 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
@@ -5,8 +5,11 @@
 
 #include "sd-event.h"
 
+#include "hash-funcs.h"
 #include "pidref.h"
 
+extern const struct hash_ops event_source_hash_ops;
+
 int event_reset_time(
                 sd_event *e,
                 sd_event_source **s,
@@ -38,5 +41,11 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
 #if 0 /* NM_IGNORED */
 int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
 
+int event_source_get_child_pidref(sd_event_source *s, PidRef *ret);
+
 dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts);
+
+void event_source_unref_many(sd_event_source **array, size_t n);
+
+int event_forward_signals(sd_event *e, sd_event_source *child, const int *signals, size_t n_signals, sd_event_source ***ret_sources, size_t *ret_n_sources);
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
index b345b145..4f04e058 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
@@ -5,6 +5,7 @@
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
 #include <sys/wait.h>
+#include <threads.h>
 
 #include "sd-daemon.h"
 #include "sd-event.h"
@@ -26,12 +27,11 @@
 #include "memory-util.h"
 #include "missing_magic.h"
 #include "missing_syscall.h"
-#include "missing_threads.h"
 #include "missing_wait.h"
 #include "origin-id.h"
 #include "path-util.h"
-#include "prioq.h"
 #include "pidfd-util.h"
+#include "prioq.h"
 #include "process-util.h"
 #include "psi-util.h"
 #include "set.h"
@@ -45,11 +45,10 @@
 
 #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
 
-static bool EVENT_SOURCE_WATCH_PIDFD(sd_event_source *s) {
+static bool EVENT_SOURCE_WATCH_PIDFD(const sd_event_source *s) {
         /* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
         return s &&
                 s->type == SOURCE_CHILD &&
-                s->child.pidfd >= 0 &&
                 s->child.options == WEXITED;
 }
 
@@ -434,7 +433,7 @@ _public_ int sd_event_new(sd_event** ret) {
 
         if (secure_getenv("SD_EVENT_PROFILE_DELAYS")) {
                 log_debug("Event loop profiling enabled. Logarithmic histogram of event loop iterations in the range 2^0 %s 2^63 us will be logged every 5s.",
-                          special_glyph(SPECIAL_GLYPH_ELLIPSIS));
+                          glyph(GLYPH_ELLIPSIS));
                 e->profile_delays = true;
         }
 
@@ -992,7 +991,7 @@ static void source_disconnect(sd_event_source *s) {
                                 s->event->n_online_child_sources--;
                         }
 
-                        (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid));
+                        assert_se(hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)));
                 }
 
                 if (EVENT_SOURCE_WATCH_PIDFD(s))
@@ -1092,12 +1091,11 @@ static sd_event_source* source_free(sd_event_source *s) {
                 /* Eventually the kernel will do this automatically for us, but for now let's emulate this (unreliably) in userspace. */
 
                 if (s->child.process_owned) {
+                        assert(s->child.pid > 0);
+                        assert(s->child.pidfd >= 0);
 
                         if (!s->child.exited) {
-                                if (s->child.pidfd >= 0)
-                                        r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
-                                else
-                                        r = RET_NERRNO(kill(s->child.pid, SIGKILL));
+                                r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
                                 if (r < 0 && r != -ESRCH)
                                         log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
                                                         s->child.pid);
@@ -1107,10 +1105,7 @@ static sd_event_source* source_free(sd_event_source *s) {
                                 siginfo_t si = {};
 
                                 /* Reap the child if we can */
-                                if (s->child.pidfd >= 0)
-                                        (void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
-                                else
-                                        (void) waitid(P_PID, s->child.pid, &si, WEXITED);
+                                (void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
                         }
                 }
 
@@ -1903,15 +1898,15 @@ _public_ int sd_event_trim_memory(void) {
                    LOG_MESSAGE("Memory trimming took %s, returned %s to OS.",
                                FORMAT_TIMESPAN(period, 0),
                                FORMAT_BYTES(l)),
-                   "MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR,
-                   "TRIMMED_BYTES=%zu", l,
-                   "TRIMMED_USEC=" USEC_FMT, period);
+                   LOG_MESSAGE_ID(SD_MESSAGE_MEMORY_TRIM_STR),
+                   LOG_ITEM("TRIMMED_BYTES=%zu", l),
+                   LOG_ITEM("TRIMMED_USEC=" USEC_FMT, period));
 #else
         log_struct(LOG_DEBUG,
                    LOG_MESSAGE("Memory trimming took %s.",
                                FORMAT_TIMESPAN(period, 0)),
-                   "MESSAGE_ID=" SD_MESSAGE_MEMORY_TRIM_STR,
-                   "TRIMMED_USEC=" USEC_FMT, period);
+                   LOG_MESSAGE_ID(SD_MESSAGE_MEMORY_TRIM_STR),
+                   LOG_ITEM("TRIMMED_USEC=" USEC_FMT, period));
 #endif
 
         return 0;
@@ -2740,9 +2735,11 @@ _public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t *ret) {
         assert_return(s, -EINVAL);
         assert_return(ret, -EINVAL);
         assert_return(s->type == SOURCE_IO, -EDOM);
-        assert_return(s->pending, -ENODATA);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
+        if (!s->pending)
+                return -ENODATA;
+
         *ret = s->io.revents;
         return 0;
 }
@@ -3009,13 +3006,13 @@ static int event_source_online(
 
         case SOURCE_CHILD:
                 if (EVENT_SOURCE_WATCH_PIDFD(s)) {
-                        /* yes, we have pidfd */
+                        /* yes, we can rely on pidfd */
 
                         r = source_child_pidfd_register(s, enabled);
                         if (r < 0)
                                 return r;
                 } else {
-                        /* no pidfd, or something other to watch for than WEXITED */
+                        /* something other to watch for than WEXITED */
 
                         r = event_make_signal_data(s->event, SIGCHLD, NULL);
                         if (r < 0) {
@@ -3208,9 +3205,6 @@ _public_ int sd_event_source_get_child_pidfd(sd_event_source *s) {
         assert_return(s->type == SOURCE_CHILD, -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        if (s->child.pidfd < 0)
-                return -EOPNOTSUPP;
-
         return s->child.pidfd;
 }
 
@@ -3219,51 +3213,26 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
         assert_return(s->type == SOURCE_CHILD, -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
         assert_return(SIGNAL_VALID(sig), -EINVAL);
+        assert(s->child.pidfd >= 0);
 
-        /* If we already have seen indication the process exited refuse sending a signal early. This way we
-         * can be sure we don't accidentally kill the wrong process on PID reuse when pidfds are not
-         * available. */
+        /* If we already have seen indication the process exited refuse sending a signal early. */
         if (s->child.exited)
                 return -ESRCH;
+        assert(!s->child.waited);
 
-        if (s->child.pidfd >= 0) {
-                siginfo_t copy;
-
-                /* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the
-                 * structure here */
-                if (si)
-                        copy = *si;
-
-                if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, 0) < 0)
-                        return -errno;
-
-                return 0;
-        }
-
-        /* Flags are only supported for pidfd_send_signal(), not for rt_sigqueueinfo(), hence let's refuse
-         * this here. */
-        if (flags != 0)
-                return -EOPNOTSUPP;
+        /* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the structure here. */
+        siginfo_t copy;
+        if (si)
+                copy = *si;
 
-        if (si) {
-                /* We use rt_sigqueueinfo() only if siginfo_t is specified. */
-                siginfo_t copy = *si;
-
-                if (rt_sigqueueinfo(s->child.pid, sig, &copy) < 0)
-                        return -errno;
-        } else if (kill(s->child.pid, sig) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, flags));
 }
 
 _public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
         assert_return(s, -EINVAL);
         assert_return(s->type == SOURCE_CHILD, -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
-
-        if (s->child.pidfd < 0)
-                return -EOPNOTSUPP;
+        assert(s->child.pidfd >= 0);
 
         return s->child.pidfd_owned;
 }
@@ -3272,9 +3241,7 @@ _public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) {
         assert_return(s, -EINVAL);
         assert_return(s->type == SOURCE_CHILD, -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
-
-        if (s->child.pidfd < 0)
-                return -EOPNOTSUPP;
+        assert(s->child.pidfd >= 0);
 
         s->child.pidfd_owned = own;
         return 0;
@@ -3733,9 +3700,9 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
 
         e->need_process_child = false;
 
-        /* So, this is ugly. We iteratively invoke waitid() with P_PID + WNOHANG for each PID we wait
-         * for, instead of using P_ALL. This is because we only want to get child information of very
-         * specific child processes, and not all of them. We might not have processed the SIGCHLD event
+        /* So, this is ugly. We iteratively invoke waitid() + WNOHANG with each child process we shall wait for,
+         * instead of using P_ALL. This is because we only want to get child information of very specific
+         * child processes, and not all of them. We might not have processed the SIGCHLD event
          * of a previous invocation and we don't want to maintain a unbounded *per-child* event queue,
          * hence we really don't want anything flushed out of the kernel's queue that we don't care
          * about. Since this is O(n) this means that if you have a lot of processes you probably want
@@ -3746,6 +3713,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
 
         HASHMAP_FOREACH(s, e->child_sources) {
                 assert(s->type == SOURCE_CHILD);
+                assert(s->child.pidfd >= 0);
 
                 if (s->priority > threshold)
                         continue;
@@ -3765,23 +3733,21 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
                         continue;
 
                 zero(s->child.siginfo);
-                if (waitid(P_PID, s->child.pid, &s->child.siginfo,
+                if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo,
                            WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options) < 0)
                         return negative_errno();
 
                 if (s->child.siginfo.si_pid != 0) {
-                        bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
+                        bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
 
                         if (zombie)
                                 s->child.exited = true;
-
-                        if (!zombie && (s->child.options & WEXITED)) {
-                                /* If the child isn't dead then let's immediately remove the state
-                                 * change from the queue, since there's no benefit in leaving it
-                                 * queued. */
+                        else if (s->child.options & WEXITED) {
+                                /* If the child isn't dead then let's immediately remove the state change
+                                 * from the queue, since there's no benefit in leaving it queued. */
 
                                 assert(s->child.options & (WSTOPPED|WCONTINUED));
-                                (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
+                                (void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
                         }
 
                         r = source_set_pending(s, true);
@@ -3802,6 +3768,7 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
         assert(e);
         assert(s);
         assert(s->type == SOURCE_CHILD);
+        assert(s->child.pidfd >= 0);
 
         if (s->pending)
                 return 0;
@@ -3812,14 +3779,19 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
         if (!EVENT_SOURCE_WATCH_PIDFD(s))
                 return 0;
 
+        /* Note that pidfd would also generate EPOLLHUP when the process gets reaped. But at this point we
+         * only permit EPOLLIN, under the assumption that upon EPOLLHUP the child source should already
+         * be set to pending, and we would have returned early above. */
+        assert(!s->child.exited);
+
         zero(s->child.siginfo);
-        if (waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
+        if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
                 return -errno;
 
         if (s->child.siginfo.si_pid == 0)
                 return 0;
 
-        if (IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED))
+        if (SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code))
                 s->child.exited = true;
 
         return source_set_pending(s, true);
@@ -4232,15 +4204,13 @@ static int source_dispatch(sd_event_source *s) {
                 break;
 
         case SOURCE_CHILD: {
-                bool zombie;
-
-                zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
+                bool zombie = SIGINFO_CODE_IS_DEAD(s->child.siginfo.si_code);
 
                 r = s->child.callback(s, &s->child.siginfo, s->userdata);
 
                 /* Now, reap the PID for good. */
                 if (zombie) {
-                        (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
+                        (void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED);
                         s->child.waited = true;
                 }
 
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
index 6d515a94..406e6f22 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
@@ -11,6 +11,7 @@
 #include "hexdecoct.h"
 #include "id128-util.h"
 #include "io-util.h"
+#include "log.h"
 #include "namespace-util.h"
 #include "process-util.h"
 #include "sha256.h"
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
index 9f6ef719..8f3cd8c4 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
@@ -4,6 +4,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <threads.h>
 #include <unistd.h>
 
 #include "sd-id128.h"
@@ -16,16 +17,16 @@
 #include "id128-util.h"
 #include "io-util.h"
 #include "keyring-util.h"
+#include "log.h"
 #include "macro.h"
 #include "missing_syscall.h"
-#include "missing_threads.h"
 #include "path-util.h"
 #include "random-util.h"
 #include "stat-util.h"
 #include "user-util.h"
 
 #if 0 /* NM_IGNORED */
-_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
+_public_ char *sd_id128_to_string(sd_id128_t id, char s[static SD_ID128_STRING_MAX]) {
         size_t k = 0;
 
         assert_return(s, NULL);
@@ -41,7 +42,7 @@ _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID12
         return s;
 }
 
-_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]) {
+_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[static SD_ID128_UUID_STRING_MAX]) {
         size_t k = 0;
 
         assert_return(s, NULL);
@@ -221,8 +222,10 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
 
         key = request_key("user", "invocation_id", NULL, 0);
         if (key == -1) {
-                /* Keyring support not available? No invocation key stored? */
-                if (IN_SET(errno, ENOSYS, ENOKEY))
+                /* Keyring support not available? Keyring access locked down? No invocation key stored? */
+                if (ERRNO_IS_NOT_SUPPORTED(errno) ||
+                    ERRNO_IS_PRIVILEGE(errno) ||
+                    errno == ENOKEY)
                         return -ENXIO;
 
                 return -errno;
diff --git a/src/libnm-systemd-core/src/systemd/_sd-common.h b/src/libnm-systemd-core/src/systemd/_sd-common.h
index 5792dd81..00537eaf 100644
--- a/src/libnm-systemd-core/src/systemd/_sd-common.h
+++ b/src/libnm-systemd-core/src/systemd/_sd-common.h
@@ -19,7 +19,7 @@
 
 /* This is a private header; never even think of including this directly! */
 
-#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__)
+#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 && !defined(__COVERITY__) && !defined(__clang_analyzer__)
 #  error "Do not include _sd-common.h directly; it is a private header."
 #endif
 
diff --git a/src/libnm-systemd-core/src/systemd/sd-device.h b/src/libnm-systemd-core/src/systemd/sd-device.h
index f627ae6d..8f6141b3 100644
--- a/src/libnm-systemd-core/src/systemd/sd-device.h
+++ b/src/libnm-systemd-core/src/systemd/sd-device.h
@@ -23,11 +23,10 @@
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 
+#include "_sd-common.h"
 #include "sd-event.h"
 #include "sd-id128.h"
 
-#include "_sd-common.h"
-
 _SD_BEGIN_DECLARATIONS;
 
 typedef struct sd_device sd_device;
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
index d551b4dd..6054dc44 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
@@ -23,14 +23,13 @@
 #include <net/ethernet.h>
 #include <sys/types.h>
 
+#include "_sd-common.h"
 #include "sd-device.h"
 #include "sd-dhcp-duid.h"
 #include "sd-dhcp6-lease.h"
 #include "sd-dhcp6-option.h"
 #include "sd-event.h"
 
-#include "_sd-common.h"
-
 _SD_BEGIN_DECLARATIONS;
 
 enum {
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
index d6bcceb2..5d082b0c 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
@@ -23,9 +23,8 @@
 #include <netinet/in.h>
 #include <sys/types.h>
 
-#include "sd-dhcp6-option.h"
-
 #include "_sd-common.h"
+#include "sd-dhcp6-option.h"
 
 _SD_BEGIN_DECLARATIONS;
 
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
index 32012426..69f3eaa3 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h
@@ -20,9 +20,8 @@
 #include <inttypes.h>
 #include <sys/types.h>
 
-#include "sd-dhcp6-protocol.h"
-
 #include "_sd-common.h"
+#include "sd-dhcp6-protocol.h"
 
 _SD_BEGIN_DECLARATIONS;
 
diff --git a/src/libnm-systemd-core/src/systemd/sd-ndisc.h b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
index 85fcf6bc..a8f8c47b 100644
--- a/src/libnm-systemd-core/src/systemd/sd-ndisc.h
+++ b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
@@ -25,14 +25,13 @@
 #include <netinet/in.h>
 #include <sys/types.h>
 
+#include "_sd-common.h"
 #include "sd-event.h"
 #include "sd-ndisc-neighbor.h"
 #include "sd-ndisc-protocol.h"
 #include "sd-ndisc-redirect.h"
 #include "sd-ndisc-router.h"
 
-#include "_sd-common.h"
-
 _SD_BEGIN_DECLARATIONS;
 
 typedef struct sd_ndisc sd_ndisc;