diff options
Diffstat (limited to '')
-rw-r--r-- | src/libsystemd/sd-device/device-internal.h | 44 | ||||
-rw-r--r-- | src/libsystemd/sd-device/device-private.c | 200 | ||||
-rw-r--r-- | src/libsystemd/sd-device/device-private.h | 5 | ||||
-rw-r--r-- | src/libsystemd/sd-device/sd-device.c | 50 | ||||
-rw-r--r-- | src/rfkill/rfkill.c | 86 | ||||
-rw-r--r-- | src/shared/dissect-image.c | 224 | ||||
-rw-r--r-- | src/shared/udev-util.c | 78 | ||||
-rw-r--r-- | src/shared/udev-util.h | 4 | ||||
-rw-r--r-- | src/udev/udev-event.c | 2 |
9 files changed, 286 insertions, 407 deletions
diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index 39e984cf7c..3ffca35cbe 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -5,12 +5,14 @@ #include "hashmap.h" #include "set.h" +#include "time-util.h" struct sd_device { unsigned n_ref; + int watch_handle; + sd_device *parent; - bool parent_set; /* no need to try to reload parent */ OrderedHashmap *properties; Iterator properties_iterator; @@ -24,60 +26,59 @@ struct sd_device { Set *sysattrs; /* names of sysattrs */ Iterator sysattrs_iterator; - bool sysattrs_read; /* don't try to re-read sysattrs once read */ Set *tags; Iterator tags_iterator; uint64_t tags_generation; /* changes whenever the tags are changed */ uint64_t tags_iterator_generation; /* generation when iteration was started */ - bool property_tags_outdated; /* need to update TAGS= property */ 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 */ - bool property_devlinks_outdated; /* need to update DEVLINKS= property */ int devlink_priority; + int ifindex; + char *devtype; + char *devname; + dev_t devnum; + char **properties_strv; /* the properties hashmap as a strv */ uint8_t *properties_nulstr; /* the same as a nulstr */ size_t properties_nulstr_len; - bool properties_buf_outdated; /* need to reread hashmap */ - - int watch_handle; char *syspath; const char *devpath; const char *sysnum; char *sysname; - bool sysname_set; /* don't reread sysname */ - - char *devtype; - int ifindex; - char *devname; - dev_t devnum; char *subsystem; - bool subsystem_set; /* don't reread subsystem */ char *driver_subsystem; /* only set for the 'drivers' subsystem */ - bool driver_subsystem_set; /* don't reread subsystem */ char *driver; - bool driver_set; /* don't reread driver */ char *id_filename; - bool is_initialized; uint64_t usec_initialized; mode_t devmode; uid_t devuid; gid_t devgid; - bool uevent_loaded; /* don't reread uevent */ + 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= property */ + bool property_devlinks_outdated:1; /* need to update DEVLINKS= property */ + bool properties_buf_outdated:1; /* need to reread hashmap */ + bool sysname_set:1; /* don't reread sysname */ + bool subsystem_set:1; /* don't reread subsystem */ + bool driver_subsystem_set:1; /* don't reread subsystem */ + bool driver_set:1; /* don't reread driver */ + bool uevent_loaded:1; /* don't reread uevent */ bool db_loaded; /* don't reread db */ - bool sealed; /* don't read more information from uevent/db */ - bool db_persist; /* don't clean up the db when switching from initrd to real root */ + 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 */ }; typedef enum DeviceAction { @@ -97,7 +98,6 @@ int device_new_aux(sd_device **ret); int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db); int device_add_property_internal(sd_device *device, const char *key, const char *value); int device_read_uevent_file(sd_device *device); -int device_read_db_aux(sd_device *device, bool force); int device_set_syspath(sd_device *device, const char *_syspath, bool verify); int device_set_ifindex(sd_device *device, const char *ifindex); @@ -107,7 +107,7 @@ int device_set_devtype(sd_device *device, const char *_devtype); int device_set_devnum(sd_device *device, const char *major, const char *minor); int device_set_subsystem(sd_device *device, const char *_subsystem); int device_set_driver(sd_device *device, const char *_driver); -int device_set_usec_initialized(sd_device *device, const char *initialized); +int device_set_usec_initialized(sd_device *device, usec_t when); DeviceAction device_action_from_string(const char *s) _pure_; const char *device_action_to_string(DeviceAction a) _const_; diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 95a581e66e..36beb3e7df 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -47,81 +47,6 @@ int device_add_property(sd_device *device, const char *key, const char *value) { return 0; } -static int device_add_property_internal_from_string(sd_device *device, const char *str) { - _cleanup_free_ char *key = NULL; - char *value; - - assert(device); - assert(str); - - key = strdup(str); - if (!key) - return -ENOMEM; - - value = strchr(key, '='); - if (!value) - return -EINVAL; - - *value = '\0'; - - if (isempty(++value)) - value = NULL; - - return device_add_property_internal(device, key, value); -} - -static int handle_db_line(sd_device *device, char key, const char *value) { - char *path; - int r; - - assert(device); - assert(value); - - switch (key) { - case 'S': - path = strjoina("/dev/", value); - r = device_add_devlink(device, path); - if (r < 0) - return r; - - break; - case 'L': - r = safe_atoi(value, &device->devlink_priority); - if (r < 0) - return r; - - break; - case 'E': - r = device_add_property_internal_from_string(device, value); - if (r < 0) - return r; - - break; - case 'G': - r = device_add_tag(device, value); - if (r < 0) - return r; - - break; - case 'W': - r = safe_atoi(value, &device->watch_handle); - if (r < 0) - return r; - - break; - case 'I': - r = device_set_usec_initialized(device, value); - if (r < 0) - return r; - - break; - default: - log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key); - } - - return 0; -} - void device_set_devlink_priority(sd_device *device, int priority) { assert(device); @@ -135,119 +60,16 @@ void device_set_is_initialized(sd_device *device) { } int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) { - char num[DECIMAL_STR_MAX(usec_t)]; - usec_t usec_initialized; - int r; + usec_t when; assert(device); if (device_old && device_old->usec_initialized > 0) - usec_initialized = device_old->usec_initialized; + when = device_old->usec_initialized; else - usec_initialized = now(CLOCK_MONOTONIC); - - r = snprintf(num, sizeof(num), USEC_FMT, usec_initialized); - if (r < 0) - return -errno; - - r = device_set_usec_initialized(device, num); - if (r < 0) - return r; - - return 0; -} - -static int device_read_db(sd_device *device) { - _cleanup_free_ char *db = NULL; - char *path; - const char *id, *value; - char key; - size_t db_len; - unsigned i; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - INVALID_LINE, - } state = PRE_KEY; - - assert(device); - - if (device->db_loaded || device->sealed) - return 0; - - r = device_get_id_filename(device, &id); - if (r < 0) - return r; - - path = strjoina("/run/udev/data/", id); - - r = read_full_file(path, &db, &db_len); - if (r < 0) { - if (r == -ENOENT) - return 0; - else - return log_device_debug_errno(device, r, "sd-device: Failed to read db '%s': %m", path); - } - - /* devices with a database entry are initialized */ - device_set_is_initialized(device); - - for (i = 0; i < db_len; i++) { - switch (state) { - case PRE_KEY: - if (!strchr(NEWLINE, db[i])) { - key = db[i]; + when = now(CLOCK_MONOTONIC); - state = KEY; - } - - break; - case KEY: - if (db[i] != ':') { - log_device_debug(device, "sd-device: Invalid db entry with key '%c', ignoring", key); - - state = INVALID_LINE; - } else { - db[i] = '\0'; - - state = PRE_VALUE; - } - - break; - case PRE_VALUE: - value = &db[i]; - - state = VALUE; - - break; - case INVALID_LINE: - if (strchr(NEWLINE, db[i])) - state = PRE_KEY; - - break; - case VALUE: - if (strchr(NEWLINE, db[i])) { - db[i] = '\0'; - r = handle_db_line(device, key, value); - if (r < 0) - log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m", key, value); - - state = PRE_KEY; - } - - break; - default: - assert_not_reached("Invalid state when parsing db"); - } - } - - device->db_loaded = true; - - return 0; + return device_set_usec_initialized(device, when); } uint64_t device_get_properties_generation(sd_device *device) { @@ -391,7 +213,13 @@ static int device_amend(sd_device *device, const char *key, const char *value) { if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value); } else if (streq(key, "USEC_INITIALIZED")) { - r = device_set_usec_initialized(device, value); + usec_t t; + + r = safe_atou64(value, &t); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value); + + r = device_set_usec_initialized(device, t); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value); } else if (streq(key, "DRIVER")) { @@ -1115,9 +943,3 @@ int device_delete_db(sd_device *device) { return 0; } - -int device_read_db_force(sd_device *device) { - assert(device); - - return device_read_db_aux(device, true); -} diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 7e4a78e067..56558b38c6 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -49,4 +49,7 @@ int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, int device_tag_index(sd_device *dev, sd_device *dev_old, bool add); int device_update_db(sd_device *device); int device_delete_db(sd_device *device); -int device_read_db_force(sd_device *device); +int device_read_db_internal(sd_device *device, bool force); +static inline int device_read_db(sd_device *device) { + return device_read_db_internal(device, false); +} diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index d5583488f2..db58615df5 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -22,6 +22,7 @@ #include "set.h" #include "socket-util.h" #include "stat-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "strxcpyx.h" @@ -498,8 +499,6 @@ int device_read_uevent_file(sd_device *device) { if (device->uevent_loaded || device->sealed) return 0; - device->uevent_loaded = true; - r = sd_device_get_syspath(device, &syspath); if (r < 0) return r; @@ -507,15 +506,19 @@ int device_read_uevent_file(sd_device *device) { path = strjoina(syspath, "/uevent"); r = read_full_file(path, &uevent, &uevent_len); - if (r == -EACCES) + if (r == -EACCES) { /* empty uevent files may be write-only */ + device->uevent_loaded = true; return 0; - else if (r == -ENOENT) + } + if (r == -ENOENT) /* some devices may not have uevent files, see set_syspath() */ return 0; - else if (r < 0) + if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path); + device->uevent_loaded = true; + for (i = 0; i < uevent_len; i++) switch (state) { case PRE_KEY: @@ -1147,23 +1150,19 @@ static int device_add_property_internal_from_string(sd_device *device, const cha return device_add_property_internal(device, key, value); } -int device_set_usec_initialized(sd_device *device, const char *initialized) { - uint64_t usec_initialized; +int device_set_usec_initialized(sd_device *device, usec_t when) { + char s[DECIMAL_STR_MAX(usec_t)]; int r; assert(device); - assert(initialized); - r = safe_atou64(initialized, &usec_initialized); - if (r < 0) - return r; + xsprintf(s, USEC_FMT, when); - r = device_add_property_internal(device, "USEC_INITIALIZED", initialized); + r = device_add_property_internal(device, "USEC_INITIALIZED", s); if (r < 0) return r; - device->usec_initialized = usec_initialized; - + device->usec_initialized = when; return 0; } @@ -1194,12 +1193,19 @@ static int handle_db_line(sd_device *device, char key, const char *value) { return r; break; - case 'I': - r = device_set_usec_initialized(device, value); + case 'I': { + usec_t t; + + r = safe_atou64(value, &t); + if (r < 0) + return r; + + r = device_set_usec_initialized(device, t); if (r < 0) return r; break; + } case 'L': r = safe_atoi(value, &device->devlink_priority); if (r < 0) @@ -1281,7 +1287,7 @@ int device_get_id_filename(sd_device *device, const char **ret) { return 0; } -int device_read_db_aux(sd_device *device, bool force) { +int device_read_db_internal(sd_device *device, bool force) { _cleanup_free_ char *db = NULL; char *path; const char *id, *value; @@ -1298,11 +1304,11 @@ int device_read_db_aux(sd_device *device, bool force) { INVALID_LINE, } state = PRE_KEY; + assert(device); + if (device->db_loaded || (!force && device->sealed)) return 0; - device->db_loaded = true; - r = device_get_id_filename(device, &id); if (r < 0) return r; @@ -1320,6 +1326,8 @@ int device_read_db_aux(sd_device *device, bool force) { /* devices with a database entry are initialized */ device->is_initialized = true; + device->db_loaded = true; + for (i = 0; i < db_len; i++) { switch (state) { case PRE_KEY: @@ -1372,10 +1380,6 @@ int device_read_db_aux(sd_device *device, bool force) { return 0; } -static int device_read_db(sd_device *device) { - return device_read_db_aux(device, false); -} - _public_ int sd_device_get_is_initialized(sd_device *device) { int r; diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c index 1cc887df12..ac21dc064c 100644 --- a/src/rfkill/rfkill.c +++ b/src/rfkill/rfkill.c @@ -18,6 +18,7 @@ #include "proc-cmdline.h" #include "string-table.h" #include "string-util.h" +#include "udev-util.h" #include "util.h" #include "list.h" @@ -88,89 +89,6 @@ static int find_device( return 0; } -struct DeviceMonitorData { - const char *sysname; - sd_device *device; -}; - -static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) { - struct DeviceMonitorData *data = userdata; - const char *sysname; - - assert(device); - assert(data); - assert(data->sysname); - - if (sd_device_get_sysname(device, &sysname) >= 0 && streq(sysname, data->sysname)) { - data->device = sd_device_ref(device); - return sd_event_exit(sd_device_monitor_get_event(monitor), 0); - } - - return 0; -} - -static int wait_for_initialized( - sd_device *device, - sd_device **ret) { - - _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_(sd_device_unrefp) sd_device *d = NULL; - struct DeviceMonitorData data = {}; - int r; - - assert(device); - assert(ret); - - if (sd_device_get_is_initialized(device) > 0) { - *ret = sd_device_ref(device); - return 0; - } - - assert_se(sd_device_get_sysname(device, &data.sysname) >= 0); - - /* Wait until the device is initialized, so that we can get - * access to the ID_PATH property */ - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to get default event: %m"); - - r = sd_device_monitor_new(&monitor); - if (r < 0) - return log_error_errno(r, "Failed to acquire monitor: %m"); - - r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "rfkill", NULL); - if (r < 0) - return log_error_errno(r, "Failed to add rfkill device match to monitor: %m"); - - r = sd_device_monitor_attach_event(monitor, event); - if (r < 0) - return log_error_errno(r, "Failed to attach event to device monitor: %m"); - - r = sd_device_monitor_start(monitor, device_monitor_handler, &data); - if (r < 0) - return log_error_errno(r, "Failed to start device monitor: %m"); - - /* Check again, maybe things changed */ - r = sd_device_new_from_subsystem_sysname(&d, "rfkill", data.sysname); - if (r < 0) - return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r, - "Failed to open device '%s': %m", data.sysname); - - if (sd_device_get_is_initialized(d) > 0) { - *ret = TAKE_PTR(d); - return 0; - } - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Event loop failed: %m"); - - *ret = TAKE_PTR(data.device); - return 0; -} - static int determine_state_file( const struct rfkill_event *event, char **ret) { @@ -187,7 +105,7 @@ static int determine_state_file( if (r < 0) return r; - r = wait_for_initialized(d, &device); + r = device_wait_for_initialization(d, "rfkill", &device); if (r < 0) return r; diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 6ea8e4df8d..4c2e41c8b8 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -40,6 +40,7 @@ #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" +#include "udev-util.h" #include "user-util.h" #include "xattr-util.h" @@ -116,6 +117,133 @@ static bool device_is_block(sd_device *d) { return streq(ss, "block"); } + +static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + int r; + + r = sd_device_enumerator_new(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_allow_uninitialized(e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_parent(e, d); + if (r < 0) + return r; + + *ret = TAKE_PTR(e); + return 0; +} + +/* how many times to wait for the device nodes to appear */ +#define N_DEVICE_NODE_LIST_ATTEMPTS 10 + +static int wait_for_partitions_to_appear( + int fd, + sd_device *d, + unsigned num_partitions, + sd_device_enumerator **ret_enumerator) { + + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *q; + unsigned n; + int r; + + r = enumerator_for_parent(d, &e); + if (r < 0) + return r; + + /* Count the partitions enumerated by the kernel */ + n = 0; + FOREACH_DEVICE(e, q) { + if (sd_device_get_devnum(q, NULL) < 0) + continue; + if (!device_is_block(q)) + continue; + if (device_is_mmc_special_partition(q)) + continue; + + r = device_wait_for_initialization(q, "block", NULL); + if (r < 0) + return r; + + n++; + } + + if (n == num_partitions + 1) { + *ret_enumerator = TAKE_PTR(e); + return 0; /* success! */ + } + if (n > num_partitions + 1) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "blkid and kernel partition lists do not match."); + + /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running or it + * got EBUSY because udev already opened the device. Let's reprobe the device, which is a synchronous + * call that waits until probing is complete. */ + + for (unsigned j = 0; ; j++) { + if (j++ > 20) + return -EBUSY; + + if (ioctl(fd, BLKRRPART, 0) >= 0) + break; + r = -errno; + if (r == -EINVAL) { + struct loop_info64 info; + + /* If we are running on a loop device that has partition scanning off, return + * an explicit recognizable error about this, so that callers can generate a + * proper message explaining the situation. */ + + if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) { + log_debug("Device is a loop device and partition scanning is off!"); + return -EPROTONOSUPPORT; + } + } + if (r != -EBUSY) + return r; + + /* If something else has the device open, such as an udev rule, the ioctl will return + * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a bit, + * and try again. + * + * This is really something they should fix in the kernel! */ + (void) usleep(50 * USEC_PER_MSEC); + + } + + return -EAGAIN; /* no success yet, try again */ +} + +static int loop_wait_for_partitions_to_appear( + int fd, + sd_device *d, + unsigned num_partitions, + sd_device_enumerator **ret_enumerator) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + int r; + + log_debug("Waiting for device (parent + %d partitions) to appear...", num_partitions); + + r = device_wait_for_initialization(d, "block", &device); + if (r < 0) + return r; + + for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) { + r = wait_for_partitions_to_appear(fd, device, num_partitions, ret_enumerator); + if (r != -EAGAIN) + return r; + } + + return log_debug_errno(SYNTHETIC_ERRNO(ENXIO), + "Kernel partitions dit not appear within %d attempts", + N_DEVICE_NODE_LIST_ATTEMPTS); +} + #endif int dissect_image( @@ -204,6 +332,10 @@ int dissect_image( if (!m) return -ENOMEM; + r = sd_device_new_from_devnum(&d, 'b', st.st_rdev); + if (r < 0) + return r; + if (!(flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_REQUIRE_ROOT)) { const char *usage = NULL; @@ -237,6 +369,10 @@ int dissect_image( m->encrypted = streq_ptr(fstype, "crypto_LUKS"); + r = loop_wait_for_partitions_to_appear(fd, d, 0, &e); + if (r < 0) + return r; + *ret = TAKE_PTR(m); return 0; @@ -258,96 +394,10 @@ int dissect_image( if (!pl) return -errno ?: -ENOMEM; - r = sd_device_new_from_devnum(&d, 'b', st.st_rdev); + r = loop_wait_for_partitions_to_appear(fd, d, blkid_partlist_numof_partitions(pl), &e); if (r < 0) return r; - for (i = 0;; i++) { - int n, z; - - if (i >= 10) { - log_debug("Kernel partitions never appeared."); - return -ENXIO; - } - - r = sd_device_enumerator_new(&e); - if (r < 0) - return r; - - r = sd_device_enumerator_allow_uninitialized(e); - if (r < 0) - return r; - - r = sd_device_enumerator_add_match_parent(e, d); - if (r < 0) - return r; - - /* Count the partitions enumerated by the kernel */ - n = 0; - FOREACH_DEVICE(e, q) { - if (sd_device_get_devnum(q, NULL) < 0) - continue; - - if (!device_is_block(q)) - continue; - - if (device_is_mmc_special_partition(q)) - continue; - n++; - } - - /* Count the partitions enumerated by blkid */ - z = blkid_partlist_numof_partitions(pl); - if (n == z + 1) - break; - if (n > z + 1) { - log_debug("blkid and kernel partition list do not match."); - return -EIO; - } - if (n < z + 1) { - unsigned j = 0; - - /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running - * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a - * synchronous call that waits until probing is complete. */ - - for (;;) { - if (j++ > 20) - return -EBUSY; - - if (ioctl(fd, BLKRRPART, 0) < 0) { - r = -errno; - - if (r == -EINVAL) { - struct loop_info64 info; - - /* If we are running on a loop device that has partition scanning off, - * return an explicit recognizable error about this, so that callers - * can generate a proper message explaining the situation. */ - - if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) { - log_debug("Device is loop device and partition scanning is off!"); - return -EPROTONOSUPPORT; - } - } - if (r != -EBUSY) - return r; - } else - break; - - /* If something else has the device open, such as an udev rule, the ioctl will return - * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a - * bit, and try again. - * - * This is really something they should fix in the kernel! */ - - (void) usleep(50 * USEC_PER_MSEC); - } - } - - e = sd_device_enumerator_unref(e); - } - FOREACH_DEVICE(e, q) { unsigned long long pflags; blkid_partition pp; diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index fdeaf8f613..4200032b3b 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -91,3 +91,81 @@ int udev_parse_config_full( return 0; } + +struct DeviceMonitorData { + const char *sysname; + sd_device *device; +}; + +static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) { + struct DeviceMonitorData *data = userdata; + const char *sysname; + + assert(device); + assert(data); + assert(data->sysname); + assert(!data->device); + + if (sd_device_get_sysname(device, &sysname) >= 0 && streq(sysname, data->sysname)) { + data->device = sd_device_ref(device); + return sd_event_exit(sd_device_monitor_get_event(monitor), 0); + } + + return 0; +} + +int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + struct DeviceMonitorData data = {}; + int r; + + assert(device); + assert(subsystem); + + if (sd_device_get_is_initialized(device) > 0) { + if (ret) + *ret = sd_device_ref(device); + return 0; + } + + assert_se(sd_device_get_sysname(device, &data.sysname) >= 0); + + /* Wait until the device is initialized, so that we can get access to the ID_PATH property */ + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to get default event: %m"); + + r = sd_device_monitor_new(&monitor); + if (r < 0) + return log_error_errno(r, "Failed to acquire monitor: %m"); + + r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, subsystem, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add %s subsystem match to monitor: %m", subsystem); + + r = sd_device_monitor_attach_event(monitor, event); + if (r < 0) + return log_error_errno(r, "Failed to attach event to device monitor: %m"); + + r = sd_device_monitor_start(monitor, device_monitor_handler, &data); + if (r < 0) + return log_error_errno(r, "Failed to start device monitor: %m"); + + /* Check again, maybe things changed. Udev will re-read the db if the device wasn't initialized + * yet. */ + if (sd_device_get_is_initialized(device) > 0) { + if (ret) + *ret = sd_device_ref(device); + return 0; + } + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Event loop failed: %m"); + + if (ret) + *ret = TAKE_PTR(data.device); + return 0; +} diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index be08fe1ff8..932c4a9cd5 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include "sd-device.h" + #include "time-util.h" typedef enum ResolveNameTiming { @@ -23,3 +25,5 @@ int udev_parse_config_full( static inline int udev_parse_config(void) { return udev_parse_config_full(NULL, NULL, NULL, NULL); } + +int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index bc0a32f39b..e28d6a5d08 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -785,7 +785,7 @@ static void event_execute_rules_on_remove( sd_device *dev = event->dev; int r; - r = device_read_db_force(dev); + r = device_read_db_internal(dev, true); if (r < 0) log_device_debug_errno(dev, r, "Failed to read database under /run/udev/data/: %m"); |