mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-11 05:17:44 +03:00
Merge pull request #22872 from yuwata/udevadm-wait
udevadm: introduce 'wait' command
This commit is contained in:
commit
b8529cf376
@ -48,6 +48,9 @@
|
||||
<cmdsynopsis>
|
||||
<command>udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>udevadm wait <optional>options</optional> <replaceable>device|syspath</replaceable></command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1><title>Description</title>
|
||||
@ -405,15 +408,9 @@
|
||||
<para>When <option>--initialized-nomatch</option> is specified, trigger events for devices
|
||||
that are not initialized by <command>systemd-udevd</command> yet, and skip devices that
|
||||
are already initialized.</para>
|
||||
<para>Here, initialized devices are those for which at least one udev rule already
|
||||
completed execution – for any action but <literal>remove</literal> — 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 <literal>remove</literal> 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.</para>
|
||||
<para>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.</para>
|
||||
<para>WARNING: <option>--initialized-nomatch</option> 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 @@
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>udevadm wait
|
||||
<arg choice="opt"><replaceable>options</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>device|syspath</replaceable></arg>
|
||||
…
|
||||
</title>
|
||||
|
||||
<para>Wait for devices or device symlinks being created and initialized by
|
||||
<command>systemd-udevd</command>. Each device path must start with
|
||||
<literal>/dev/</literal> or <literal>/sys/</literal>, e.g. <literal>/dev/sda</literal>,
|
||||
<literal>/dev/disk/by-path/pci-0000:3c:00.0-nvme-1-part1</literal>,
|
||||
<literal>/sys/devices/pci0000:00/0000:00:1f.6/net/eth0</literal>, or
|
||||
<literal>/sys/class/net/eth0</literal>. This can take multiple devices. This may be useful for
|
||||
waiting for devices being processed by <command>systemd-udevd</command> after e.g. partitioning
|
||||
or formatting the devices.</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>-t</option></term>
|
||||
<term><option>--timeout=<replaceable>SECONDS</replaceable></option></term>
|
||||
<listitem>
|
||||
<para>Maximum number of seconds to wait for the specified devices or device symlinks being
|
||||
created, initialized, or removed. The default value is <literal>infinity</literal>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--initialized=<replaceable>BOOL</replaceable></option></term>
|
||||
<listitem>
|
||||
<para>Check if <command>systemd-udevd</command> 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.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--removed</option></term>
|
||||
<listitem>
|
||||
<para>When specified, the command wait for devices being removed instead of created or
|
||||
initialized. If this is specified, <option>--initialized=</option> will be ignored.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--settle</option></term>
|
||||
<listitem>
|
||||
<para>When specified, also watches the udev event queue, and wait for all queued events
|
||||
being processed by <command>systemd-udevd</command>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Initialized Devices</title>
|
||||
|
||||
<para>Initialized devices are those for which at least one udev rule already completed execution
|
||||
– for any action but <literal>remove</literal> — 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
|
||||
<literal>remove</literal> 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).</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -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[*]}
|
||||
;;
|
||||
|
@ -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
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/btrfs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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/<fd> path needs */
|
||||
#define PROC_FD_PATH_MAX \
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/loop.h>
|
||||
|
||||
#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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,56 +1,139 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#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));
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -13,6 +13,7 @@ udevadm_sources = files(
|
||||
'udevadm-trigger.c',
|
||||
'udevadm-util.c',
|
||||
'udevadm-util.h',
|
||||
'udevadm-wait.c',
|
||||
'udevd.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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
378
src/udev/udevadm-wait.c
Normal file
378
src/udev/udevadm-wait.c
Normal file
@ -0,0 +1,378 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -26,6 +26,7 @@ static int help(void) {
|
||||
{ "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 },
|
||||
{}
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user