X Tutup
Skip to content

Commit b8529cf

Browse files
authored
Merge pull request systemd#22872 from yuwata/udevadm-wait
udevadm: introduce 'wait' command
2 parents 5b89bff + 78e278a commit b8529cf

File tree

22 files changed

+781
-167
lines changed

22 files changed

+781
-167
lines changed

man/udevadm.xml

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848
<cmdsynopsis>
4949
<command>udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable></command>
5050
</cmdsynopsis>
51+
<cmdsynopsis>
52+
<command>udevadm wait <optional>options</optional> <replaceable>device|syspath</replaceable></command>
53+
</cmdsynopsis>
5154
</refsynopsisdiv>
5255

5356
<refsect1><title>Description</title>
@@ -405,15 +408,9 @@
405408
<para>When <option>--initialized-nomatch</option> is specified, trigger events for devices
406409
that are not initialized by <command>systemd-udevd</command> yet, and skip devices that
407410
are already initialized.</para>
408-
<para>Here, initialized devices are those for which at least one udev rule already
409-
completed execution – for any action but <literal>remove</literal> — that set a property
410-
or other device setting (and thus has an entry in the udev device database). Devices are
411-
no longer considered initialized if a <literal>remove</literal> action is seen for them
412-
(which removes their entry in the udev device database). Note that devices that have no
413-
udev rules are never considered initialized, but might still be announced via the sd-device
414-
API (or similar). Typically, it is thus essential that applications which intend to use
415-
such a match, make sure a suitable udev rule is installed that sets at least one property
416-
on devices that shall be matched.</para>
411+
<para>Typically, it is essential that applications which intend to use such a match, make
412+
sure a suitable udev rule is installed that sets at least one property on devices that
413+
shall be matched. See also Initialized Devices section below for more details.</para>
417414
<para>WARNING: <option>--initialized-nomatch</option> can potentially save a significant
418415
amount of time compared to re-triggering all devices in the system and e.g. can be used to
419416
optimize boot time. However, this is not safe to be used in a boot sequence in general.
@@ -694,6 +691,73 @@
694691
<xi:include href="standard-options.xml" xpointer="help" />
695692
</variablelist>
696693
</refsect2>
694+
695+
<refsect2>
696+
<title>udevadm wait
697+
<arg choice="opt"><replaceable>options</replaceable></arg>
698+
<arg choice="opt"><replaceable>device|syspath</replaceable></arg>
699+
700+
</title>
701+
702+
<para>Wait for devices or device symlinks being created and initialized by
703+
<command>systemd-udevd</command>. Each device path must start with
704+
<literal>/dev/</literal> or <literal>/sys/</literal>, e.g. <literal>/dev/sda</literal>,
705+
<literal>/dev/disk/by-path/pci-0000:3c:00.0-nvme-1-part1</literal>,
706+
<literal>/sys/devices/pci0000:00/0000:00:1f.6/net/eth0</literal>, or
707+
<literal>/sys/class/net/eth0</literal>. This can take multiple devices. This may be useful for
708+
waiting for devices being processed by <command>systemd-udevd</command> after e.g. partitioning
709+
or formatting the devices.</para>
710+
711+
<variablelist>
712+
<varlistentry>
713+
<term><option>-t</option></term>
714+
<term><option>--timeout=<replaceable>SECONDS</replaceable></option></term>
715+
<listitem>
716+
<para>Maximum number of seconds to wait for the specified devices or device symlinks being
717+
created, initialized, or removed. The default value is <literal>infinity</literal>.</para>
718+
</listitem>
719+
</varlistentry>
720+
721+
<varlistentry>
722+
<term><option>--initialized=<replaceable>BOOL</replaceable></option></term>
723+
<listitem>
724+
<para>Check if <command>systemd-udevd</command> initialized devices. Defaults to true. When
725+
false, the command only checks if the specified devices exist. Set false to this setting if
726+
there is no udev rules for the specified devices, as the devices will never be considered
727+
as initialized in that case. See Initialized Devices section below for more details.</para>
728+
</listitem>
729+
</varlistentry>
730+
731+
<varlistentry>
732+
<term><option>--removed</option></term>
733+
<listitem>
734+
<para>When specified, the command wait for devices being removed instead of created or
735+
initialized. If this is specified, <option>--initialized=</option> will be ignored.</para>
736+
</listitem>
737+
</varlistentry>
738+
739+
<varlistentry>
740+
<term><option>--settle</option></term>
741+
<listitem>
742+
<para>When specified, also watches the udev event queue, and wait for all queued events
743+
being processed by <command>systemd-udevd</command>.</para>
744+
</listitem>
745+
</varlistentry>
746+
747+
<xi:include href="standard-options.xml" xpointer="help" />
748+
</variablelist>
749+
</refsect2>
750+
</refsect1>
751+
752+
<refsect1>
753+
<title>Initialized Devices</title>
754+
755+
<para>Initialized devices are those for which at least one udev rule already completed execution
756+
– for any action but <literal>remove</literal> — that set a property or other device setting (and
757+
thus has an entry in the udev device database). Devices are no longer considered initialized if a
758+
<literal>remove</literal> action is seen for them (which removes their entry in the udev device
759+
database). Note that devices that have no udev rules are never considered initialized, but might
760+
still be announced via the sd-device API (or similar).</para>
697761
</refsect1>
698762

