diff options
| author | Michael Biebl <biebl@debian.org> | 2026-02-22 00:39:58 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2026-02-22 00:39:58 +0100 |
| commit | 067fb576988f685e83ac8b0ae690334aff547c85 (patch) | |
| tree | 204c7c8a0300691c53627cd3418cf54671ada1a8 /src/libnm-systemd-core | |
| parent | 6de29285e533f4fec22a219013f3687edb6b7399 (diff) | |
New upstream version 1.56.0 upstream/1.56.0
Diffstat (limited to 'src/libnm-systemd-core')
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 ? © : 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, ©) < 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 ? © : 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; |