TEST-17-UDEV: fix the device watch test
- Match the watch rules against the root block device's kernel name instead of a hard-coded 'sda', which is not portable (e.g. 'vda' on virtio). - The nowatch check used 'test ! -e', but the watch symlinks form a bidirectional (circular) pair (ID -> wd -> ID), so dereferencing them with -e always fails with ELOOP and 'test ! -e' always succeeds, regardless of whether the watch is still present. Use '(! test -L ...)', which does not dereference the symlink. - Drop never worked checks for the 1st and 2nd invocations. - Drop unnecessary 'function' keyword. - Drop unnecessary 00-debug.rules. - Fix broken drop-in config file. - Restart udevd after the drop-in config file. - Trigger block devices after rules change to restore the original watches. - Also verify that the device's inode is listed in the inotify fd's fdinfo. (cherry picked from commit 8bcd2c8ee03b16ed84b5f5554c02a3bf2e900587)
This commit is contained in:
committed by
Luca Boccassi
parent
b17f8cd9cc
commit
53eb316e26
@@ -8,7 +8,7 @@ set -o pipefail
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
function check_validity() {
|
||||
check_validity() {
|
||||
local f ID_OR_HANDLE
|
||||
|
||||
for f in /run/udev/watch/*; do
|
||||
@@ -22,7 +22,7 @@ function check_validity() {
|
||||
done
|
||||
}
|
||||
|
||||
function check() {
|
||||
check() {
|
||||
for _ in {1..2}; do
|
||||
systemctl reset-failed systemd-udevd.service
|
||||
systemctl restart systemd-udevd.service
|
||||
@@ -52,32 +52,56 @@ function check() {
|
||||
done
|
||||
}
|
||||
|
||||
# Check if the first invocation (should be in initrd) pushed the inotify fd to fdstore,
|
||||
# and the next invocation gained the fd from service manager.
|
||||
# TNote the service may be started without generating debugging logs. Let's check failure log.
|
||||
if ! journalctl -n 1 -q -u systemd-udevd.service --invocation=1 --grep 'Pushed inotify fd to service manager.'; then
|
||||
assert_eq "$(journalctl -n 1 -q -u systemd-udevd.service --invocation=1 --grep 'Failed to push inotify fd to service manager.' || :)" ""
|
||||
fi
|
||||
if ! journalctl -n 1 -q -u systemd-udevd.service --invocation=2 --grep 'Received inotify fd \(\d+\) from service manager.'; then
|
||||
assert_eq "$(journalctl -n 1 -q -u systemd-udevd.service --invocation=2 --grep 'Pushed inotify fd to service manager.' || :)" ""
|
||||
fi
|
||||
udevd_inotify_fdinfo() {
|
||||
local pid fd
|
||||
|
||||
# Print the path of systemd-udevd's inotify group fdinfo.
|
||||
|
||||
pid="$(systemctl show --property MainPID --value systemd-udevd.service)"
|
||||
[[ "${pid:-0}" -gt 0 ]] || return 1
|
||||
|
||||
for fd in /proc/"$pid"/fd/*; do
|
||||
if [[ "$(readlink "$fd" 2>/dev/null)" == "anon_inode:inotify" ]]; then
|
||||
echo "/proc/$pid/fdinfo/${fd##*/}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
device_is_watched() {
|
||||
local devnode="${1:?}"
|
||||
local ino fdinfo
|
||||
|
||||
fdinfo="$(udevd_inotify_fdinfo)" || exit 1
|
||||
ino="$(printf '%x' "$(stat -c '%i' "$devnode")")"
|
||||
grep -qE "^inotify wd:[0-9a-f]+ ino:${ino} " "$fdinfo"
|
||||
}
|
||||
|
||||
device_is_watched_by_link() {
|
||||
local devnode="${1:?}"
|
||||
local id
|
||||
|
||||
id="$(udevadm info "$devnode" | awk '$1 == "J:" { print $2 }')"
|
||||
test -L "/run/udev/watch/${id}"
|
||||
}
|
||||
|
||||
mkdir -p /run/systemd/system/systemd-udevd.service.d/
|
||||
cat >/run/systemd/system/systemd-udevd.service.d/10-debug.conf <<EOF
|
||||
[Service]
|
||||
SYSTEMD_LOG_LEVEL=debug
|
||||
Environment=SYSTEMD_LOG_LEVEL=debug
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
mkdir -p /run/udev/rules.d/
|
||||
|
||||
cat >/run/udev/rules.d/00-debug.rules <<EOF
|
||||
SUBSYSTEM=="block", KERNEL=="sda*", OPTIONS="log_level=debug"
|
||||
EOF
|
||||
ROOTDEV="$(bootctl -RR)"
|
||||
ROOTDEV_NAME="$(udevadm info --query=name "$ROOTDEV")"
|
||||
|
||||
cat >/run/udev/rules.d/50-testsuite.rules <<EOF
|
||||
ACTION=="add", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="watch"
|
||||
cat >/run/udev/rules.d/99-testsuite.rules <<EOF
|
||||
ACTION=="add", SUBSYSTEM=="block", KERNEL=="${ROOTDEV_NAME}", OPTIONS:="watch"
|
||||
EOF
|
||||
|
||||
# Unfortunately, journalctl --invocation= is unstable when debug logging is enabled on service manager.
|
||||
@@ -86,28 +110,25 @@ systemctl log-level info
|
||||
|
||||
check
|
||||
|
||||
ROOTDEV="$(bootctl -RR)"
|
||||
device_is_watched "$ROOTDEV"
|
||||
device_is_watched_by_link "$ROOTDEV"
|
||||
|
||||
MAJOR="$(udevadm info "$ROOTDEV" | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')"
|
||||
MINOR="$(udevadm info "$ROOTDEV" | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')"
|
||||
test -L "/run/udev/watch/b${MAJOR}:${MINOR}"
|
||||
|
||||
cat >/run/udev/rules.d/50-testsuite.rules <<EOF
|
||||
ACTION=="change", SUBSYSTEM=="block", KERNEL=="sda", OPTIONS:="nowatch"
|
||||
cat >/run/udev/rules.d/99-testsuite.rules <<EOF
|
||||
ACTION=="change", SUBSYSTEM=="block", KERNEL=="${ROOTDEV_NAME}", OPTIONS:="nowatch"
|
||||
EOF
|
||||
|
||||
check
|
||||
|
||||
MAJOR="$(udevadm info "$ROOTDEV" | grep -e '^E: MAJOR=' | sed -e 's/^E: MAJOR=//')"
|
||||
MINOR="$(udevadm info "$ROOTDEV" | grep -e '^E: MINOR=' | sed -e 's/^E: MINOR=//')"
|
||||
test ! -e "/run/udev/watch/b${MAJOR}:${MINOR}"
|
||||
(! device_is_watched "$ROOTDEV")
|
||||
(! device_is_watched_by_link "$ROOTDEV")
|
||||
|
||||
rm /run/udev/rules.d/00-debug.rules
|
||||
rm /run/udev/rules.d/50-testsuite.rules
|
||||
udevadm control --reload
|
||||
rm /run/udev/rules.d/99-testsuite.rules
|
||||
|
||||
rm -f /run/systemd/system/systemd-udevd.service.d/10-debug.conf
|
||||
systemctl daemon-reload
|
||||
systemctl restart systemd-udevd.service
|
||||
|
||||
udevadm trigger -w --action change --subsystem-match=block
|
||||
|
||||
systemctl log-level "$SAVED_LOG_LEVEL"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user