From 030a0d79ffaa84cda267a9ca53d5a0768dbcd862 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 10 Aug 2020 11:24:51 +0100 Subject: [PATCH] udev-util: add device_wait_for_devlink Allows to wait for an event by matching on the devlink that gets created. --- src/shared/udev-util.c | 97 ++++++++++++++++++++++++++++++++++++------ src/shared/udev-util.h | 1 + 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 7cd7156a89..eb79bbbcf0 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -1,12 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "alloc-util.h" #include "device-util.h" #include "env-file.h" #include "log.h" #include "parse-util.h" +#include "path-util.h" #include "signal-util.h" #include "string-table.h" #include "string-util.h" @@ -108,8 +110,31 @@ int udev_parse_config_full( return 0; } +/* Note that if -ENOENT is returned, it will be logged at debug level rather than error, + * because it's an expected, common occurrence that the caller will handle with a fallback */ +static int device_new_from_dev_path(const char *devlink, sd_device **ret_device) { + struct stat st; + int r; + + assert(devlink); + + r = stat(devlink, &st); + if (r < 0) + return log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to stat() %s: %m", devlink); + + if (!S_ISBLK(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "%s does not point to a block device: %m", devlink); + + r = sd_device_new_from_devnum(ret_device, 'b', st.st_rdev); + if (r < 0) + return log_error_errno(r, "Failed to initialize device from %s: %m", devlink); + + return 0; +} + struct DeviceMonitorData { const char *sysname; + const char *devlink; sd_device *device; }; @@ -119,37 +144,68 @@ static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, assert(device); assert(data); - assert(data->sysname); + assert(data->sysname || data->devlink); 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); + if (data->sysname && sd_device_get_sysname(device, &sysname) >= 0 && streq(sysname, data->sysname)) + goto found; + + if (data->devlink) { + const char *devlink; + + FOREACH_DEVICE_DEVLINK(device, devlink) + if (path_equal(devlink, data->devlink)) + goto found; + + if (sd_device_get_devname(device, &devlink) >= 0 && path_equal(devlink, data->devlink)) + goto found; } return 0; + +found: + data->device = sd_device_ref(device); + return sd_event_exit(sd_device_monitor_get_event(monitor), 0); } static int device_timeout_handler(sd_event_source *s, uint64_t usec, void *userdata) { return sd_event_exit(sd_event_source_get_event(s), -ETIMEDOUT); } -int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout, sd_device **ret) { +static int device_wait_for_initialization_internal( + sd_device *_device, + const char *devlink, + const char *subsystem, + usec_t timeout, + sd_device **ret) { _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; _cleanup_(sd_event_source_unrefp) sd_event_source *timeout_source = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; - struct DeviceMonitorData data = {}; + /* Ensure that if !_device && devlink, device gets unrefd on errors since it will be new */ + _cleanup_(sd_device_unrefp) sd_device *device = sd_device_ref(_device); + struct DeviceMonitorData data = { + .devlink = devlink, + }; int r; - assert(device); + assert(device || (subsystem && devlink)); - if (sd_device_get_is_initialized(device) > 0) { - if (ret) - *ret = sd_device_ref(device); - return 0; + /* Devlink might already exist, if it does get the device to use the sysname filtering */ + if (!device && devlink) { + r = device_new_from_dev_path(devlink, &device); + if (r < 0 && r != -ENOENT) + return r; } - assert_se(sd_device_get_sysname(device, &data.sysname) >= 0); + if (device) { + if (sd_device_get_is_initialized(device) > 0) { + if (ret) + *ret = sd_device_ref(device); + return 0; + } + /* We need either the sysname or the devlink for filtering */ + assert_se(sd_device_get_sysname(device, &data.sysname) >= 0 || devlink); + } /* Wait until the device is initialized, so that we can get access to the ID_PATH property */ @@ -161,7 +217,7 @@ int device_wait_for_initialization(sd_device *device, const char *subsystem, use if (r < 0) return log_error_errno(r, "Failed to acquire monitor: %m"); - if (!subsystem) { + if (device && !subsystem) { r = sd_device_get_subsystem(device, &subsystem); if (r < 0 && r != -ENOENT) return log_device_error_errno(device, r, "Failed to get subsystem: %m"); @@ -192,7 +248,12 @@ int device_wait_for_initialization(sd_device *device, const char *subsystem, use /* 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 (!device && devlink) { + r = device_new_from_dev_path(devlink, &device); + if (r < 0 && r != -ENOENT) + return r; + } + if (device && sd_device_get_is_initialized(device) > 0) { if (ret) *ret = sd_device_ref(device); return 0; @@ -207,6 +268,14 @@ int device_wait_for_initialization(sd_device *device, const char *subsystem, use return 0; } +int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout, sd_device **ret) { + return device_wait_for_initialization_internal(device, NULL, subsystem, timeout, ret); +} + +int device_wait_for_devlink(const char *devlink, const char *subsystem, usec_t timeout, sd_device **ret) { + return device_wait_for_initialization_internal(NULL, devlink, subsystem, timeout, ret); +} + int device_is_renaming(sd_device *dev) { int r; diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index c35d29344f..04c7ce5520 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -29,5 +29,6 @@ static inline int udev_parse_config(void) { } int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout, sd_device **ret); +int device_wait_for_devlink(const char *path, const char *subsystem, usec_t timeout, sd_device **ret); int device_is_renaming(sd_device *dev); bool device_for_action(sd_device *dev, DeviceAction action);