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
+ options
+ device|syspath
+ …
+
+
+ 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"