]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev-util: add device_wait_for_devlink
authorLuca Boccassi <luca.boccassi@microsoft.com>
Mon, 10 Aug 2020 10:24:51 +0000 (11:24 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Fri, 14 Aug 2020 14:26:04 +0000 (15:26 +0100)
Allows to wait for an event by matching on the devlink that gets
created.

src/shared/udev-util.c
src/shared/udev-util.h

index 7cd7156a892fb57b8aa2b66c817dafb68b9e6cd2..eb79bbbcf06189d40c96bf9fa9ea51429dcdacfc 100644 (file)
@@ -1,12 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <errno.h>
+#include <unistd.h>
 
 #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;
 
index c35d29344f731b39ecb7fedffaae7d6c4fbb203e..04c7ce55203df2d2ef2467dc275b155f3388856d 100644 (file)
@@ -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);