699763
<refsect1>

shell-completion/bash/udevadm

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ __get_all_sysdevs() {
3232

3333
__get_all_devs() {
3434
local i
35-
for i in /dev/* /dev/*/*; do
35+
for i in /dev/* /dev/*/* /dev/*/*/*; do
3636
echo $i
3737
done
3838
}
@@ -64,9 +64,10 @@ _udevadm() {
6464
[MONITOR_ARG]='-s --subsystem-match -t --tag-match'
6565
[TEST]='-a --action -N --resolve-names'
6666
[TEST_BUILTIN]='-a --action'
67+
[WAIT]='-t --timeout --initialized=no --removed --settle'
6768
)
6869

69-
local verbs=(info trigger settle control monitor test-builtin test)
70+
local verbs=(info trigger settle control monitor test-builtin test wait)
7071
local builtins=(blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess)
7172

7273
for ((i=0; i < COMP_CWORD; i++)); do
@@ -245,6 +246,25 @@ _udevadm() {
245246
fi
246247
;;
247248

249+
'wait')
250+
if __contains_word "$prev" ${OPTS[WAIT]}; then
251+
case $prev in
252+
*)
253+
comps=''
254+
;;
255+
esac
256+
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
257+
return 0
258+
fi
259+
260+
if [[ $cur = -* ]]; then
261+
comps="${OPTS[COMMON]} ${OPTS[WAIT]}"
262+
else
263+
comps=$( __get_all_devs )
264+
local IFS=$'\n'
265+
fi
266+
;;
267+
248268
*)
249269
comps=${VERBS[*]}
250270
;;

shell-completion/zsh/_udevadm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ _udevadm_test-builtin(){
104104
fi
105105
}
106106

107+
(( $+functions[_udevadm_wait] )) ||
108+
_udevadm_wait(){
109+
_arguments \
110+
'--timeout=[Maximum number of seconds to wait for the devices being created.]' \
111+
'--initialized=[Wait for devices being initialized by systemd-udevd.]:boolean:(yes no)' \
112+
'--removed[Wait for devices being removed.]' \
113+
'--settle[Also wait for udev queue being empty.]' \
114+
'--help[Print help text.]' \
115+
'*::devpath:_files -P /dev/ -W /dev'
116+
}
117+
107118
(( $+functions[_udevadm_mounts] )) ||
108119
_udevadm_mounts(){
109120
local dev_tmp dpath_tmp mp_tmp mline

src/basic/fd-util.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <errno.h>
44
#include <fcntl.h>
55
#include <linux/btrfs.h>
6+
#include <linux/fs.h>
67
#include <linux/magic.h>
78
#include <sys/ioctl.h>
89
#include <sys/resource.h>
@@ -17,6 +18,7 @@
1718
#include "io-util.h"
1819
#include "macro.h"
1920
#include "missing_fcntl.h"
21+
#include "missing_fs.h"
2022
#include "missing_syscall.h"
2123
#include "parse-util.h"
2224
#include "path-util.h"
@@ -788,3 +790,24 @@ int btrfs_defrag_fd(int fd) {
788790

789791
return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL));
790792
}
793+
794+
int fd_get_diskseq(int fd, uint64_t *ret) {
795+
uint64_t diskseq;
796+
797+
assert(fd >= 0);
798+
assert(ret);
799+
800+
if (ioctl(fd, BLKGETDISKSEQ, &diskseq) < 0) {
801+
/* Note that the kernel is weird: non-existing ioctls currently return EINVAL
802+
* rather than ENOTTY on loopback block devices. They should fix that in the kernel,
803+
* but in the meantime we accept both here. */
804+
if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL)
805+
return -errno;
806+
807+
return -EOPNOTSUPP;
808+
}
809+
810+
*ret = diskseq;
811+
812+
return 0;
813+
}

