diff --git a/man/udevadm.xml b/man/udevadm.xml index af48571122..e299a75879 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -48,6 +48,9 @@ udevadm test-builtin options command devpath + + udevadm wait options device|syspath + Description @@ -405,15 +408,9 @@ When is specified, trigger events for devices that are not initialized by systemd-udevd yet, and skip devices that are already initialized. - Here, initialized devices are those for which at least one udev rule already - completed execution – for any action but remove — that set a property - or other device setting (and thus has an entry in the udev device database). Devices are - no longer considered initialized if a remove action is seen for them - (which removes their entry in the udev device database). Note that devices that have no - udev rules are never considered initialized, but might still be announced via the sd-device - API (or similar). Typically, it is thus essential that applications which intend to use - such a match, make sure a suitable udev rule is installed that sets at least one property - on devices that shall be matched. + Typically, it is essential that applications which intend to use such a match, make + sure a suitable udev rule is installed that sets at least one property on devices that + shall be matched. See also Initialized Devices section below for more details. WARNING: can potentially save a significant amount of time compared to re-triggering all devices in the system and e.g. can be used to optimize boot time. However, this is not safe to be used in a boot sequence in general. @@ -694,6 +691,73 @@ + + + udevadm wait + <arg choice="opt"><replaceable>options</replaceable></arg> + <arg choice="opt"><replaceable>device|syspath</replaceable></arg> + … + + + Wait for devices or device symlinks being created and initialized by + systemd-udevd. Each device path must start with + /dev/ or /sys/, e.g. /dev/sda, + /dev/disk/by-path/pci-0000:3c:00.0-nvme-1-part1, + /sys/devices/pci0000:00/0000:00:1f.6/net/eth0, or + /sys/class/net/eth0. This can take multiple devices. This may be useful for + waiting for devices being processed by systemd-udevd after e.g. partitioning + or formatting the devices. + + + + + + + Maximum number of seconds to wait for the specified devices or device symlinks being + created, initialized, or removed. The default value is infinity. + + + + + + + Check if systemd-udevd initialized devices. Defaults to true. When + false, the command only checks if the specified devices exist. Set false to this setting if + there is no udev rules for the specified devices, as the devices will never be considered + as initialized in that case. See Initialized Devices section below for more details. + + + + + + + When specified, the command wait for devices being removed instead of created or + initialized. If this is specified, will be ignored. + + + + + + + When specified, also watches the udev event queue, and wait for all queued events + being processed by systemd-udevd. + + + + + + + + + + Initialized Devices + + Initialized devices are those for which at least one udev rule already completed execution + – for any action but remove — that set a property or other device setting (and + thus has an entry in the udev device database). Devices are no longer considered initialized if a + remove action is seen for them (which removes their entry in the udev device + database). Note that devices that have no udev rules are never considered initialized, but might + still be announced via the sd-device API (or similar). diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm index 23ce02365c..08446b2d5d 100644 --- a/shell-completion/bash/udevadm +++ b/shell-completion/bash/udevadm @@ -32,7 +32,7 @@ __get_all_sysdevs() { __get_all_devs() { local i - for i in /dev/* /dev/*/*; do + for i in /dev/* /dev/*/* /dev/*/*/*; do echo $i done } @@ -64,9 +64,10 @@ _udevadm() { [MONITOR_ARG]='-s --subsystem-match -t --tag-match' [TEST]='-a --action -N --resolve-names' [TEST_BUILTIN]='-a --action' + [WAIT]='-t --timeout --initialized=no --removed --settle' ) - local verbs=(info trigger settle control monitor test-builtin test) + local verbs=(info trigger settle control monitor test-builtin test wait) local builtins=(blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess) for ((i=0; i < COMP_CWORD; i++)); do @@ -245,6 +246,25 @@ _udevadm() { fi ;; + 'wait') + if __contains_word "$prev" ${OPTS[WAIT]}; then + case $prev in + *) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ $cur = -* ]]; then + comps="${OPTS[COMMON]} ${OPTS[WAIT]}" + else + comps=$( __get_all_devs ) + local IFS=$'\n' + fi + ;; + *) comps=${VERBS[*]} ;; diff --git a/shell-completion/zsh/_udevadm b/shell-completion/zsh/_udevadm index 63df8b7c9e..a277ad7929 100644 --- a/shell-completion/zsh/_udevadm +++ b/shell-completion/zsh/_udevadm @@ -104,6 +104,17 @@ _udevadm_test-builtin(){ fi } +(( $+functions[_udevadm_wait] )) || +_udevadm_wait(){ + _arguments \ + '--timeout=[Maximum number of seconds to wait for the devices being created.]' \ + '--initialized=[Wait for devices being initialized by systemd-udevd.]:boolean:(yes no)' \ + '--removed[Wait for devices being removed.]' \ + '--settle[Also wait for udev queue being empty.]' \ + '--help[Print help text.]' \ + '*::devpath:_files -P /dev/ -W /dev' +} + (( $+functions[_udevadm_mounts] )) || _udevadm_mounts(){ local dev_tmp dpath_tmp mp_tmp mline diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index c02b52992b..6c1de92a26 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include "io-util.h" #include "macro.h" #include "missing_fcntl.h" +#include "missing_fs.h" #include "missing_syscall.h" #include "parse-util.h" #include "path-util.h" @@ -788,3 +790,24 @@ int btrfs_defrag_fd(int fd) { return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL)); } + +int fd_get_diskseq(int fd, uint64_t *ret) { + uint64_t diskseq; + + assert(fd >= 0); + assert(ret); + + if (ioctl(fd, BLKGETDISKSEQ, &diskseq) < 0) { + /* Note that the kernel is weird: non-existing ioctls currently return EINVAL + * rather than ENOTTY on loopback block devices. They should fix that in the kernel, + * but in the meantime we accept both here. */ + if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL) + return -errno; + + return -EOPNOTSUPP; + } + + *ret = diskseq; + + return 0; +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index f5cfcb4ede..808cac4d5d 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -109,6 +109,7 @@ static inline int make_null_stdio(void) { int fd_reopen(int fd, int flags); int read_nr_open(void); int btrfs_defrag_fd(int fd); +int fd_get_diskseq(int fd, uint64_t *ret); /* The maximum length a buffer for a /proc/self/fd/ path needs */ #define PROC_FD_PATH_MAX \ diff --git a/src/basic/missing_fs.h b/src/basic/missing_fs.h index cc43d7c277..0cacd49bcf 100644 --- a/src/basic/missing_fs.h +++ b/src/basic/missing_fs.h @@ -6,6 +6,10 @@ #define RENAME_NOREPLACE (1 << 0) #endif +#ifndef BLKGETDISKSEQ +#define BLKGETDISKSEQ _IOR(0x12,128,__u64) +#endif + /* linux/fs.h or sys/mount.h */ #ifndef MS_MOVE #define MS_MOVE 8192 diff --git a/src/basic/missing_loop.h b/src/basic/missing_loop.h index 5fe63ad1ca..449858d3a7 100644 --- a/src/basic/missing_loop.h +++ b/src/basic/missing_loop.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include #include #ifndef LOOP_CONFIGURE @@ -15,10 +14,6 @@ struct loop_config { #define LOOP_CONFIGURE 0x4C0A #endif -#ifndef BLKGETDISKSEQ -#define BLKGETDISKSEQ _IOR(0x12,128,__u64) -#endif - #ifndef LOOP_SET_STATUS_SETTABLE_FLAGS #define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN) #endif diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 2773d28b4b..bdf76b467b 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -773,4 +773,7 @@ global: LIBSYSTEMD_251 { global: sd_id128_to_uuid_string; + sd_device_new_from_devname; + sd_device_new_from_path; + sd_device_open; } LIBSYSTEMD_250; diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 19bf01abc7..4f413880eb 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -415,6 +415,46 @@ _public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st return sd_device_new_from_devnum(ret, type, st->st_rdev); } +_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) { + struct stat st; + + assert_return(ret, -EINVAL); + assert_return(devname, -EINVAL); + + /* This function actually accepts both devlinks and devnames, i.e. both symlinks and device + * nodes below /dev/. */ + + /* Also ignore when the specified path is "/dev". */ + if (isempty(path_startswith(devname, "/dev"))) + return -EINVAL; + + if (device_path_parse_major_minor(devname, NULL, NULL) >= 0) { + _cleanup_free_ char *syspath = NULL; + + /* Let's shortcut when "/dev/block/maj:min" or "/dev/char/maj:min" is specified. + * In that case, we directly convert the path to syspath, hence it is not necessary + * that the specified path exists. So, this works fine without udevd being running. */ + + syspath = path_join("/sys", devname); + return sd_device_new_from_syspath(ret, syspath); + } + + if (stat(devname, &st) < 0) + return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno; + + return sd_device_new_from_stat_rdev(ret, &st); +} + +_public_ int sd_device_new_from_path(sd_device **ret, const char *path) { + assert_return(ret, -EINVAL); + assert_return(path, -EINVAL); + + if (path_startswith(path, "/dev")) + return sd_device_new_from_devname(ret, path); + + return sd_device_new_from_syspath(ret, path); +} + int device_set_devtype(sd_device *device, const char *devtype) { _cleanup_free_ char *t = NULL; int r; @@ -2197,3 +2237,68 @@ _public_ int sd_device_trigger_with_uuid( *ret_uuid = u; return 0; } + +_public_ int sd_device_open(sd_device *device, int flags) { + _cleanup_close_ int fd = -1, fd2 = -1; + const char *devname, *subsystem = NULL; + uint64_t q, diskseq = 0; + struct stat st; + dev_t devnum; + int r; + + assert_return(device, -EINVAL); + assert_return(FLAGS_SET(flags, O_PATH) || !FLAGS_SET(flags, O_NOFOLLOW), -EINVAL); + + r = sd_device_get_devname(device, &devname); + if (r == -ENOENT) + return -ENOEXEC; + if (r < 0) + return r; + + r = sd_device_get_devnum(device, &devnum); + if (r == -ENOENT) + return -ENOEXEC; + if (r < 0) + return r; + + r = sd_device_get_subsystem(device, &subsystem); + if (r < 0 && r != -ENOENT) + return r; + + r = sd_device_get_diskseq(device, &diskseq); + if (r < 0 && r != -ENOENT) + return r; + + fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + + if (st.st_rdev != devnum) + return -ENXIO; + + if (streq_ptr(subsystem, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) + return -ENXIO; + + /* If flags has O_PATH, then we cannot check diskseq. Let's return earlier. */ + if (FLAGS_SET(flags, O_PATH)) + return TAKE_FD(fd); + + fd2 = open(FORMAT_PROC_FD_PATH(fd), flags); + if (fd2 < 0) + return -errno; + + if (diskseq == 0) + return TAKE_FD(fd2); + + r = fd_get_diskseq(fd2, &q); + if (r < 0) + return r; + + if (q != diskseq) + return -ENXIO; + + return TAKE_FD(fd2); +} diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index 8a534ca8ef..8d7cb792d6 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -1,56 +1,139 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "device-enumerator-private.h" #include "device-internal.h" #include "device-private.h" #include "device-util.h" #include "errno-util.h" +#include "fd-util.h" #include "hashmap.h" #include "nulstr-util.h" +#include "path-util.h" #include "string-util.h" #include "tests.h" #include "time-util.h" static void test_sd_device_one(sd_device *d) { - _cleanup_(sd_device_unrefp) sd_device *device_from_id = NULL; - const char *syspath, *subsystem, *id, *val; + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + const char *syspath, *sysname, *subsystem = NULL, *id, *devname, *val; + bool is_block = false; dev_t devnum; usec_t usec; int i, r; assert_se(sd_device_get_syspath(d, &syspath) >= 0); + assert_se(path_startswith(syspath, "/sys")); + assert_se(sd_device_new_from_syspath(&dev, syspath) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + assert_se(sd_device_new_from_path(&dev, syspath) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + + assert_se(sd_device_get_sysname(d, &sysname) >= 0); r = sd_device_get_subsystem(d, &subsystem); - assert_se(r >= 0 || r == -ENOENT); + if (r >= 0) { + const char *name; + + if (streq(subsystem, "drivers")) + name = strjoina(d->driver_subsystem, ":", sysname); + else + name = sysname; + assert_se(sd_device_new_from_subsystem_sysname(&dev, subsystem, name) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + } else + assert_se(r == -ENOENT); + + is_block = streq_ptr(subsystem, "block"); + + r = sd_device_get_devname(d, &devname); + if (r >= 0) { + r = sd_device_new_from_devname(&dev, devname); + if (r >= 0) { + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + } else + assert_se(r == -ENODEV || ERRNO_IS_PRIVILEGE(r)); + + r = sd_device_new_from_path(&dev, devname); + if (r >= 0) { + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + + _cleanup_close_ int fd = -1; + fd = sd_device_open(d, O_CLOEXEC| O_NONBLOCK | (is_block ? O_RDONLY : O_NOCTTY | O_PATH)); + assert_se(fd >= 0 || ERRNO_IS_PRIVILEGE(fd)); + } else + assert_se(r == -ENODEV || ERRNO_IS_PRIVILEGE(r)); + } else + assert_se(r == -ENOENT); + + r = sd_device_get_devnum(d, &devnum); + if (r >= 0) { + _cleanup_free_ char *p = NULL; + + assert_se(major(devnum) > 0); + + assert_se(sd_device_new_from_devnum(&dev, is_block ? 'b' : 'c', devnum) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + + assert_se(asprintf(&p, "/dev/%s/%u:%u", is_block ? "block" : "char", major(devnum), minor(devnum)) >= 0); + assert_se(sd_device_new_from_devname(&dev, p) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + + assert_se(sd_device_new_from_path(&dev, p) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + } else + assert_se(r == -ENOENT); + + r = sd_device_get_ifindex(d, &i); + if (r >= 0) { + assert_se(i > 0); + + assert_se(sd_device_new_from_ifindex(&dev, i) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + + assert_se(sd_device_new_from_ifname(&dev, sysname) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); + assert_se(streq(syspath, val)); + dev = sd_device_unref(dev); + } else + assert_se(r == -ENOENT); + + assert_se(sd_device_get_devpath(d, &val) >= 0); r = sd_device_get_devtype(d, &val); assert_se(r >= 0 || r == -ENOENT); - r = sd_device_get_devnum(d, &devnum); - assert_se((r >= 0 && major(devnum) > 0) || r == -ENOENT); - - r = sd_device_get_ifindex(d, &i); - assert_se((r >= 0 && i > 0) || r == -ENOENT); - r = sd_device_get_driver(d, &val); assert_se(r >= 0 || r == -ENOENT); - assert_se(sd_device_get_devpath(d, &val) >= 0); - - r = sd_device_get_devname(d, &val); - assert_se(r >= 0 || r == -ENOENT); - - assert_se(sd_device_get_sysname(d, &val) >= 0); - r = sd_device_get_sysnum(d, &val); assert_se(r >= 0 || r == -ENOENT); - i = sd_device_get_is_initialized(d); - assert_se(i >= 0); - if (i > 0) { + r = sd_device_get_is_initialized(d); + if (r > 0) { r = sd_device_get_usec_since_initialized(d, &usec); assert_se((r >= 0 && usec > 0) || r == -ENODATA); - } + } else + assert(r == 0); r = sd_device_get_sysattr_value(d, "name_assign_type", &val); assert_se(r >= 0 || ERRNO_IS_PRIVILEGE(r) || IN_SET(r, -ENOENT, -EINVAL)); @@ -58,14 +141,9 @@ static void test_sd_device_one(sd_device *d) { r = sd_device_get_property_value(d, "ID_NET_DRIVER", &val); assert_se(r >= 0 || r == -ENOENT); - r = device_get_device_id(d, &id); - assert_se(r >= 0); - - r = sd_device_new_from_device_id(&device_from_id, id); - assert_se(r >= 0); - - r = sd_device_get_syspath(device_from_id, &val); - assert_se(r >= 0); + assert_se(device_get_device_id(d, &id) >= 0); + assert_se(sd_device_new_from_device_id(&dev, id) >= 0); + assert_se(sd_device_get_syspath(dev, &val) >= 0); assert_se(streq(syspath, val)); log_info("syspath:%s subsystem:%s id:%s initialized:%s", syspath, strna(subsystem), id, yes_no(i)); diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index b55d4e44ba..6356ff44d8 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -128,27 +128,6 @@ static int device_has_block_children(sd_device *d) { return 0; } -static int loop_get_diskseq(int fd, uint64_t *ret_diskseq) { - uint64_t diskseq; - - assert(fd >= 0); - assert(ret_diskseq); - - if (ioctl(fd, BLKGETDISKSEQ, &diskseq) < 0) { - /* Note that the kernel is weird: non-existing ioctls currently return EINVAL - * rather than ENOTTY on loopback block devices. They should fix that in the kernel, - * but in the meantime we accept both here. */ - if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL) - return -errno; - - return -EOPNOTSUPP; - } - - *ret_diskseq = diskseq; - - return 0; -} - static int loop_configure( int fd, int nr, @@ -453,7 +432,7 @@ static int loop_device_make_internal( if (copy < 0) return copy; - r = loop_get_diskseq(copy, &diskseq); + r = fd_get_diskseq(copy, &diskseq); if (r < 0 && r != -EOPNOTSUPP) return r; @@ -592,7 +571,7 @@ static int loop_device_make_internal( assert(S_ISBLK(st.st_mode)); uint64_t diskseq = 0; - r = loop_get_diskseq(loop_with_fd, &diskseq); + r = fd_get_diskseq(loop_with_fd, &diskseq); if (r < 0 && r != -EOPNOTSUPP) return r; diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h index 189a110ea0..f0c3c115e4 100644 --- a/src/systemd/sd-device.h +++ b/src/systemd/sd-device.h @@ -62,6 +62,8 @@ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum); int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname); int sd_device_new_from_device_id(sd_device **ret, const char *id); int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st); +int sd_device_new_from_devname(sd_device **ret, const char *devname); +int sd_device_new_from_path(sd_device **ret, const char *path); int sd_device_new_from_ifname(sd_device **ret, const char *ifname); int sd_device_new_from_ifindex(sd_device **ret, int ifindex); @@ -107,6 +109,7 @@ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const ch int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4); int sd_device_trigger(sd_device *device, sd_device_action_t action); int sd_device_trigger_with_uuid(sd_device *device, sd_device_action_t action, sd_id128_t *ret_uuid); +int sd_device_open(sd_device *device, int flags); /* device enumerator */ diff --git a/src/udev/meson.build b/src/udev/meson.build index a4648be3b4..8a2926db30 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -13,6 +13,7 @@ udevadm_sources = files( 'udevadm-trigger.c', 'udevadm-util.c', 'udevadm-util.h', + 'udevadm-wait.c', 'udevd.c', ) diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index 586f083ffa..c30c78fad5 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -302,11 +302,12 @@ static int builtin_blkid(sd_device *dev, sd_netlink **rtnl, int argc, char *argv if (r < 0) return log_device_debug_errno(dev, r, "Failed to get device name: %m"); - fd = open(devnode, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (fd < 0) { - log_device_debug_errno(dev, errno, "Failed to open block device %s%s: %m", - devnode, errno == ENOENT ? ", ignoring" : ""); - return errno == ENOENT ? 0 : -errno; + bool ignore = ERRNO_IS_DEVICE_ABSENT(fd); + log_device_debug_errno(dev, fd, "Failed to open block device %s%s: %m", + devnode, ignore ? ", ignoring" : ""); + return ignore ? 0 : fd; } errno = 0; diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index a34f6de617..55f7d3f44d 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -45,12 +45,12 @@ static int abs_size_mm(const struct input_absinfo *absinfo) { return (absinfo->maximum - absinfo->minimum) / absinfo->resolution; } -static void extract_info(sd_device *dev, const char *devpath, bool test) { +static void extract_info(sd_device *dev, bool test) { char width[DECIMAL_STR_MAX(int)], height[DECIMAL_STR_MAX(int)]; struct input_absinfo xabsinfo = {}, yabsinfo = {}; _cleanup_close_ int fd = -1; - fd = open(devpath, O_RDONLY|O_CLOEXEC); + fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (fd < 0) return; @@ -330,7 +330,7 @@ static int builtin_input_id(sd_device *dev, sd_netlink **rtnl, int argc, char *a unsigned long bitmask_key[NBITS(KEY_MAX)]; unsigned long bitmask_rel[NBITS(REL_MAX)]; unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)]; - const char *sysname, *devnode; + const char *sysname; bool is_pointer; bool is_key; @@ -375,10 +375,9 @@ static int builtin_input_id(sd_device *dev, sd_netlink **rtnl, int argc, char *a } - if (sd_device_get_devname(dev, &devnode) >= 0 && - sd_device_get_sysname(dev, &sysname) >= 0 && + if (sd_device_get_sysname(dev, &sysname) >= 0 && startswith(sysname, "event")) - extract_info(dev, devnode, test); + extract_info(dev, test); return 0; } diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c index cc266cc663..b09757bfc2 100644 --- a/src/udev/udev-builtin-keyboard.c +++ b/src/udev/udev-builtin-keyboard.c @@ -195,9 +195,9 @@ static int builtin_keyboard(sd_device *dev, sd_netlink **rtnl, int argc, char *a } if (fd < 0) { - fd = open(node, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (fd < 0) - return log_device_error_errno(dev, errno, "Failed to open device '%s': %m", node); + return log_device_error_errno(dev, fd, "Failed to open device '%s': %m", node); } (void) map_keycode(dev, fd, scancode, keycode); @@ -212,9 +212,9 @@ static int builtin_keyboard(sd_device *dev, sd_netlink **rtnl, int argc, char *a } if (fd < 0) { - fd = open(node, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + fd = sd_device_open(dev, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (fd < 0) - return log_device_error_errno(dev, errno, "Failed to open device '%s': %m", node); + return log_device_error_errno(dev, fd, "Failed to open device '%s': %m", node); } if (has_abs == -1) { diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index 1824ccef6a..c313181041 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -598,7 +598,7 @@ int udev_node_apply_permissions( gid_t gid, OrderedHashmap *seclabel_list) { - const char *devnode, *subsystem, *id = NULL; + const char *devnode, *subsystem; bool apply_mode, apply_uid, apply_gid; _cleanup_close_ int node_fd = -1; struct stat stats; @@ -616,33 +616,25 @@ int udev_node_apply_permissions( r = sd_device_get_devnum(dev, &devnum); if (r < 0) return log_device_debug_errno(dev, r, "Failed to get devnum: %m"); - (void) device_get_device_id(dev, &id); if (streq(subsystem, "block")) mode |= S_IFBLK; else mode |= S_IFCHR; - node_fd = open(devnode, O_PATH|O_NOFOLLOW|O_CLOEXEC); + node_fd = sd_device_open(dev, O_PATH|O_CLOEXEC); if (node_fd < 0) { - if (errno == ENOENT) { - log_device_debug_errno(dev, errno, "Device node %s is missing, skipping handling.", devnode); + if (ERRNO_IS_DEVICE_ABSENT(node_fd)) { + log_device_debug_errno(dev, node_fd, "Device node %s is missing, skipping handling.", devnode); return 0; /* This is necessarily racey, so ignore missing the device */ } - return log_device_debug_errno(dev, errno, "Cannot open node %s: %m", devnode); + return log_device_debug_errno(dev, node_fd, "Cannot open node %s: %m", devnode); } if (fstat(node_fd, &stats) < 0) return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode); - if ((mode != MODE_INVALID && (stats.st_mode & S_IFMT) != (mode & S_IFMT)) || stats.st_rdev != devnum) { - log_device_debug(dev, "Found node '%s' with non-matching devnum %s, skipping handling.", - devnode, strna(id)); - return 0; /* We might process a device that already got replaced by the time we have a look - * at it, handle this gracefully and step away. */ - } - apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777); apply_uid = uid_is_valid(uid) && stats.st_uid != uid; apply_gid = gid_is_valid(gid) && stats.st_gid != gid; diff --git a/src/udev/udevadm-wait.c b/src/udev/udevadm-wait.c new file mode 100644 index 0000000000..0ea0ed501b --- /dev/null +++ b/src/udev/udevadm-wait.c @@ -0,0 +1,378 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include + +#include "sd-event.h" + +#include "alloc-util.h" +#include "chase-symlinks.h" +#include "device-util.h" +#include "errno-util.h" +#include "fd-util.h" +#include "inotify-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "static-destruct.h" +#include "string-table.h" +#include "strv.h" +#include "udev-util.h" +#include "udevadm.h" + +typedef enum WaitUntil { + WAIT_UNTIL_INITIALIZED, + WAIT_UNTIL_ADDED, + WAIT_UNTIL_REMOVED, + _WAIT_UNTIL_MAX, + _WAIT_UNTIL_INVALID = -EINVAL, +} WaitUntil; + +static WaitUntil arg_wait_until = WAIT_UNTIL_INITIALIZED; +static usec_t arg_timeout_usec = USEC_INFINITY; +static bool arg_settle = false; +static char **arg_devices = NULL; + +STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep); + +static const char * const wait_until_table[_WAIT_UNTIL_MAX] = { + [WAIT_UNTIL_INITIALIZED] = "initialized", + [WAIT_UNTIL_ADDED] = "added", + [WAIT_UNTIL_REMOVED] = "removed", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wait_until, WaitUntil); + +static int check_device(const char *path) { + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + int r; + + assert(path); + + r = sd_device_new_from_path(&dev, path); + if (r == -ENODEV) + return arg_wait_until == WAIT_UNTIL_REMOVED; + if (r < 0) + return r; + + switch (arg_wait_until) { + case WAIT_UNTIL_INITIALIZED: + return sd_device_get_is_initialized(dev); + case WAIT_UNTIL_ADDED: + return true; + case WAIT_UNTIL_REMOVED: + return false; + default: + assert_not_reached(); + } +} + +static bool check(void) { + int r; + + if (arg_settle) { + r = udev_queue_is_empty(); + if (r == 0) + return false; + if (r < 0) + log_warning_errno(r, "Failed to check if udev queue is empty, assuming empty: %m"); + } + + STRV_FOREACH(p, arg_devices) { + r = check_device(*p); + if (r <= 0) { + if (r < 0) + log_warning_errno(r, "Failed to check if device \"%s\" is %s, assuming not %s: %m", + *p, + wait_until_to_string(arg_wait_until), + wait_until_to_string(arg_wait_until)); + return false; + } + } + + return true; +} + +static int check_and_exit(sd_event *event) { + assert(event); + + if (check()) + return sd_event_exit(event, 0); + + return 0; +} + +static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) { + const char *name; + int r; + + assert(monitor); + assert(device); + + if (device_for_action(device, SD_DEVICE_REMOVE) != (arg_wait_until == WAIT_UNTIL_REMOVED)) + return 0; + + if (arg_wait_until == WAIT_UNTIL_REMOVED) + /* On removed event, the received device may not contain enough information. + * Let's unconditionally check all requested devices are removed. */ + return check_and_exit(sd_device_monitor_get_event(monitor)); + + /* For other events, at first check if the received device matches with the requested devices, + * to avoid calling check() so many times within a short time. */ + + r = sd_device_get_sysname(device, &name); + if (r < 0) { + log_device_warning_errno(device, r, "Failed to get sysname of received device, ignoring: %m"); + return 0; + } + + STRV_FOREACH(p, arg_devices) { + const char *s; + + if (!path_startswith(*p, "/sys")) + continue; + + r = path_find_last_component(*p, false, NULL, &s); + if (r < 0) { + log_warning_errno(r, "Failed to extract filename from \"%s\", ignoring: %m", *p); + continue; + } + if (r == 0) + continue; + + if (strneq(s, name, r)) + return check_and_exit(sd_device_monitor_get_event(monitor)); + } + + r = sd_device_get_devname(device, &name); + if (r < 0) { + if (r != -ENOENT) + log_device_warning_errno(device, r, "Failed to get devname of received device, ignoring: %m"); + return 0; + } + + if (path_strv_contains(arg_devices, name)) + return check_and_exit(sd_device_monitor_get_event(monitor)); + + STRV_FOREACH(p, arg_devices) { + const char *link; + + if (!path_startswith(*p, "/dev")) + continue; + + FOREACH_DEVICE_DEVLINK(device, link) + if (path_equal(*p, link)) + return check_and_exit(sd_device_monitor_get_event(monitor)); + } + + return 0; +} + +static int setup_monitor(sd_event *event, sd_device_monitor **ret) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; + int r; + + assert(event); + assert(ret); + + r = sd_device_monitor_new(&monitor); + if (r < 0) + return r; + + (void) sd_device_monitor_set_receive_buffer_size(monitor, 128*1024*1024); + + r = sd_device_monitor_attach_event(monitor, event); + if (r < 0) + return r; + + r = sd_device_monitor_start(monitor, device_monitor_handler, NULL); + if (r < 0) + return r; + + r = sd_event_source_set_description(sd_device_monitor_get_event_source(monitor), + "device-monitor-event-source"); + if (r < 0) + return r; + + *ret = TAKE_PTR(monitor); + return 0; +} + +static int on_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) { + return check_and_exit(sd_event_source_get_event(s)); +} + +static int setup_inotify(sd_event *event) { + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + int r; + + assert(event); + + if (!arg_settle) + return 0; + + r = sd_event_add_inotify(event, &s, "/run/udev" , IN_CREATE | IN_DELETE, on_inotify, NULL); + if (r < 0) + return r; + + r = sd_event_source_set_description(s, "inotify-event-source"); + if (r < 0) + return r; + + return sd_event_source_set_floating(s, true); +} + +static int setup_timer(sd_event *event) { + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + int r; + + assert(event); + + if (arg_timeout_usec == USEC_INFINITY) + return 0; + + r = sd_event_add_time_relative(event, &s, CLOCK_BOOTTIME, arg_timeout_usec, 0, + NULL, INT_TO_PTR(-ETIMEDOUT)); + if (r < 0) + return r; + + r = sd_event_source_set_description(s, "timeout-event-source"); + if (r < 0) + return r; + + return sd_event_source_set_floating(s, true); +} + +static int help(void) { + printf("%s wait [OPTIONS] DEVICE [DEVICE…]\n\n" + "Wait for devices or device symlinks being created.\n\n" + " -h --help Print this message\n" + " -V --version Print version of the program\n" + " -t --timeout=SEC Maximum time to wait for the device\n" + " --initialized=BOOL Wait for devices being initialized by systemd-udevd\n" + " --removed Wait for devices being removed\n" + " --settle Also wait for all queued events being processed\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_INITIALIZED = 0x100, + ARG_REMOVED, + ARG_SETTLE, + }; + + static const struct option options[] = { + { "timeout", required_argument, NULL, 't' }, + { "initialized", required_argument, NULL, ARG_INITIALIZED }, + { "removed", no_argument, NULL, ARG_REMOVED }, + { "settle", no_argument, NULL, ARG_SETTLE }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + {} + }; + + int c, r; + + while ((c = getopt_long(argc, argv, "t:hV", options, NULL)) >= 0) + switch (c) { + case 't': + r = parse_sec(optarg, &arg_timeout_usec); + if (r < 0) + return log_error_errno(r, "Failed to parse -t/--timeout= parameter: %s", optarg); + break; + + case ARG_INITIALIZED: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --initialized= parameter: %s", optarg); + arg_wait_until = r ? WAIT_UNTIL_INITIALIZED : WAIT_UNTIL_ADDED; + break; + + case ARG_REMOVED: + arg_wait_until = WAIT_UNTIL_REMOVED; + break; + + case ARG_SETTLE: + arg_settle = true; + break; + + case 'V': + return print_version(); + + case 'h': + return help(); + + case '?': + return -EINVAL; + + default: + assert_not_reached(); + } + + if (optind >= argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Too few arguments, expected at least one device path or device symlink."); + + arg_devices = strv_copy(argv + optind); + if (!arg_devices) + return log_oom(); + + return 1; /* work to do */ +} + +int wait_main(int argc, char *argv[], void *userdata) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + int r; + + r = parse_argv(argc, argv); + if (r <= 0) + return r; + + STRV_FOREACH(p, arg_devices) { + path_simplify(*p); + + if (!path_is_safe(*p)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Device path cannot contain \"..\"."); + + if (!is_device_path(*p)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Specified path \"%s\" does not start with \"/dev/\" or \"/sys/\".", *p); + } + + /* Check before configuring event sources, as devices may be already initialized. */ + if (check()) + return 0; + + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to initialize sd-event: %m"); + + r = setup_timer(event); + if (r < 0) + return log_error_errno(r, "Failed to set up timeout: %m"); + + r = setup_inotify(event); + if (r < 0) + return log_error_errno(r, "Failed to set up inotify: %m"); + + r = setup_monitor(event, &monitor); + if (r < 0) + return log_error_errno(r, "Failed to set up device monitor: %m"); + + /* Check before entering the event loop, as devices may be initialized during setting up event sources. */ + if (check()) + return 0; + + r = sd_event_loop(event); + if (r == -ETIMEDOUT) + return log_error_errno(r, "Timed out for waiting devices being %s.", + wait_until_to_string(arg_wait_until)); + if (r < 0) + return log_error_errno(r, "Event loop failed: %m"); + + return 0; +} diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index ba17d9348b..df23a60c1a 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -19,13 +19,14 @@ static int help(void) { static const char *const short_descriptions[][2] = { - { "info", "Query sysfs or the udev database" }, - { "trigger", "Request events from the kernel" }, - { "settle", "Wait for pending udev events" }, - { "control", "Control the udev daemon" }, - { "monitor", "Listen to kernel and udev events" }, - { "test", "Test an event run" }, - { "test-builtin", "Test a built-in command" }, + { "info", "Query sysfs or the udev database" }, + { "trigger", "Request events from the kernel" }, + { "settle", "Wait for pending udev events" }, + { "control", "Control the udev daemon" }, + { "monitor", "Listen to kernel and udev events" }, + { "test", "Test an event run" }, + { "test-builtin", "Test a built-in command" }, + { "wait", "Wait for device or device symlink" }, }; _cleanup_free_ char *link = NULL; @@ -101,6 +102,7 @@ static int udevadm_main(int argc, char *argv[]) { { "hwdb", VERB_ANY, VERB_ANY, 0, hwdb_main }, { "test", VERB_ANY, VERB_ANY, 0, test_main }, { "test-builtin", VERB_ANY, VERB_ANY, 0, builtin_main }, + { "wait", VERB_ANY, VERB_ANY, 0, wait_main }, { "version", VERB_ANY, VERB_ANY, 0, version_main }, { "help", VERB_ANY, VERB_ANY, 0, help_main }, {} diff --git a/src/udev/udevadm.h b/src/udev/udevadm.h index 75ce633632..808294ec9d 100644 --- a/src/udev/udevadm.h +++ b/src/udev/udevadm.h @@ -13,6 +13,7 @@ int monitor_main(int argc, char *argv[], void *userdata); int hwdb_main(int argc, char *argv[], void *userdata); int test_main(int argc, char *argv[], void *userdata); int builtin_main(int argc, char *argv[], void *userdata); +int wait_main(int argc, char *argv[], void *userdata); static inline int print_version(void) { /* Dracut relies on the version being a single integer */ diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 424ecfc2cf..4a37a76924 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -422,12 +422,11 @@ static int worker_send_result(Manager *manager, int result) { return loop_write(manager->worker_watch[WRITE_END], &result, sizeof(result), false); } -static int device_get_whole_disk(sd_device *dev, const char **ret) { +static int device_get_whole_disk(sd_device *dev, sd_device **ret_device, const char **ret_devname) { const char *val; int r; assert(dev); - assert(ret); if (device_for_action(dev, SD_DEVICE_REMOVE)) goto irrelevant; @@ -463,16 +462,23 @@ static int device_get_whole_disk(sd_device *dev, const char **ret) { if (r < 0) return log_device_debug_errno(dev, r, "Failed to get devname: %m"); - *ret = val; + if (ret_device) + *ret_device = dev; + if (ret_devname) + *ret_devname = val; return 1; irrelevant: - *ret = NULL; + if (ret_device) + *ret_device = NULL; + if (ret_devname) + *ret_devname = NULL; return 0; } static int worker_lock_whole_disk(sd_device *dev, int *ret_fd) { _cleanup_close_ int fd = -1; + sd_device *dev_whole_disk; const char *val; int r; @@ -484,19 +490,19 @@ static int worker_lock_whole_disk(sd_device *dev, int *ret_fd) { * event handling; in the case udev acquired the lock, the external process can block until udev has * finished its event handling. */ - r = device_get_whole_disk(dev, &val); + r = device_get_whole_disk(dev, &dev_whole_disk, &val); if (r < 0) return r; if (r == 0) goto nolock; - fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + fd = sd_device_open(dev_whole_disk, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (fd < 0) { - bool ignore = ERRNO_IS_DEVICE_ABSENT(errno); + bool ignore = ERRNO_IS_DEVICE_ABSENT(fd); - log_device_debug_errno(dev, errno, "Failed to open '%s'%s: %m", val, ignore ? ", ignoring" : ""); + log_device_debug_errno(dev, fd, "Failed to open '%s'%s: %m", val, ignore ? ", ignoring" : ""); if (!ignore) - return -errno; + return fd; goto nolock; } @@ -545,15 +551,9 @@ static int worker_mark_block_device_read_only(sd_device *dev) { if (STARTSWITH_SET(val, "dm-", "md", "drbd", "loop", "nbd", "zram")) return 0; - r = sd_device_get_devname(dev, &val); - if (r == -ENOENT) - return 0; - if (r < 0) - return log_device_debug_errno(dev, r, "Failed to get devname: %m"); - - fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (fd < 0) - return log_device_debug_errno(dev, errno, "Failed to open '%s', ignoring: %m", val); + return log_device_debug_errno(dev, fd, "Failed to open '%s', ignoring: %m", val); if (ioctl(fd, BLKROSET, &state) < 0) return log_device_warning_errno(dev, errno, "Failed to mark block device '%s' read-only: %m", val); @@ -1093,7 +1093,7 @@ static int event_queue_assume_block_device_unlocked(Manager *manager, sd_device * device is not locked anymore. The assumption may not be true, but that should not cause any * issues, as in that case events will be requeued soon. */ - r = device_get_whole_disk(dev, &devname); + r = device_get_whole_disk(dev, NULL, &devname); if (r <= 0) return r; @@ -1106,7 +1106,7 @@ static int event_queue_assume_block_device_unlocked(Manager *manager, sd_device if (event->retry_again_next_usec == 0) continue; - if (device_get_whole_disk(event->dev, &event_devname) <= 0) + if (device_get_whole_disk(event->dev, NULL, &event_devname) <= 0) continue; if (!streq(devname, event_devname)) @@ -1403,18 +1403,13 @@ static int synthesize_change(sd_device *dev) { !startswith(sysname, "dm-")) { _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; bool part_table_read = false, has_partitions = false; - const char *devname; sd_device *d; int fd; - r = sd_device_get_devname(dev, &devname); - if (r < 0) - return r; - /* Try to re-read the partition table. This only succeeds if none of the devices is * busy. The kernel returns 0 if no partition table is found, and we will not get an * event for the disk. */ - fd = open(devname, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (fd >= 0) { r = flock(fd, LOCK_EX|LOCK_NB); if (r >= 0) diff --git a/test/units/testsuite-64.sh b/test/units/testsuite-64.sh index 58b6157e6f..af83e52790 100755 --- a/test/units/testsuite-64.sh +++ b/test/units/testsuite-64.sh @@ -41,23 +41,6 @@ helper_check_device_symlinks() {( done < <(find "${paths[@]}" -type l) )} -# Wait for a specific device link to appear -# Arguments: -# $1 - device path -# $2 - number of retries (default: 10) -helper_wait_for_dev() { - local dev="${1:?}" - local ntries="${2:-10}" - local i - - for ((i = 0; i < ntries; i++)); do - test ! -e "$dev" || return 0 - sleep .2 - done - - return 1 -} - # Wrapper around `helper_wait_for_lvm_activate()` and `helper_wait_for_pvscan()` # functions to cover differences between pre and post lvm 2.03.14, which introduced # a new way of vgroup autoactivation @@ -583,15 +566,7 @@ testcase_iscsi_lvm() { # Configure the iSCSI initiator iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login - udevadm settle - # Check if all device symlinks are valid and if all expected device symlinks exist - for link in "${expected_symlinks[@]}"; do - # We need to do some active waiting anyway, as it may take kernel a bit - # to attach the newly connected SCSI devices - helper_wait_for_dev "$link" - test -e "$link" - done - udevadm settle + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" helper_check_device_symlinks # Cleanup iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout @@ -626,15 +601,7 @@ testcase_iscsi_lvm() { # Configure the iSCSI initiator iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login - udevadm settle - # Check if all device symlinks are valid and if all expected device symlinks exist - for link in "${expected_symlinks[@]}"; do - # We need to do some active waiting anyway, as it may take kernel a bit - # to attach the newly connected SCSI devices - helper_wait_for_dev "$link" - test -e "$link" - done - udevadm settle + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" helper_check_device_symlinks # Add all iSCSI devices into a LVM volume group, create two logical volumes, # and check if necessary symlinks exist (and are valid) @@ -658,24 +625,16 @@ testcase_iscsi_lvm() { # "Reset" the DM state, since we yanked the backing storage from under the LVM, # so the currently active VGs/LVs are invalid dmsetup remove_all --deferred - udevadm settle # The LVM and iSCSI related symlinks should be gone - test ! -e "/dev/$vgroup" - test ! -e "/dev/disk/by-label/mylvpart1" - for link in "${expected_symlinks[@]}"; do - test ! -e "$link" - done + udevadm wait --settle --timeout=30 --removed "/dev/$vgroup" "/dev/disk/by-label/mylvpart1" "${expected_symlinks[@]}" helper_check_device_symlinks "/dev/disk" # Reconnect the iSCSI devices and check if everything get detected correctly iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login - udevadm settle + udevadm wait --settle --timeout=30 "${expected_symlinks[@]}" for link in "${expected_symlinks[@]}"; do - helper_wait_for_dev "$link" helper_wait_for_vgroup "$link" "$vgroup" - test -e "$link" done - udevadm settle test -e "/dev/$vgroup/mypart1" test -e "/dev/$vgroup/mypart2" test -e "/dev/disk/by-label/mylvpart1"