src/basic/fd-util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ static inline int make_null_stdio(void) {
109109
int fd_reopen(int fd, int flags);
110110
int read_nr_open(void);
111111
int btrfs_defrag_fd(int fd);
112+
int fd_get_diskseq(int fd, uint64_t *ret);
112113

113114
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
114115
#define PROC_FD_PATH_MAX \

src/basic/missing_fs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#define RENAME_NOREPLACE (1 << 0)
77
#endif
88

9+
#ifndef BLKGETDISKSEQ
10+
#define BLKGETDISKSEQ _IOR(0x12,128,__u64)
11+
#endif
12+
913
/* linux/fs.h or sys/mount.h */
1014
#ifndef MS_MOVE
1115
#define MS_MOVE 8192

src/basic/missing_loop.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/* SPDX-License-Identifier: LGPL-2.1-or-later */
22
#pragma once
33

4-
#include <linux/fs.h>
54
#include <linux/loop.h>
65

76
#ifndef LOOP_CONFIGURE
@@ -15,10 +14,6 @@ struct loop_config {
1514
#define LOOP_CONFIGURE 0x4C0A
1615
#endif
1716

18-
#ifndef BLKGETDISKSEQ
19-
#define BLKGETDISKSEQ _IOR(0x12,128,__u64)
20-
#endif
21-
2217
#ifndef LOOP_SET_STATUS_SETTABLE_FLAGS
2318
#define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN)
2419
#endif

src/libsystemd/libsystemd.sym

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,4 +773,7 @@ global:
773773
LIBSYSTEMD_251 {
774774
global:
775775
sd_id128_to_uuid_string;
776+
sd_device_new_from_devname;
777+
sd_device_new_from_path;
778+
sd_device_open;
776779
} LIBSYSTEMD_250;

src/libsystemd/sd-device/sd-device.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,46 @@ _public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st
415415
return sd_device_new_from_devnum(ret, type, st->st_rdev);
416416
}
417417

418+
_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
419+
struct stat st;
420+
421+
assert_return(ret, -EINVAL);
422+
assert_return(devname, -EINVAL);
423+
424+
/* This function actually accepts both devlinks and devnames, i.e. both symlinks and device
425+
* nodes below /dev/. */
426+
427+
/* Also ignore when the specified path is "/dev". */
428+
if (isempty(path_startswith(devname, "/dev")))
429+
return -EINVAL;
430+
431+
if (device_path_parse_major_minor(devname, NULL, NULL) >= 0) {
432+
_cleanup_free_ char *syspath = NULL;
433+
434+
/* Let's shortcut when "/dev/block/maj:min" or "/dev/char/maj:min" is specified.
435+
* In that case, we directly convert the path to syspath, hence it is not necessary
436+
* that the specified path exists. So, this works fine without udevd being running. */
437+
438+
syspath = path_join("/sys", devname);
439+
return sd_device_new_from_syspath(ret, syspath);
440+
}
441+
442+
if (stat(devname, &st) < 0)
443+
return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno;
444+
445+
return sd_device_new_from_stat_rdev(ret, &st);
446+
}
447+
448+
_public_ int sd_device_new_from_path(sd_device **ret, const char *path) {
449+
assert_return(ret, -EINVAL);
450+
assert_return(path, -EINVAL);
451+
452+
if (path_startswith(path, "/dev"))
453+
return sd_device_new_from_devname(ret, path);
454+
455+
return sd_device_new_from_syspath(ret, path);
456+
}
457+
418458
int device_set_devtype(sd_device *device, const char *devtype) {
419459
_cleanup_free_ char *t = NULL;
420460
int r;
@@ -2197,3 +2237,68 @@ _public_ int sd_device_trigger_with_uuid(
21972237
*ret_uuid = u;
21982238
return 0;
21992239
}
2240+
2241+
_public_ int sd_device_open(sd_device *device, int flags) {
2242+
_cleanup_close_ int fd = -1, fd2 = -1;
2243+
const char *devname, *subsystem = NULL;
2244+
uint64_t q, diskseq = 0;
2245+
struct stat st;
2246+
dev_t devnum;
2247+
int r;
2248+
2249+
assert_return(device, -EINVAL);
2250+
assert_return(FLAGS_SET(flags, O_PATH) || !FLAGS_SET(flags, O_NOFOLLOW), -EINVAL);
2251+
2252+
r = sd_device_get_devname(device, &devname);
2253+
if (r == -ENOENT)
2254+
return -ENOEXEC;
2255+
if (r < 0)
2256+
return r;
2257+
2258+
r = sd_device_get_devnum(device, &devnum);
2259+
if (r == -ENOENT)
2260+
return -ENOEXEC;
2261+
if (r < 0)
2262+
return r;
2263+
2264+
r = sd_device_get_subsystem(device, &subsystem);
2265+
if (r < 0 && r != -ENOENT)
2266+
return r;
2267+
2268+
r = sd_device_get_diskseq(device, &diskseq);
2269+
if (r < 0 && r != -ENOENT)
2270+
return r;
2271+
2272+
fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH);
2273+
if (fd < 0)
2274+
return -errno;
2275+
2276+
if (fstat(fd, &st) < 0)
2277+
return -errno;
2278+
2279+
if (st.st_rdev != devnum)
2280+
return -ENXIO;
2281+
2282+
if (streq_ptr(subsystem, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
2283+
return -ENXIO;
2284+
2285+
/* If flags has O_PATH, then we cannot check diskseq. Let's return earlier. */
2286+
if (FLAGS_SET(flags, O_PATH))
2287+
return TAKE_FD(fd);
2288+
2289+
fd2 = open(FORMAT_PROC_FD_PATH(fd), flags);
2290+
if (fd2 < 0)
2291+
return -errno;
2292+
2293+
if (diskseq == 0)
2294+
return TAKE_FD(fd2);
2295+
2296+
r = fd_get_diskseq(fd2, &q);
2297+
if (r < 0)
2298+
return r;
2299+
2300+
if (q != diskseq)
2301+
return -ENXIO;
2302+
2303+
return TAKE_FD(fd2);
2304+
}

0 commit comments

Comments
 (0)
X Tutup