summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/Makefile23
-rw-r--r--test/Makefile.testdir10
-rw-r--r--test/TEST-01-BASIC/Makefile1
-rwxr-xr-xtest/TEST-01-BASIC/create-root.sh22
-rwxr-xr-xtest/TEST-01-BASIC/test-init.sh24
-rwxr-xr-xtest/TEST-01-BASIC/test.sh71
-rw-r--r--test/TEST-02-SYSTEMD/Makefile1
-rwxr-xr-xtest/TEST-02-SYSTEMD/create-root.sh21
-rwxr-xr-xtest/TEST-02-SYSTEMD/systemd-analyze.sh15
-rwxr-xr-xtest/TEST-02-SYSTEMD/test-init.sh24
-rwxr-xr-xtest/TEST-02-SYSTEMD/test.sh76
-rw-r--r--test/TEST-03-USR-MOUNT/Makefile1
-rwxr-xr-xtest/TEST-03-USR-MOUNT/create-root.sh33
-rw-r--r--test/TEST-03-USR-MOUNT/fstab2
-rwxr-xr-xtest/TEST-03-USR-MOUNT/test-init.sh24
-rwxr-xr-xtest/TEST-03-USR-MOUNT/test.sh96
-rw-r--r--test/TEST-04-FULL-SYSTEMD/Makefile1
-rwxr-xr-xtest/TEST-04-FULL-SYSTEMD/create-root.sh33
-rw-r--r--test/TEST-04-FULL-SYSTEMD/fstab2
-rwxr-xr-xtest/TEST-04-FULL-SYSTEMD/test-init.sh41
-rwxr-xr-xtest/TEST-04-FULL-SYSTEMD/test.sh163
-rw-r--r--test/TEST-10-RAID/Makefile1
-rwxr-xr-xtest/TEST-10-RAID/create-root.sh47
-rwxr-xr-xtest/TEST-10-RAID/cryptroot-ask.sh5
-rwxr-xr-xtest/TEST-10-RAID/finished-false.sh2
-rwxr-xr-xtest/TEST-10-RAID/hard-off.sh3
-rwxr-xr-xtest/TEST-10-RAID/test-init.sh24
-rwxr-xr-xtest/TEST-10-RAID/test.sh124
-rw-r--r--test/TEST-11-LVM/Makefile1
-rwxr-xr-xtest/TEST-11-LVM/create-root.sh28
-rwxr-xr-xtest/TEST-11-LVM/finished-false.sh2
-rwxr-xr-xtest/TEST-11-LVM/hard-off.sh3
-rwxr-xr-xtest/TEST-11-LVM/test-init.sh24
-rwxr-xr-xtest/TEST-11-LVM/test.sh117
-rw-r--r--test/TEST-12-RAID-DEG/Makefile1
-rwxr-xr-xtest/TEST-12-RAID-DEG/create-root.sh47
-rwxr-xr-xtest/TEST-12-RAID-DEG/cryptroot-ask.sh5
-rwxr-xr-xtest/TEST-12-RAID-DEG/finished-false.sh2
-rwxr-xr-xtest/TEST-12-RAID-DEG/hard-off.sh4
-rwxr-xr-xtest/TEST-12-RAID-DEG/test-init.sh27
-rwxr-xr-xtest/TEST-12-RAID-DEG/test.sh160
-rw-r--r--test/TEST-13-ENC-RAID-LVM/Makefile1
-rwxr-xr-xtest/TEST-13-ENC-RAID-LVM/create-root.sh50
-rwxr-xr-xtest/TEST-13-ENC-RAID-LVM/cryptroot-ask.sh5
-rwxr-xr-xtest/TEST-13-ENC-RAID-LVM/finished-false.sh2
-rwxr-xr-xtest/TEST-13-ENC-RAID-LVM/hard-off.sh3
-rwxr-xr-xtest/TEST-13-ENC-RAID-LVM/test-init.sh27
-rwxr-xr-xtest/TEST-13-ENC-RAID-LVM/test.sh158
-rw-r--r--test/TEST-14-IMSM/Makefile1
-rwxr-xr-xtest/TEST-14-IMSM/create-root.sh78
-rwxr-xr-xtest/TEST-14-IMSM/cryptroot-ask.sh5
-rwxr-xr-xtest/TEST-14-IMSM/hard-off.sh3
-rwxr-xr-xtest/TEST-14-IMSM/test-init.sh27
-rwxr-xr-xtest/TEST-14-IMSM/test.sh148
-rw-r--r--test/TEST-15-BTRFSRAID/Makefile1
-rwxr-xr-xtest/TEST-15-BTRFSRAID/create-root.sh28
-rwxr-xr-xtest/TEST-15-BTRFSRAID/finished-false.sh2
-rwxr-xr-xtest/TEST-15-BTRFSRAID/hard-off.sh3
-rwxr-xr-xtest/TEST-15-BTRFSRAID/test-init.sh25
-rwxr-xr-xtest/TEST-15-BTRFSRAID/test.sh123
-rw-r--r--test/TEST-16-DMSQUASH/Makefile1
-rwxr-xr-xtest/TEST-16-DMSQUASH/create-root.sh41
-rwxr-xr-xtest/TEST-16-DMSQUASH/test-init.sh31
-rwxr-xr-xtest/TEST-16-DMSQUASH/test.sh155
-rw-r--r--test/TEST-17-LVM-THIN/Makefile1
-rwxr-xr-xtest/TEST-17-LVM-THIN/create-root.sh34
-rwxr-xr-xtest/TEST-17-LVM-THIN/finished-false.sh2
-rwxr-xr-xtest/TEST-17-LVM-THIN/hard-off.sh3
-rwxr-xr-xtest/TEST-17-LVM-THIN/test-init.sh24
-rwxr-xr-xtest/TEST-17-LVM-THIN/test.sh115
-rw-r--r--test/TEST-18-UEFI/Makefile1
-rwxr-xr-xtest/TEST-18-UEFI/test-init.sh23
-rwxr-xr-xtest/TEST-18-UEFI/test.sh92
-rw-r--r--test/TEST-20-NFS/Makefile1
-rwxr-xr-xtest/TEST-20-NFS/client-init.sh49
-rw-r--r--test/TEST-20-NFS/client.link6
-rwxr-xr-xtest/TEST-20-NFS/create-root.sh23
-rw-r--r--test/TEST-20-NFS/dhcpd.conf177
-rw-r--r--test/TEST-20-NFS/exports5
-rwxr-xr-xtest/TEST-20-NFS/finished-false.sh2
-rwxr-xr-xtest/TEST-20-NFS/hard-off.sh3
-rw-r--r--test/TEST-20-NFS/hosts5
-rwxr-xr-xtest/TEST-20-NFS/server-init.sh109
-rw-r--r--test/TEST-20-NFS/server.link6
-rwxr-xr-xtest/TEST-20-NFS/test.sh434
-rwxr-xr-xtest/TEST-20-NFS/wait-if-server.sh3
-rw-r--r--test/TEST-30-ISCSI/Makefile7
-rwxr-xr-xtest/TEST-30-ISCSI/client-init.sh24
-rw-r--r--test/TEST-30-ISCSI/client.link6
-rwxr-xr-xtest/TEST-30-ISCSI/create-client-root.sh33
-rwxr-xr-xtest/TEST-30-ISCSI/create-server-root.sh20
-rw-r--r--test/TEST-30-ISCSI/dhcpd.conf48
-rw-r--r--test/TEST-30-ISCSI/hosts8
-rw-r--r--test/TEST-30-ISCSI/ibft.pl458
-rw-r--r--test/TEST-30-ISCSI/ibft.tablebin0 -> 381 bytes
-rwxr-xr-xtest/TEST-30-ISCSI/server-init.sh88
-rw-r--r--test/TEST-30-ISCSI/server.link6
-rwxr-xr-xtest/TEST-30-ISCSI/test.sh240
-rwxr-xr-xtest/TEST-30-ISCSI/wait-if-server.sh4
-rw-r--r--test/TEST-35-ISCSI-MULTI/Makefile1
-rwxr-xr-xtest/TEST-35-ISCSI-MULTI/client-init.sh24
-rw-r--r--test/TEST-35-ISCSI-MULTI/client.link6
-rwxr-xr-xtest/TEST-35-ISCSI-MULTI/create-client-root.sh33
-rwxr-xr-xtest/TEST-35-ISCSI-MULTI/create-server-root.sh20
-rw-r--r--test/TEST-35-ISCSI-MULTI/dhcpd.conf48
-rwxr-xr-xtest/TEST-35-ISCSI-MULTI/finished-false.sh2
-rwxr-xr-xtest/TEST-35-ISCSI-MULTI/hard-off.sh3
-rw-r--r--test/TEST-35-ISCSI-MULTI/hosts8
-rwxr-xr-xtest/TEST-35-ISCSI-MULTI/server-init.sh88
-rw-r--r--test/TEST-35-ISCSI-MULTI/server.link6
-rwxr-xr-xtest/TEST-35-ISCSI-MULTI/test.sh345
-rwxr-xr-xtest/TEST-35-ISCSI-MULTI/wait-if-server.sh4
-rw-r--r--test/TEST-40-NBD/Makefile1
-rwxr-xr-xtest/TEST-40-NBD/client-init.sh29
-rw-r--r--test/TEST-40-NBD/client.link6
-rwxr-xr-xtest/TEST-40-NBD/create-client-root.sh24
-rwxr-xr-xtest/TEST-40-NBD/create-encrypted-root.sh41
-rwxr-xr-xtest/TEST-40-NBD/create-server-root.sh26
-rwxr-xr-xtest/TEST-40-NBD/cryptroot-ask.sh5
-rw-r--r--test/TEST-40-NBD/dhcpd.conf66
-rwxr-xr-xtest/TEST-40-NBD/finished-false.sh2
-rwxr-xr-xtest/TEST-40-NBD/hard-off.sh3
-rw-r--r--test/TEST-40-NBD/hosts8
-rwxr-xr-xtest/TEST-40-NBD/server-init.sh68
-rw-r--r--test/TEST-40-NBD/server.link6
-rwxr-xr-xtest/TEST-40-NBD/test.sh507
-rwxr-xr-xtest/TEST-40-NBD/wait-if-server.sh3
-rw-r--r--test/TEST-50-MULTINIC/Makefile1
-rwxr-xr-xtest/TEST-50-MULTINIC/client-init.sh40
-rw-r--r--test/TEST-50-MULTINIC/client.link6
-rwxr-xr-xtest/TEST-50-MULTINIC/create-root.sh23
-rw-r--r--test/TEST-50-MULTINIC/dhcpd.conf36
-rw-r--r--test/TEST-50-MULTINIC/exports1
-rwxr-xr-xtest/TEST-50-MULTINIC/finished-false.sh2
-rwxr-xr-xtest/TEST-50-MULTINIC/hard-off.sh3
-rw-r--r--test/TEST-50-MULTINIC/hosts5
-rwxr-xr-xtest/TEST-50-MULTINIC/server-init.sh98
-rw-r--r--test/TEST-50-MULTINIC/server.link6
-rwxr-xr-xtest/TEST-50-MULTINIC/test.sh376
-rwxr-xr-xtest/TEST-50-MULTINIC/wait-if-server.sh3
-rw-r--r--test/TEST-60-BONDBRIDGEVLAN/Makefile1
-rwxr-xr-xtest/TEST-60-BONDBRIDGEVLAN/client-init.sh64
-rw-r--r--test/TEST-60-BONDBRIDGEVLAN/client.link6
-rwxr-xr-xtest/TEST-60-BONDBRIDGEVLAN/create-root.sh22
-rw-r--r--test/TEST-60-BONDBRIDGEVLAN/dhcpd.conf27
-rw-r--r--test/TEST-60-BONDBRIDGEVLAN/exports1
-rwxr-xr-xtest/TEST-60-BONDBRIDGEVLAN/finished-false.sh2
-rwxr-xr-xtest/TEST-60-BONDBRIDGEVLAN/hard-off.sh3
-rw-r--r--test/TEST-60-BONDBRIDGEVLAN/hosts5
-rwxr-xr-xtest/TEST-60-BONDBRIDGEVLAN/server-init.sh140
-rw-r--r--test/TEST-60-BONDBRIDGEVLAN/server.link6
-rwxr-xr-xtest/TEST-60-BONDBRIDGEVLAN/test.sh404
-rwxr-xr-xtest/TEST-60-BONDBRIDGEVLAN/wait-if-server.sh6
-rw-r--r--test/TEST-62-SKIPCPIO/Makefile1
-rwxr-xr-xtest/TEST-62-SKIPCPIO/test.sh80
-rw-r--r--test/TEST-63-DRACUT-CPIO/Makefile1
-rwxr-xr-xtest/TEST-63-DRACUT-CPIO/test.sh85
-rw-r--r--test/TEST-98-GETARG/Makefile1
-rwxr-xr-xtest/TEST-98-GETARG/test.sh159
-rw-r--r--test/container/Dockerfile-Arch17
-rw-r--r--test/container/Dockerfile-Debian67
-rw-r--r--test/container/Dockerfile-Fedora-latest71
-rw-r--r--test/container/Dockerfile-Gentoo57
-rw-r--r--test/container/Dockerfile-OpenSuse-latest13
-rw-r--r--test/container/Dockerfile-Ubuntu63
-rwxr-xr-xtest/run-qemu74
-rw-r--r--test/test-functions206
167 files changed, 7854 insertions, 0 deletions
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..dfaa450
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,23 @@
+.PHONY: all check clean $(wildcard TEST-??-*)
+
+$(wildcard TEST-??-*):
+ @[ "$(shell id -u)" = 0 ] || { echo "'check' must be run as root! Please use 'sudo'."; exit 1; }
+ @{ \
+ [ -d $@ ] || exit 0; \
+ [ -f $@/Makefile ] || exit 0; \
+ if [ -n "$$TESTS" ]; then t=$${$@##TEST-}; t=$${t%%-*}; [ "$${TESTS#*$$t*}" != "$$TESTS" ] || exit 0; fi; \
+ if [ -n "$$SKIP" ]; then t=$${$@##TEST-}; t=$${t%%-*}; [ "$${SKIP#*$$t*}" != "$$SKIP" ] && exit 0; fi; \
+ $(MAKE) -C $@ all ; \
+ }
+
+check: $(wildcard TEST-??-*)
+
+clean:
+ @for i in TEST-[0-9]*; do \
+ [ -d $$i ] || continue ; \
+ [ -f $$i/Makefile ] || continue ; \
+ $(MAKE) -C $$i clean ; \
+ done
+
+all:
+
diff --git a/test/Makefile.testdir b/test/Makefile.testdir
new file mode 100644
index 0000000..e3b31b1
--- /dev/null
+++ b/test/Makefile.testdir
@@ -0,0 +1,10 @@
+.PHONY: all setup clean run
+
+all:
+ @V=$(V) testdir="$(realpath ../)" ./test.sh --all
+setup:
+ @testdir="$(realpath ../)" ./test.sh --setup
+clean:
+ @testdir="$(realpath ../)" ./test.sh --clean
+run:
+ @testdir="$(realpath ../)" ./test.sh --run
diff --git a/test/TEST-01-BASIC/Makefile b/test/TEST-01-BASIC/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-01-BASIC/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-01-BASIC/create-root.sh b/test/TEST-01-BASIC/create-root.sh
new file mode 100755
index 0000000..633b11f
--- /dev/null
+++ b/test/TEST-01-BASIC/create-root.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+
+mkfs.ext4 -L ' rdinit=/bin/sh' /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-01-BASIC/test-init.sh b/test/TEST-01-BASIC/test-init.sh
new file mode 100755
index 0000000..108e626
--- /dev/null
+++ b/test/TEST-01-BASIC/test-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh
new file mode 100755
index 0000000..b3a8656
--- /dev/null
+++ b/test/TEST-01-BASIC/test.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on a ext4 filesystem"
+
+# Uncomment this to debug failures
+# DEBUGFAIL="rd.shell rd.break"
+
+test_run() {
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.img root
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -device i6300esb -watchdog-action poweroff \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot \"root=LABEL= rdinit=/bin/sh\" rw systemd.log_level=debug systemd.log_target=console rd.retry=3 rd.debug console=ttyS0,115200n81 rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ test_marker_check || return 1
+}
+
+test_setup() {
+ # Create what will eventually be our root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root" \
+ -i ./test-init.sh /sbin/init \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ --no-hostonly --no-hostonly-cmdline --nomdadmconf --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+ mkdir -p "$TESTDIR"/overlay/source && mv "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.*
+
+ # second, install the files needed to make the root filesystem
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "test-makeroot" \
+ -I "mkfs.ext4" \
+ -i ./create-root.sh /lib/dracut/hooks/initqueue/01-create-root.sh \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.img root 80
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+
+ # make sure --omit-drivers does not filter out drivers using regexp to test for an earlier regression (assuming there is no one letter linux kernel module needed to run the test)
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -a "test watchdog" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod i6300esb ib700wdt" \
+ --omit-drivers 'a b c d e f g h i j k l m n o p q r s t u v w x y z' \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-02-SYSTEMD/Makefile b/test/TEST-02-SYSTEMD/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-02-SYSTEMD/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-02-SYSTEMD/create-root.sh b/test/TEST-02-SYSTEMD/create-root.sh
new file mode 100755
index 0000000..c1fbe76
--- /dev/null
+++ b/test/TEST-02-SYSTEMD/create-root.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+set -e
+
+udevadm settle
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-02-SYSTEMD/systemd-analyze.sh b/test/TEST-02-SYSTEMD/systemd-analyze.sh
new file mode 100755
index 0000000..55f8728
--- /dev/null
+++ b/test/TEST-02-SYSTEMD/systemd-analyze.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+for i in \
+ sysinit.target \
+ basic.target \
+ initrd-fs.target \
+ initrd.target \
+ initrd-switch-root.target \
+ emergency.target \
+ shutdown.target; do
+ if ! systemd-analyze verify "$i"; then
+ warn "systemd-analyze verify $i failed"
+ poweroff
+ fi
+done
diff --git a/test/TEST-02-SYSTEMD/test-init.sh b/test/TEST-02-SYSTEMD/test-init.sh
new file mode 100755
index 0000000..108e626
--- /dev/null
+++ b/test/TEST-02-SYSTEMD/test-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-02-SYSTEMD/test.sh b/test/TEST-02-SYSTEMD/test.sh
new file mode 100755
index 0000000..40d1b3e
--- /dev/null
+++ b/test/TEST-02-SYSTEMD/test.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on a ext4 filesystem"
+
+test_check() {
+ command -v systemctl &> /dev/null
+}
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell=1 rd.break=pre-mount"
+test_run() {
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.img root
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=LABEL=dracut rw loglevel=77 systemd.log_level=debug systemd.log_target=console rd.retry=3 rd.info console=ttyS0,115200n81 selinux=0 init=/sbin/init rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ test_marker_check || return 1
+}
+
+test_setup() {
+ # Create what will eventually be our root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root" \
+ -i ./test-init.sh /sbin/init \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ --no-hostonly --no-hostonly-cmdline --nomdadmconf --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+ mkdir -p "$TESTDIR"/overlay/source && mv "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.*
+
+ # second, install the files needed to make the root filesystem
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "test-makeroot" \
+ -I "mkfs.ext4" \
+ -i ./create-root.sh /lib/dracut/hooks/initqueue/01-create-root.sh \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.img root 80
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+
+ # systemd-analyze.sh calls man indirectly
+ # make the man command succeed always
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -a "test systemd" \
+ -o "network kernel-network-modules" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ -i ./systemd-analyze.sh /lib/dracut/hooks/pre-pivot/00-systemd-analyze.sh \
+ -i "/bin/true" "/usr/bin/man" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-03-USR-MOUNT/Makefile b/test/TEST-03-USR-MOUNT/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-03-USR-MOUNT/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-03-USR-MOUNT/create-root.sh b/test/TEST-03-USR-MOUNT/create-root.sh
new file mode 100755
index 0000000..411fa99
--- /dev/null
+++ b/test/TEST-03-USR-MOUNT/create-root.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+set -e
+
+udevadm settle
+modprobe btrfs || :
+mkfs.btrfs -L dracut /dev/disk/by-id/ata-disk_root
+mkfs.btrfs -L dracutusr /dev/disk/by-id/ata-disk_usr
+btrfs device scan /dev/disk/by-id/ata-disk_root
+btrfs device scan /dev/disk/by-id/ata-disk_usr
+mkdir -p /root
+mount -t btrfs /dev/disk/by-id/ata-disk_root /root
+[ -d /root/usr ] || mkdir -p /root/usr
+mount -t btrfs /dev/disk/by-id/ata-disk_usr /root/usr
+btrfs subvolume create /root/usr/usr
+umount /root/usr
+mount -t btrfs -o subvol=usr /dev/disk/by-id/ata-disk_usr /root/usr
+cp -a -t /root /source/*
+mkdir -p /root/run
+btrfs filesystem sync /root/usr
+btrfs filesystem sync /root
+umount /root/usr
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-03-USR-MOUNT/fstab b/test/TEST-03-USR-MOUNT/fstab
new file mode 100644
index 0000000..feac57a
--- /dev/null
+++ b/test/TEST-03-USR-MOUNT/fstab
@@ -0,0 +1,2 @@
+/dev/disk/by-id/ata-disk_root / btrfs defaults 0 0
+/dev/disk/by-id/ata-disk_usr /usr btrfs subvol=usr,ro 0 0
diff --git a/test/TEST-03-USR-MOUNT/test-init.sh b/test/TEST-03-USR-MOUNT/test-init.sh
new file mode 100755
index 0000000..108e626
--- /dev/null
+++ b/test/TEST-03-USR-MOUNT/test-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-03-USR-MOUNT/test.sh b/test/TEST-03-USR-MOUNT/test.sh
new file mode 100755
index 0000000..69ffda3
--- /dev/null
+++ b/test/TEST-03-USR-MOUNT/test.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on a btrfs filesystem with /usr subvolume"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell rd.break"
+
+client_run() {
+ local test_name="$1"
+ shift
+ local client_opts="$*"
+
+ echo "CLIENT TEST START: $test_name"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.btrfs root
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/usr.btrfs usr
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -device i6300esb -watchdog-action poweroff \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=LABEL=dracut $client_opts loglevel=7 rd.retry=3 rd.info console=ttyS0,115200n81 selinux=0 rd.debug rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ if ! test_marker_check; then
+ echo "CLIENT TEST END: $test_name [FAILED]"
+ return 1
+ fi
+ echo "CLIENT TEST END: $test_name [OK]"
+}
+
+test_run() {
+ client_run "no option specified" || return 1
+ client_run "readonly root" "ro" || return 1
+ client_run "writeable root" "rw" || return 1
+}
+
+test_setup() {
+ # Create what will eventually be our root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root" \
+ -i ./test-init.sh /sbin/init \
+ -i ./fstab /etc/fstab \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ --no-hostonly --no-hostonly-cmdline --nomdadmconf --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+ mkdir -p "$TESTDIR"/overlay/source && mv "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.*
+
+ # second, install the files needed to make the root filesystem
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "test-makeroot" \
+ -I "mkfs.btrfs" \
+ -i ./create-root.sh /lib/dracut/hooks/initqueue/01-create-root.sh \
+ --nomdadmconf \
+ --nohardlink \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank file to use as a root filesystem
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.btrfs root 160
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/usr.btrfs usr 160
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=btrfs quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+
+ if ! test_marker_check dracut-root-block-created; then
+ echo "Could not create root filesystem"
+ return 1
+ fi
+
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -a "test watchdog" \
+ -o "network kernel-network-modules" \
+ -d "piix ide-gd_mod ata_piix btrfs sd_mod i6300esb ib700wdt" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-04-FULL-SYSTEMD/Makefile b/test/TEST-04-FULL-SYSTEMD/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-04-FULL-SYSTEMD/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-04-FULL-SYSTEMD/create-root.sh b/test/TEST-04-FULL-SYSTEMD/create-root.sh
new file mode 100755
index 0000000..411fa99
--- /dev/null
+++ b/test/TEST-04-FULL-SYSTEMD/create-root.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+set -e
+
+udevadm settle
+modprobe btrfs || :
+mkfs.btrfs -L dracut /dev/disk/by-id/ata-disk_root
+mkfs.btrfs -L dracutusr /dev/disk/by-id/ata-disk_usr
+btrfs device scan /dev/disk/by-id/ata-disk_root
+btrfs device scan /dev/disk/by-id/ata-disk_usr
+mkdir -p /root
+mount -t btrfs /dev/disk/by-id/ata-disk_root /root
+[ -d /root/usr ] || mkdir -p /root/usr
+mount -t btrfs /dev/disk/by-id/ata-disk_usr /root/usr
+btrfs subvolume create /root/usr/usr
+umount /root/usr
+mount -t btrfs -o subvol=usr /dev/disk/by-id/ata-disk_usr /root/usr
+cp -a -t /root /source/*
+mkdir -p /root/run
+btrfs filesystem sync /root/usr
+btrfs filesystem sync /root
+umount /root/usr
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-04-FULL-SYSTEMD/fstab b/test/TEST-04-FULL-SYSTEMD/fstab
new file mode 100644
index 0000000..feac57a
--- /dev/null
+++ b/test/TEST-04-FULL-SYSTEMD/fstab
@@ -0,0 +1,2 @@
+/dev/disk/by-id/ata-disk_root / btrfs defaults 0 0
+/dev/disk/by-id/ata-disk_usr /usr btrfs subvol=usr,ro 0 0
diff --git a/test/TEST-04-FULL-SYSTEMD/test-init.sh b/test/TEST-04-FULL-SYSTEMD/test-init.sh
new file mode 100755
index 0000000..b7261b5
--- /dev/null
+++ b/test/TEST-04-FULL-SYSTEMD/test-init.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+systemctl --failed --no-legend --no-pager > /failed
+
+if ! ismounted /usr; then
+ echo "**************************FAILED**************************"
+ echo "/usr not mounted!!"
+ cat /proc/mounts
+ echo "**************************FAILED**************************"
+else
+ if [ -s /failed ]; then
+ echo "**************************FAILED**************************"
+ cat /failed
+ echo "**************************FAILED**************************"
+
+ else
+ echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+ echo "All OK"
+ fi
+fi
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+systemctl --no-block poweroff
+exit 0
diff --git a/test/TEST-04-FULL-SYSTEMD/test.sh b/test/TEST-04-FULL-SYSTEMD/test.sh
new file mode 100755
index 0000000..160104f
--- /dev/null
+++ b/test/TEST-04-FULL-SYSTEMD/test.sh
@@ -0,0 +1,163 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="Full systemd serialization/deserialization test with /usr mount"
+
+test_check() {
+ command -v systemctl &> /dev/null
+}
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell rd.break"
+#DEBUGOUT="quiet systemd.log_level=debug systemd.log_target=console loglevel=77 rd.info rd.debug"
+DEBUGOUT="loglevel=0 "
+client_run() {
+ local test_name="$1"
+ shift
+ local client_opts="$*"
+
+ echo "CLIENT TEST START: $test_name"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.btrfs root
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/usr.btrfs usr
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "systemd.unit=testsuite.target systemd.mask=systemd-firstboot panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=LABEL=dracut $client_opts rd.retry=3 console=ttyS0,115200n81 selinux=0 $DEBUGOUT rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ if ! test_marker_check; then
+ echo "CLIENT TEST END: $test_name [FAILED]"
+ return 1
+ fi
+ echo "CLIENT TEST END: $test_name [OK]"
+
+}
+
+test_run() {
+ client_run "no option specified" || return 1
+ client_run "readonly root" "ro" || return 1
+ client_run "writeable root" "rw" || return 1
+ return 0
+}
+
+test_setup() {
+ # shellcheck disable=SC2064
+ trap "$(shopt -p globstar)" RETURN
+ shopt -q -s globstar
+
+ # Create what will eventually be our root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root dbus" \
+ -I "ldconfig" \
+ -i ./test-init.sh /sbin/test-init \
+ -i ./fstab /etc/fstab \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ --no-hostonly --no-hostonly-cmdline --nomdadmconf --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+
+ mkdir -p "$TESTDIR"/overlay/source && cp -a "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.* && export initdir=$TESTDIR/overlay/source
+
+ if type -P rpm &> /dev/null; then
+ rpm -ql systemd | xargs -r "$PKGLIBDIR"/dracut-install ${initdir:+-D "$initdir"} -o -a -l
+ elif type -P dpkg &> /dev/null; then
+ dpkg -L systemd | xargs -r "$PKGLIBDIR"/dracut-install ${initdir:+-D "$initdir"} -o -a -l
+ elif type -P pacman &> /dev/null; then
+ pacman -Q -l systemd | while read -r _ a; do printf -- "%s\0" "$a"; done | xargs -0 -r "$PKGLIBDIR"/dracut-install ${initdir:+-D "$initdir"} -o -a -l
+ else
+ echo "Can't install systemd base"
+ return 1
+ fi
+
+ # softlink mtab
+ ln -fs /proc/self/mounts "$initdir"/etc/mtab
+
+ # install any Execs from the service files
+ grep -Eho '^Exec[^ ]*=[^ ]+' "$initdir"{,/usr}/lib/systemd/system/*.service \
+ | while read -r i || [ -n "$i" ]; do
+ i=${i##Exec*=}
+ i=${i##-}
+ "$PKGLIBDIR"/dracut-install ${initdir:+-D "$initdir"} -o -a -l "$i"
+ done
+
+ # setup the testsuite target
+ mkdir -p "$initdir"/etc/systemd/system
+ cat > "$initdir"/etc/systemd/system/testsuite.target << EOF
+[Unit]
+Description=Testsuite target
+Requires=basic.target
+After=basic.target
+Conflicts=rescue.target
+AllowIsolate=yes
+EOF
+
+ # setup the testsuite service
+ cat > "$initdir"/etc/systemd/system/testsuite.service << EOF
+[Unit]
+Description=Testsuite service
+After=basic.target
+
+[Service]
+ExecStart=/sbin/test-init
+Type=oneshot
+StandardInput=tty
+StandardOutput=tty
+EOF
+
+ mkdir -p "$initdir"/etc/systemd/system/testsuite.target.wants
+ ln -fs ../testsuite.service "$initdir"/etc/systemd/system/testsuite.target.wants/testsuite.service
+
+ # second, install the files needed to make the root filesystem
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "test-makeroot bash btrfs" \
+ -I "mkfs.btrfs" \
+ -i ./create-root.sh /lib/dracut/hooks/initqueue/01-create-root.sh \
+ --nomdadmconf \
+ --nohardlink \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay/*
+
+ # Create the blank file to use as a root filesystem
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.btrfs root 160
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/usr.btrfs usr 160
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=btrfs quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+
+ if ! test_marker_check dracut-root-block-created; then
+ echo "Could not create root filesystem"
+ return 1
+ fi
+
+ [ -e /etc/machine-id ] && EXTRA_MACHINE="/etc/machine-id"
+ [ -e /etc/machine-info ] && EXTRA_MACHINE+=" /etc/machine-info"
+
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -a "test systemd i18n qemu" \
+ ${EXTRA_MACHINE:+-I "$EXTRA_MACHINE"} \
+ -o "network plymouth lvm mdraid resume crypt caps dm terminfo usrmount kernel-network-modules rngd" \
+ -d "piix ide-gd_mod ata_piix btrfs sd_mod i6300esb ib700wdt" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+
+ rm -rf -- "$TESTDIR"/overlay
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-10-RAID/Makefile b/test/TEST-10-RAID/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-10-RAID/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-10-RAID/create-root.sh b/test/TEST-10-RAID/create-root.sh
new file mode 100755
index 0000000..05dec0f
--- /dev/null
+++ b/test/TEST-10-RAID/create-root.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+set -ex
+mdadm --create /dev/md0 --run --auto=yes --level=5 --raid-devices=3 /dev/disk/by-id/ata-disk_raid[123]
+# wait for the array to finish initializing, otherwise this sometimes fails
+# randomly.
+mdadm -W /dev/md0 || :
+printf test > keyfile
+cryptsetup -q luksFormat /dev/md0 /keyfile
+echo "The passphrase is test"
+cryptsetup luksOpen /dev/md0 dracut_crypt_test < /keyfile
+lvm pvcreate -ff -y /dev/mapper/dracut_crypt_test
+lvm vgcreate dracut /dev/mapper/dracut_crypt_test
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+mkfs.ext4 -L root /dev/dracut/root
+mkdir -p /sysroot
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+mkdir -p /sysroot/run
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+udevadm settle
+cryptsetup luksClose /dev/mapper/dracut_crypt_test
+udevadm settle
+mdadm -W /dev/md0 || :
+udevadm settle
+mdadm --detail --export /dev/md0 | grep -F MD_UUID > /tmp/mduuid
+. /tmp/mduuid
+udevadm settle
+eval "$(udevadm info --query=property --name=/dev/md0 | while read -r line || [ -n "$line" ]; do [ "$line" != "${line#*ID_FS_UUID*}" ] && echo "$line"; done)"
+{
+ echo "dracut-root-block-created"
+ echo MD_UUID="$MD_UUID"
+ echo "ID_FS_UUID=$ID_FS_UUID"
+} | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-10-RAID/cryptroot-ask.sh b/test/TEST-10-RAID/cryptroot-ask.sh
new file mode 100755
index 0000000..7e956b3
--- /dev/null
+++ b/test/TEST-10-RAID/cryptroot-ask.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+[ -b "/dev/mapper/$2" ] && exit 0
+printf test > /keyfile
+/sbin/cryptsetup luksOpen "$1" "$2" < /keyfile
diff --git a/test/TEST-10-RAID/finished-false.sh b/test/TEST-10-RAID/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-10-RAID/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-10-RAID/hard-off.sh b/test/TEST-10-RAID/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-10-RAID/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-10-RAID/test-init.sh b/test/TEST-10-RAID/test-init.sh
new file mode 100755
index 0000000..108e626
--- /dev/null
+++ b/test/TEST-10-RAID/test-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-10-RAID/test.sh b/test/TEST-10-RAID/test.sh
new file mode 100755
index 0000000..15cbf56
--- /dev/null
+++ b/test/TEST-10-RAID/test.sh
@@ -0,0 +1,124 @@
+#!/bin/bash
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on an encrypted LVM PV on a RAID-5"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell rd.udev.log-priority=debug loglevel=70 systemd.log_target=kmsg"
+#DEBUGFAIL="rd.break rd.shell rd.debug debug"
+test_run() {
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-1.img raid1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-2.img raid2
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-3.img raid3
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=/dev/dracut/root rd.auto rw rd.retry=10 console=ttyS0,115200n81 selinux=0 rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ test_marker_check || return 1
+}
+
+test_setup() {
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc var/run tmp
+ mkdir -p root usr/bin usr/lib usr/lib64 usr/sbin
+ )
+ inst_multiple sh df free ls shutdown poweroff stty cat ps ln \
+ mount dmesg mkdir cp dd sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst_simple /etc/os-release
+ inst ./test-init.sh /sbin/init
+ inst_multiple grep
+ inst_multiple -o /lib/systemd/systemd-shutdown
+ find_binary plymouth > /dev/null && inst_multiple plymouth
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount dd sync grep
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash crypt lvm mdraid kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank files to use as a root filesystem
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-1.img raid1 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-2.img raid2 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-3.img raid3 40
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/cannotreach rw rootfstype=ext4 console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ eval "$(grep -F -a -m 1 ID_FS_UUID "$TESTDIR"/marker.img)"
+
+ (
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown dd
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ inst ./cryptroot-ask.sh /sbin/cryptroot-ask
+ mkdir -p "$initdir"/etc
+ echo "testluks UUID=$ID_FS_UUID /etc/key" > "$initdir"/etc/crypttab
+ #echo "luks-$ID_FS_UUID /dev/md0 none" > $initdir/etc/crypttab
+ echo -n "test" > "$initdir"/etc/key
+ )
+
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth network kernel-network-modules" \
+ -a "debug" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-11-LVM/Makefile b/test/TEST-11-LVM/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-11-LVM/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-11-LVM/create-root.sh b/test/TEST-11-LVM/create-root.sh
new file mode 100755
index 0000000..015bfe7
--- /dev/null
+++ b/test/TEST-11-LVM/create-root.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+for dev in /dev/disk/by-id/ata-disk_disk[123]; do
+ lvm pvcreate -ff -y "$dev"
+done
+
+lvm vgcreate dracut /dev/disk/by-id/ata-disk_disk[123]
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+mkfs.ext4 /dev/dracut/root
+mkdir -p /sysroot
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-11-LVM/finished-false.sh b/test/TEST-11-LVM/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-11-LVM/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-11-LVM/hard-off.sh b/test/TEST-11-LVM/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-11-LVM/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-11-LVM/test-init.sh b/test/TEST-11-LVM/test-init.sh
new file mode 100755
index 0000000..108e626
--- /dev/null
+++ b/test/TEST-11-LVM/test-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-11-LVM/test.sh b/test/TEST-11-LVM/test.sh
new file mode 100755
index 0000000..2782810
--- /dev/null
+++ b/test/TEST-11-LVM/test.sh
@@ -0,0 +1,117 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on LVM PV"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.break rd.shell"
+
+test_run() {
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-1.img disk1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-2.img disk2
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-3.img disk3
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=/dev/dracut/root rw rd.auto=1 quiet rd.retry=3 rd.info console=ttyS0,115200n81 selinux=0 rd.debug rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ test_marker_check || return 1
+}
+
+test_setup() {
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc var/run tmp
+ mkdir -p root usr/bin usr/lib usr/lib64 usr/sbin
+ )
+ inst_multiple sh df free ls shutdown poweroff stty cat ps ln \
+ mount dmesg mkdir cp dd sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst_multiple grep
+ inst_simple /etc/os-release
+ inst ./test-init.sh /sbin/init
+ find_binary plymouth > /dev/null && inst_multiple plymouth
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ mkdir -p "$initdir"/run
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount dd sync
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash lvm mdraid kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank files to use as a root filesystem
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-1.img disk1 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-2.img disk2 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-3.img disk3 40
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+
+ (
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown dd
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ )
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth network kernel-network-modules" \
+ -a "debug" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-12-RAID-DEG/Makefile b/test/TEST-12-RAID-DEG/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-12-RAID-DEG/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-12-RAID-DEG/create-root.sh b/test/TEST-12-RAID-DEG/create-root.sh
new file mode 100755
index 0000000..05dec0f
--- /dev/null
+++ b/test/TEST-12-RAID-DEG/create-root.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+set -ex
+mdadm --create /dev/md0 --run --auto=yes --level=5 --raid-devices=3 /dev/disk/by-id/ata-disk_raid[123]
+# wait for the array to finish initializing, otherwise this sometimes fails
+# randomly.
+mdadm -W /dev/md0 || :
+printf test > keyfile
+cryptsetup -q luksFormat /dev/md0 /keyfile
+echo "The passphrase is test"
+cryptsetup luksOpen /dev/md0 dracut_crypt_test < /keyfile
+lvm pvcreate -ff -y /dev/mapper/dracut_crypt_test
+lvm vgcreate dracut /dev/mapper/dracut_crypt_test
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+mkfs.ext4 -L root /dev/dracut/root
+mkdir -p /sysroot
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+mkdir -p /sysroot/run
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+udevadm settle
+cryptsetup luksClose /dev/mapper/dracut_crypt_test
+udevadm settle
+mdadm -W /dev/md0 || :
+udevadm settle
+mdadm --detail --export /dev/md0 | grep -F MD_UUID > /tmp/mduuid
+. /tmp/mduuid
+udevadm settle
+eval "$(udevadm info --query=property --name=/dev/md0 | while read -r line || [ -n "$line" ]; do [ "$line" != "${line#*ID_FS_UUID*}" ] && echo "$line"; done)"
+{
+ echo "dracut-root-block-created"
+ echo MD_UUID="$MD_UUID"
+ echo "ID_FS_UUID=$ID_FS_UUID"
+} | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-12-RAID-DEG/cryptroot-ask.sh b/test/TEST-12-RAID-DEG/cryptroot-ask.sh
new file mode 100755
index 0000000..a6b7ac7
--- /dev/null
+++ b/test/TEST-12-RAID-DEG/cryptroot-ask.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+[ -b /dev/mapper/"$2" ] && exit 0
+printf test > /keyfile
+/sbin/cryptsetup luksOpen "$1" "$2" < /keyfile
diff --git a/test/TEST-12-RAID-DEG/finished-false.sh b/test/TEST-12-RAID-DEG/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-12-RAID-DEG/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-12-RAID-DEG/hard-off.sh b/test/TEST-12-RAID-DEG/hard-off.sh
new file mode 100755
index 0000000..8179d57
--- /dev/null
+++ b/test/TEST-12-RAID-DEG/hard-off.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+sleep 5
+getargbool 0 rd.shell || poweroff -f
+! getargbool 0 rd.break && getargbool 0 failme && poweroff -f
diff --git a/test/TEST-12-RAID-DEG/test-init.sh b/test/TEST-12-RAID-DEG/test-init.sh
new file mode 100755
index 0000000..a5360ef
--- /dev/null
+++ b/test/TEST-12-RAID-DEG/test-init.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+if [ -d /run/initramfs/etc ]; then
+ echo " rd.debug=0 " >> /run/initramfs/etc/cmdline
+fi
+poweroff -f
diff --git a/test/TEST-12-RAID-DEG/test.sh b/test/TEST-12-RAID-DEG/test.sh
new file mode 100755
index 0000000..da5177b
--- /dev/null
+++ b/test/TEST-12-RAID-DEG/test.sh
@@ -0,0 +1,160 @@
+#!/bin/bash
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on an encrypted LVM PV on a degraded RAID-5"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell rd.break rd.debug"
+#DEBUGFAIL="rd.shell rd.break=pre-mount udev.log-priority=debug"
+#DEBUGFAIL="rd.shell rd.udev.log-priority=debug loglevel=70 systemd.log_target=kmsg"
+#DEBUGFAIL="rd.shell loglevel=70 systemd.log_target=kmsg"
+
+client_run() {
+ echo "CLIENT TEST START: $*"
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ # degrade the RAID
+ # qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-1.img raid1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-2.img raid2
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-3.img raid3
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot $* systemd.log_target=kmsg root=LABEL=root rw rd.retry=10 rd.info console=ttyS0,115200n81 log_buf_len=2M selinux=0 rd.shell=0 $DEBUGFAIL " \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ if ! test_marker_check; then
+ echo "CLIENT TEST END: $* [FAIL]"
+ return 1
+ fi
+
+ echo "CLIENT TEST END: $* [OK]"
+ return 0
+}
+
+test_run() {
+ read -r LUKS_UUID < "$TESTDIR"/luksuuid
+ read -r MD_UUID < "$TESTDIR"/mduuid
+
+ client_run failme && return 1
+ client_run rd.auto || return 1
+
+ client_run rd.luks.uuid="$LUKS_UUID" rd.md.uuid="$MD_UUID" rd.md.conf=0 rd.lvm.vg=dracut || return 1
+
+ client_run rd.luks.uuid="$LUKS_UUID" rd.md.uuid=failme rd.md.conf=0 rd.lvm.vg=dracut failme && return 1
+
+ client_run rd.luks.uuid="$LUKS_UUID" rd.md.uuid="$MD_UUID" rd.lvm=0 failme && return 1
+ client_run rd.luks.uuid="$LUKS_UUID" rd.md.uuid="$MD_UUID" rd.lvm=0 rd.auto=1 failme && return 1
+ client_run rd.luks.uuid="$LUKS_UUID" rd.md.uuid="$MD_UUID" rd.lvm.vg=failme failme && return 1
+ client_run rd.luks.uuid="$LUKS_UUID" rd.md.uuid="$MD_UUID" rd.lvm.vg=dracut || return 1
+ client_run rd.luks.uuid="$LUKS_UUID" rd.md.uuid="$MD_UUID" rd.lvm.lv=dracut/failme failme && return 1
+ client_run rd.luks.uuid="$LUKS_UUID" rd.md.uuid="$MD_UUID" rd.lvm.lv=dracut/root || return 1
+
+ return 0
+}
+
+test_setup() {
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc var/run tmp
+ mkdir -p root usr/bin usr/lib usr/lib64 usr/sbin
+ )
+ inst_multiple sh df free ls shutdown poweroff stty cat ps ln \
+ mount dmesg mkdir cp dd sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst_multiple grep
+ inst_simple /etc/os-release
+ inst ./test-init.sh /sbin/init
+ find_binary plymouth > /dev/null && inst_multiple plymouth
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount dd grep sync
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash crypt lvm mdraid kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank files to use as a root filesystem
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-1.img raid1 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-2.img raid2 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-3.img raid3 40
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+
+ test_marker_check dracut-root-block-created || return 1
+ eval "$(grep -F --binary-files=text -m 1 MD_UUID "$TESTDIR"/marker.img)"
+ eval "$(grep -F -a -m 1 ID_FS_UUID "$TESTDIR"/marker.img)"
+ echo "$ID_FS_UUID" > "$TESTDIR"/luksuuid
+ eval "$(grep -F --binary-files=text -m 1 MD_UUID "$TESTDIR"/marker.img)"
+ echo "$MD_UUID" > "$TESTDIR"/mduuid
+
+ (
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown dd
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ inst ./cryptroot-ask.sh /sbin/cryptroot-ask
+ mkdir -p "$initdir"/etc
+ echo "ARRAY /dev/md0 level=raid5 num-devices=3 UUID=$MD_UUID" > "$initdir"/etc/mdadm.conf
+ echo "luks-$ID_FS_UUID UUID=$ID_FS_UUID /etc/key" > "$initdir"/etc/crypttab
+ echo -n test > "$initdir"/etc/key
+ chmod 0600 "$initdir"/etc/key
+ )
+
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth network kernel-network-modules" \
+ -a "debug" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-13-ENC-RAID-LVM/Makefile b/test/TEST-13-ENC-RAID-LVM/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-13-ENC-RAID-LVM/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-13-ENC-RAID-LVM/create-root.sh b/test/TEST-13-ENC-RAID-LVM/create-root.sh
new file mode 100755
index 0000000..b681e6b
--- /dev/null
+++ b/test/TEST-13-ENC-RAID-LVM/create-root.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+set -x
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+printf test > keyfile
+cryptsetup -q luksFormat /dev/disk/by-id/ata-disk_disk1 /keyfile
+cryptsetup -q luksFormat /dev/disk/by-id/ata-disk_disk2 /keyfile
+cryptsetup -q luksFormat /dev/disk/by-id/ata-disk_disk3 /keyfile
+cryptsetup luksOpen /dev/disk/by-id/ata-disk_disk1 dracut_disk1 < /keyfile
+cryptsetup luksOpen /dev/disk/by-id/ata-disk_disk2 dracut_disk2 < /keyfile
+cryptsetup luksOpen /dev/disk/by-id/ata-disk_disk3 dracut_disk3 < /keyfile
+mdadm --create /dev/md0 --run --auto=yes --level=5 --raid-devices=3 /dev/mapper/dracut_disk1 /dev/mapper/dracut_disk2 /dev/mapper/dracut_disk3
+# wait for the array to finish initializing, otherwise this sometimes fails
+# randomly.
+mdadm -W /dev/md0
+lvm pvcreate -ff -y /dev/md0
+lvm vgcreate dracut /dev/md0
+
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+mkfs.ext4 /dev/dracut/root
+mkdir -p /sysroot
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+mdadm -W /dev/md0 || :
+mdadm --stop /dev/md0
+cryptsetup luksClose /dev/mapper/dracut_disk1
+cryptsetup luksClose /dev/mapper/dracut_disk2
+cryptsetup luksClose /dev/mapper/dracut_disk3
+
+{
+ echo "dracut-root-block-created"
+ for i in /dev/disk/by-id/ata-disk_disk[123]; do
+ udevadm info --query=property --name="$i" | grep -F 'ID_FS_UUID='
+ done
+} | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-13-ENC-RAID-LVM/cryptroot-ask.sh b/test/TEST-13-ENC-RAID-LVM/cryptroot-ask.sh
new file mode 100755
index 0000000..a6b7ac7
--- /dev/null
+++ b/test/TEST-13-ENC-RAID-LVM/cryptroot-ask.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+[ -b /dev/mapper/"$2" ] && exit 0
+printf test > /keyfile
+/sbin/cryptsetup luksOpen "$1" "$2" < /keyfile
diff --git a/test/TEST-13-ENC-RAID-LVM/finished-false.sh b/test/TEST-13-ENC-RAID-LVM/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-13-ENC-RAID-LVM/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-13-ENC-RAID-LVM/hard-off.sh b/test/TEST-13-ENC-RAID-LVM/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-13-ENC-RAID-LVM/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-13-ENC-RAID-LVM/test-init.sh b/test/TEST-13-ENC-RAID-LVM/test-init.sh
new file mode 100755
index 0000000..a5360ef
--- /dev/null
+++ b/test/TEST-13-ENC-RAID-LVM/test-init.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+if [ -d /run/initramfs/etc ]; then
+ echo " rd.debug=0 " >> /run/initramfs/etc/cmdline
+fi
+poweroff -f
diff --git a/test/TEST-13-ENC-RAID-LVM/test.sh b/test/TEST-13-ENC-RAID-LVM/test.sh
new file mode 100755
index 0000000..fe79223
--- /dev/null
+++ b/test/TEST-13-ENC-RAID-LVM/test.sh
@@ -0,0 +1,158 @@
+#!/bin/bash
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on LVM on encrypted partitions of a RAID-5"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell rd.break" # udev.log-priority=debug
+#DEBUGFAIL="rd.shell rd.udev.log-priority=debug loglevel=70 systemd.log_target=kmsg systemd.log_target=debug"
+#DEBUGFAIL="rd.shell loglevel=70 systemd.log_target=kmsg systemd.log_target=debug"
+
+test_run() {
+ LUKSARGS=$(cat "$TESTDIR"/luks.txt)
+
+ echo "CLIENT TEST START: $LUKSARGS"
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-1.img disk1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-2.img disk2
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-3.img disk3
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=/dev/dracut/root rw rd.auto rd.retry=20 console=ttyS0,115200n81 selinux=0 rd.debug rootwait $LUKSARGS rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing
+ test_marker_check || return 1
+ echo "CLIENT TEST END: [OK]"
+
+ test_marker_reset
+
+ echo "CLIENT TEST START: Any LUKS"
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=/dev/dracut/root rw quiet rd.auto rd.retry=20 rd.info console=ttyS0,115200n81 selinux=0 rd.debug $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing
+ test_marker_check || return 1
+ echo "CLIENT TEST END: [OK]"
+
+ test_marker_reset
+
+ echo "CLIENT TEST START: Wrong LUKS UUID"
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=/dev/dracut/root rw quiet rd.auto rd.retry=10 rd.info console=ttyS0,115200n81 selinux=0 rd.debug $DEBUGFAIL rd.luks.uuid=failme" \
+ -initrd "$TESTDIR"/initramfs.testing
+ test_marker_check && return 1
+ echo "CLIENT TEST END: [OK]"
+
+ return 0
+}
+
+test_setup() {
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc var/run tmp
+ mkdir -p root usr/bin usr/lib usr/lib64 usr/sbin
+ )
+ inst_multiple sh df free ls shutdown poweroff stty cat ps ln \
+ mount dmesg mkdir cp dd
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst_multiple grep
+ inst_simple /etc/os-release
+ inst ./test-init.sh /sbin/init
+ find_binary plymouth > /dev/null && inst_multiple plymouth
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount grep dd sync
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash crypt lvm mdraid kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank files to use as a root filesystem
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-1.img disk1 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-2.img disk2 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-3.img disk3 40
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ cryptoUUIDS=$(grep -F --binary-files=text -m 3 ID_FS_UUID "$TESTDIR"/marker.img)
+ for uuid in $cryptoUUIDS; do
+ eval "$uuid"
+ printf ' rd.luks.uuid=luks-%s ' "$ID_FS_UUID"
+ done > "$TESTDIR"/luks.txt
+
+ (
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown dd
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ inst ./cryptroot-ask.sh /sbin/cryptroot-ask
+ mkdir -p "$initdir"/etc
+ i=1
+ for uuid in $cryptoUUIDS; do
+ eval "$uuid"
+ printf 'luks-%s /dev/disk/by-id/ata-disk_disk%s /etc/key timeout=0\n' "$ID_FS_UUID" $i
+ ((i += 1))
+ done > "$initdir"/etc/crypttab
+ echo -n test > "$initdir"/etc/key
+ chmod 0600 "$initdir"/etc/key
+ )
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth network kernel-network-modules" \
+ -a "debug" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-14-IMSM/Makefile b/test/TEST-14-IMSM/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-14-IMSM/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-14-IMSM/create-root.sh b/test/TEST-14-IMSM/create-root.sh
new file mode 100755
index 0000000..97d29d9
--- /dev/null
+++ b/test/TEST-14-IMSM/create-root.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 61-dmraid-imsm.rules 64-md-raid.rules 65-md-incremental-imsm.rules 65-md-incremental.rules 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ rm -f -- "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+
+udevadm control --reload
+udevadm settle
+
+# dmraid does not want symlinks in --disk "..."
+echo y | dmraid -f isw -C Test0 --type 1 --disk "$(realpath /dev/disk/by-id/ata-disk_disk1) $(realpath /dev/disk/by-id/ata-disk_disk2)"
+udevadm settle
+
+SETS=$(dmraid -c -s)
+# scan and activate all DM RAIDS
+for s in $SETS; do
+ dmraid -ay -i -p --rm_partitions "$s"
+ [ -e "/dev/mapper/$s" ] && kpartx -a -p p "/dev/mapper/$s"
+done
+
+udevadm settle
+sleep 1
+udevadm settle
+
+sfdisk -g /dev/mapper/isw*Test0
+sfdisk --no-reread /dev/mapper/isw*Test0 << EOF
+,4M
+,28M
+,28M
+,28M
+EOF
+
+set -x
+
+udevadm settle
+dmraid -a n
+udevadm settle
+
+SETS=$(dmraid -c -s -i)
+# scan and activate all DM RAIDS
+for s in $SETS; do
+ dmraid -ay -i -p --rm_partitions "$s"
+ [ -e "/dev/mapper/$s" ] && kpartx -a -p p "/dev/mapper/$s"
+done
+
+udevadm settle
+
+mdadm --create /dev/md0 --run --auto=yes --level=5 --raid-devices=3 \
+ /dev/mapper/isw*p*[234]
+# wait for the array to finish initializing, otherwise this sometimes fails
+# randomly.
+mdadm -W /dev/md0
+set -e
+lvm pvcreate -ff -y /dev/md0
+lvm vgcreate dracut /dev/md0
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+mkfs.ext4 -L root /dev/dracut/root
+mkdir -p /sysroot
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+udevadm settle
+mdadm --detail --export /dev/md0 | grep -F MD_UUID > /tmp/mduuid
+. /tmp/mduuid
+echo "MD_UUID=$MD_UUID"
+{
+ echo "dracut-root-block-created"
+ echo MD_UUID="$MD_UUID"
+} | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+mdadm --wait-clean /dev/md0
+sync
+poweroff -f
diff --git a/test/TEST-14-IMSM/cryptroot-ask.sh b/test/TEST-14-IMSM/cryptroot-ask.sh
new file mode 100755
index 0000000..a6b7ac7
--- /dev/null
+++ b/test/TEST-14-IMSM/cryptroot-ask.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+[ -b /dev/mapper/"$2" ] && exit 0
+printf test > /keyfile
+/sbin/cryptsetup luksOpen "$1" "$2" < /keyfile
diff --git a/test/TEST-14-IMSM/hard-off.sh b/test/TEST-14-IMSM/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-14-IMSM/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-14-IMSM/test-init.sh b/test/TEST-14-IMSM/test-init.sh
new file mode 100755
index 0000000..a5360ef
--- /dev/null
+++ b/test/TEST-14-IMSM/test-init.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+if [ -d /run/initramfs/etc ]; then
+ echo " rd.debug=0 " >> /run/initramfs/etc/cmdline
+fi
+poweroff -f
diff --git a/test/TEST-14-IMSM/test.sh b/test/TEST-14-IMSM/test.sh
new file mode 100755
index 0000000..06959cd
--- /dev/null
+++ b/test/TEST-14-IMSM/test.sh
@@ -0,0 +1,148 @@
+#!/bin/bash
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on LVM PV on a isw dmraid"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell"
+#DEBUGFAIL="$DEBUGFAIL udev.log-priority=debug"
+
+client_run() {
+ echo "CLIENT TEST START: $*"
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-1.img disk1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-2.img disk2
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot $* root=LABEL=root rw debug rd.retry=5 rd.debug console=ttyS0,115200n81 selinux=0 rd.info rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ if ! test_marker_check; then
+ echo "CLIENT TEST END: $* [FAIL]"
+ return 1
+ fi
+
+ echo "CLIENT TEST END: $* [OK]"
+ return 0
+}
+
+test_run() {
+ read -r MD_UUID < "$TESTDIR"/mduuid
+ if [[ -z $MD_UUID ]]; then
+ echo "Setup failed"
+ return 1
+ fi
+
+ client_run rd.auto rd.md.imsm=0 || return 1
+ client_run rd.md.uuid="$MD_UUID" rd.dm=0 || return 1
+ # This test succeeds, because the mirror parts are found without
+ # assembling the mirror itself, which is what we want
+ client_run rd.md.uuid="$MD_UUID" rd.md=0 rd.md.imsm failme && return 1
+ client_run rd.md.uuid="$MD_UUID" rd.md=0 failme && return 1
+ # the following test hangs on newer md
+ client_run rd.md.uuid="$MD_UUID" rd.dm=0 rd.md.imsm rd.md.conf=0 || return 1
+ return 0
+}
+
+test_setup() {
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc var/run tmp
+ mkdir -p root usr/bin usr/lib usr/lib64 usr/sbin
+ )
+ inst_multiple sh df free ls shutdown poweroff stty cat ps ln \
+ mount dmesg mkdir cp dd sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+ inst_simple /etc/os-release
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst_multiple grep
+ inst ./test-init.sh /sbin/init
+ find_binary plymouth > /dev/null && inst_multiple plymouth
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ mkdir -p "$initdir"/run
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount grep dd sync realpath
+ inst_hook initqueue 01 ./create-root.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash lvm mdraid dmraid kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod dm-multipath dm-crypt dm-round-robin faulty linear multipath raid0 raid10 raid1 raid456" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank files to use as a root filesystem
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-1.img disk1 100
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-2.img disk2 100
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ eval "$(grep -F --binary-files=text -m 1 MD_UUID "$TESTDIR"/marker.img)"
+
+ if [[ -z $MD_UUID ]]; then
+ echo "Setup failed"
+ return 1
+ fi
+
+ echo "$MD_UUID" > "$TESTDIR"/mduuid
+ (
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ )
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth network kernel-network-modules" \
+ -a "debug" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-15-BTRFSRAID/Makefile b/test/TEST-15-BTRFSRAID/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-15-BTRFSRAID/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-15-BTRFSRAID/create-root.sh b/test/TEST-15-BTRFSRAID/create-root.sh
new file mode 100755
index 0000000..075deac
--- /dev/null
+++ b/test/TEST-15-BTRFSRAID/create-root.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+modprobe btrfs || :
+udevadm control --reload
+udevadm settle
+
+set -e
+
+mkfs.btrfs -draid10 -mraid10 -L root /dev/disk/by-id/ata-disk_raid[1234]
+udevadm settle
+
+btrfs device scan
+udevadm settle
+
+mkdir -p /sysroot
+mount -t btrfs /dev/disk/by-id/ata-disk_raid4 /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-15-BTRFSRAID/finished-false.sh b/test/TEST-15-BTRFSRAID/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-15-BTRFSRAID/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-15-BTRFSRAID/hard-off.sh b/test/TEST-15-BTRFSRAID/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-15-BTRFSRAID/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-15-BTRFSRAID/test-init.sh b/test/TEST-15-BTRFSRAID/test-init.sh
new file mode 100755
index 0000000..068e8f3
--- /dev/null
+++ b/test/TEST-15-BTRFSRAID/test-init.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+
+poweroff -f
diff --git a/test/TEST-15-BTRFSRAID/test.sh b/test/TEST-15-BTRFSRAID/test.sh
new file mode 100755
index 0000000..068146e
--- /dev/null
+++ b/test/TEST-15-BTRFSRAID/test.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on multiple device btrfs"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell"
+test_run() {
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-1.img raid1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-2.img raid2
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-3.img raid3
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-4.img raid4
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=LABEL=root rw rd.retry=3 rd.info console=ttyS0,115200n81 selinux=0 rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing
+ test_marker_check || return 1
+}
+
+test_setup() {
+ # Create the blank file to use as a root filesystem
+ DISKIMAGE=$TESTDIR/TEST-15-BTRFSRAID-root.img
+ rm -f -- "$DISKIMAGE"
+ dd if=/dev/zero of="$DISKIMAGE" bs=1M count=1024
+
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc var/run tmp
+ mkdir -p root usr/bin usr/lib usr/lib64 usr/sbin
+ )
+ inst_multiple sh df free ls shutdown poweroff stty cat ps ln \
+ mount dmesg mkdir cp sync dd
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst_multiple grep
+ inst ./test-init.sh /sbin/init
+ inst_simple /etc/os-release
+ find_binary plymouth > /dev/null && inst_multiple plymouth
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.btrfs poweroff cp umount dd sync
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash btrfs rootfs-block kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix btrfs sd_mod" \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank files to use as a root filesystem
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-1.img raid1 150
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-2.img raid2 150
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-3.img raid3 150
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid-4.img raid4 150
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+
+ test_marker_check dracut-root-block-created || return 1
+
+ (
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ )
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth network kernel-network-modules" \
+ -a "debug" \
+ -d "piix ide-gd_mod ata_piix btrfs sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-16-DMSQUASH/Makefile b/test/TEST-16-DMSQUASH/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-16-DMSQUASH/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-16-DMSQUASH/create-root.sh b/test/TEST-16-DMSQUASH/create-root.sh
new file mode 100755
index 0000000..f17b22f
--- /dev/null
+++ b/test/TEST-16-DMSQUASH/create-root.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+set -e
+
+udevadm settle
+
+# create a single partition using 50% of the capacity of the image file created by test_setup() in test.sh
+sfdisk /dev/disk/by-id/ata-disk_root << EOF
+2048,161792
+EOF
+
+udevadm settle
+
+mkfs.ext4 -q -L dracut /dev/disk/by-id/ata-disk_root-part1
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root-part1 /root
+mkdir -p /root/run /root/testdir
+cp -a -t /root /source/*
+echo "Creating squashfs"
+mksquashfs /source /root/testdir/rootfs.img -quiet
+
+# Copy rootfs.img to the NTFS drive if exists
+if [ -e "/dev/disk/by-id/ata-disk_root_ntfs" ]; then
+ mkfs.ntfs -F -L dracut_ntfs /dev/disk/by-id/ata-disk_root_ntfs
+ mkdir -p /root_ntfs
+ mount -t ntfs3 /dev/disk/by-id/ata-disk_root_ntfs /root_ntfs
+ mkdir -p /root_ntfs/run /root_ntfs/testdir
+ cp /root/testdir/rootfs.img /root_ntfs/testdir/rootfs.img
+fi
+
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-16-DMSQUASH/test-init.sh b/test/TEST-16-DMSQUASH/test-init.sh
new file mode 100755
index 0000000..959fa25
--- /dev/null
+++ b/test/TEST-16-DMSQUASH/test-init.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+if grep -qF ' rd.live.overlay=LABEL=persist ' /proc/cmdline; then
+ # Writing to a file in the root filesystem lets test_run() verify that the autooverlay module successfully created
+ # and formatted the overlay partition and that the dmsquash-live module used it when setting up the rootfs overlay.
+ echo "dracut-autooverlay-success" > /overlay-marker
+fi
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+
+poweroff -f
diff --git a/test/TEST-16-DMSQUASH/test.sh b/test/TEST-16-DMSQUASH/test.sh
new file mode 100755
index 0000000..3fecc92
--- /dev/null
+++ b/test/TEST-16-DMSQUASH/test.sh
@@ -0,0 +1,155 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="live root on a squash filesystem"
+
+# Uncomment these to debug failures
+#DEBUGFAIL="rd.shell rd.debug rd.live.debug loglevel=7"
+
+test_run() {
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.img root
+
+ # NTFS drive
+ if modprobe --dry-run ntfs3 &> /dev/null && command -v mkfs.ntfs &> /dev/null; then
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root_ntfs.img root_ntfs
+ fi
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -boot order=d \
+ -append "rd.live.overlay.overlayfs=1 root=live:/dev/disk/by-label/dracut console=ttyS0,115200n81 quiet selinux=0 rd.info rd.shell=0 panic=1 oops=panic softlockup_panic=1 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ test_marker_check || return 1
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -boot order=d \
+ -append "rd.live.image rd.live.overlay.overlayfs=1 root=LABEL=dracut console=ttyS0,115200n81 quiet selinux=0 rd.info rd.shell=0 panic=1 oops=panic softlockup_panic=1 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ test_marker_check || return 1
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -boot order=d \
+ -append "rd.live.image rd.live.overlay.overlayfs=1 rd.live.dir=testdir root=LABEL=dracut console=ttyS0,115200n81 quiet selinux=0 rd.info rd.shell=0 panic=1 oops=panic softlockup_panic=1 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ test_marker_check || return 1
+
+ # Run the NTFS test only if mkfs.ntfs is available
+ if modprobe --dry-run ntfs3 &> /dev/null && command -v mkfs.ntfs &> /dev/null; then
+ dd if=/dev/zero of="$TESTDIR"/marker.img bs=1MiB count=1
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -boot order=d \
+ -append "rd.live.image rd.live.overlay.overlayfs=1 rd.live.dir=testdir root=LABEL=dracut_ntfs console=ttyS0,115200n81 quiet selinux=0 rd.info rd.shell=0 panic=1 oops=panic softlockup_panic=1 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ test_marker_check || return 1
+ fi
+
+ test_marker_reset
+ rootPartitions=$(sfdisk -d "$TESTDIR"/root.img | grep -c 'root\.img[0-9]')
+ [ "$rootPartitions" -eq 1 ] || return 1
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -boot order=d \
+ -append "rd.live.image rd.live.overlay.overlayfs=1 rd.live.overlay=LABEL=persist rd.live.dir=testdir root=LABEL=dracut console=ttyS0,115200n81 quiet selinux=0 rd.info rd.shell=0 panic=1 oops=panic softlockup_panic=1 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing-autooverlay
+
+ rootPartitions=$(sfdisk -d "$TESTDIR"/root.img | grep -c 'root\.img[0-9]')
+ [ "$rootPartitions" -eq 2 ] || return 1
+
+ (
+ # Ensure that this test works when run with the `V=1` parameter, which runs the script with `set -o pipefail`.
+ set +o pipefail
+
+ # Verify that the string "dracut-autooverlay-success" occurs in the second partition in the image file.
+ dd if="$TESTDIR"/root.img bs=1MiB skip=80 status=none \
+ | grep -U --binary-files=binary -F -m 1 -q dracut-autooverlay-success
+ ) || return 1
+}
+
+test_setup() {
+ # Create what will eventually be our root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root" \
+ -i ./test-init.sh /sbin/init \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ --no-hostonly --no-hostonly-cmdline --nomdadmconf --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+ mkdir -p "$TESTDIR"/overlay/source && mv "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.*
+
+ # second, install the files needed to make the root filesystem
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ --modules "test-makeroot" \
+ --install "sfdisk mkfs.ext4 mkfs.ntfs mksquashfs" \
+ --drivers "ntfs3" \
+ --include ./create-root.sh /lib/dracut/hooks/initqueue/01-create-root.sh \
+ --no-hostonly --no-hostonly-cmdline --no-early-microcode --nofscks --nomdadmconf --nohardlink --nostrip \
+ --force "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank file to use as a root filesystem
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.img root 160
+
+ # NTFS drive
+ if modprobe --dry-run ntfs3 &> /dev/null && command -v mkfs.ntfs &> /dev/null; then
+ dd if=/dev/zero of="$TESTDIR"/root_ntfs.img bs=1MiB count=160
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/root_ntfs.img root_ntfs
+ fi
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+
+ if ! test_marker_check dracut-root-block-created; then
+ echo "Could not create root filesystem"
+ return 1
+ fi
+
+ # mount NTFS with ntfs3 driver inside the generated initramfs
+ cat > /tmp/ntfs3.rules << 'EOF'
+SUBSYSTEM=="block", ENV{ID_FS_TYPE}=="ntfs", ENV{ID_FS_TYPE}="ntfs3"
+EOF
+
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ --modules "test dash dmsquash-live qemu" \
+ --omit "rngd" \
+ --drivers "ext4 ntfs3 sd_mod" \
+ --install "mkfs.ext4" \
+ --include /tmp/ntfs3.rules /lib/udev/rules.d/ntfs3.rules \
+ --no-hostonly --no-hostonly-cmdline \
+ --force "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ --modules "test dmsquash-live-autooverlay qemu" \
+ --omit "rngd" \
+ --drivers "ext4 sd_mod" \
+ --install "mkfs.ext4" \
+ --no-hostonly --no-hostonly-cmdline \
+ --force "$TESTDIR"/initramfs.testing-autooverlay "$KVERSION" || return 1
+
+ rm -rf -- "$TESTDIR"/overlay
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-17-LVM-THIN/Makefile b/test/TEST-17-LVM-THIN/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-17-LVM-THIN/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-17-LVM-THIN/create-root.sh b/test/TEST-17-LVM-THIN/create-root.sh
new file mode 100755
index 0000000..2547d1c
--- /dev/null
+++ b/test/TEST-17-LVM-THIN/create-root.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+for dev in /dev/disk/by-id/ata-disk_disk[123]; do
+ lvm pvcreate -ff -y "$dev"
+done
+
+lvm vgcreate dracut /dev/disk/by-id/ata-disk_disk[123]
+lvm lvcreate --ignoremonitoring -l 100%FREE -T dracut/mythinpool
+lvm lvcreate --ignoremonitoring -V100M -T dracut/mythinpool -n root
+lvm vgchange --ignoremonitoring -ay
+mkfs.ext4 /dev/dracut/root
+mkdir -p /sysroot
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+
+if ! dmsetup status | grep -q out_of_data_space; then
+ echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+fi
+
+sync
+poweroff -f
diff --git a/test/TEST-17-LVM-THIN/finished-false.sh b/test/TEST-17-LVM-THIN/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-17-LVM-THIN/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-17-LVM-THIN/hard-off.sh b/test/TEST-17-LVM-THIN/hard-off.sh
new file mode 100755
index 0000000..f4d19dc
--- /dev/null
+++ b/test/TEST-17-LVM-THIN/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getarg failme && poweroff -f
diff --git a/test/TEST-17-LVM-THIN/test-init.sh b/test/TEST-17-LVM-THIN/test-init.sh
new file mode 100755
index 0000000..108e626
--- /dev/null
+++ b/test/TEST-17-LVM-THIN/test-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+: > /dev/watchdog
+
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+[ -f /etc/mtab ] || ln -sfn /proc/mounts /etc/mtab
+[ -f /etc/fstab ] || ln -sfn /proc/mounts /etc/fstab
+stty sane
+echo "made it to the rootfs!"
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+echo "Powering down."
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-17-LVM-THIN/test.sh b/test/TEST-17-LVM-THIN/test.sh
new file mode 100755
index 0000000..5010e48
--- /dev/null
+++ b/test/TEST-17-LVM-THIN/test.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on LVM PV with thin pool"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.break rd.shell"
+
+test_run() {
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-1.img disk1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-2.img disk2
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-3.img disk3
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=/dev/dracut/root rw rd.auto=1 quiet rd.retry=3 rd.info console=ttyS0,115200n81 selinux=0 rd.debug rd.shell=0 $DEBUGFAIL" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+ test_marker_check || return 1
+}
+
+test_setup() {
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc var/run tmp
+ mkdir -p root usr/bin usr/lib usr/lib64 usr/sbin
+ )
+ inst_multiple sh df free ls shutdown poweroff stty cat ps ln \
+ mount dmesg mkdir cp dd sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst_multiple grep
+ inst_simple /etc/os-release
+ inst ./test-init.sh /sbin/init
+ find_binary plymouth > /dev/null && inst_multiple plymouth
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ mkdir -p "$initdir"/run
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount grep dmsetup dd sync
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash lvm mdraid kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ # Create the blank files to use as a root filesystem
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-1.img disk1 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-2.img disk2 40
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/disk-3.img disk3 40
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+
+ (
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ )
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth network kernel-network-modules" \
+ -a "debug" -I lvs \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-18-UEFI/Makefile b/test/TEST-18-UEFI/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-18-UEFI/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-18-UEFI/test-init.sh b/test/TEST-18-UEFI/test-init.sh
new file mode 100755
index 0000000..03966d2
--- /dev/null
+++ b/test/TEST-18-UEFI/test-init.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+
+[ -e /proc/self/mounts ] \
+ || (mkdir -p /proc && mount -t proc -o nosuid,noexec,nodev proc /proc)
+
+grep -q '^sysfs /sys sysfs' /proc/self/mounts \
+ || (mkdir -p /sys && mount -t sysfs -o nosuid,noexec,nodev sysfs /sys)
+
+grep -q '^devtmpfs /dev devtmpfs' /proc/self/mounts \
+ || (mkdir -p /dev && mount -t devtmpfs -o mode=755,noexec,nosuid,strictatime devtmpfs /dev)
+
+grep -q '^tmpfs /run tmpfs' /proc/self/mounts \
+ || (mkdir -p /run && mount -t tmpfs -o mode=755,noexec,nosuid,strictatime tmpfs /run)
+
+: > /dev/watchdog
+
+exec > /dev/console 2>&1
+
+echo "made it to the rootfs! Powering down."
+echo "dracut-root-block-success" | dd oflag=direct,dsync of=/dev/sdb
+poweroff -f
diff --git a/test/TEST-18-UEFI/test.sh b/test/TEST-18-UEFI/test.sh
new file mode 100755
index 0000000..4191fed
--- /dev/null
+++ b/test/TEST-18-UEFI/test.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="UEFI boot"
+
+# Linux kernel requirements
+# CONFIG_BLK_DEV_INITRD for initramfs
+# CONFIG_EFI_HANDOVER_PROTOCOL for ovmf (Open Virtual Machine Firmware)
+# CONFIG_SATA_AHCI for ahci.ko
+# CONFIG_BLK_DEV_SD for sd_mod.ko
+# CONFIG_SQUASHFS_ZLIB for squashfs.ko
+
+ovmf_code() {
+ for path in \
+ "/usr/share/OVMF/OVMF_CODE.fd" \
+ "/usr/share/edk2/x64/OVMF_CODE.fd" \
+ "/usr/share/edk2-ovmf/OVMF_CODE.fd" \
+ "/usr/share/qemu/ovmf-x86_64-4m.bin"; do
+ [[ -s $path ]] && echo -n "$path" && return
+ done
+}
+
+test_check() {
+ [[ -n "$(ovmf_code)" ]]
+}
+
+KVERSION="${KVERSION-$(uname -r)}"
+
+test_marker_reset() {
+ dd if=/dev/zero of="$TESTDIR"/marker.img bs=1MiB count=1
+}
+
+test_marker_check() {
+ grep -U --binary-files=binary -F -m 1 -q dracut-root-block-success -- "$TESTDIR"/marker.img
+ return $?
+}
+
+test_dracut() {
+ TEST_DRACUT_ARGS+=" --local --no-hostonly --no-early-microcode --add test --kver $KVERSION"
+
+ # shellcheck disable=SC2162
+ IFS=' ' read -a TEST_DRACUT_ARGS_ARRAY <<< "$TEST_DRACUT_ARGS"
+
+ "$DRACUT" "$@" \
+ --kernel-cmdline "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot selinux=0 console=ttyS0,115200n81 $DEBUGFAIL" \
+ "${TEST_DRACUT_ARGS_ARRAY[@]}" || return 1
+}
+
+test_run() {
+ declare -a disk_args=()
+ declare -i disk_index=1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/squashfs.img root
+
+ test_marker_reset
+ "$testdir"/run-qemu "${disk_args[@]}" -net none \
+ -drive file=fat:rw:"$TESTDIR"/ESP,format=vvfat,label=EFI \
+ -global driver=cfi.pflash01,property=secure,value=on \
+ -drive if=pflash,format=raw,unit=0,file="$(ovmf_code)",readonly=on
+ test_marker_check || return 1
+}
+
+test_setup() {
+ # Create what will eventually be our root filesystem
+ "$DRACUT" --local --no-hostonly --no-early-microcode --nofscks \
+ --tmpdir "$TESTDIR" --keep --modules "test-root" --include ./test-init.sh /sbin/init \
+ "$TESTDIR"/tmp-initramfs.root "$KVERSION" || return 1
+
+ mkdir -p "$TESTDIR"/dracut.*/initramfs/proc
+ mksquashfs "$TESTDIR"/dracut.*/initramfs/ "$TESTDIR"/squashfs.img -quiet -no-progress
+
+ mkdir -p "$TESTDIR"/ESP/EFI/BOOT
+
+ if [ -f "/usr/lib/gummiboot/linuxx64.efi.stub" ]; then
+ TEST_DRACUT_ARGS+=" --uefi-stub /usr/lib/gummiboot/linuxx64.efi.stub "
+ fi
+
+ mkdir -p "$TESTDIR"/ESP/EFI/BOOT
+ test_dracut \
+ --modules 'rootfs-block test' \
+ --kernel-cmdline 'root=/dev/sdc ro rd.skipfsck rootfstype=squashfs' \
+ --drivers 'ahci sd_mod squashfs' \
+ --uefi \
+ "$TESTDIR"/ESP/EFI/BOOT/BOOTX64.efi
+}
+
+test_cleanup() {
+ return 0
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-20-NFS/Makefile b/test/TEST-20-NFS/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-20-NFS/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-20-NFS/client-init.sh b/test/TEST-20-NFS/client-init.sh
new file mode 100755
index 0000000..061a2b1
--- /dev/null
+++ b/test/TEST-20-NFS/client-init.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+: > /dev/watchdog
+. /lib/dracut-lib.sh
+. /lib/url-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+stty sane
+if getargbool 0 rd.shell; then
+ [ -c /dev/watchdog ] && printf 'V' > /dev/watchdog
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+
+echo "made it to the rootfs! Powering down."
+
+while read -r dev _ fstype opts rest || [ -n "$dev" ]; do
+ [ "$fstype" != "nfs" -a "$fstype" != "nfs4" ] && continue
+ echo "nfs-OK $dev $fstype $opts" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+ break
+done < /proc/mounts
+
+if [ "$fstype" = "nfs" -o "$fstype" = "nfs4" ]; then
+
+ serverip=${dev%:*}
+ path=${dev#*:}
+ echo serverip="${serverip}"
+ echo path="${path}"
+ echo /proc/mounts status
+ cat /proc/mounts
+
+ echo test:nfs_fetch_url nfs::"${serverip}":"${path}"/root/fetchfile
+ if nfs_fetch_url nfs::"${serverip}":"${path}"/root/fetchfile /run/nfsfetch.out; then
+ echo nfsfetch-OK
+ echo "nfsfetch-OK" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker2
+ fi
+else
+ echo nfsfetch-BYPASS fstype="${fstype}"
+ echo "nfsfetch-OK" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker2
+fi
+
+: > /dev/watchdog
+
+sync
+poweroff -f
diff --git a/test/TEST-20-NFS/client.link b/test/TEST-20-NFS/client.link
new file mode 100644
index 0000000..b992bfd
--- /dev/null
+++ b/test/TEST-20-NFS/client.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+MACAddressPolicy=keep
diff --git a/test/TEST-20-NFS/create-root.sh b/test/TEST-20-NFS/create-root.sh
new file mode 100755
index 0000000..8060b00
--- /dev/null
+++ b/test/TEST-20-NFS/create-root.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-20-NFS/dhcpd.conf b/test/TEST-20-NFS/dhcpd.conf
new file mode 100644
index 0000000..02fcc4d
--- /dev/null
+++ b/test/TEST-20-NFS/dhcpd.conf
@@ -0,0 +1,177 @@
+ddns-update-style none;
+
+use-host-decl-names true;
+
+subnet 192.168.50.0 netmask 255.255.255.0 {
+option subnet-mask 255.255.255.0;
+option routers 192.168.50.1;
+next-server 192.168.50.1;
+server-identifier 192.168.50.1;
+option domain-name-servers 192.168.50.1;
+option domain-search "example.com";
+option domain-name "other.com";
+
+# MAC numbering scheme:
+# NFSv3: last octet starts at 0x00 and works up
+
+ group {
+ # NFSv3 root=dhcp or root={/dev/,}nfs, use server-id
+ option root-path "/nfs/client";
+
+ host nfs3-1 {
+ hardware ethernet 52:54:00:12:34:00;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv3 root=dhcp or root={/dev/,}nfs, use given IP
+ option root-path "192.168.50.2:/nfs/client";
+
+ host nfs3-2 {
+ hardware ethernet 52:54:00:12:34:01;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv3 root=dhcp, use protocol from root-path
+ option root-path "nfs:192.168.50.3:/nfs/client";
+
+ host nfs3-3 {
+ hardware ethernet 52:54:00:12:34:02;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv3 root=dhcp, use protocol, options from root-path
+ option root-path "nfs:192.168.50.3:/nfs/client:wsize=4096";
+
+ host nfs3-4 {
+ hardware ethernet 52:54:00:12:34:03;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv3 root=dhcp, nfsroot=/path and nfsroot=IP:/path testing
+
+ host nfs3-5 {
+ hardware ethernet 52:54:00:12:34:04;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv3 root=dhcp, use path, comma-separated options
+ option root-path "/nfs/client,wsize=4096";
+
+ host nfs3-6 {
+ hardware ethernet 52:54:00:12:34:05;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv3 root=dhcp, use IP, path, comma-separated options
+ option root-path "192.168.50.2:/nfs/client,wsize=4096";
+
+ host nfs3-7 {
+ hardware ethernet 52:54:00:12:34:06;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv3 root=dhcp, use proto, IP, path, comma-separated options
+ option root-path "nfs:192.168.50.3:/nfs/client,wsize=4096";
+
+ host nfs3-8 {
+ hardware ethernet 52:54:00:12:34:07;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ # MAC numbering scheme:
+ # NFSv4: last octet starts at 0x80 and works up
+
+ group {
+ # NFSv4 root={/dev/,}nfs4, use server-id
+ option root-path "/client";
+
+ host nfs4-1 {
+ hardware ethernet 52:54:00:12:34:80;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv4 root={/dev/,}nfs4, use given IP
+ option root-path "192.168.50.2:/client";
+
+ host nfs4-2 {
+ hardware ethernet 52:54:00:12:34:81;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv4 root=dhcp, use profocol from root-path
+ option root-path "nfs4:192.168.50.3:/client";
+
+ host nfs4-3 {
+ hardware ethernet 52:54:00:12:34:82;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv4 root=dhcp, use profocol, options from root-path
+ option root-path "nfs4:192.168.50.3:/client:wsize=4096";
+
+ host nfs4-4 {
+ hardware ethernet 52:54:00:12:34:83;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv3 root=nfs4, nfsroot=/path and nfsroot=IP:/path testing
+ host nfs4-5 {
+ hardware ethernet 52:54:00:12:34:84;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv4 root={/dev/,}nfs4, use server-id, comma-separated opts
+ option root-path "/client,wsize=4096";
+
+ host nfs4-6 {
+ hardware ethernet 52:54:00:12:34:85;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv4 root={/dev/,}nfs4, use given IP, comma-separated opts
+ option root-path "192.168.50.2:/client,wsize=4096";
+
+ host nfs4-7 {
+ hardware ethernet 52:54:00:12:34:86;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ # NFSv4 root=dhcp, use comma-separated opts
+ option root-path "nfs4:192.168.50.3:/client,wsize=4096";
+
+ host nfs4-8 {
+ hardware ethernet 52:54:00:12:34:87;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+}
diff --git a/test/TEST-20-NFS/exports b/test/TEST-20-NFS/exports
new file mode 100644
index 0000000..95619e7
--- /dev/null
+++ b/test/TEST-20-NFS/exports
@@ -0,0 +1,5 @@
+/nfs 192.168.50.0/24(ro,fsid=0,insecure,no_subtree_check,no_root_squash)
+/nfs/client 192.168.50.0/24(ro,insecure,no_subtree_check,no_root_squash)
+/nfs/nfs3-5 192.168.50.0/24(ro,insecure,no_subtree_check,no_root_squash,nohide)
+/nfs/ip/192.168.50.101 192.168.50.0/24(ro,insecure,no_subtree_check,no_root_squash,nohide)
+/nfs/tftpboot/nfs4-5 192.168.50.0/24(ro,insecure,no_subtree_check,no_root_squash,nohide)
diff --git a/test/TEST-20-NFS/finished-false.sh b/test/TEST-20-NFS/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-20-NFS/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-20-NFS/hard-off.sh b/test/TEST-20-NFS/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-20-NFS/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-20-NFS/hosts b/test/TEST-20-NFS/hosts
new file mode 100644
index 0000000..36be908
--- /dev/null
+++ b/test/TEST-20-NFS/hosts
@@ -0,0 +1,5 @@
+127.0.0.1 localhost
+192.168.50.1 server
+192.168.50.2 server-ip
+192.168.50.3 server-proto-ip
+192.168.50.101 workstation2
diff --git a/test/TEST-20-NFS/server-init.sh b/test/TEST-20-NFS/server-init.sh
new file mode 100755
index 0000000..0c06eea
--- /dev/null
+++ b/test/TEST-20-NFS/server-init.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+exec < /dev/console > /dev/console 2>&1
+set -x
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+export TERM=linux
+export PS1='nfstest-server:\w\$ '
+: > /dev/watchdog
+stty sane
+echo "made it to the rootfs!"
+echo server > /proc/sys/kernel/hostname
+
+wait_for_if_link() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 600 ]; do
+ li=$(ip -o link show dev "$1" 2> /dev/null)
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_if_up() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 200 ]; do
+ li=$(ip -o link show up dev "$1")
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_route_ok() {
+ local cnt=0
+ while [ $cnt -lt 200 ]; do
+ li=$(ip route show)
+ [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+linkup() {
+ wait_for_if_link "$1" 2> /dev/null && ip link set "$1" up 2> /dev/null && wait_for_if_up "$1" 2> /dev/null
+}
+
+wait_for_if_link enx525400123456
+
+ip addr add 127.0.0.1/8 dev lo
+ip link set lo up
+ip addr add 192.168.50.1/24 dev enx525400123456
+ip addr add 192.168.50.2/24 dev enx525400123456
+ip addr add 192.168.50.3/24 dev enx525400123456
+linkup enx525400123456
+
+: > /dev/watchdog
+modprobe af_packet
+: > /dev/watchdog
+mkdir /nfs/nfs3-5
+mount --bind /nfs/client /nfs/nfs3-5
+: > /dev/watchdog
+mkdir -p /nfs/ip/192.168.50.101
+mount --bind /nfs/client /nfs/ip/192.168.50.101
+: > /dev/watchdog
+mkdir -p /nfs/tftpboot/nfs4-5
+mount --bind /nfs/client /nfs/tftpboot/nfs4-5
+: > /dev/watchdog
+modprobe sunrpc
+: > /dev/watchdog
+mount -t rpc_pipefs sunrpc /var/lib/nfs/rpc_pipefs
+: > /dev/watchdog
+[ -x /sbin/portmap ] && portmap
+: > /dev/watchdog
+mkdir -p /run/rpcbind
+[ -x /sbin/rpcbind ] && rpcbind
+: > /dev/watchdog
+modprobe nfsd
+: > /dev/watchdog
+mount -t nfsd nfsd /proc/fs/nfsd
+: > /dev/watchdog
+exportfs -r
+: > /dev/watchdog
+rpc.nfsd
+: > /dev/watchdog
+rpc.mountd
+: > /dev/watchdog
+command -v rpc.idmapd > /dev/null && [ -z "$(pidof rpc.idmapd)" ] && rpc.idmapd
+: > /dev/watchdog
+exportfs -r
+: > /dev/watchdog
+mkdir -p /var/lib/dhcpd
+: > /var/lib/dhcpd/dhcpd.leases
+: > /dev/watchdog
+chmod 777 /var/lib/dhcpd/dhcpd.leases
+: > /dev/watchdog
+rm -f /var/run/dhcpd.pid
+dhcpd -d -cf /etc/dhcpd.conf -lf /var/lib/dhcpd/dhcpd.leases &
+exportfs -s
+echo "Serving NFS mounts"
+while :; do
+ [ -n "$(jobs -rp)" ] && : > /dev/watchdog
+ sleep 10
+done
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-20-NFS/server.link b/test/TEST-20-NFS/server.link
new file mode 100644
index 0000000..1d21856
--- /dev/null
+++ b/test/TEST-20-NFS/server.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=mac
+MACAddressPolicy=keep
diff --git a/test/TEST-20-NFS/test.sh b/test/TEST-20-NFS/test.sh
new file mode 100755
index 0000000..b7cca75
--- /dev/null
+++ b/test/TEST-20-NFS/test.sh
@@ -0,0 +1,434 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on NFS with $USE_NETWORK"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.debug loglevel=7 rd.break=initqueue rd.shell"
+SERVER_DEBUG="rd.debug loglevel=7"
+#SERIAL="unix:/tmp/server.sock"
+
+run_server() {
+ # Start server first
+ echo "NFS TEST SETUP: Starting DHCP/NFS server"
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img root 0 1
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -net socket,listen=127.0.0.1:12320 \
+ -net nic,macaddr=52:54:00:12:34:56,model=e1000 \
+ -serial "${SERIAL:-"file:$TESTDIR/server.log"}" \
+ -device i6300esb -watchdog-action poweroff \
+ -append "panic=1 oops=panic softlockup_panic=1 root=LABEL=dracut rootfstype=ext4 rw console=ttyS0,115200n81 selinux=0 $SERVER_DEBUG" \
+ -initrd "$TESTDIR"/initramfs.server \
+ -pidfile "$TESTDIR"/server.pid -daemonize || return 1
+ chmod 644 "$TESTDIR"/server.pid || return 1
+
+ # Cleanup the terminal if we have one
+ tty -s && stty sane
+
+ if ! [[ $SERIAL ]]; then
+ while ! grep -q Serving "$TESTDIR"/server.log; do
+ echo "Waiting for the server to startup"
+ sleep 1
+ done
+ else
+ echo Sleeping 10 seconds to give the server a head start
+ sleep 10
+ fi
+}
+
+client_test() {
+ local test_name="$1"
+ local mac=$2
+ local cmdline="$3"
+ local server="$4"
+ local check_opt="$5"
+ local nfsinfo opts found expected
+
+ echo "CLIENT TEST START: $test_name"
+
+ # Need this so kvm-qemu will boot (needs non-/dev/zero local disk)
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker2.img marker2 1
+ cmdline="$cmdline rd.net.timeout.dhcp=30"
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -net nic,macaddr="$mac",model=e1000 \
+ -net socket,connect=127.0.0.1:12320 \
+ -device i6300esb -watchdog-action poweroff \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot rd.shell=0 $cmdline $DEBUGFAIL rd.retry=10 quiet ro console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ # shellcheck disable=SC2181
+ if [[ $? -ne 0 ]] || ! test_marker_check nfs-OK; then
+ echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]"
+ return 1
+ fi
+
+ # nfsinfo=( server:/path nfs{,4} options )
+ read -r -a nfsinfo < <(awk '{print $2, $3, $4; exit}' "$TESTDIR"/marker.img)
+
+ if [[ ${nfsinfo[0]%%:*} != "$server" ]]; then
+ echo "CLIENT TEST INFO: got server: ${nfsinfo[0]%%:*}"
+ echo "CLIENT TEST INFO: expected server: $server"
+ echo "CLIENT TEST END: $test_name [FAILED - WRONG SERVER]"
+ return 1
+ fi
+
+ found=0
+ expected=1
+ if [[ ${check_opt:0:1} == '-' ]]; then
+ expected=0
+ check_opt=${check_opt:1}
+ fi
+
+ opts=${nfsinfo[2]},
+ while [[ $opts ]]; do
+ if [[ ${opts%%,*} == "$check_opt" ]]; then
+ found=1
+ break
+ fi
+ opts=${opts#*,}
+ done
+
+ if [[ $found -ne $expected ]]; then
+ echo "CLIENT TEST INFO: got options: ${nfsinfo[2]%%:*}"
+ if [[ $expected -eq 0 ]]; then
+ echo "CLIENT TEST INFO: did not expect: $check_opt"
+ echo "CLIENT TEST END: $test_name [FAILED - UNEXPECTED OPTION]"
+ else
+ echo "CLIENT TEST INFO: missing: $check_opt"
+ echo "CLIENT TEST END: $test_name [FAILED - MISSING OPTION]"
+ fi
+ return 1
+ fi
+
+ if ! test_marker_check nfsfetch-OK marker2.img; then
+ echo "CLIENT TEST END: $test_name [FAILED - NFS FETCH FAILED]"
+ return 1
+ fi
+
+ echo "CLIENT TEST END: $test_name [OK]"
+ return 0
+}
+
+test_nfsv3() {
+ # MAC numbering scheme:
+ # NFSv3: last octet starts at 0x00 and works up
+ # NFSv4: last octet starts at 0x80 and works up
+
+ client_test "NFSv3 root=dhcp DHCP path only" 52:54:00:12:34:00 \
+ "root=dhcp" 192.168.50.1 -wsize=4096 || return 1
+
+ client_test "NFSv3 Legacy root=/dev/nfs nfsroot=IP:path" 52:54:00:12:34:01 \
+ "root=/dev/nfs nfsroot=192.168.50.1:/nfs/client" 192.168.50.1 -wsize=4096 || return 1
+
+ client_test "NFSv3 Legacy root=/dev/nfs DHCP path only" 52:54:00:12:34:00 \
+ "root=/dev/nfs" 192.168.50.1 -wsize=4096 || return 1
+
+ client_test "NFSv3 Legacy root=/dev/nfs DHCP IP:path" 52:54:00:12:34:01 \
+ "root=/dev/nfs" 192.168.50.2 -wsize=4096 || return 1
+
+ client_test "NFSv3 root=dhcp DHCP IP:path" 52:54:00:12:34:01 \
+ "root=dhcp" 192.168.50.2 -wsize=4096 || return 1
+
+ client_test "NFSv3 root=dhcp DHCP proto:IP:path" 52:54:00:12:34:02 \
+ "root=dhcp" 192.168.50.3 -wsize=4096 || return 1
+
+ client_test "NFSv3 root=dhcp DHCP proto:IP:path:options" 52:54:00:12:34:03 \
+ "root=dhcp" 192.168.50.3 wsize=4096 || return 1
+
+ client_test "NFSv3 root=nfs:..." 52:54:00:12:34:04 \
+ "root=nfs:192.168.50.1:/nfs/client" 192.168.50.1 -wsize=4096 || return 1
+
+ client_test "NFSv3 Bridge root=nfs:..." 52:54:00:12:34:04 \
+ "root=nfs:192.168.50.1:/nfs/client bridge net.ifnames=0" 192.168.50.1 -wsize=4096 || return 1
+
+ client_test "NFSv3 Legacy root=IP:path" 52:54:00:12:34:04 \
+ "root=192.168.50.1:/nfs/client" 192.168.50.1 -wsize=4096 || return 1
+
+ # This test must fail: nfsroot= requires root=/dev/nfs
+ client_test "NFSv3 Invalid root=dhcp nfsroot=/nfs/client" 52:54:00:12:34:04 \
+ "root=dhcp nfsroot=/nfs/client failme" 192.168.50.1 -wsize=4096 && return 1
+
+ client_test "NFSv3 root=dhcp DHCP path,options" 52:54:00:12:34:05 \
+ "root=dhcp" 192.168.50.1 wsize=4096 || return 1
+
+ client_test "NFSv3 Bridge Customized root=dhcp DHCP path,options" 52:54:00:12:34:05 \
+ "root=dhcp bridge=foobr0:enp0s1" 192.168.50.1 wsize=4096 || return 1
+
+ client_test "NFSv3 root=dhcp DHCP IP:path,options" 52:54:00:12:34:06 \
+ "root=dhcp" 192.168.50.2 wsize=4096 || return 1
+
+ client_test "NFSv3 root=dhcp DHCP proto:IP:path,options" 52:54:00:12:34:07 \
+ "root=dhcp" 192.168.50.3 wsize=4096 || return 1
+
+ client_test "NFSv3 Overlayfs root=nfs:..." 52:54:00:12:34:04 \
+ "root=nfs:192.168.50.1:/nfs/client rd.live.overlay.overlayfs=1" \
+ 192.168.50.1 -wsize=4096 || return 1
+
+ client_test "NFSv3 Live Overlayfs root=nfs:..." 52:54:00:12:34:04 \
+ "root=nfs:192.168.50.1:/nfs/client rd.live.image rd.live.overlay.overlayfs=1" \
+ 192.168.50.1 -wsize=4096 || return 1
+
+ return 0
+}
+
+test_nfsv4() {
+ # There is a mandatory 90 second recovery when starting the NFSv4
+ # server, so put these later in the list to avoid a pause when doing
+ # switch_root
+
+ client_test "NFSv4 root=dhcp DHCP proto:IP:path" 52:54:00:12:34:82 \
+ "root=dhcp" 192.168.50.3 -wsize=4096 || return 1
+
+ client_test "NFSv4 root=dhcp DHCP proto:IP:path:options" 52:54:00:12:34:83 \
+ "root=dhcp" 192.168.50.3 wsize=4096 || return 1
+
+ client_test "NFSv4 root=nfs4:..." 52:54:00:12:34:84 \
+ "root=nfs4:192.168.50.1:/client" 192.168.50.1 -wsize=4096 || return 1
+
+ client_test "NFSv4 root=dhcp DHCP proto:IP:path,options" 52:54:00:12:34:87 \
+ "root=dhcp" 192.168.50.3 wsize=4096 || return 1
+
+ return 0
+}
+
+test_run() {
+ if [[ -s server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+
+ if ! run_server; then
+ echo "Failed to start server" 1>&2
+ return 1
+ fi
+
+ test_nfsv3 \
+ && test_nfsv4
+
+ ret=$?
+
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+
+ return $ret
+}
+
+test_setup() {
+ export kernel=$KVERSION
+ export srcmods="/lib/modules/$kernel/"
+ # Detect lib paths
+
+ rm -rf -- "$TESTDIR"/overlay
+ (
+ mkdir -p "$TESTDIR"/server/overlay/source
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/server/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+
+ (
+ cd "$initdir" || exit
+ mkdir -p dev sys proc run etc var/run tmp var/lib/{dhcpd,rpcbind}
+ mkdir -p var/lib/nfs/{v4recovery,rpc_pipefs}
+ chmod 777 var/lib/rpcbind var/lib/nfs
+ )
+
+ inst_multiple sh ls shutdown poweroff stty cat ps ln ip \
+ dmesg mkdir cp ping exportfs \
+ modprobe rpc.nfsd rpc.mountd showmount tcpdump \
+ sleep mount chmod rm
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ if [ -f "${_terminfodir}"/l/linux ]; then
+ inst_multiple -o "${_terminfodir}"/l/linux
+ break
+ fi
+ done
+ type -P portmap > /dev/null && inst_multiple portmap
+ type -P rpcbind > /dev/null && inst_multiple rpcbind
+
+ [ -f /etc/netconfig ] && inst_multiple /etc/netconfig
+ type -P dhcpd > /dev/null && inst_multiple dhcpd
+ instmods nfsd sunrpc ipv6 lockd af_packet
+ inst ./server-init.sh /sbin/init
+ inst_simple /etc/os-release
+ inst ./hosts /etc/hosts
+ inst ./exports /etc/exports
+ inst ./dhcpd.conf /etc/dhcpd.conf
+ inst_multiple -o {,/usr}/etc/nsswitch.conf {,/usr}/etc/rpc \
+ {,/usr}/etc/protocols {,/usr}/etc/services
+ inst_multiple -o rpc.idmapd /etc/idmapd.conf
+
+ inst_libdir_file 'libnfsidmap_nsswitch.so*'
+ inst_libdir_file 'libnfsidmap/*.so*'
+ inst_libdir_file 'libnfsidmap*.so*'
+
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ inst /etc/passwd /etc/passwd
+ inst /etc/group /etc/group
+
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ dracut_kernel_post
+ )
+
+ # Make client root inside server root
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/server/overlay/source/nfs/client
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+
+ (
+ cd "$initdir" || exit
+ mkdir -p dev sys proc etc run root usr var/lib/nfs/rpc_pipefs
+ echo "TEST FETCH FILE" > root/fetchfile
+ )
+
+ inst_multiple sh shutdown poweroff stty cat ps ln ip dd \
+ mount dmesg mkdir cp ping grep setsid ls vi less cat sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ if [ -f "${_terminfodir}"/l/linux ]; then
+ inst_multiple -o "${_terminfodir}"/l/linux
+ break
+ fi
+ done
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/45url-lib/url-lib.sh" "/lib/url-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/40network/net-lib.sh" "/lib/net-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/95nfs/nfs-lib.sh" "/lib/nfs-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst ./client-init.sh /sbin/init
+ inst_simple /etc/os-release
+ inst_multiple -o {,/usr}/etc/nsswitch.conf
+ inst /etc/passwd /etc/passwd
+ inst /etc/group /etc/group
+
+ inst_libdir_file 'libnfsidmap_nsswitch.so*'
+ inst_libdir_file 'libnfsidmap/*.so*'
+ inst_libdir_file 'libnfsidmap*.so*'
+
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/server/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount sync dd
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/server/overlay / \
+ -m "bash rootfs-block kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/server
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img root 80
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+
+ # Make an overlay with needed tools for the test harness
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir="$TESTDIR"/overlay
+ mkdir -p "$TESTDIR"/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ inst_simple ./client.link /etc/systemd/network/01-client.link
+ )
+
+ # Make client's dracut image
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth" \
+ -a "dmsquash-live debug watchdog ${USE_NETWORK}" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+
+ (
+ # shellcheck disable=SC2031
+ export initdir="$TESTDIR"/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ rm "$initdir"/etc/systemd/network/01-client.link
+ inst_simple ./server.link /etc/systemd/network/01-server.link
+ inst_hook pre-mount 99 ./wait-if-server.sh
+ )
+ # Make server's dracut image
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash rootfs-block debug kernel-modules watchdog qemu network network-legacy" \
+ -d "af_packet piix ide-gd_mod ata_piix ext4 sd_mod e1000 i6300esb" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.server "$KVERSION" || return 1
+
+ rm -rf -- "$TESTDIR"/overlay
+}
+
+test_cleanup() {
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-20-NFS/wait-if-server.sh b/test/TEST-20-NFS/wait-if-server.sh
new file mode 100755
index 0000000..8ae21f8
--- /dev/null
+++ b/test/TEST-20-NFS/wait-if-server.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+. /lib/net-lib.sh
+wait_for_if_link enx525400123456
diff --git a/test/TEST-30-ISCSI/Makefile b/test/TEST-30-ISCSI/Makefile
new file mode 100644
index 0000000..88db701
--- /dev/null
+++ b/test/TEST-30-ISCSI/Makefile
@@ -0,0 +1,7 @@
+-include ../Makefile.testdir
+
+ibft.table: Makefile ibft.pl
+ perl ibft.pl \
+ --initiator iqn=iqn.1994-05.com.redhat:633114aacf2 \
+ --nic ip=192.168.50.101,prefix=24,gw=192.168.50.1,dns1=192.168.50.1,dhcp=192.168.50.1,mac=52:54:00:12:34:00,pci=00:03.0 \
+ --target nic=0,ip=192.168.50.1,port=3260,lun=1,name=iqn.2009-06.dracut:target0 >$@
diff --git a/test/TEST-30-ISCSI/client-init.sh b/test/TEST-30-ISCSI/client-init.sh
new file mode 100755
index 0000000..46a5e3f
--- /dev/null
+++ b/test/TEST-30-ISCSI/client-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+stty sane
+echo "made it to the rootfs! Powering down."
+while read -r dev _ fstype opts rest || [ -n "$dev" ]; do
+ [ "$fstype" != "ext4" ] && continue
+ echo "iscsi-OK $dev $fstype $opts" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+ break
+done < /proc/mounts
+
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+
+sync
+poweroff -f
diff --git a/test/TEST-30-ISCSI/client.link b/test/TEST-30-ISCSI/client.link
new file mode 100644
index 0000000..b992bfd
--- /dev/null
+++ b/test/TEST-30-ISCSI/client.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+MACAddressPolicy=keep
diff --git a/test/TEST-30-ISCSI/create-client-root.sh b/test/TEST-30-ISCSI/create-client-root.sh
new file mode 100755
index 0000000..267c93a
--- /dev/null
+++ b/test/TEST-30-ISCSI/create-client-root.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+
+mkfs.ext4 -j -L singleroot -F /dev/disk/by-id/ata-disk_singleroot
+mkdir -p /sysroot
+mount -t ext4 /dev/disk/by-id/ata-disk_singleroot /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+mdadm --create /dev/md0 --run --auto=yes --level=stripe --raid-devices=2 /dev/disk/by-id/ata-disk_raid0-1 /dev/disk/by-id/ata-disk_raid0-2
+mdadm -W /dev/md0 || :
+lvm pvcreate -ff -y /dev/md0
+lvm vgcreate dracut /dev/md0
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+mkfs.ext4 -j -L sysroot /dev/dracut/root
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-30-ISCSI/create-server-root.sh b/test/TEST-30-ISCSI/create-server-root.sh
new file mode 100755
index 0000000..2dbc2da
--- /dev/null
+++ b/test/TEST-30-ISCSI/create-server-root.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-30-ISCSI/dhcpd.conf b/test/TEST-30-ISCSI/dhcpd.conf
new file mode 100644
index 0000000..fd306ea
--- /dev/null
+++ b/test/TEST-30-ISCSI/dhcpd.conf
@@ -0,0 +1,48 @@
+ddns-update-style none;
+
+use-host-decl-names true;
+
+subnet 192.168.50.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.50.1;
+ next-server 192.168.50.1;
+ server-identifier 192.168.50.1;
+ option domain-name-servers 192.168.50.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+
+ # MAC numbering scheme:
+ # NFSv3: last octet starts at 0x00 and works up
+
+ group {
+ option root-path "iscsi:192.168.50.1:::1:iqn.2009-06.dracut:target0";
+
+ host iscsi-1 {
+ hardware ethernet 52:54:00:12:34:00;
+ fixed-address 192.168.50.101;
+ }
+ }
+}
+
+subnet 192.168.51.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.51.1;
+ next-server 192.168.51.1;
+ server-identifier 192.168.51.1;
+ option domain-name-servers 192.168.51.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+
+ # MAC numbering scheme:
+ # NFSv3: last octet starts at 0x00 and works up
+
+ group {
+ #option root-path "iscsi:192.168.51.1:::1:iqn.2009-06.dracut:target1";
+
+ host iscsi-2 {
+ hardware ethernet 52:54:00:12:34:01;
+ fixed-address 192.168.51.101;
+ }
+
+ }
+}
diff --git a/test/TEST-30-ISCSI/hosts b/test/TEST-30-ISCSI/hosts
new file mode 100644
index 0000000..f8c18b6
--- /dev/null
+++ b/test/TEST-30-ISCSI/hosts
@@ -0,0 +1,8 @@
+127.0.0.1 localhost
+192.168.50.1 server
+192.168.50.2 server-ip
+192.168.50.3 server-proto-ip
+192.168.50.100 workstation1
+192.168.50.101 workstation2
+192.168.50.102 workstation3
+192.168.50.103 workstation4
diff --git a/test/TEST-30-ISCSI/ibft.pl b/test/TEST-30-ISCSI/ibft.pl
new file mode 100644
index 0000000..c612951
--- /dev/null
+++ b/test/TEST-30-ISCSI/ibft.pl
@@ -0,0 +1,458 @@
+#!/usr/bin/perl
+# SPDX-License-Identifier: GPL-2.0+
+#
+# iBFT ACPI table generator
+# $ perldoc ibft.pl if you'd like to read the manual, poor you:
+
+=head1 NAME
+
+ibft.pl - Generate iBFT ACPI table
+
+=head1 SYNOPSIS
+
+ibft.pl
+[--oemid <oemid>]
+[--tableid <tableid>
+[--initiator isns=<ip>,slp=<ip>,radius1=<ip>,radius2=<ip>,iqn=<iqn>]
+[--nic ip=<ip>[,prefix=<prefix>][,gw=<ip>][,dns1=<ip>][,dns2=<ip>][,dhcp=<ip>][,vlan=<id>][,mac=<mac>][,pci=<pci>][,hostname=<hostname>] ...]
+[--target ip=<ip>[,port=<port>][,lun=<lun>][,name=<iqn> ...]
+
+=head1 DESCRIPTION
+
+B<ibft.pl> creates an image of iBFT ACPI table similar to what a real network
+boot firmware would do. This is mainly useful for testing.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<< --oemid <oemid> >>
+
+Create a table with a particular OEM ID, limited to 6 characters.
+It generally doesn't matter.
+
+Defaults to I<DRACUT>.
+
+=item B<< --tableid <tableid> >>
+
+Create a table with a particular OEM Table ID.
+
+Defaults to I<TEST>, but any four-letter word would do. Any.
+
+=item B<< --initiator >>
+
+Configure the Initiator Structure.
+Following parameters are supported:
+
+=over 4
+
+=item B<< isns=<ip> >>
+
+iSNS server address.
+
+=item B<< slp=<ip> >>
+
+SLP server address.
+
+=item B<< radius1=<ip> >>, B<< radius2=<ip> >>
+
+Primary and secondary Radius server addresses.
+
+=item B<< iqn=<iqn> >>
+
+Override the IQN, which defaults to I<iqn.2009-06.dracut:initiator0>.
+
+=back
+
+=item B<< --nic >>
+
+Configure a NIC Structure. This option can be used up multiple times.
+
+Following parameters are supported:
+
+=over 4
+
+=item B<< ip=<ip> >>
+
+Set the IP address. Both I<AF_INET> and I<AF_INET6> families are supported.
+This parameter is mandatory.
+
+=item B<< prefix=<prefix> >>
+
+Set the IP address prefix. You generally also want to set this in order to
+get a sensible iBFT.
+
+=item B<< gw=<ip> >>
+
+Set the gateway IP address.
+
+=item B<< dns1=<ip> >>, B<< dns2=<ip> >>
+
+Set the domain service server addresses.
+
+=item B<< dhcp=<ip> >>
+
+Specify the address of the DHCP server in case dynamic configuration is used.
+
+=item B<< vlan=<id> >>
+
+The VLAN Id. Duh.
+
+=item B<< mac=<mac> >>
+
+Specify the ethernet hardware address, in form of six colon-delimited
+hexadecimal octets.
+
+=item B<< pci=<pci> >>
+
+Specify the ethernet hardware's PCI bus location, in form of
+B<< <bus> >>:B<< <device> >>.B<< <function> >> where the numbers are in
+hexadecimal.
+
+=item B<< hostname=<hostname> >>
+
+The host name. Defaults to B<client>.
+
+=back
+
+=item B<< --target >>
+
+Configure a Target Structure. This option can be used multiple times.
+
+Following parameters are supported:
+
+=over 4
+
+=item B<< ip=<ip> >>
+
+The iSCSI target IP address.
+
+=item B<< port=<port> >>
+
+The iSCSI TCP port, in case the default of I<3260> is not good enough for
+you.
+
+=item B<< lun=<1> >>
+
+The LUN number. Defaults to I<1> no less.
+
+=item B<< name=<iqn> >>
+
+The iSCSI volume name. Defaults to I<iqn.2009-06.dracut:target0> for the first
+target, I<iqn.2009-06.dracut:target1> for the second one.
+
+=back
+
+=back
+
+=cut
+
+use strict;
+use warnings;
+
+sub ip4 {
+ shift =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
+ or die 'not an INET address';
+ return (map { 0x00 } 0..9), 0xff, 0xff, $1, $2, $3, $4;
+}
+
+sub ip6
+{
+ my ($beg, $end) = map { [ map { /^([0-9a-fA-F]{0,2}?)([0-9a-fA-F]{1,2})$/
+ ? (hex $1, hex $2)
+ : die "'$_' not valid in a INET6 address"
+ } split /:/ ] } split /::/, shift;
+
+ $beg ||= [];
+ $end ||= [];
+
+ my $fill = 16 - scalar @$beg + scalar@$end;
+ die 'INET6 address too long' if $fill < 0;
+
+ @$beg, (map { 0 } 1..$fill), @$end;
+}
+
+sub ip
+{
+ my @val;
+ @val = eval { @val = ip6 ($_[0]) };
+ @val = eval { @val = ip4 ($_[0]) } unless @val;
+ die "Saatana: $_[0] is not an INET or INET6 address" unless @val;
+
+ return pack 'C16', @val;
+}
+
+sub mac
+{
+ return pack 'C8', map { hex $_ } split /:/, shift;
+}
+
+sub pci
+{
+ shift =~ /^([0-9a-fA-F]{1,2}?):([0-9a-fA-F]{1,2})\.([0-9a-fA-F]+)$/
+ or die 'Not a PCI address';
+ return (hex $1) << 8 | (hex $2) << 3 | (hex $3);
+}
+
+sub lun
+{
+ return pack 'C8', 0, shift, 0, 0, 0, 0, 0, 0;
+}
+
+# signature, length, revision, checksum, oem_id, oem_table_id, reserved
+sub pack_table_hdr { pack 'a4 V C C a6 a8 a24 x![C8]', @_ }
+
+# id, version, length, index, flags
+# extensions, initiator_off, nic0_off, tgt0_off, nic1_off, tgt1_off, ext*
+sub pack_control { pack 'C C S C C S S S S S S S* x![C8]', @_ }
+
+# id, version, length, index, flags
+# isns_adr, slp_adr, radius1_adr, radius2_adr, iqn_len, iqn_off
+sub pack_initiator { pack 'C C S C C a16 a16 a16 a16 SS x![C8]', @_ }
+
+# id, version, length, index, flags
+# adr, prefix, origin, gw, dns1, dns2, dhcp, vlan_id, mac, pci_bdf, hostname_len, hostname_off
+sub pack_nic { $_[5] ? pack 'C C S C C a16 C C a16 a16 a16 a16 S a6 S SS x![C8]', @_ : '' }
+
+# id, version, length, index, flags
+# tgt_adr, tgt_port, tgt_lun, chap_type, nic_id, tgt_len, tgt_off,
+# chap_name_len, chap_name_off, chap_secret_len, chap_secret_off
+# rchap_name_len, rchap_name_off, rchap_secret_len, rchap_secret_off
+sub pack_tgt { $_[5] ? pack 'C C S C C a16 S a8 C C SS SS SS SS SS x![C8]', @_ : '' };
+
+# str
+sub pack_str { pack 'Z*', @_ }
+
+# Initialize some defaults
+my @table_hdr = ('iBFT', 0000, 1, 0000, 'DRACUT', 'TEST', '');
+my @control = (1, 1, 18, 0, 0, 0000, 0000, 0000, 0000, 0000, 0000);
+my @initiator = (2, 1, 74, 0, 0x03, '', '', '', '', (0000, 0000));
+my @nics;
+my @tgts;
+my $iqn = 'iqn.2009-06.dracut:initiator0';
+my @hostnames;
+my @tgt_names;
+
+while (@ARGV) {
+ my $arg = shift @ARGV;
+ die "Saatana: $arg is missing an argument" unless @ARGV;
+
+ if ($arg eq '--oemid') {
+ $table_hdr[4] = shift @ARGV;
+ } elsif ($arg eq '--tableid') {
+ $table_hdr[5] = shift @ARGV;
+ } elsif ($arg eq '--initiator') {
+ my %val = split /[,=]/, shift @ARGV;
+ $initiator[5] = ip (delete $val{isns}) if exists $val{isns};
+ $initiator[6] = ip (delete $val{slp}) if exists $val{slp};
+ $initiator[7] = ip (delete $val{radius1}) if exists $val{radius1};
+ $initiator[8] = ip (delete $val{radius2}) if exists $val{radius2};
+ $iqn = delete $val{iqn} if exists $val{iqn};
+ die "Saatana: Extra arguments to --initiator: ".join (', ', %val) if %val;
+ } elsif ($arg eq '--nic') {
+ my @nic = (3, 1, 102, 0, 0x03,
+ undef, 0, 0x01, '', '', '', '', 0, '', 0, (0000, 0000));
+ push @nics, \@nic;
+
+ my %val = split /[,=]/, shift @ARGV;
+ die 'Saatana: --nic needs an ip' unless exists $val{ip};
+ $nic[3] = $#nics;
+ $nic[5] = ip (delete $val{ip});
+ $nic[6] = delete $val{prefix} if exists $val{prefix};
+ $nic[7] = 0x03 if exists $val{dhcp};
+ $nic[8] = ip (delete $val{gw}) if exists $val{gw};
+ $nic[9] = ip (delete $val{dns1}) if exists $val{dns1};
+ $nic[10] = ip (delete $val{dns2}) if exists $val{dns2};
+ $nic[11] = ip (delete $val{dhcp}) if exists $val{dhcp};
+ $nic[12] = delete $val{vlan} if exists $val{vlan};
+ $nic[13] = mac (delete $val{mac}) if exists $val{mac};
+ $nic[14] = pci (delete $val{pci}) if exists $val{pci};
+ $hostnames[$#nics] = exists $val{hostname} ? delete $val{hostname} : 'client';
+ $hostnames[$#nics] = pack_str $hostnames[$#nics];
+ die "Saatana: Extra arguments to --nic: ".join (', ', %val) if %val;
+
+ # Allocate an control expansion entry
+ if ($#nics > 1) {
+ $control[2] += 2;
+ push @control, (0x4444);
+ }
+ } elsif ($arg eq '--target') {
+ my @tgt = (4, 1, 54, 0, 0x03,
+ undef, 3260, lun (1), 0, 0,
+ (0000, 0000),
+ (0000, 0000),
+ (0000, 0000),
+ (0000, 0000),
+ (0000, 0000));
+ push @tgts, \@tgt;
+
+ my %val = split /[,=]/, shift @ARGV;
+ die 'Saatana: --target needs an ip' unless exists $val{ip};
+ $tgt[3] = $#tgts;
+ $tgt[5] = ip (delete $val{ip}) if exists $val{ip};
+ $tgt[6] = delete $val{port} if exists $val{port};
+ $tgt[7] = lun (delete $val{lun}) if exists $val{lun};
+ $tgt[9] = delete $val{nic} if exists $val{nic};
+ $tgt_names[$#tgts] = exists $val{name} ? delete $val{name}
+ : 'iqn.2009-06.dracut:target'.$#tgts;
+ $tgt_names[$#tgts] = pack_str $tgt_names[$#tgts];
+ die "Saatana: Extra arguments to --target: ".join (', ', %val) if %val;
+
+ # Allocate an control expansion entry if necessary
+ if ($#tgts > 1) {
+ $control[2] += 2;
+ push @control, (0x1111);
+ }
+ } else {
+ die "Saatana: Unknown argument: $arg";
+ }
+}
+
+# Pass 1
+my $table_hdr = pack_table_hdr @table_hdr;
+my $control = pack_control @control;
+my $initiator = pack_initiator @initiator;
+my @packed_nics = map { pack_nic @$_ } @nics;
+my @packed_tgts = map { pack_tgt @$_ } @tgts;
+$iqn = pack_str $iqn;
+
+
+# Resolve the offsets
+my $len = 0;
+$len += length $table_hdr;
+$len += length $control;
+$control[6] = $len;
+$len += length $initiator;
+
+for my $i (0..$#packed_nics) {
+ if ($i == 0) {
+ # NIC 0
+ $control[7] = $len;
+ } elsif ($i == 1) {
+ # NIC 1
+ $control[9] = $len;
+ } else {
+ # Expansion
+ $control[11 + $i - 2] = $len;
+ }
+ $len += length $packed_nics[$i];
+}
+
+for my $i (0..$#packed_tgts) {
+ if ($i == 0) {
+ # Target 0
+ $control[8] = $len;
+ } elsif ($i == 1) {
+ # Target 1
+ $control[10] = $len;
+ } else {
+ # Expansion
+ $control[11 + scalar @packed_nics - 2 + $i - 2] = $len;
+ }
+ $len += length $packed_tgts[$i];
+}
+
+$initiator[9] = -1 + length $iqn;
+$initiator[10] = $len;
+$len += length $iqn;
+
+for my $i (0..$#hostnames) {
+ $nics[$i]->[15] = -1 + length $hostnames[$i];
+ $nics[$i]->[16] = $len;
+ $len += length $hostnames[$i];
+}
+
+for my $i (0..$#tgt_names) {
+ $tgts[$i]->[10] = -1 + length $tgt_names[$i];
+ $tgts[$i]->[11] = $len;
+ $len += length $tgt_names[$i];
+}
+
+@table_hdr[1] = $len;
+
+# Pass 2, with the offsets resolved
+$table_hdr = pack_table_hdr @table_hdr;
+$control = pack_control @control;
+$initiator = pack_initiator @initiator;
+@packed_nics = map { pack_nic @$_ } @nics;
+@packed_tgts = map { pack_tgt @$_ } @tgts;
+
+# Pass 3, calculate checksum
+my $cksum = 0xff;
+$cksum += ord $_ foreach split //, join '', $table_hdr, $control, $initiator,
+ @packed_nics, @packed_tgts, $iqn, @hostnames, @tgt_names;
+$cksum = ~$cksum & 0xff;
+$table_hdr[3] = $cksum;
+$table_hdr = pack_table_hdr @table_hdr;
+
+# Puke stuff out
+print $table_hdr;
+print $control;
+print $initiator;
+print @packed_nics;
+print @packed_tgts;
+print $iqn;
+print @hostnames;
+print @tgt_names;
+
+=head1 EXAMPLES
+
+=over
+
+=item B<< perl ibft.pl --oemid FENSYS --tableid iPXE --nic ip=192.168.50.101,prefix=24,gw=192.168.50.1,dns1=192.168.50.1,dhcp=192.168.50.1,vlan=0,mac=52:54:00:12:34:00,pci=00:02.0,hostname=iscsi-1 --target ip=192.168.50.1 >ibft.img >>
+
+Generate an iBFT image with a single NIC while pretending we're iPXE for
+no good reason.
+
+=item B<<perl ibft.pl --initiator iqn=iqn.1994-05.com.redhat:633114aacf2 --nic ip=192.168.50.101,prefix=24,gw=192.168.50.1,dns1=192.168.50.1,dhcp=192.168.50.1,mac=52:54:00:12:34:00,pci=00:03.0 --nic ip=192.168.51.101,prefix=24,gw=192.168.51.1,dns1=192.168.51.1,dhcp=192.168.51.1,mac=52:54:00:12:34:01,pci=00:04.0 --target ip=192.168.50.1,port=3260,lun=1,name=iqn.2009-06.dracut:target0 --target ip=192.168.51.1,port=3260,lun=2,name=iqn.2009-06.dracut:target1 >ibft.img >>
+
+Generate an iBFT image for two NICs while being slightly more expressive
+than necessary.
+
+=item B<qemy-system-x86_64 -acpitable file=ibft.img>
+
+Use the image with QEMU.
+
+=back
+
+=head1 BUGS
+
+No support for CHAP secrets.
+
+=head1 SEE ALSO
+
+=over 4
+
+=item L<qemu(1)>,
+
+=item L<iSCSI Boot Firmware Table (iBFT)|ftp://ftp.software.ibm.com/systems/support/bladecenter/iscsi_boot_firmware_table_v1.03.pdf>,
+
+=item L<NL_PREFIX_ORIGIN Enumeration|https://docs.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin>
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (C) 2019 Lubomir Rintel
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+=head1 AUTHOR
+
+Lubomir Rintel C<lkundrak@v3.sk>
+
+=cut
+
+# Forgive me.
+# This would have been much easier with FORTH.
diff --git a/test/TEST-30-ISCSI/ibft.table b/test/TEST-30-ISCSI/ibft.table
new file mode 100644
index 0000000..0837940
--- /dev/null
+++ b/test/TEST-30-ISCSI/ibft.table
Binary files differ
diff --git a/test/TEST-30-ISCSI/server-init.sh b/test/TEST-30-ISCSI/server-init.sh
new file mode 100755
index 0000000..a1c3b7e
--- /dev/null
+++ b/test/TEST-30-ISCSI/server-init.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+exec < /dev/console > /dev/console 2>&1
+set -x
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+export TERM=linux
+export PS1='server:\w\$ '
+stty sane
+echo "made it to the rootfs!"
+echo server > /proc/sys/kernel/hostname
+
+wait_for_if_link() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 600 ]; do
+ li=$(ip -o link show dev "$1" 2> /dev/null)
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_if_up() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 200 ]; do
+ li=$(ip -o link show up dev "$1")
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_route_ok() {
+ local cnt=0
+ while [ $cnt -lt 200 ]; do
+ li=$(ip route show)
+ [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+linkup() {
+ wait_for_if_link "$1" 2> /dev/null && ip link set "$1" up 2> /dev/null && wait_for_if_up "$1" 2> /dev/null
+}
+
+wait_for_if_link enx525400123456
+wait_for_if_link enx525400123457
+
+ip addr add 127.0.0.1/8 dev lo
+ip link set lo up
+
+ip addr add 192.168.50.1/24 dev enx525400123456
+linkup enx525400123456
+
+ip addr add 192.168.51.1/24 dev enx525400123457
+linkup enx525400123457
+
+modprobe af_packet
+
+: > /var/lib/dhcpd/dhcpd.leases
+chmod 777 /var/lib/dhcpd/dhcpd.leases
+dhcpd -d -cf /etc/dhcpd.conf -lf /var/lib/dhcpd/dhcpd.leases &
+
+tgtd
+tgtadm --lld iscsi --mode target --op new --tid 1 --targetname iqn.2009-06.dracut:target0
+tgtadm --lld iscsi --mode target --op new --tid 2 --targetname iqn.2009-06.dracut:target1
+tgtadm --lld iscsi --mode target --op new --tid 3 --targetname iqn.2009-06.dracut:target2
+tgtadm --lld iscsi --mode logicalunit --op new --tid 1 --lun 1 -b /dev/disk/by-id/ata-disk_singleroot
+tgtadm --lld iscsi --mode logicalunit --op new --tid 2 --lun 2 -b /dev/disk/by-id/ata-disk_raid0-1
+tgtadm --lld iscsi --mode logicalunit --op new --tid 3 --lun 3 -b /dev/disk/by-id/ata-disk_raid0-2
+tgtadm --lld iscsi --mode target --op bind --tid 1 -I 192.168.50.101
+tgtadm --lld iscsi --mode target --op bind --tid 2 -I 192.168.51.101
+tgtadm --lld iscsi --mode target --op bind --tid 3 -I 192.168.50.101
+
+# Wait forever for the VM to die
+echo "Serving iSCSI"
+while pidof tgtd > /dev/null; do
+ : > /dev/watchdog
+ dmesg -c
+ sleep 1
+done
+dmesg -c
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-30-ISCSI/server.link b/test/TEST-30-ISCSI/server.link
new file mode 100644
index 0000000..1d21856
--- /dev/null
+++ b/test/TEST-30-ISCSI/server.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=mac
+MACAddressPolicy=keep
diff --git a/test/TEST-30-ISCSI/test.sh b/test/TEST-30-ISCSI/test.sh
new file mode 100755
index 0000000..ac9f096
--- /dev/null
+++ b/test/TEST-30-ISCSI/test.sh
@@ -0,0 +1,240 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem over iSCSI with $USE_NETWORK"
+
+#DEBUGFAIL="rd.shell rd.break rd.debug loglevel=7 "
+#SERVER_DEBUG="rd.debug loglevel=7"
+#SERIAL="tcp:127.0.0.1:9999"
+
+run_server() {
+ # Start server first
+ echo "iSCSI TEST SETUP: Starting DHCP/iSCSI server"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img serverroot 0 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/singleroot.img singleroot
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-1.img raid0-1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-2.img raid0-2
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -serial "${SERIAL:-"file:$TESTDIR/server.log"}" \
+ -net nic,macaddr=52:54:00:12:34:56,model=e1000 \
+ -net nic,macaddr=52:54:00:12:34:57,model=e1000 \
+ -net socket,listen=127.0.0.1:12330 \
+ -append "panic=1 oops=panic softlockup_panic=1 quiet root=/dev/disk/by-id/ata-disk_serverroot rootfstype=ext4 rw console=ttyS0,115200n81 selinux=0 $SERVER_DEBUG" \
+ -initrd "$TESTDIR"/initramfs.server \
+ -pidfile "$TESTDIR"/server.pid -daemonize || return 1
+ chmod 644 "$TESTDIR"/server.pid || return 1
+
+ # Cleanup the terminal if we have one
+ tty -s && stty sane
+
+ if ! [[ $SERIAL ]]; then
+ while :; do
+ grep Serving "$TESTDIR"/server.log && break
+ echo "Waiting for the server to startup"
+ tail "$TESTDIR"/server.log
+ sleep 1
+ done
+ else
+ echo Sleeping 10 seconds to give the server a head start
+ sleep 10
+ fi
+
+}
+
+run_client() {
+ local test_name=$1
+ shift
+ echo "CLIENT TEST START: $test_name"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -net nic,macaddr=52:54:00:12:34:00,model=e1000 \
+ -net nic,macaddr=52:54:00:12:34:01,model=e1000 \
+ -net socket,connect=127.0.0.1:12330 \
+ -acpitable file=ibft.table \
+ -append "$*" \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ # shellcheck disable=SC2181
+ if [[ $? -ne 0 ]] || ! test_marker_check iscsi-OK; then
+ echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]"
+ return 1
+ fi
+
+ echo "CLIENT TEST END: $test_name [OK]"
+ return 0
+}
+
+do_test_run() {
+ initiator=$(iscsi-iname)
+
+ run_client "root=dhcp" \
+ "root=/dev/root netroot=dhcp ip=enp0s1:dhcp" \
+ "rd.iscsi.initiator=$initiator" \
+ || return 1
+
+ run_client "netroot=iscsi target0" \
+ "root=LABEL=singleroot netroot=iscsi:192.168.50.1::::iqn.2009-06.dracut:target0" \
+ "ip=192.168.50.101::192.168.50.1:255.255.255.0:iscsi-1:enp0s1:off" \
+ "rd.iscsi.initiator=$initiator" \
+ || return 1
+
+ run_client "netroot=iscsi target1 target2" \
+ "root=LABEL=sysroot" \
+ "ip=dhcp" \
+ "netroot=iscsi:192.168.51.1::::iqn.2009-06.dracut:target1" \
+ "netroot=iscsi:192.168.50.1::::iqn.2009-06.dracut:target2" \
+ "rd.iscsi.initiator=$initiator" \
+ || return 1
+
+ run_client "root=ibft" \
+ "root=LABEL=singleroot" \
+ "rd.iscsi.ibft=1" \
+ "rd.iscsi.firmware=1" \
+ || return 1
+
+ echo "All tests passed [OK]"
+ return 0
+}
+
+test_run() {
+ if ! run_server; then
+ echo "Failed to start server" 1>&2
+ return 1
+ fi
+ do_test_run
+ ret=$?
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+ return $ret
+}
+
+test_check() {
+ if ! command -v tgtd &> /dev/null || ! command -v tgtadm &> /dev/null; then
+ echo "Need tgtd and tgtadm from scsi-target-utils"
+ return 1
+ fi
+}
+
+test_setup() {
+ # Create what will eventually be the client root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root" \
+ -i ./client-init.sh /sbin/init \
+ -I "ip ping grep setsid" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ --no-hostonly --no-hostonly-cmdline --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+ mkdir -p "$TESTDIR"/overlay/source && mv "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.*
+
+ mkdir -p -- "$TESTDIR"/overlay/source/var/lib/nfs/rpc_pipefs
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "test-makeroot crypt lvm mdraid" \
+ -I "mkfs.ext4 setsid blockdev" \
+ -i ./create-client-root.sh /lib/dracut/hooks/initqueue/01-create-client-root.sh \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/singleroot.img singleroot 200
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-1.img raid0-1 100
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-2.img raid0-2 100
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+
+ # Create what will eventually be the server root filesystem onto an overlay
+ "$DRACUT" -l --keep --tmpdir "$TESTDIR" \
+ -m "test-root network network-legacy" \
+ -d "iscsi_tcp crc32c ipv6" \
+ -i ./server-init.sh /sbin/init \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh" \
+ -i "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh" \
+ -I "modprobe chmod ip ping tcpdump setsid pidof tgtd tgtadm /etc/passwd" \
+ --install-optional "/etc/netconfig dhcpd /etc/group /etc/nsswitch.conf /etc/rpc /etc/protocols /etc/services /usr/etc/nsswitch.conf /usr/etc/rpc /usr/etc/protocols /usr/etc/services" \
+ -i "./hosts" "/etc/hosts" \
+ -i "./dhcpd.conf" "/etc/dhcpd.conf" \
+ --no-hostonly --no-hostonly-cmdline --nohardlink \
+ -f "$TESTDIR"/initramfs.root "$KVERSION" || return 1
+ mkdir -p "$TESTDIR"/overlay/source && mv "$TESTDIR"/dracut.*/initramfs/* "$TESTDIR"/overlay/source && rm -rf "$TESTDIR"/dracut.*
+
+ mkdir -p "$TESTDIR"/overlay/source/var/lib/dhcpd
+
+ # second, install the files needed to make the root filesystem
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "test-makeroot" \
+ -I "mkfs.ext4" \
+ -i ./create-server-root.sh /lib/dracut/hooks/initqueue/01-create-server-root.sh \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img root 240
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+
+ # Make server's dracut image
+ "$DRACUT" -l \
+ -a "dash rootfs-block test kernel-modules network network-legacy" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod e1000 drbg" \
+ -i "./server.link" "/etc/systemd/network/01-server.link" \
+ -i ./wait-if-server.sh /lib/dracut/hooks/pre-mount/99-wait-if-server.sh \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.server "$KVERSION" || return 1
+
+ # Make client's dracut image
+ test_dracut \
+ --add "$USE_NETWORK" \
+ --include "./client.link" "/etc/systemd/network/01-client.link" \
+ --kernel-cmdline "rw rd.auto rd.retry=50" \
+ "$TESTDIR"/initramfs.testing
+}
+
+test_cleanup() {
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-30-ISCSI/wait-if-server.sh b/test/TEST-30-ISCSI/wait-if-server.sh
new file mode 100755
index 0000000..b53e41f
--- /dev/null
+++ b/test/TEST-30-ISCSI/wait-if-server.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+. /lib/net-lib.sh
+wait_for_if_link enx525400123456
+wait_for_if_link enx525400123457
diff --git a/test/TEST-35-ISCSI-MULTI/Makefile b/test/TEST-35-ISCSI-MULTI/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-35-ISCSI-MULTI/client-init.sh b/test/TEST-35-ISCSI-MULTI/client-init.sh
new file mode 100755
index 0000000..46a5e3f
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/client-init.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+stty sane
+echo "made it to the rootfs! Powering down."
+while read -r dev _ fstype opts rest || [ -n "$dev" ]; do
+ [ "$fstype" != "ext4" ] && continue
+ echo "iscsi-OK $dev $fstype $opts" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+ break
+done < /proc/mounts
+
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+
+sync
+poweroff -f
diff --git a/test/TEST-35-ISCSI-MULTI/client.link b/test/TEST-35-ISCSI-MULTI/client.link
new file mode 100644
index 0000000..b992bfd
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/client.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+MACAddressPolicy=keep
diff --git a/test/TEST-35-ISCSI-MULTI/create-client-root.sh b/test/TEST-35-ISCSI-MULTI/create-client-root.sh
new file mode 100755
index 0000000..267c93a
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/create-client-root.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+
+mkfs.ext4 -j -L singleroot -F /dev/disk/by-id/ata-disk_singleroot
+mkdir -p /sysroot
+mount -t ext4 /dev/disk/by-id/ata-disk_singleroot /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+mdadm --create /dev/md0 --run --auto=yes --level=stripe --raid-devices=2 /dev/disk/by-id/ata-disk_raid0-1 /dev/disk/by-id/ata-disk_raid0-2
+mdadm -W /dev/md0 || :
+lvm pvcreate -ff -y /dev/md0
+lvm vgcreate dracut /dev/md0
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+mkfs.ext4 -j -L sysroot /dev/dracut/root
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+lvm lvchange -a n /dev/dracut/root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-35-ISCSI-MULTI/create-server-root.sh b/test/TEST-35-ISCSI-MULTI/create-server-root.sh
new file mode 100755
index 0000000..2dbc2da
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/create-server-root.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-35-ISCSI-MULTI/dhcpd.conf b/test/TEST-35-ISCSI-MULTI/dhcpd.conf
new file mode 100644
index 0000000..fd306ea
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/dhcpd.conf
@@ -0,0 +1,48 @@
+ddns-update-style none;
+
+use-host-decl-names true;
+
+subnet 192.168.50.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.50.1;
+ next-server 192.168.50.1;
+ server-identifier 192.168.50.1;
+ option domain-name-servers 192.168.50.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+
+ # MAC numbering scheme:
+ # NFSv3: last octet starts at 0x00 and works up
+
+ group {
+ option root-path "iscsi:192.168.50.1:::1:iqn.2009-06.dracut:target0";
+
+ host iscsi-1 {
+ hardware ethernet 52:54:00:12:34:00;
+ fixed-address 192.168.50.101;
+ }
+ }
+}
+
+subnet 192.168.51.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.51.1;
+ next-server 192.168.51.1;
+ server-identifier 192.168.51.1;
+ option domain-name-servers 192.168.51.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+
+ # MAC numbering scheme:
+ # NFSv3: last octet starts at 0x00 and works up
+
+ group {
+ #option root-path "iscsi:192.168.51.1:::1:iqn.2009-06.dracut:target1";
+
+ host iscsi-2 {
+ hardware ethernet 52:54:00:12:34:01;
+ fixed-address 192.168.51.101;
+ }
+
+ }
+}
diff --git a/test/TEST-35-ISCSI-MULTI/finished-false.sh b/test/TEST-35-ISCSI-MULTI/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-35-ISCSI-MULTI/hard-off.sh b/test/TEST-35-ISCSI-MULTI/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-35-ISCSI-MULTI/hosts b/test/TEST-35-ISCSI-MULTI/hosts
new file mode 100644
index 0000000..f8c18b6
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/hosts
@@ -0,0 +1,8 @@
+127.0.0.1 localhost
+192.168.50.1 server
+192.168.50.2 server-ip
+192.168.50.3 server-proto-ip
+192.168.50.100 workstation1
+192.168.50.101 workstation2
+192.168.50.102 workstation3
+192.168.50.103 workstation4
diff --git a/test/TEST-35-ISCSI-MULTI/server-init.sh b/test/TEST-35-ISCSI-MULTI/server-init.sh
new file mode 100755
index 0000000..a1c3b7e
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/server-init.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+exec < /dev/console > /dev/console 2>&1
+set -x
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+export TERM=linux
+export PS1='server:\w\$ '
+stty sane
+echo "made it to the rootfs!"
+echo server > /proc/sys/kernel/hostname
+
+wait_for_if_link() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 600 ]; do
+ li=$(ip -o link show dev "$1" 2> /dev/null)
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_if_up() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 200 ]; do
+ li=$(ip -o link show up dev "$1")
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_route_ok() {
+ local cnt=0
+ while [ $cnt -lt 200 ]; do
+ li=$(ip route show)
+ [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+linkup() {
+ wait_for_if_link "$1" 2> /dev/null && ip link set "$1" up 2> /dev/null && wait_for_if_up "$1" 2> /dev/null
+}
+
+wait_for_if_link enx525400123456
+wait_for_if_link enx525400123457
+
+ip addr add 127.0.0.1/8 dev lo
+ip link set lo up
+
+ip addr add 192.168.50.1/24 dev enx525400123456
+linkup enx525400123456
+
+ip addr add 192.168.51.1/24 dev enx525400123457
+linkup enx525400123457
+
+modprobe af_packet
+
+: > /var/lib/dhcpd/dhcpd.leases
+chmod 777 /var/lib/dhcpd/dhcpd.leases
+dhcpd -d -cf /etc/dhcpd.conf -lf /var/lib/dhcpd/dhcpd.leases &
+
+tgtd
+tgtadm --lld iscsi --mode target --op new --tid 1 --targetname iqn.2009-06.dracut:target0
+tgtadm --lld iscsi --mode target --op new --tid 2 --targetname iqn.2009-06.dracut:target1
+tgtadm --lld iscsi --mode target --op new --tid 3 --targetname iqn.2009-06.dracut:target2
+tgtadm --lld iscsi --mode logicalunit --op new --tid 1 --lun 1 -b /dev/disk/by-id/ata-disk_singleroot
+tgtadm --lld iscsi --mode logicalunit --op new --tid 2 --lun 2 -b /dev/disk/by-id/ata-disk_raid0-1
+tgtadm --lld iscsi --mode logicalunit --op new --tid 3 --lun 3 -b /dev/disk/by-id/ata-disk_raid0-2
+tgtadm --lld iscsi --mode target --op bind --tid 1 -I 192.168.50.101
+tgtadm --lld iscsi --mode target --op bind --tid 2 -I 192.168.51.101
+tgtadm --lld iscsi --mode target --op bind --tid 3 -I 192.168.50.101
+
+# Wait forever for the VM to die
+echo "Serving iSCSI"
+while pidof tgtd > /dev/null; do
+ : > /dev/watchdog
+ dmesg -c
+ sleep 1
+done
+dmesg -c
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-35-ISCSI-MULTI/server.link b/test/TEST-35-ISCSI-MULTI/server.link
new file mode 100644
index 0000000..1d21856
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/server.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=mac
+MACAddressPolicy=keep
diff --git a/test/TEST-35-ISCSI-MULTI/test.sh b/test/TEST-35-ISCSI-MULTI/test.sh
new file mode 100755
index 0000000..3e649a8
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/test.sh
@@ -0,0 +1,345 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem over multiple iSCSI with $USE_NETWORK"
+
+#DEBUGFAIL="rd.shell rd.break rd.debug loglevel=7 "
+#SERVER_DEBUG="rd.debug loglevel=7"
+#SERIAL="tcp:127.0.0.1:9999"
+
+run_server() {
+ # Start server first
+ echo "iSCSI TEST SETUP: Starting DHCP/iSCSI server"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img serverroot 0 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/singleroot.img singleroot
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-1.img raid0-1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-2.img raid0-2
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -serial "${SERIAL:-"file:$TESTDIR/server.log"}" \
+ -net nic,macaddr=52:54:00:12:34:56,model=e1000 \
+ -net nic,macaddr=52:54:00:12:34:57,model=e1000 \
+ -net socket,listen=127.0.0.1:12331 \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=/dev/disk/by-id/ata-disk_serverroot rootfstype=ext4 rw console=ttyS0,115200n81 selinux=0 $SERVER_DEBUG" \
+ -initrd "$TESTDIR"/initramfs.server \
+ -pidfile "$TESTDIR"/server.pid -daemonize || return 1
+ chmod 644 "$TESTDIR"/server.pid || return 1
+
+ # Cleanup the terminal if we have one
+ tty -s && stty sane
+
+ if ! [[ $SERIAL ]]; then
+ while :; do
+ grep Serving "$TESTDIR"/server.log && break
+ echo "Waiting for the server to startup"
+ tail "$TESTDIR"/server.log
+ sleep 1
+ done
+ else
+ echo Sleeping 10 seconds to give the server a head start
+ sleep 10
+ fi
+}
+
+run_client() {
+ local test_name=$1
+ shift
+ echo "CLIENT TEST START: $test_name"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -net nic,macaddr=52:54:00:12:34:00,model=e1000 \
+ -net nic,macaddr=52:54:00:12:34:01,model=e1000 \
+ -net socket,connect=127.0.0.1:12331 \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot rw rd.auto rd.retry=50 console=ttyS0,115200n81 selinux=0 rd.debug=0 rd.shell=0 $DEBUGFAIL $*" \
+ -initrd "$TESTDIR"/initramfs.testing
+ if ! test_marker_check iscsi-OK; then
+ echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]"
+ return 1
+ fi
+
+ echo "CLIENT TEST END: $test_name [OK]"
+ return 0
+}
+
+do_test_run() {
+ initiator=$(iscsi-iname)
+ run_client "netroot=iscsi target1 target2" \
+ "root=LABEL=sysroot" \
+ "ip=192.168.50.101:::255.255.255.0::enp0s1:off" \
+ "ip=192.168.51.101:::255.255.255.0::enp0s2:off" \
+ "netroot=iscsi:192.168.51.1::::iqn.2009-06.dracut:target1" \
+ "netroot=iscsi:192.168.50.1::::iqn.2009-06.dracut:target2" \
+ "rd.iscsi.initiator=$initiator" \
+ || return 1
+
+ run_client "netroot=iscsi target1 target2 rd.iscsi.waitnet=0" \
+ "root=LABEL=sysroot" \
+ "ip=192.168.50.101:::255.255.255.0::enp0s1:off" \
+ "ip=192.168.51.101:::255.255.255.0::enp0s2:off" \
+ "netroot=iscsi:192.168.51.1::::iqn.2009-06.dracut:target1" \
+ "netroot=iscsi:192.168.50.1::::iqn.2009-06.dracut:target2" \
+ "rd.iscsi.firmware" \
+ "rd.iscsi.initiator=$initiator" \
+ "rd.iscsi.waitnet=0" \
+ || return 1
+
+ run_client "netroot=iscsi target1 target2 rd.iscsi.waitnet=0 rd.iscsi.testroute=0" \
+ "root=LABEL=sysroot" \
+ "ip=192.168.50.101:::255.255.255.0::enp0s1:off" \
+ "ip=192.168.51.101:::255.255.255.0::enp0s2:off" \
+ "netroot=iscsi:192.168.51.1::::iqn.2009-06.dracut:target1" \
+ "netroot=iscsi:192.168.50.1::::iqn.2009-06.dracut:target2" \
+ "rd.iscsi.firmware" \
+ "rd.iscsi.initiator=$initiator" \
+ "rd.iscsi.waitnet=0 rd.iscsi.testroute=0" \
+ || return 1
+
+ run_client "netroot=iscsi target1 target2 rd.iscsi.waitnet=0 rd.iscsi.testroute=0 default GW" \
+ "root=LABEL=sysroot" \
+ "ip=192.168.50.101::192.168.50.1:255.255.255.0::enp0s1:off" \
+ "ip=192.168.51.101::192.168.51.1:255.255.255.0::enp0s2:off" \
+ "netroot=iscsi:192.168.51.1::::iqn.2009-06.dracut:target1" \
+ "netroot=iscsi:192.168.50.1::::iqn.2009-06.dracut:target2" \
+ "rd.iscsi.firmware" \
+ "rd.iscsi.initiator=$initiator" \
+ "rd.iscsi.waitnet=0 rd.iscsi.testroute=0" \
+ || return 1
+
+ echo "All tests passed [OK]"
+ return 0
+}
+
+test_run() {
+ if ! run_server; then
+ echo "Failed to start server" 1>&2
+ return 1
+ fi
+ do_test_run
+ ret=$?
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+ return $ret
+}
+
+test_check() {
+ if ! command -v tgtd &> /dev/null || ! command -v tgtadm &> /dev/null; then
+ echo "Need tgtd and tgtadm from scsi-target-utils"
+ return 1
+ fi
+}
+
+test_setup() {
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ rm -rf -- "$TESTDIR"/overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc var/run tmp
+ mkdir -p root usr/bin usr/lib usr/lib64 usr/sbin
+ mkdir -p -- var/lib/nfs/rpc_pipefs
+ )
+ inst_multiple sh shutdown poweroff stty cat ps ln ip \
+ mount dmesg mkdir cp ping grep setsid dd sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+ inst_simple /etc/os-release
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst ./client-init.sh /sbin/init
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount setsid dd sync blockdev
+ inst_hook initqueue 01 ./create-client-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "dash crypt lvm mdraid kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/singleroot.img singleroot 200
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-1.img raid0-1 100
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/raid0-2.img raid0-2 100
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+
+ # shellcheck disable=SC2031
+ export kernel=$KVERSION
+ rm -rf -- "$TESTDIR"/overlay
+ (
+ mkdir -p "$TESTDIR"/overlay/source
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p dev sys proc etc var/run tmp var/lib/dhcpd /etc/iscsi
+ )
+ inst /etc/passwd /etc/passwd
+ inst_multiple sh ls shutdown poweroff stty cat ps ln ip \
+ dmesg mkdir cp ping modprobe tcpdump setsid \
+ sleep mount chmod pidof
+ inst_multiple tgtd tgtadm
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+ instmods iscsi_tcp crc32c ipv6 af_packet
+ [ -f /etc/netconfig ] && inst_multiple /etc/netconfig
+ type -P dhcpd > /dev/null && inst_multiple dhcpd
+ inst ./server-init.sh /sbin/init
+ inst ./hosts /etc/hosts
+ inst ./dhcpd.conf /etc/dhcpd.conf
+ inst_multiple -o {,/usr}/etc/nsswitch.conf {,/usr}/etc/rpc \
+ {,/usr}/etc/protocols {,/usr}/etc/services \
+ /etc/group /etc/os-release
+
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ dracut_kernel_post
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount sync dd
+ inst_hook initqueue 01 ./create-server-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "dash rootfs-block kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img root 60
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+
+ # Make an overlay with needed tools for the test harness
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ inst_simple ./client.link /etc/systemd/network/01-client.link
+ )
+ # Make client's dracut image
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth dmraid nfs" \
+ -a "debug ${USE_NETWORK}" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+
+ (
+ # shellcheck disable=SC2031
+ export initdir="$TESTDIR"/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ rm "$initdir"/etc/systemd/network/01-client.link
+ inst_simple ./server.link /etc/systemd/network/01-server.link
+ inst_hook pre-mount 99 ./wait-if-server.sh
+ )
+ # Make server's dracut image
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -a "dash rootfs-block debug kernel-modules network network-legacy" \
+ -d "af_packet piix ide-gd_mod ata_piix ext4 sd_mod e1000 drbg" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.server "$KVERSION" || return 1
+
+ rm -rf -- "$TESTDIR"/overlay
+}
+
+test_cleanup() {
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-35-ISCSI-MULTI/wait-if-server.sh b/test/TEST-35-ISCSI-MULTI/wait-if-server.sh
new file mode 100755
index 0000000..b53e41f
--- /dev/null
+++ b/test/TEST-35-ISCSI-MULTI/wait-if-server.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+. /lib/net-lib.sh
+wait_for_if_link enx525400123456
+wait_for_if_link enx525400123457
diff --git a/test/TEST-40-NBD/Makefile b/test/TEST-40-NBD/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-40-NBD/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-40-NBD/client-init.sh b/test/TEST-40-NBD/client-init.sh
new file mode 100755
index 0000000..c5c57a2
--- /dev/null
+++ b/test/TEST-40-NBD/client-init.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+: > /dev/watchdog
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+while read -r dev fs fstype opts rest || [ -n "$dev" ]; do
+ [ "$dev" = "rootfs" ] && continue
+ [ "$fs" != "/" ] && continue
+ echo "nbd-OK $fstype $opts" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+ echo "nbd-OK $fstype $opts"
+ break
+done < /proc/mounts
+export TERM=linux
+export PS1='nbdclient-test:\w\$ '
+stty sane
+echo "made it to the rootfs! Powering down."
+
+if getargbool 0 rd.shell; then
+ strstr "$(setsid --help)" "control" && CTTY="-c"
+ setsid $CTTY sh -i
+fi
+
+mount -n -o remount,ro /
+
+sync
+poweroff -f
diff --git a/test/TEST-40-NBD/client.link b/test/TEST-40-NBD/client.link
new file mode 100644
index 0000000..b992bfd
--- /dev/null
+++ b/test/TEST-40-NBD/client.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+MACAddressPolicy=keep
diff --git a/test/TEST-40-NBD/create-client-root.sh b/test/TEST-40-NBD/create-client-root.sh
new file mode 100755
index 0000000..a214f38
--- /dev/null
+++ b/test/TEST-40-NBD/create-client-root.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+set -e
+
+udevadm settle
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+{
+ echo "dracut-root-block-created"
+ echo "ID_FS_UUID=$ID_FS_UUID"
+} | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+poweroff -f
diff --git a/test/TEST-40-NBD/create-encrypted-root.sh b/test/TEST-40-NBD/create-encrypted-root.sh
new file mode 100755
index 0000000..0ea90e3
--- /dev/null
+++ b/test/TEST-40-NBD/create-encrypted-root.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+
+printf test > keyfile
+cryptsetup -q luksFormat /dev/disk/by-id/ata-disk_root /keyfile
+echo "The passphrase is test"
+cryptsetup luksOpen /dev/disk/by-id/ata-disk_root dracut_crypt_test < /keyfile
+lvm pvcreate -ff -y /dev/mapper/dracut_crypt_test
+lvm vgcreate dracut /dev/mapper/dracut_crypt_test
+lvm lvcreate -l 100%FREE -n root dracut
+lvm vgchange -ay
+udevadm settle
+mkfs.ext4 -L dracut -j /dev/dracut/root
+mkdir -p /sysroot
+mount -t ext4 /dev/dracut/root /sysroot
+cp -a -t /sysroot /source/*
+umount /sysroot
+sleep 1
+lvm lvchange -a n /dev/dracut/root
+udevadm settle
+cryptsetup luksClose /dev/mapper/dracut_crypt_test
+udevadm settle
+sleep 1
+eval "$(udevadm info --query=property --name=/dev/disk/by-id/ata-disk_root | while read -r line || [ -n "$line" ]; do [ "$line" != "${line#*ID_FS_UUID*}" ] && echo "$line"; done)"
+{
+ echo "dracut-root-block-created"
+ echo "ID_FS_UUID=$ID_FS_UUID"
+} | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-40-NBD/create-server-root.sh b/test/TEST-40-NBD/create-server-root.sh
new file mode 100755
index 0000000..de4d670
--- /dev/null
+++ b/test/TEST-40-NBD/create-server-root.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+set -e
+
+udevadm settle
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+{
+ echo "dracut-root-block-created"
+ echo "ID_FS_UUID=$ID_FS_UUID"
+} | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-40-NBD/cryptroot-ask.sh b/test/TEST-40-NBD/cryptroot-ask.sh
new file mode 100755
index 0000000..a6b7ac7
--- /dev/null
+++ b/test/TEST-40-NBD/cryptroot-ask.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+[ -b /dev/mapper/"$2" ] && exit 0
+printf test > /keyfile
+/sbin/cryptsetup luksOpen "$1" "$2" < /keyfile
diff --git a/test/TEST-40-NBD/dhcpd.conf b/test/TEST-40-NBD/dhcpd.conf
new file mode 100644
index 0000000..08461f9
--- /dev/null
+++ b/test/TEST-40-NBD/dhcpd.conf
@@ -0,0 +1,66 @@
+ddns-update-style none;
+
+use-host-decl-names true;
+
+subnet 192.168.50.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.50.1;
+ next-server 192.168.50.1;
+ server-identifier 192.168.50.1;
+ option domain-name-servers 192.168.50.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+
+ group {
+ host nbd-1 {
+ hardware ethernet 52:54:00:12:34:00;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ host nbd-2 {
+ option root-path "nbd:192.168.50.1:raw";
+
+ hardware ethernet 52:54:00:12:34:01;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ host nbd-3 {
+ option root-path "nbd:192.168.50.1:raw:ext2";
+
+ hardware ethernet 52:54:00:12:34:02;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ host nbd-4 {
+ option root-path "nbd:192.168.50.1:raw::errors=panic";
+
+ hardware ethernet 52:54:00:12:34:03;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ host nbd-5 {
+ option root-path "nbd:192.168.50.1:raw:ext2:errors=panic";
+
+ hardware ethernet 52:54:00:12:34:04;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ host nbd-6 {
+ # Use the encrypted image
+ option root-path "nbd:192.168.50.1:encrypted:ext2:errors=panic";
+
+ hardware ethernet 52:54:00:12:34:05;
+ fixed-address 192.168.50.101;
+ }
+ }
+}
diff --git a/test/TEST-40-NBD/finished-false.sh b/test/TEST-40-NBD/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-40-NBD/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-40-NBD/hard-off.sh b/test/TEST-40-NBD/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-40-NBD/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-40-NBD/hosts b/test/TEST-40-NBD/hosts
new file mode 100644
index 0000000..f8c18b6
--- /dev/null
+++ b/test/TEST-40-NBD/hosts
@@ -0,0 +1,8 @@
+127.0.0.1 localhost
+192.168.50.1 server
+192.168.50.2 server-ip
+192.168.50.3 server-proto-ip
+192.168.50.100 workstation1
+192.168.50.101 workstation2
+192.168.50.102 workstation3
+192.168.50.103 workstation4
diff --git a/test/TEST-40-NBD/server-init.sh b/test/TEST-40-NBD/server-init.sh
new file mode 100755
index 0000000..bc52b2e
--- /dev/null
+++ b/test/TEST-40-NBD/server-init.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+exec < /dev/console > /dev/console 2>&1
+set -x
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+export TERM=linux
+export PS1='nbdtest-server:\w\$ '
+stty sane
+echo "made it to the rootfs!"
+echo server > /proc/sys/kernel/hostname
+
+wait_for_if_link() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 600 ]; do
+ li=$(ip -o link show dev "$1" 2> /dev/null)
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_if_up() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 200 ]; do
+ li=$(ip -o link show up dev "$1")
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_route_ok() {
+ local cnt=0
+ while [ $cnt -lt 200 ]; do
+ li=$(ip route show)
+ [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+linkup() {
+ wait_for_if_link "$1" 2> /dev/null && ip link set "$1" up 2> /dev/null && wait_for_if_up "$1" 2> /dev/null
+}
+
+ip addr add 127.0.0.1/8 dev lo
+ip link set lo up
+
+wait_for_if_link enx525400123456
+ip addr add 192.168.50.1/24 dev enx525400123456
+linkup enx525400123456
+
+modprobe af_packet
+nbd-server
+: > /var/lib/dhcpd/dhcpd.leases
+chmod 777 /var/lib/dhcpd/dhcpd.leases
+dhcpd -d -cf /etc/dhcpd.conf -lf /var/lib/dhcpd/dhcpd.leases &
+echo "Serving NBD disks"
+while pidof nbd-server && pidof dhcpd; do
+ echo > /dev/watchdog
+ sleep 1
+done
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-40-NBD/server.link b/test/TEST-40-NBD/server.link
new file mode 100644
index 0000000..1d21856
--- /dev/null
+++ b/test/TEST-40-NBD/server.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=mac
+MACAddressPolicy=keep
diff --git a/test/TEST-40-NBD/test.sh b/test/TEST-40-NBD/test.sh
new file mode 100755
index 0000000..7fd1efc
--- /dev/null
+++ b/test/TEST-40-NBD/test.sh
@@ -0,0 +1,507 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on NBD with $USE_NETWORK"
+
+# Uncomment this to debug failures
+# DEBUGFAIL="rd.debug systemd.log_target=console loglevel=7"
+#DEBUGFAIL="rd.shell rd.break rd.debug systemd.log_target=console loglevel=7 systemd.log_level=debug"
+#SERIAL="tcp:127.0.0.1:9999"
+
+test_check() {
+ if ! type -p nbd-server &> /dev/null; then
+ echo "Test needs nbd-server... Skipping"
+ return 1
+ fi
+
+ if ! modinfo -k "$KVERSION" nbd &> /dev/null; then
+ echo "Kernel module nbd does not exist"
+ return 1
+ fi
+
+ return 0
+}
+
+run_server() {
+ # Start server first
+ echo "NBD TEST SETUP: Starting DHCP/NBD server"
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/unencrypted.img unencrypted
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/encrypted.img encrypted
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img serverroot
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -serial "${SERIAL:-"file:$TESTDIR/server.log"}" \
+ -net nic,macaddr=52:54:00:12:34:56,model=e1000 \
+ -net socket,listen=127.0.0.1:12340 \
+ -append "panic=1 oops=panic softlockup_panic=1 rd.luks=0 systemd.crash_reboot quiet root=/dev/disk/by-id/ata-disk_serverroot rootfstype=ext4 rw console=ttyS0,115200n81 selinux=0 $SERVER_DEBUG" \
+ -initrd "$TESTDIR"/initramfs.server \
+ -pidfile "$TESTDIR"/server.pid -daemonize || return 1
+ chmod 644 "$TESTDIR"/server.pid || return 1
+
+ # Cleanup the terminal if we have one
+ tty -s && stty sane
+
+ if ! [[ $SERIAL ]]; then
+ echo "Waiting for the server to startup"
+ while :; do
+ grep Serving "$TESTDIR"/server.log && break
+ tail "$TESTDIR"/server.log
+ sleep 1
+ done
+ else
+ echo Sleeping 10 seconds to give the server a head start
+ sleep 10
+ fi
+}
+
+client_test() {
+ local test_name="$1"
+ local mac=$2
+ local cmdline="$3"
+ local fstype=$4
+ local fsopt=$5
+ local found opts nbdinfo
+
+ [[ $fstype ]] || fstype=ext4
+ [[ $fsopt ]] || fsopt="ro"
+
+ echo "CLIENT TEST START: $test_name"
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -net nic,macaddr="$mac",model=e1000 \
+ -net socket,connect=127.0.0.1:12340 \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot rd.shell=0 $cmdline $DEBUGFAIL rd.auto rd.info rd.retry=10 ro console=ttyS0,115200n81 selinux=0 " \
+ -initrd "$TESTDIR"/initramfs.testing
+
+ # shellcheck disable=SC2181
+ if [[ $? -ne 0 ]] || ! test_marker_check nbd-OK "$TESTDIR"/marker.img; then
+ echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]"
+ return 1
+ fi
+
+ # nbdinfo=( fstype fsoptions )
+ read -r -a nbdinfo < <(awk '{print $2, $3; exit}' "$TESTDIR"/marker.img)
+
+ if [[ ${nbdinfo[0]} != "$fstype" ]]; then
+ echo "CLIENT TEST END: $test_name [FAILED - WRONG FS TYPE] \"${nbdinfo[0]}\" != \"$fstype\""
+ return 1
+ fi
+
+ opts=${nbdinfo[1]},
+ while [[ $opts ]]; do
+ if [[ ${opts%%,*} == "$fsopt" ]]; then
+ found=1
+ break
+ fi
+ opts=${opts#*,}
+ done
+
+ if [[ ! $found ]]; then
+ echo "CLIENT TEST END: $test_name [FAILED - BAD FS OPTS] \"${nbdinfo[1]}\" != \"$fsopt\""
+ return 1
+ fi
+
+ echo "CLIENT TEST END: $test_name [OK]"
+}
+
+test_run() {
+ if ! run_server; then
+ echo "Failed to start server" 1>&2
+ return 1
+ fi
+ client_run
+ kill_server
+}
+
+client_run() {
+ # The default is ext4,errors=continue so use that to determine
+ # if our options were parsed and used
+ client_test "NBD root=nbd:IP:port" 52:54:00:12:34:00 \
+ "root=nbd:192.168.50.1:raw rd.luks=0" || return 1
+
+ client_test "NBD root=nbd:IP:port::fsopts" 52:54:00:12:34:00 \
+ "root=nbd:192.168.50.1:raw::errors=panic rd.luks=0" \
+ ext4 errors=panic || return 1
+
+ client_test "NBD root=nbd:IP:port:fstype" 52:54:00:12:34:00 \
+ "root=nbd:192.168.50.1:raw:ext4 rd.luks=0" ext4 || return 1
+
+ client_test "NBD root=nbd:IP:port:fstype:fsopts" 52:54:00:12:34:00 \
+ "root=nbd:192.168.50.1:raw:ext4:errors=panic rd.luks=0" \
+ ext4 errors=panic || return 1
+
+ # DHCP root-path parsing
+
+ client_test "NBD root=/dev/root netroot=dhcp DHCP root-path nbd:srv:port" 52:54:00:12:34:01 \
+ "root=/dev/root netroot=dhcp ip=dhcp rd.luks=0" || return 1
+
+ client_test "NBD root=/dev/root netroot=dhcp DHCP root-path nbd:srv:port:fstype" \
+ 52:54:00:12:34:02 "root=/dev/root netroot=dhcp ip=dhcp rd.luks=0" ext2 || return 1
+
+ client_test "NBD root=/dev/root netroot=dhcp DHCP root-path nbd:srv:port::fsopts" \
+ 52:54:00:12:34:03 "root=/dev/root netroot=dhcp ip=dhcp rd.luks=0" ext4 errors=panic || return 1
+
+ client_test "NBD root=/dev/root netroot=dhcp DHCP root-path nbd:srv:port:fstype:fsopts" \
+ 52:54:00:12:34:04 "root=/dev/root netroot=dhcp ip=dhcp rd.luks=0" ext2 errors=panic || return 1
+
+ # netroot handling
+
+ client_test "NBD netroot=nbd:IP:port" 52:54:00:12:34:00 \
+ "root=LABEL=dracut netroot=nbd:192.168.50.1:raw ip=dhcp rd.luks=0" || return 1
+
+ client_test "NBD root=/dev/root netroot=dhcp DHCP root-path nbd:srv:port:fstype:fsopts" \
+ 52:54:00:12:34:04 "root=/dev/root netroot=dhcp ip=dhcp rd.luks=0" ext2 errors=panic || return 1
+
+ # Encrypted root handling via LVM/LUKS over NBD
+
+ # shellcheck disable=SC1090
+ . "$TESTDIR"/luks.uuid
+
+ client_test "NBD root=LABEL=dracut netroot=nbd:IP:port" \
+ 52:54:00:12:34:00 \
+ "root=LABEL=dracut rd.luks.uuid=$ID_FS_UUID rd.lv.vg=dracut ip=dhcp netroot=nbd:192.168.50.1:encrypted" || return 1
+
+ # XXX This should be ext4,errors=panic but that doesn't currently
+ # XXX work when you have a real root= line in addition to netroot=
+ # XXX How we should work here needs clarification
+ # client_test "NBD root=LABEL=dracut netroot=dhcp (w/ fstype and opts)" \
+ # 52:54:00:12:34:05 \
+ # "root=LABEL=dracut rd.luks.uuid=$ID_FS_UUID rd.lv.vg=dracut netroot=dhcp" || return 1
+
+ if [[ -s server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+
+}
+
+make_encrypted_root() {
+ rm -fr "$TESTDIR"/overlay
+ kernel=$KVERSION
+ # Create what will eventually be our root filesystem onto an overlay
+ (
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ mkdir -p "$initdir"
+ (
+ cd "$initdir" || exit
+ mkdir -p dev sys proc etc run var/run tmp
+ )
+
+ inst_multiple sh df free ls shutdown poweroff stty cat ps ln ip \
+ mount dmesg mkdir cp ping dd sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst ./client-init.sh /sbin/init
+ inst_simple /etc/os-release
+ find_binary plymouth > /dev/null && inst_multiple plymouth
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ (
+ cd "$initdir" || exit
+ mkdir -p dev sys proc etc tmp var run root
+ ln -s ../run var/run
+ )
+ inst_multiple mkfs.ext4 poweroff cp umount dd sync
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ inst_hook initqueue 01 ./create-encrypted-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "dash crypt lvm mdraid kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/encrypted.img root 120
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/fakeroot rw quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ grep -F -a -m 1 ID_FS_UUID "$TESTDIR"/marker.img > "$TESTDIR"/luks.uuid
+}
+
+make_client_root() {
+ rm -fr "$TESTDIR"/overlay
+ kernel=$KVERSION
+ (
+ mkdir -p "$TESTDIR"/overlay/source
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ mkdir -p "$initdir"
+ (
+ cd "$initdir" || exit
+ mkdir -p dev sys proc etc run var/run tmp
+ )
+ inst_multiple sh ls shutdown poweroff stty cat ps ln ip \
+ dmesg mkdir cp ping dd mount sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst ./client-init.sh /sbin/init
+ inst_simple /etc/os-release
+ inst_multiple -o {,/usr}/etc/nsswitch.conf
+ inst /etc/passwd /etc/passwd
+ inst /etc/group /etc/group
+ for i in /usr/lib*/libnss_files* /lib*/libnss_files*; do
+ [ -e "$i" ] || continue
+ inst "$i"
+ done
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount sync dd
+ inst_hook initqueue 01 ./create-client-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "dash rootfs-block kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/unencrypted.img root 120
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -fr "$TESTDIR"/overlay
+}
+
+make_server_root() {
+ rm -fr "$TESTDIR"/overlay
+ # shellcheck disable=SC2031
+ export kernel=$KVERSION
+ (
+ mkdir -p "$TESTDIR"/overlay/source
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ mkdir -p "$initdir"
+ (
+ cd "$initdir" || exit
+ mkdir -p run dev sys proc etc var var/lib/dhcpd tmp etc/nbd-server
+ ln -s ../run var/run
+ )
+ cat > "$initdir/etc/nbd-server/config" << EOF
+[generic]
+[raw]
+exportname = /dev/disk/by-id/ata-disk_unencrypted
+port = 2000
+bs = 4096
+[encrypted]
+exportname = /dev/disk/by-id/ata-disk_encrypted
+port = 2001
+bs = 4096
+EOF
+ inst_multiple sh ls shutdown poweroff stty cat ps ln ip \
+ dmesg mkdir cp ping grep \
+ sleep nbd-server chmod modprobe vi pidof
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f ${_terminfodir}/l/linux ] && break
+ done
+ inst_multiple -o ${_terminfodir}/l/linux
+ instmods nfsd sunrpc ipv6 lockd af_packet 8021q ipvlan macvlan
+ type -P dhcpd > /dev/null && inst_multiple dhcpd
+ inst ./server-init.sh /sbin/init
+ inst_simple /etc/os-release
+ inst ./hosts /etc/hosts
+ inst ./dhcpd.conf /etc/dhcpd.conf
+ inst_multiple -o {,/usr}/etc/nsswitch.conf
+ inst /etc/passwd /etc/passwd
+ inst /etc/group /etc/group
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ dracut_kernel_post
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount sync dd sync
+ inst_hook initqueue 01 ./create-server-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "dash rootfs-block kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img root 120
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -fr "$TESTDIR"/overlay
+}
+
+test_setup() {
+ make_encrypted_root || return 1
+ make_client_root || return 1
+ make_server_root || return 1
+
+ rm -fr "$TESTDIR"/overlay
+ # Make the test image
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown dd
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst ./cryptroot-ask.sh /sbin/cryptroot-ask
+
+ # inst ./debug-shell.service /lib/systemd/system/debug-shell.service
+ # mkdir -p "${initdir}/lib/systemd/system/sysinit.target.wants"
+ # ln -fs ../debug-shell.service "${initdir}/lib/systemd/system/sysinit.target.wants/debug-shell.service"
+
+ # shellcheck disable=SC1090
+ . "$TESTDIR"/luks.uuid
+ mkdir -p "$initdir"/etc
+ echo "luks-$ID_FS_UUID /dev/nbd0 /etc/key" > "$initdir"/etc/crypttab
+ echo -n test > "$initdir"/etc/key
+ inst_simple ./client.link /etc/systemd/network/01-client.link
+ )
+
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "plymouth iscsi nfs" \
+ -a "debug watchdog ${USE_NETWORK}" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+
+ (
+ # shellcheck disable=SC2031
+ export initdir="$TESTDIR"/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ rm "$initdir"/etc/systemd/network/01-client.link
+ inst_simple ./server.link /etc/systemd/network/01-server.link
+ inst_hook pre-mount 99 ./wait-if-server.sh
+ )
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -a "rootfs-block debug kernel-modules network network-legacy" \
+ -d "af_packet piix ide-gd_mod ata_piix ext4 sd_mod e1000 drbg" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.server "$KVERSION" || return 1
+
+ rm -rf -- "$TESTDIR"/overlay
+}
+
+kill_server() {
+ if [[ -s $TESTDIR/server.pid ]]; then
+ kill -TERM "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+}
+
+test_cleanup() {
+ kill_server
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-40-NBD/wait-if-server.sh b/test/TEST-40-NBD/wait-if-server.sh
new file mode 100755
index 0000000..8ae21f8
--- /dev/null
+++ b/test/TEST-40-NBD/wait-if-server.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+. /lib/net-lib.sh
+wait_for_if_link enx525400123456
diff --git a/test/TEST-50-MULTINIC/Makefile b/test/TEST-50-MULTINIC/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-50-MULTINIC/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-50-MULTINIC/client-init.sh b/test/TEST-50-MULTINIC/client-init.sh
new file mode 100755
index 0000000..4c51e27
--- /dev/null
+++ b/test/TEST-50-MULTINIC/client-init.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+. /lib/dracut-lib.sh
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+command -v plymouth > /dev/null 2>&1 && plymouth --quit
+exec > /dev/console 2>&1
+
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+stty sane
+echo "made it to the rootfs! Powering down."
+
+set -x
+
+for i in /sys/class/net/*; do
+ # booting with network-manager module
+ state=/run/NetworkManager/devices/$(cat "$i"/ifindex)
+ grep -q connection-uuid= "$state" 2> /dev/null || continue
+ i=${i##*/}
+ [ "$i" = lo ] && continue
+ ip link show "$i" | grep -q master && continue
+ IFACES="${IFACES}${i} "
+done
+
+for i in /run/initramfs/net.*.did-setup; do
+ # booting with network-legacy module
+ [ -f "$i" ] || continue
+ strglobin "$i" ":*:*:*:*:" && continue
+ i=${i%.did-setup}
+ IFACES="${IFACES}${i##*/net.} "
+done
+{
+ echo "OK"
+ echo "$IFACES"
+} | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+
+getargbool 0 rd.shell && sh -i
+
+sync
+poweroff -f
diff --git a/test/TEST-50-MULTINIC/client.link b/test/TEST-50-MULTINIC/client.link
new file mode 100644
index 0000000..b992bfd
--- /dev/null
+++ b/test/TEST-50-MULTINIC/client.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+MACAddressPolicy=keep
diff --git a/test/TEST-50-MULTINIC/create-root.sh b/test/TEST-50-MULTINIC/create-root.sh
new file mode 100755
index 0000000..8060b00
--- /dev/null
+++ b/test/TEST-50-MULTINIC/create-root.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+
+set -ex
+
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-50-MULTINIC/dhcpd.conf b/test/TEST-50-MULTINIC/dhcpd.conf
new file mode 100644
index 0000000..be8dee7
--- /dev/null
+++ b/test/TEST-50-MULTINIC/dhcpd.conf
@@ -0,0 +1,36 @@
+ddns-update-style none;
+
+use-host-decl-names true;
+
+subnet 192.168.50.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.50.1;
+ next-server 192.168.50.1;
+ server-identifier 192.168.50.1;
+ option domain-name-servers 192.168.50.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+
+ group {
+ host client-if1 {
+ hardware ethernet 52:54:00:12:34:00;
+ fixed-address 192.168.50.100;
+ }
+ }
+
+ group {
+ host client-if2 {
+ hardware ethernet 52:54:00:12:34:01;
+ fixed-address 192.168.50.101;
+ }
+ }
+
+ group {
+ option root-path "nfs:192.168.50.1:/nfs/client";
+
+ host client-if3 {
+ hardware ethernet 52:54:00:12:34:02;
+ fixed-address 192.168.50.102;
+ }
+ }
+}
diff --git a/test/TEST-50-MULTINIC/exports b/test/TEST-50-MULTINIC/exports
new file mode 100644
index 0000000..ff5f29b
--- /dev/null
+++ b/test/TEST-50-MULTINIC/exports
@@ -0,0 +1 @@
+/nfs/client 192.168.50.0/24(rw,insecure,no_subtree_check,no_root_squash)
diff --git a/test/TEST-50-MULTINIC/finished-false.sh b/test/TEST-50-MULTINIC/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-50-MULTINIC/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-50-MULTINIC/hard-off.sh b/test/TEST-50-MULTINIC/hard-off.sh
new file mode 100755
index 0000000..01acb19
--- /dev/null
+++ b/test/TEST-50-MULTINIC/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getargbool 0 rd.shell || poweroff -f
+getargbool 0 failme && poweroff -f
diff --git a/test/TEST-50-MULTINIC/hosts b/test/TEST-50-MULTINIC/hosts
new file mode 100644
index 0000000..d02a4e9
--- /dev/null
+++ b/test/TEST-50-MULTINIC/hosts
@@ -0,0 +1,5 @@
+127.0.0.1 localhost
+192.168.50.1 server
+192.168.50.100 client-if1
+192.168.50.101 client-if2
+192.168.50.102 client-if3
diff --git a/test/TEST-50-MULTINIC/server-init.sh b/test/TEST-50-MULTINIC/server-init.sh
new file mode 100755
index 0000000..997b1d1
--- /dev/null
+++ b/test/TEST-50-MULTINIC/server-init.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+exec < /dev/console > /dev/console 2>&1
+set -x
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+export TERM=linux
+export PS1='nfstest-server:\w\$ '
+: > /dev/watchdog
+stty sane
+echo "made it to the rootfs!"
+echo server > /proc/sys/kernel/hostname
+
+wait_for_if_link() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 600 ]; do
+ li=$(ip -o link show dev "$1" 2> /dev/null)
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_if_up() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 200 ]; do
+ li=$(ip -o link show up dev "$1")
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_route_ok() {
+ local cnt=0
+ while [ $cnt -lt 200 ]; do
+ li=$(ip route show)
+ [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+linkup() {
+ wait_for_if_link "$1" 2> /dev/null && ip link set "$1" up 2> /dev/null && wait_for_if_up "$1" 2> /dev/null
+}
+
+wait_for_if_link enx525401123456
+
+ip addr add 127.0.0.1/8 dev lo
+ip link set lo up
+ip addr add 192.168.50.1/24 dev enx525401123456
+linkup enx525401123456
+
+: > /dev/watchdog
+modprobe af_packet
+: > /dev/watchdog
+modprobe sunrpc
+: > /dev/watchdog
+mount -t rpc_pipefs sunrpc /var/lib/nfs/rpc_pipefs
+: > /dev/watchdog
+[ -x /sbin/portmap ] && portmap
+: > /dev/watchdog
+mkdir -p /run/rpcbind
+[ -x /sbin/rpcbind ] && rpcbind
+: > /dev/watchdog
+modprobe nfsd
+: > /dev/watchdog
+mount -t nfsd nfsd /proc/fs/nfsd
+: > /dev/watchdog
+exportfs -r
+: > /dev/watchdog
+rpc.nfsd
+: > /dev/watchdog
+rpc.mountd
+: > /dev/watchdog
+command -v rpc.idmapd > /dev/null && [ -z "$(pidof rpc.idmapd)" ] && rpc.idmapd
+: > /dev/watchdog
+exportfs -r
+: > /dev/watchdog
+mkdir -p /var/lib/dhcpd
+: > /var/lib/dhcpd/dhcpd.leases
+: > /dev/watchdog
+chmod 777 /var/lib/dhcpd/dhcpd.leases
+: > /dev/watchdog
+rm -f /var/run/dhcpd.pid
+dhcpd -d -cf /etc/dhcpd.conf -lf /var/lib/dhcpd/dhcpd.leases &
+exportfs -s
+echo "Serving NFS mounts"
+while :; do
+ [ -n "$(jobs -rp)" ] && : > /dev/watchdog
+ sleep 10
+done
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-50-MULTINIC/server.link b/test/TEST-50-MULTINIC/server.link
new file mode 100644
index 0000000..1d21856
--- /dev/null
+++ b/test/TEST-50-MULTINIC/server.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=mac
+MACAddressPolicy=keep
diff --git a/test/TEST-50-MULTINIC/test.sh b/test/TEST-50-MULTINIC/test.sh
new file mode 100755
index 0000000..4f81235
--- /dev/null
+++ b/test/TEST-50-MULTINIC/test.sh
@@ -0,0 +1,376 @@
+#!/bin/bash
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on NFS with multiple nics with $USE_NETWORK"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="loglevel=7 rd.shell rd.break"
+#SERIAL="tcp:127.0.0.1:9999"
+
+run_server() {
+ # Start server first
+ echo "MULTINIC TEST SETUP: Starting DHCP/NFS server"
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img root
+
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -net socket,listen=127.0.0.1:12350 \
+ -net nic,macaddr=52:54:01:12:34:56,model=e1000 \
+ -serial "${SERIAL:-"file:$TESTDIR/server.log"}" \
+ -device i6300esb -watchdog-action poweroff \
+ -append "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot root=LABEL=dracut rootfstype=ext4 rw console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.server \
+ -pidfile "$TESTDIR"/server.pid -daemonize || return 1
+
+ chmod 644 -- "$TESTDIR"/server.pid || return 1
+
+ # Cleanup the terminal if we have one
+ tty -s && stty sane
+
+ if ! [[ $SERIAL ]]; then
+ while :; do
+ grep Serving "$TESTDIR"/server.log && break
+ echo "Waiting for the server to startup"
+ tail "$TESTDIR"/server.log
+ sleep 1
+ done
+ else
+ echo Sleeping 10 seconds to give the server a head start
+ sleep 10
+ fi
+}
+
+client_test() {
+ local test_name="$1"
+ local mac1="$2"
+ local mac2="$3"
+ local mac3="$4"
+ local cmdline="$5"
+ local check="$6"
+
+ echo "CLIENT TEST START: $test_name"
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker
+ cmdline="$cmdline rd.net.timeout.dhcp=30"
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ test_marker_reset
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -net socket,connect=127.0.0.1:12350 \
+ -net nic,macaddr=52:54:00:12:34:"$mac1",model=e1000 \
+ -net nic,macaddr=52:54:00:12:34:"$mac2",model=e1000 \
+ -net nic,macaddr=52:54:00:12:34:"$mac3",model=e1000 \
+ -netdev hubport,id=n1,hubid=1 \
+ -netdev hubport,id=n2,hubid=2 \
+ -device e1000,netdev=n1,mac=52:54:00:12:34:98 \
+ -device e1000,netdev=n2,mac=52:54:00:12:34:99 \
+ -device i6300esb -watchdog-action poweroff \
+ -append "quiet panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot rd.shell=0 $cmdline $DEBUGFAIL rd.retry=5 ro console=ttyS0,115200n81 selinux=0 init=/sbin/init rd.debug systemd.log_target=console" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ {
+ read -r OK
+ read -r IFACES
+ } < "$TESTDIR"/marker.img
+
+ if [[ $OK != "OK" ]]; then
+ echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]"
+ return 1
+ fi
+
+ for i in $check; do
+ if [[ " $IFACES " != *\ $i\ * ]]; then
+ echo "$i not in '$IFACES'"
+ echo "CLIENT TEST END: $test_name [FAILED - BAD IF]"
+ return 1
+ fi
+ done
+
+ for i in $IFACES; do
+ if [[ " $check " != *\ $i\ * ]]; then
+ echo "$i in '$IFACES', but should not be"
+ echo "CLIENT TEST END: $test_name [FAILED - BAD IF]"
+ return 1
+ fi
+ done
+
+ echo "CLIENT TEST END: $test_name [OK]"
+ return 0
+}
+
+test_run() {
+ if ! run_server; then
+ echo "Failed to start server" 1>&2
+ return 1
+ fi
+ test_client
+ ret=$?
+ kill_server
+ return $ret
+}
+
+test_client() {
+ # Mac Numbering Scheme
+ # ...:00-02 receive IP addresses all others don't
+ # ...:02 receives a dhcp root-path
+
+ # PXE Style BOOTIF=
+ client_test "MULTINIC root=nfs BOOTIF=" \
+ 00 01 02 \
+ "root=nfs:192.168.50.1:/nfs/client BOOTIF=52-54-00-12-34-00" \
+ "enp0s1" || return 1
+
+ client_test "MULTINIC root=nfs BOOTIF= ip=enp0s3:dhcp" \
+ 00 01 02 \
+ "root=nfs:192.168.50.1:/nfs/client BOOTIF=52-54-00-12-34-00 ip=enp0s2:dhcp" \
+ "enp0s1 enp0s2" || return 1
+
+ # PXE Style BOOTIF= with dhcp root-path
+ client_test "MULTINIC root=dhcp BOOTIF=" \
+ 00 01 02 \
+ "root=dhcp BOOTIF=52-54-00-12-34-02" \
+ "enp0s3" || return 1
+
+ # Multinic case, where only one nic works
+ client_test "MULTINIC root=nfs ip=dhcp" \
+ FF 00 FE \
+ "root=nfs:192.168.50.1:/nfs/client ip=dhcp" \
+ "enp0s2" || return 1
+
+ # Require two interfaces
+ client_test "MULTINIC root=nfs ip=enp0s2:dhcp ip=enp0s3:dhcp bootdev=enp0s2" \
+ 00 01 02 \
+ "root=nfs:192.168.50.1:/nfs/client ip=enp0s2:dhcp ip=enp0s3:dhcp bootdev=enp0s2" \
+ "enp0s2 enp0s3" || return 1
+
+ # Require three interfaces with dhcp root-path
+ client_test "MULTINIC root=dhcp ip=enp0s1:dhcp ip=enp0s2:dhcp ip=enp0s3:dhcp bootdev=enp0s3" \
+ 00 01 02 \
+ "root=dhcp ip=enp0s1:dhcp ip=enp0s2:dhcp ip=enp0s3:dhcp bootdev=enp0s3" \
+ "enp0s1 enp0s2 enp0s3" || return 1
+
+ client_test "MULTINIC bonding" \
+ 00 01 02 \
+ "root=nfs:192.168.50.1:/nfs/client ip=bond0:dhcp bond=bond0:enp0s1,enp0s2,enp0s3:mode=balance-rr" \
+ "bond0" || return 1
+
+ # bridge, where only one interface is actually connected
+ client_test "MULTINIC bridging" \
+ 00 01 02 \
+ "root=nfs:192.168.50.1:/nfs/client ip=bridge0:dhcp::52:54:00:12:34:00 bridge=bridge0:enp0s1,enp0s5,enp0s6" \
+ "bridge0" || return 1
+ return 0
+}
+
+test_setup() {
+ export kernel=$KVERSION
+ export srcmods="/lib/modules/$kernel/"
+ rm -rf -- "$TESTDIR"/overlay
+ (
+ mkdir -p "$TESTDIR"/overlay/source
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+
+ (
+ cd "$initdir" || exit
+ mkdir -p dev sys proc run etc var/run tmp var/lib/{dhcpd,rpcbind}
+ mkdir -p var/lib/nfs/{v4recovery,rpc_pipefs}
+ chmod 777 var/lib/rpcbind var/lib/nfs
+ )
+
+ inst_multiple sh ls shutdown poweroff stty cat ps ln ip \
+ dmesg mkdir cp ping exportfs \
+ modprobe rpc.nfsd rpc.mountd showmount tcpdump \
+ sleep mount chmod rm
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ if [ -f "${_terminfodir}"/l/linux ]; then
+ inst_multiple -o "${_terminfodir}"/l/linux
+ break
+ fi
+ done
+ type -P portmap > /dev/null && inst_multiple portmap
+ type -P rpcbind > /dev/null && inst_multiple rpcbind
+ [ -f /etc/netconfig ] && inst_multiple /etc/netconfig
+ type -P dhcpd > /dev/null && inst_multiple dhcpd
+ instmods nfsd sunrpc ipv6 lockd af_packet
+ inst ./server-init.sh /sbin/init
+ inst_simple /etc/os-release
+ inst ./hosts /etc/hosts
+ inst ./exports /etc/exports
+ inst ./dhcpd.conf /etc/dhcpd.conf
+ inst_multiple -o {,/usr}/etc/nsswitch.conf {,/usr}/etc/rpc \
+ {,/usr}/etc/protocols {,/usr}/etc/services
+ inst_multiple -o rpc.idmapd /etc/idmapd.conf
+
+ inst_libdir_file 'libnfsidmap_nsswitch.so*'
+ inst_libdir_file 'libnfsidmap/*.so*'
+ inst_libdir_file 'libnfsidmap*.so*'
+
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ inst /etc/passwd /etc/passwd
+ inst /etc/group /etc/group
+
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ dracut_kernel_post
+ )
+
+ # Make client root inside server root
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay/source/nfs/client
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+
+ (
+ cd "$initdir" || exit
+ mkdir -p dev sys proc etc run root usr var/lib/nfs/rpc_pipefs
+ )
+
+ inst_multiple sh shutdown poweroff stty cat ps ln ip dd \
+ mount dmesg mkdir cp ping grep setsid ls vi less cat sync
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ if [ -f "${_terminfodir}"/l/linux ]; then
+ inst_multiple -o "${_terminfodir}"/l/linux
+ break
+ fi
+ done
+
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-lib.sh" "/lib/dracut-lib.sh"
+ inst_simple "${PKGLIBDIR}/modules.d/99base/dracut-dev-lib.sh" "/lib/dracut-dev-lib.sh"
+ inst_binary "${PKGLIBDIR}/dracut-util" "/usr/bin/dracut-util"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getarg"
+ ln -s dracut-util "${initdir}/usr/bin/dracut-getargs"
+
+ inst ./client-init.sh /sbin/init
+ inst_simple /etc/os-release
+ inst_multiple -o {,/usr}/etc/nsswitch.conf
+ inst /etc/passwd /etc/passwd
+ inst /etc/group /etc/group
+
+ inst_libdir_file 'libnfsidmap_nsswitch.so*'
+ inst_libdir_file 'libnfsidmap/*.so*'
+ inst_libdir_file 'libnfsidmap*.so*'
+
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ cp -a /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount sync dd
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash rootfs-block kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+ rm -rf -- "$TESTDIR"/overlay
+
+ declare -a disk_args=()
+ # shellcheck disable=SC2034
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.img root 120
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+
+ # Make an overlay with needed tools for the test harness
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir="$TESTDIR"/overlay
+ mkdir -p "$TESTDIR"/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown
+ inst_hook shutdown-emergency 000 ./hard-off.sh
+ inst_hook emergency 000 ./hard-off.sh
+ inst_simple ./client.link /etc/systemd/network/01-client.link
+
+ inst_binary awk
+ inst_hook pre-pivot 85 "$PKGLIBDIR/modules.d/45ifcfg/write-ifcfg.sh"
+ )
+ # Make client's dracut image
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -o "ifcfg plymouth" \
+ -a "debug watchdog ${USE_NETWORK}" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+
+ (
+ # shellcheck disable=SC2031
+ export initdir="$TESTDIR"/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ rm "$initdir"/etc/systemd/network/01-client.link
+ inst_simple ./server.link /etc/systemd/network/01-server.link
+ inst_hook pre-mount 99 ./wait-if-server.sh
+ )
+ # Make server's dracut image
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash rootfs-block debug kernel-modules watchdog qemu network network-legacy" \
+ -d "af_packet piix ide-gd_mod ata_piix ext4 sd_mod nfsv2 nfsv3 nfsv4 nfs_acl nfs_layout_nfsv41_files nfsd e1000 i6300esb ib700wdt" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.server "$KVERSION" || return 1
+
+}
+
+kill_server() {
+ if [[ -s "$TESTDIR"/server.pid ]]; then
+ kill -TERM -- "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+}
+
+test_cleanup() {
+ kill_server
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-50-MULTINIC/wait-if-server.sh b/test/TEST-50-MULTINIC/wait-if-server.sh
new file mode 100755
index 0000000..cea7259
--- /dev/null
+++ b/test/TEST-50-MULTINIC/wait-if-server.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+. /lib/net-lib.sh
+wait_for_if_link enx525401123456
diff --git a/test/TEST-60-BONDBRIDGEVLAN/Makefile b/test/TEST-60-BONDBRIDGEVLAN/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-60-BONDBRIDGEVLAN/client-init.sh b/test/TEST-60-BONDBRIDGEVLAN/client-init.sh
new file mode 100755
index 0000000..b1d1db4
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/client-init.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+exec > /dev/console 2>&1
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+strstr() { [ "${1#*"$2"*}" != "$1" ]; }
+CMDLINE=$(while read -r line; do echo "$line"; done < /proc/cmdline)
+export TERM=linux
+export PS1='initramfs-test:\w\$ '
+stty sane
+echo "made it to the rootfs! Powering down."
+
+testnum=$(grep -Eo "rd.dracut.test.num=[^[:space:]]+" /proc/cmdline | sed -nr 's/.*=(.*)/\1/p')
+netmodule=$(grep -Eo "rd.dracut.test.net-module=[^[:space:]]+" /proc/cmdline | sed -nr 's/.*=(.*)/\1/p')
+
+(
+ echo OK
+
+ ip -o -4 address show scope global | while read -r _ if rest; do echo "$if"; done | sort
+
+ case "$testnum" in
+ 1)
+ ping -c 2 192.168.50.1 > /dev/null
+ echo PING1=$?
+ ping -c 2 192.168.54.1 > /dev/null
+ echo PING2=$?
+ ping -c 2 192.168.55.1 > /dev/null
+ echo PING3=$?
+ ping -c 2 192.168.56.1 > /dev/null
+ echo PING4=$?
+ ping -c 2 192.168.57.1 > /dev/null
+ echo PING5=$?
+ ;;
+ 2)
+ ping -c 2 192.168.51.1 > /dev/null
+ echo PING1=$?
+ ip link show net3 | grep "master bond0" > /dev/null
+ echo NET3=$?
+ ip link show net4 | grep "master bond0" > /dev/null
+ echo NET4=$?
+ ;;
+ 3)
+ ping -c 2 192.168.51.1 > /dev/null
+ echo PING1=$?
+ ip link show net1 | grep "master br0" > /dev/null
+ echo NET1=$?
+ ip link show net5 | grep "master br0" > /dev/null
+ echo NET5=$?
+
+ ;;
+ esac
+
+ case "$netmodule" in
+ network-legacy)
+ for i in /run/initramfs/state/etc/sysconfig/network-scripts/ifcfg-*; do
+ basename "$i"
+ grep -v 'UUID=' "$i"
+ done
+ ;;
+ esac
+
+ echo EOF
+) | dd oflag=direct,dsync of=/dev/sda
+
+strstr "$CMDLINE" "rd.shell" && sh -i
+poweroff -f
diff --git a/test/TEST-60-BONDBRIDGEVLAN/client.link b/test/TEST-60-BONDBRIDGEVLAN/client.link
new file mode 100644
index 0000000..b992bfd
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/client.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=keep kernel database onboard slot path
+MACAddressPolicy=keep
diff --git a/test/TEST-60-BONDBRIDGEVLAN/create-root.sh b/test/TEST-60-BONDBRIDGEVLAN/create-root.sh
new file mode 100755
index 0000000..fe7b515
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/create-root.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+trap 'poweroff -f' EXIT
+
+# don't let udev and this script step on eachother's toes
+for x in 64-lvm.rules 70-mdadm.rules 99-mount-rules; do
+ : > "/etc/udev/rules.d/$x"
+done
+rm -f -- /etc/lvm/lvm.conf
+udevadm control --reload
+udevadm settle
+set -e
+
+mkfs.ext4 -L dracut /dev/disk/by-id/ata-disk_root
+mkdir -p /root
+mount -t ext4 /dev/disk/by-id/ata-disk_root /root
+cp -a -t /root /source/*
+mkdir -p /root/run
+umount /root
+echo "dracut-root-block-created" | dd oflag=direct,dsync of=/dev/disk/by-id/ata-disk_marker
+sync
+poweroff -f
diff --git a/test/TEST-60-BONDBRIDGEVLAN/dhcpd.conf b/test/TEST-60-BONDBRIDGEVLAN/dhcpd.conf
new file mode 100644
index 0000000..e26bd60
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/dhcpd.conf
@@ -0,0 +1,27 @@
+ddns-update-style none;
+
+use-host-decl-names true;
+
+subnet 192.168.50.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.50.1;
+ next-server 192.168.50.1;
+ server-identifier 192.168.50.1;
+ option domain-name-servers 192.168.50.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+ option root-path "nfs:192.168.50.1:/nfs/client";
+ range 192.168.50.10 192.168.50.100;
+}
+
+subnet 192.168.51.0 netmask 255.255.255.0 {
+ option subnet-mask 255.255.255.0;
+ option routers 192.168.51.1;
+ next-server 192.168.51.1;
+ server-identifier 192.168.51.1;
+ option domain-name-servers 192.168.51.1;
+ option domain-search "example.com";
+ option domain-name "other.com";
+ option root-path "nfs:192.168.51.1:/nfs/client";
+ range 192.168.51.10 192.168.51.100;
+}
diff --git a/test/TEST-60-BONDBRIDGEVLAN/exports b/test/TEST-60-BONDBRIDGEVLAN/exports
new file mode 100644
index 0000000..9c07923
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/exports
@@ -0,0 +1 @@
+/nfs/client 192.168.50.0/24(rw,insecure,no_subtree_check,no_root_squash) 192.168.51.0/24(rw,insecure,no_subtree_check,no_root_squash)
diff --git a/test/TEST-60-BONDBRIDGEVLAN/finished-false.sh b/test/TEST-60-BONDBRIDGEVLAN/finished-false.sh
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/finished-false.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/test/TEST-60-BONDBRIDGEVLAN/hard-off.sh b/test/TEST-60-BONDBRIDGEVLAN/hard-off.sh
new file mode 100755
index 0000000..12c3d5a
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/hard-off.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+getarg rd.shell || poweroff -f
+getarg failme && poweroff -f
diff --git a/test/TEST-60-BONDBRIDGEVLAN/hosts b/test/TEST-60-BONDBRIDGEVLAN/hosts
new file mode 100644
index 0000000..d02a4e9
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/hosts
@@ -0,0 +1,5 @@
+127.0.0.1 localhost
+192.168.50.1 server
+192.168.50.100 client-if1
+192.168.50.101 client-if2
+192.168.50.102 client-if3
diff --git a/test/TEST-60-BONDBRIDGEVLAN/server-init.sh b/test/TEST-60-BONDBRIDGEVLAN/server-init.sh
new file mode 100755
index 0000000..3b0f55e
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/server-init.sh
@@ -0,0 +1,140 @@
+#!/bin/sh
+exec < /dev/console > /dev/console 2>&1
+set -x
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+export TERM=linux
+export PS1='nfstest-server:\w\$ '
+stty sane
+echo "made it to the rootfs!"
+echo server > /proc/sys/kernel/hostname
+
+wait_for_if_link() {
+ local cnt=0
+ local li
+
+ while [ $cnt -lt 600 ]; do
+ ip link show
+
+ li=$(ip -o link show dev "$1" 2> /dev/null)
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_if_up() {
+ local cnt=0
+ local li
+ while [ $cnt -lt 200 ]; do
+ li=$(ip -o link show up dev "$1")
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_route_ok() {
+ local cnt=0
+ while [ $cnt -lt 200 ]; do
+ li=$(ip route show)
+ [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+linkup() {
+ wait_for_if_link "$1" 2> /dev/null && ip link set "$1" up 2> /dev/null && wait_for_if_up "$1" 2> /dev/null
+}
+
+udevadm settle
+
+ip link show
+
+wait_for_if_link enx525401123456
+wait_for_if_link enx525401123457
+wait_for_if_link enx525401123458
+wait_for_if_link enx525401123459
+
+ip link set dev enx525401123456 name net1
+ip link set dev enx525401123457 name net2
+ip link set dev enx525401123458 name net3
+ip link set dev enx525401123459 name net4
+
+modprobe --all -b -q 8021q bonding
+: > /dev/watchdog
+
+ip addr add 127.0.0.1/8 dev lo
+linkup lo
+
+ip addr add 192.168.50.1/24 dev net1
+linkup net1
+: > /dev/watchdog
+
+ip link add dev net2.1 link net2 type vlan id 1
+ip link add dev net2.2 link net2 type vlan id 2
+ip link add dev net2.3 link net2 type vlan id 3
+ip link add dev net2.4 link net2 type vlan id 4
+ip addr add 192.168.54.1/24 dev net2.1
+ip addr add 192.168.55.1/24 dev net2.2
+ip addr add 192.168.56.1/24 dev net2.3
+ip addr add 192.168.57.1/24 dev net2.4
+linkup net2
+ip link set dev net2.1 up
+ip link set dev net2.2 up
+ip link set dev net2.3 up
+ip link set dev net2.4 up
+
+ip link add bond0 type bond
+ip link set net3 master bond0
+ip link set net4 master bond0
+ip link set net3 up
+ip link set net4 up
+ip link set bond0 up
+ip addr add 192.168.51.1/24 dev bond0
+
+: > /dev/watchdog
+modprobe af_packet
+: > /dev/watchdog
+modprobe sunrpc
+: > /dev/watchdog
+mount -t rpc_pipefs sunrpc /var/lib/nfs/rpc_pipefs
+: > /dev/watchdog
+[ -x /sbin/portmap ] && portmap
+: > /dev/watchdog
+mkdir -p /run/rpcbind
+[ -x /sbin/rpcbind ] && rpcbind
+: > /dev/watchdog
+modprobe nfsd
+: > /dev/watchdog
+mount -t nfsd nfsd /proc/fs/nfsd
+: > /dev/watchdog
+exportfs -r
+: > /dev/watchdog
+rpc.nfsd
+: > /dev/watchdog
+rpc.mountd
+: > /dev/watchdog
+command -v rpc.idmapd > /dev/null && [ -z "$(pidof rpc.idmapd)" ] && rpc.idmapd -S
+: > /dev/watchdog
+exportfs -r
+: > /dev/watchdog
+: > /var/lib/dhcpd/dhcpd.leases
+: > /dev/watchdog
+chmod 777 /var/lib/dhcpd/dhcpd.leases
+: > /dev/watchdog
+dhcpd -cf /etc/dhcpd.conf -lf /var/lib/dhcpd/dhcpd.leases net1 bond0
+#echo -n 'V' : > /dev/watchdog
+#sh -i
+#tcpdump -i net1
+# Wait forever for the VM to die
+echo "Serving"
+while :; do
+ sleep 10
+ : > /dev/watchdog
+done
+mount -n -o remount,ro /
+poweroff -f
diff --git a/test/TEST-60-BONDBRIDGEVLAN/server.link b/test/TEST-60-BONDBRIDGEVLAN/server.link
new file mode 100644
index 0000000..1d21856
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/server.link
@@ -0,0 +1,6 @@
+[Match]
+OriginalName=*
+
+[Link]
+NamePolicy=mac
+MACAddressPolicy=keep
diff --git a/test/TEST-60-BONDBRIDGEVLAN/test.sh b/test/TEST-60-BONDBRIDGEVLAN/test.sh
new file mode 100755
index 0000000..5110ddb
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/test.sh
@@ -0,0 +1,404 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="root filesystem on NFS with bridging/bonding/vlan with $USE_NETWORK"
+
+# Uncomment this to debug failures
+#DEBUGFAIL="rd.shell rd.break"
+#SERIAL="tcp:127.0.0.1:9999"
+
+# Network topology:
+#
+# .---------------------. .---------------.
+# | SERVER-VM | | CLIENT-VM |
+# | | | |
+# | (DHCP) net1 <------------> net1 |
+# | | | |
+# | net2 <------------> net2 |
+# | vlan 1 <-| | | |
+# | vlan 2 <-| | | |
+# | vlan 3 <-| | | |
+# | vlan 4 <-. | | |
+# | | | |
+# | / net3 <------------> net3 |
+# | bond0 | | | |
+# | (DHCP) \ net4 <------------> net4 |
+# | | | |
+# | | X-----> net5 |
+# .---------------------. .---------------.
+
+run_server() {
+ # Start server first
+ echo "MULTINIC TEST SETUP: Starting DHCP/NFS server"
+
+ "$testdir"/run-qemu \
+ -netdev socket,id=n0,listen=127.0.0.1:12370 \
+ -netdev socket,id=n1,listen=127.0.0.1:12371 \
+ -netdev socket,id=n2,listen=127.0.0.1:12372 \
+ -netdev socket,id=n3,listen=127.0.0.1:12373 \
+ -device virtio-net-pci,netdev=n0,mac=52:54:01:12:34:56 \
+ -device virtio-net-pci,netdev=n1,mac=52:54:01:12:34:57 \
+ -device virtio-net-pci,netdev=n2,mac=52:54:01:12:34:58 \
+ -device virtio-net-pci,netdev=n3,mac=52:54:01:12:34:59 \
+ -hda "$TESTDIR"/server.ext4 \
+ -serial "${SERIAL:-"file:$TESTDIR/server.log"}" \
+ -device i6300esb -watchdog-action poweroff \
+ -append "panic=1 oops=panic softlockup_panic=1 loglevel=7 root=LABEL=dracut rootfstype=ext4 rw console=ttyS0,115200n81 selinux=0 rd.debug" \
+ -initrd "$TESTDIR"/initramfs.server \
+ -pidfile "$TESTDIR"/server.pid -daemonize || return 1
+ chmod 644 -- "$TESTDIR"/server.pid || return 1
+
+ # Cleanup the terminal if we have one
+ tty -s && stty sane
+
+ if ! [[ $SERIAL ]]; then
+ echo "Waiting for the server to startup"
+ while :; do
+ grep Serving "$TESTDIR"/server.log && break
+ tail "$TESTDIR"/server.log
+ sleep 1
+ done
+ else
+ echo Sleeping 10 seconds to give the server a head start
+ sleep 10
+ fi
+}
+
+client_test() {
+ local test_name="$1"
+ local cmdline="$2"
+ local check="$3"
+ local CONF
+
+ echo "CLIENT TEST START: $test_name"
+
+ # Need this so kvm-qemu will boot (needs non-/dev/zero local disk)
+ if ! dd if=/dev/zero of="$TESTDIR"/client.img bs=1M count=1; then
+ echo "Unable to make client sda image" 1>&2
+ return 1
+ fi
+
+ "$testdir"/run-qemu \
+ -netdev socket,connect=127.0.0.1:12370,id=n1 -device virtio-net-pci,mac=52:54:00:12:34:01,netdev=n1 \
+ -netdev socket,connect=127.0.0.1:12371,id=n2 -device virtio-net-pci,mac=52:54:00:12:34:02,netdev=n2 \
+ -netdev socket,connect=127.0.0.1:12372,id=n3 -device virtio-net-pci,mac=52:54:00:12:34:03,netdev=n3 \
+ -netdev socket,connect=127.0.0.1:12373,id=n4 -device virtio-net-pci,mac=52:54:00:12:34:04,netdev=n4 \
+ -netdev hubport,id=n5,hubid=1 -device virtio-net-pci,mac=52:54:00:12:34:05,netdev=n5 \
+ -hda "$TESTDIR"/client.img \
+ -device i6300esb -watchdog-action poweroff \
+ -append "
+ panic=1 oops=panic softlockup_panic=1
+ ifname=net1:52:54:00:12:34:01
+ ifname=net2:52:54:00:12:34:02
+ ifname=net3:52:54:00:12:34:03
+ ifname=net4:52:54:00:12:34:04
+ ifname=net5:52:54:00:12:34:05
+ $cmdline rd.net.timeout.dhcp=30 systemd.crash_reboot
+ $DEBUGFAIL rd.retry=5 rw console=ttyS0,115200n81 selinux=0 init=/sbin/init" \
+ -initrd "$TESTDIR"/initramfs.testing || return 1
+
+ {
+ read -r OK _
+ if [[ $OK != "OK" ]]; then
+ echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]"
+ return 1
+ fi
+
+ while read -r line; do
+ [[ $line == END ]] && break
+ CONF+="$line "
+ done
+ } < "$TESTDIR"/client.img || return 1
+
+ if [[ $check != "$CONF" ]]; then
+ echo "Expected: '$check'"
+ echo
+ echo
+ echo "Got: '$CONF'"
+ echo "CLIENT TEST END: $test_name [FAILED - BAD CONF]"
+ return 1
+ fi
+
+ echo "CLIENT TEST END: $test_name [OK]"
+ return 0
+}
+
+test_run() {
+ if ! run_server; then
+ echo "Failed to start server" 1>&2
+ return 1
+ fi
+ test_client || {
+ kill_server
+ return 1
+ }
+}
+
+test_client() {
+
+ ### TEST 1: VLANs
+ if [ "$USE_NETWORK" = network-legacy ]; then
+ NETCONF='ifcfg-net1 # Generated by dracut initrd NAME="net1" HWADDR="52:54:00:12:34:01" DEVICE="net1" ONBOOT=yes NETBOOT=yes IPV6INIT=yes BOOTPROTO=dhcp TYPE=Ethernet ifcfg-net2.0004 # Generated by dracut initrd NAME="net2.0004" ONBOOT=yes NETBOOT=yes BOOTPROTO=none IPADDR="192.168.57.104" PREFIX="24" GATEWAY="192.168.57.1" TYPE=Vlan DEVICE="net2.0004" VLAN=yes PHYSDEV="net2" ifcfg-net2.3 # Generated by dracut initrd NAME="net2.3" ONBOOT=yes NETBOOT=yes BOOTPROTO=none IPADDR="192.168.56.103" PREFIX="24" GATEWAY="192.168.56.1" TYPE=Vlan DEVICE="net2.3" VLAN=yes PHYSDEV="net2" ifcfg-vlan0001 # Generated by dracut initrd NAME="vlan0001" ONBOOT=yes NETBOOT=yes BOOTPROTO=none IPADDR="192.168.54.101" PREFIX="24" GATEWAY="192.168.54.1" TYPE=Vlan DEVICE="vlan0001" VLAN=yes PHYSDEV="net2" ifcfg-vlan2 # Generated by dracut initrd NAME="vlan2" ONBOOT=yes NETBOOT=yes BOOTPROTO=none IPADDR="192.168.55.102" PREFIX="24" GATEWAY="192.168.55.1" TYPE=Vlan DEVICE="vlan2" VLAN=yes PHYSDEV="net2" '
+ fi
+
+ client_test "VLANs" \
+ "
+rd.dracut.test.num=1
+rd.dracut.test.net-module=$USE_NETWORK
+vlan=vlan0001:net2
+vlan=vlan2:net2
+vlan=net2.3:net2
+vlan=net2.0004:net2
+ip=net1:dhcp
+ip=192.168.54.101::192.168.54.1:24:test:vlan0001:none
+ip=192.168.55.102::192.168.55.1:24:test:vlan2:none
+ip=192.168.56.103::192.168.56.1:24:test:net2.3:none
+ip=192.168.57.104::192.168.57.1:24:test:net2.0004:none
+rd.neednet=1
+root=nfs:192.168.50.1:/nfs/client
+bootdev=net1
+" \
+ "net1 net2.0004 net2.3 vlan0001 vlan2 PING1=0 PING2=0 PING3=0 PING4=0 PING5=0 ${NETCONF}EOF " \
+ || return 1
+
+ ### TEST 2: bond
+ if [ "$USE_NETWORK" = network-legacy ]; then
+ NETCONF='ifcfg-bond0 # Generated by dracut initrd NAME="bond0" DEVICE="bond0" ONBOOT=yes NETBOOT=yes IPV6INIT=yes BOOTPROTO=dhcp BONDING_OPTS="miimon=100" NAME="bond0" TYPE=Bond ifcfg-net3 # Generated by dracut initrd NAME="net3" TYPE=Ethernet ONBOOT=yes NETBOOT=yes SLAVE=yes MASTER="bond0" DEVICE="net3" ifcfg-net4 # Generated by dracut initrd NAME="net4" TYPE=Ethernet ONBOOT=yes NETBOOT=yes SLAVE=yes MASTER="bond0" DEVICE="net4" '
+ fi
+ client_test "Bond" \
+ "
+rd.dracut.test.num=2
+rd.dracut.test.net-module=$USE_NETWORK
+bond=bond0:net3,net4:miimon=100
+ip=bond0:dhcp
+rd.neednet=1
+root=nfs:192.168.51.1:/nfs/client
+bootdev=bond0
+" \
+ "bond0 PING1=0 NET3=0 NET4=0 ${NETCONF}EOF " \
+ || return 1
+
+ ### TEST 3: bridge
+ if [ "$USE_NETWORK" = network-legacy ]; then
+ NETCONF='ifcfg-br0 # Generated by dracut initrd NAME="br0" DEVICE="br0" ONBOOT=yes NETBOOT=yes IPV6INIT=yes BOOTPROTO=dhcp TYPE=Bridge NAME="br0" ifcfg-net1 # Generated by dracut initrd NAME="net1" TYPE=Ethernet ONBOOT=yes NETBOOT=yes BRIDGE="br0" HWADDR="52:54:00:12:34:01" DEVICE="net1" ifcfg-net5 # Generated by dracut initrd NAME="net5" TYPE=Ethernet ONBOOT=yes NETBOOT=yes BRIDGE="br0" HWADDR="52:54:00:12:34:05" DEVICE="net5" '
+ fi
+ client_test "Bridge" \
+ "
+rd.dracut.test.num=3
+rd.dracut.test.net-module=$USE_NETWORK
+bridge=br0:net1,net5
+ip=br0:dhcp
+rd.neednet=1
+root=nfs:192.168.50.1:/nfs/client
+bootdev=br0
+" \
+ "br0 PING1=0 NET1=0 NET5=0 ${NETCONF}EOF " \
+ || return 1
+
+ kill_server
+ return 0
+}
+
+test_setup() {
+ kernel=$KVERSION
+ rm -rf -- "$TESTDIR"/overlay
+ (
+ mkdir -p "$TESTDIR"/overlay/source
+ # shellcheck disable=SC2030
+ export initdir=$TESTDIR/overlay/source
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc run etc var/run tmp var/lib/{dhcpd,rpcbind}
+ mkdir -p -- var/lib/nfs/{v4recovery,rpc_pipefs}
+ chmod 777 -- var/lib/rpcbind var/lib/nfs
+ )
+
+ for _f in modules.builtin.bin modules.builtin; do
+ [[ -f $srcmods/$_f ]] && break
+ done || {
+ dfatal "No modules.builtin.bin and modules.builtin found!"
+ return 1
+ }
+
+ for _f in modules.builtin.bin modules.builtin modules.order; do
+ [[ -f $srcmods/$_f ]] && inst_simple "$srcmods/$_f" "/lib/modules/$kernel/$_f"
+ done
+
+ inst_multiple sh ls shutdown poweroff stty cat ps ln ip \
+ dmesg mkdir cp ping exportfs \
+ modprobe rpc.nfsd rpc.mountd showmount tcpdump \
+ /etc/services sleep mount chmod
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [ -f "${_terminfodir}"/l/linux ] && break
+ done
+ inst_multiple -o "${_terminfodir}"/l/linux
+ type -P portmap > /dev/null && inst_multiple portmap
+ type -P rpcbind > /dev/null && inst_multiple rpcbind
+ [ -f /etc/netconfig ] && inst_multiple /etc/netconfig
+ type -P dhcpd > /dev/null && inst_multiple dhcpd
+ instmods nfsd sunrpc ipv6 lockd af_packet 8021q bonding
+ inst_simple /etc/os-release
+ inst ./server-init.sh /sbin/init
+ inst ./hosts /etc/hosts
+ inst ./exports /etc/exports
+ inst ./dhcpd.conf /etc/dhcpd.conf
+ inst_multiple -o {,/usr}/etc/nsswitch.conf {,/usr}/etc/rpc {,/usr}/etc/protocols
+
+ inst_multiple -o rpc.idmapd /etc/idmapd.conf
+
+ inst_libdir_file 'libnfsidmap_nsswitch.so*'
+ inst_libdir_file 'libnfsidmap/*.so*'
+ inst_libdir_file 'libnfsidmap*.so*'
+
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ inst /etc/passwd /etc/passwd
+ inst /etc/group /etc/group
+
+ cp -a -- /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ dracut_kernel_post
+ )
+
+ # Make client root inside server root
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay/source/nfs/client
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sh shutdown poweroff stty cat ps ln ip \
+ mount dmesg mkdir cp ping grep ls sort dd sed basename
+ for _terminfodir in /lib/terminfo /etc/terminfo /usr/share/terminfo; do
+ [[ -f ${_terminfodir}/l/linux ]] && break
+ done
+ inst_multiple -o "${_terminfodir}"/l/linux
+ inst_simple /etc/os-release
+ inst ./client-init.sh /sbin/init
+ (
+ cd "$initdir" || exit
+ mkdir -p -- dev sys proc etc run
+ mkdir -p -- var/lib/nfs/rpc_pipefs
+ )
+ inst_multiple -o {,/usr}/etc/nsswitch.conf {,/usr}/etc/rpc {,/usr}/etc/protocols
+ inst /etc/passwd /etc/passwd
+ inst /etc/group /etc/group
+
+ inst_multiple -o rpc.idmapd /etc/idmapd.conf
+ inst_libdir_file 'libnfsidmap_nsswitch.so*'
+ inst_libdir_file 'libnfsidmap/*.so*'
+ inst_libdir_file 'libnfsidmap*.so*'
+
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ cp -a -- /etc/ld.so.conf* "$initdir"/etc
+ ldconfig -r "$initdir"
+ )
+
+ # second, install the files needed to make the root filesystem
+ (
+ # shellcheck disable=SC2030
+ # shellcheck disable=SC2031
+ export initdir=$TESTDIR/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple sfdisk mkfs.ext4 poweroff cp umount sync dd
+ inst_hook initqueue 01 ./create-root.sh
+ inst_hook initqueue/finished 01 ./finished-false.sh
+ )
+
+ # create an initramfs that will create the target root filesystem.
+ # We do it this way so that we do not risk trashing the host mdraid
+ # devices, volume groups, encrypted partitions, etc.
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ -m "bash rootfs-block kernel-modules qemu" \
+ -d "piix ide-gd_mod ata_piix ext4 sd_mod" \
+ --nomdadmconf \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.makeroot "$KVERSION" || return 1
+
+ declare -a disk_args=()
+ declare -i disk_index=0
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/marker.img marker 1
+ qemu_add_drive_args disk_index disk_args "$TESTDIR"/server.ext4 root 120
+
+ # Invoke KVM and/or QEMU to actually create the target filesystem.
+ "$testdir"/run-qemu \
+ "${disk_args[@]}" \
+ -append "root=/dev/dracut/root rw rootfstype=ext4 quiet console=ttyS0,115200n81 selinux=0" \
+ -initrd "$TESTDIR"/initramfs.makeroot || return 1
+ test_marker_check dracut-root-block-created || return 1
+ rm -- "$TESTDIR"/marker.img
+ rm -fr "$TESTDIR"/overlay
+
+ # Make an overlay with needed tools for the test harness
+ (
+ # shellcheck disable=SC2031
+ # shellcheck disable=SC2030
+ export initdir="$TESTDIR"/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ inst_multiple poweroff shutdown
+ inst_hook emergency 000 ./hard-off.sh
+ inst_simple ./client.link /etc/systemd/network/01-client.link
+ )
+ # Make client's dracut image
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ --no-early-microcode \
+ -o "plymouth" \
+ -a "debug ${USE_NETWORK}" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.testing "$KVERSION" || return 1
+
+ (
+ # shellcheck disable=SC2031
+ export initdir="$TESTDIR"/overlay
+ # shellcheck disable=SC1090
+ . "$PKGLIBDIR"/dracut-init.sh
+ rm "$initdir"/etc/systemd/network/01-client.link
+ inst_simple ./server.link /etc/systemd/network/01-server.link
+ inst_hook pre-mount 99 ./wait-if-server.sh
+ )
+ # Make server's dracut image
+ "$DRACUT" -l -i "$TESTDIR"/overlay / \
+ --no-early-microcode \
+ -m "rootfs-block debug kernel-modules watchdog qemu network network-legacy" \
+ -d "ipvlan macvlan af_packet piix ide-gd_mod ata_piix ext4 sd_mod nfsv2 nfsv3 nfsv4 nfs_acl nfs_layout_nfsv41_files nfsd virtio-net i6300esb ib700wdt" \
+ --no-hostonly-cmdline -N \
+ -f "$TESTDIR"/initramfs.server "$KVERSION" || return 1
+}
+
+kill_server() {
+ if [[ -s "$TESTDIR"/server.pid ]]; then
+ kill -TERM -- "$(cat "$TESTDIR"/server.pid)"
+ rm -f -- "$TESTDIR"/server.pid
+ fi
+}
+
+test_cleanup() {
+ kill_server
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-60-BONDBRIDGEVLAN/wait-if-server.sh b/test/TEST-60-BONDBRIDGEVLAN/wait-if-server.sh
new file mode 100755
index 0000000..7cdb941
--- /dev/null
+++ b/test/TEST-60-BONDBRIDGEVLAN/wait-if-server.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+. /lib/net-lib.sh
+wait_for_if_link enx525401123456
+wait_for_if_link enx525401123457
+wait_for_if_link enx525401123458
+wait_for_if_link enx525401123459
diff --git a/test/TEST-62-SKIPCPIO/Makefile b/test/TEST-62-SKIPCPIO/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-62-SKIPCPIO/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-62-SKIPCPIO/test.sh b/test/TEST-62-SKIPCPIO/test.sh
new file mode 100755
index 0000000..2311da3
--- /dev/null
+++ b/test/TEST-62-SKIPCPIO/test.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+# This file is part of dracut.
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="test skipcpio"
+
+test_check() {
+ cpio dd truncate find sort diff &> /dev/null
+}
+
+skipcpio_simple() {
+ mkdir -p "$CPIO_TESTDIR/skipcpio_simple/first_archive"
+ pushd "$CPIO_TESTDIR/skipcpio_simple/first_archive"
+
+ for ((i = 0; i < 3; i++)); do
+ echo "first archive file $i" >> ./"$i"
+ done
+ find . -print0 | sort -z \
+ | cpio -o --null -H newc --file "$CPIO_TESTDIR/skipcpio_simple.cpio"
+ popd
+
+ mkdir -p "$CPIO_TESTDIR/skipcpio_simple/second_archive"
+ pushd "$CPIO_TESTDIR/skipcpio_simple/second_archive"
+
+ for ((i = 10; i < 13; i++)); do
+ echo "second archive file $i" >> ./"$i"
+ done
+
+ find . -print0 | sort -z \
+ | cpio -o --null -H newc >> "$CPIO_TESTDIR/skipcpio_simple.cpio"
+ popd
+
+ cpio -i --list < "$CPIO_TESTDIR/skipcpio_simple.cpio" \
+ > "$CPIO_TESTDIR/skipcpio_simple.list"
+ cat << EOF | diff - "$CPIO_TESTDIR/skipcpio_simple.list"
+.
+0
+1
+2
+EOF
+
+ if [ "$PKGLIBDIR" = "$basedir" ]; then
+ skipcpio_path="${PKGLIBDIR}/src/skipcpio"
+ else
+ skipcpio_path="${PKGLIBDIR}"
+ fi
+ "$skipcpio_path"/skipcpio "$CPIO_TESTDIR/skipcpio_simple.cpio" \
+ | cpio -i --list > "$CPIO_TESTDIR/skipcpio_simple.list"
+ cat << EOF | diff - "$CPIO_TESTDIR/skipcpio_simple.list"
+.
+10
+11
+12
+EOF
+}
+
+test_run() {
+ set -x
+ set -e
+
+ skipcpio_simple
+
+ return 0
+}
+
+test_setup() {
+ CPIO_TESTDIR=$(mktemp --directory -p "$TESTDIR" cpio-test.XXXXXXXXXX) \
+ || return 1
+ export CPIO_TESTDIR
+ return 0
+}
+
+test_cleanup() {
+ [ -d "$CPIO_TESTDIR" ] && rm -rf "$CPIO_TESTDIR"
+ return 0
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-63-DRACUT-CPIO/Makefile b/test/TEST-63-DRACUT-CPIO/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-63-DRACUT-CPIO/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-63-DRACUT-CPIO/test.sh b/test/TEST-63-DRACUT-CPIO/test.sh
new file mode 100755
index 0000000..f9dff10
--- /dev/null
+++ b/test/TEST-63-DRACUT-CPIO/test.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+# This file is part of dracut.
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="kernel cpio extraction tests for dracut-cpio"
+# see dracut-cpio source for unit tests
+
+test_check() {
+ if ! [[ -x "$PKGLIBDIR/dracut-cpio" ]]; then
+ echo "Test needs dracut-cpio... Skipping"
+ return 1
+ fi
+}
+
+test_dracut_cpio() {
+ local tdir="${CPIO_TESTDIR}/${1}"
+ shift
+ # --enhanced-cpio tells dracut to use dracut-cpio instead of GNU cpio
+ local dracut_cpio_params=("--enhanced-cpio" "$@")
+
+ mkdir -p "$tdir"
+
+ # VM script to print sentinel on boot
+ # write to kmsg so that sysrq messages don't race with console output
+ cat > "$tdir/init.sh" << EOF
+echo "Image with ${dracut_cpio_params[*]} booted successfully" > /dev/kmsg
+echo 1 > /proc/sys/kernel/sysrq
+echo o > /proc/sysrq-trigger
+sleep 20
+EOF
+
+ "$DRACUT" -l --drivers "" \
+ "${dracut_cpio_params[@]}" \
+ --modules "bash base" \
+ --include "$tdir/init.sh" /lib/dracut/hooks/emergency/00-init.sh \
+ --no-hostonly --no-hostonly-cmdline \
+ "$tdir/initramfs" \
+ || return 1
+
+ "$testdir"/run-qemu \
+ -device i6300esb -watchdog-action poweroff \
+ -daemonize -pidfile "$tdir/vm.pid" \
+ -serial "file:$tdir/console.out" \
+ -append "panic=1 oops=panic softlockup_panic=1 loglevel=7 console=ttyS0 rd.shell=1" \
+ -initrd "$tdir/initramfs" || return 1
+
+ timeout=120
+ while [[ -f $tdir/vm.pid ]] \
+ && ps -p "$(head -n1 "$tdir/vm.pid")" > /dev/null; do
+ echo "$timeout - awaiting VM shutdown"
+ sleep 1
+ [[ $((timeout--)) -le 0 ]] && return 1
+ done
+
+ cat "$tdir/console.out"
+ grep -q "Image with ${dracut_cpio_params[*]} booted successfully" \
+ "$tdir/console.out"
+}
+
+test_run() {
+ set -x
+
+ # dracut-cpio is typically used with compression and strip disabled, to
+ # increase the chance of (reflink) extent sharing.
+ test_dracut_cpio "simple" "--no-compress" "--nostrip" || return 1
+ # dracut-cpio should still work fine with compression and stripping enabled
+ test_dracut_cpio "compress" "--gzip" "--nostrip" || return 1
+ test_dracut_cpio "strip" "--gzip" "--strip" || return 1
+}
+
+test_setup() {
+ CPIO_TESTDIR=$(mktemp --directory -p "$TESTDIR" cpio-test.XXXXXXXXXX) \
+ || return 1
+ export CPIO_TESTDIR
+ return 0
+}
+
+test_cleanup() {
+ [ -d "$CPIO_TESTDIR" ] && rm -rf "$CPIO_TESTDIR"
+ return 0
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/TEST-98-GETARG/Makefile b/test/TEST-98-GETARG/Makefile
new file mode 100644
index 0000000..2dcab81
--- /dev/null
+++ b/test/TEST-98-GETARG/Makefile
@@ -0,0 +1 @@
+-include ../Makefile.testdir
diff --git a/test/TEST-98-GETARG/test.sh b/test/TEST-98-GETARG/test.sh
new file mode 100755
index 0000000..8f628a2
--- /dev/null
+++ b/test/TEST-98-GETARG/test.sh
@@ -0,0 +1,159 @@
+#!/bin/bash
+
+# This file is part of dracut.
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# shellcheck disable=SC2034
+TEST_DESCRIPTION="dracut getarg command"
+
+test_check() {
+ if ! [[ -x "$PKGLIBDIR/dracut-util" ]]; then
+ echo "Test needs dracut-util... Skipping"
+ return 1
+ fi
+}
+
+test_setup() {
+ ln -sfnr "$PKGLIBDIR"/dracut-util "$TESTDIR"/dracut-getarg
+ ln -sfnr "$PKGLIBDIR"/dracut-util "$TESTDIR"/dracut-getargs
+ ln -sfnr "$PKGLIBDIR"/modules.d/99base/dracut-lib.sh "$TESTDIR"/dracut-lib.sh
+ ln -sfnr "$PKGLIBDIR"/modules.d/99base/dracut-dev-lib.sh "$TESTDIR"/dracut-dev-lib.sh
+ return 0
+}
+
+test_run() {
+ set -x
+ (
+ cd "$TESTDIR" || exit 1
+ export CMDLINE='key1=0 key2=val key2=val2 key3=" val 3 " " key 4 ="val4 "key 5=val 5" "key 6=""val 6" key7="foo"bar" baz="end " key8 = val 8 "
+"key 9"="val 9"'
+
+ ret=0
+
+ unset TEST
+ declare -A TEST
+ TEST=(
+ ["key1"]="0"
+ ["key2"]="val2"
+ ["key3"]=" val 3 "
+ [" key 4 "]="val4"
+ ["key 5"]="val 5"
+ ["key 6"]='"val 6'
+ ["key7"]='foo"bar" baz="end'
+ [" key8 "]=" val 8 "
+ ['key 9"']="val 9"
+ )
+ for key in "${!TEST[@]}"; do
+ if ! val=$(./dracut-getarg "${key}="); then
+ echo "'$key' == '${TEST[$key]}', but not found" >&2
+ ret=$((ret + 1))
+ else
+ if [[ $val != "${TEST[$key]}" ]]; then
+ echo "'$key' != '${TEST[$key]}' but '$val'" >&2
+ ret=$((ret + 1))
+ fi
+ fi
+ done
+
+ declare -a INVALIDKEYS
+
+ INVALIDKEYS=("key" "4" "5" "6" "key8" "9" '"' "baz")
+ for key in "${INVALIDKEYS[@]}"; do
+ val=$(./dracut-getarg "$key")
+ # shellcheck disable=SC2181
+ if (($? == 0)); then
+ echo "key '$key' should not be found"
+ ret=$((ret + 1))
+ fi
+ # must have no output
+ [[ $val ]] && ret=$((ret + 1))
+ done
+
+ RESULT=("val" "val2")
+ readarray -t args < <(./dracut-getargs "key2=")
+ ((${#RESULT[@]} == ${#args[@]})) || ret=$((ret + 1))
+ for ((i = 0; i < ${#RESULT[@]}; i++)); do
+ [[ ${args[$i]} == "${RESULT[$i]}" ]] || ret=$((ret + 1))
+ done
+
+ val=$(./dracut-getarg "key1") || ret=$((ret + 1))
+ [[ $val == "0" ]] || ret=$((ret + 1))
+
+ val=$(./dracut-getarg "key2=val") && ret=$((ret + 1))
+ # must have no output
+ [[ $val ]] && ret=$((ret + 1))
+ val=$(./dracut-getarg "key2=val2") || ret=$((ret + 1))
+ # must have no output
+ [[ $val ]] && ret=$((ret + 1))
+
+ export PATH=".:$PATH"
+
+ . dracut-dev-lib.sh
+ . dracut-lib.sh
+
+ debug_off() {
+ :
+ }
+
+ debug_on() {
+ :
+ }
+
+ getcmdline() {
+ echo "rdbreak=cmdline rd.lvm rd.auto=0 rd.auto rd.retry=10"
+ }
+ RDRETRY=$(getarg rd.retry -d 'rd_retry=')
+ [[ $RDRETRY == "10" ]] || ret=$((ret + 1))
+ getarg rd.break=cmdline -d rdbreak=cmdline || ret=$((ret + 1))
+ getargbool 1 rd.lvm -d -n rd_NO_LVM || ret=$((ret + 1))
+ getargbool 0 rd.auto || ret=$((ret + 1))
+
+ getcmdline() {
+ echo "rd.break=cmdlined rd.lvm=0 rd.auto rd.auto=1 rd.auto=0"
+ }
+ getarg rd.break=cmdline -d rdbreak=cmdline && ret=$((ret + 1))
+ getargbool 1 rd.lvm -d -n rd_NO_LVM && ret=$((ret + 1))
+ getargbool 0 rd.auto && ret=$((ret + 1))
+
+ getcmdline() {
+ echo "ip=a ip=b ip=dhcp6"
+ }
+ getargs "ip=dhcp6" &> /dev/null || ret=$((ret + 1))
+ readarray -t args < <(getargs "ip=")
+ RESULT=("a" "b" "dhcp6")
+ ((${#RESULT[@]} || ${#args[@]})) || ret=$((ret + 1))
+ for ((i = 0; i < ${#RESULT[@]}; i++)); do
+ [[ ${args[$i]} == "${RESULT[$i]}" ]] || ret=$((ret + 1))
+ done
+
+ getcmdline() {
+ echo "bridge bridge=val"
+ }
+ readarray -t args < <(getargs bridge=)
+ RESULT=("bridge" "val")
+ ((${#RESULT[@]} == ${#args[@]})) || ret=$((ret + 1))
+ for ((i = 0; i < ${#RESULT[@]}; i++)); do
+ [[ ${args[$i]} == "${RESULT[$i]}" ]] || ret=$((ret + 1))
+ done
+
+ getcmdline() {
+ echo "rd.break rd.md.uuid=bf96e457:230c9ad4:1f3e59d6:745cf942 rd.md.uuid=bf96e457:230c9ad4:1f3e59d6:745cf943 rd.shell"
+ }
+ readarray -t args < <(getargs rd.md.uuid -d rd_MD_UUID=)
+ RESULT=("bf96e457:230c9ad4:1f3e59d6:745cf942" "bf96e457:230c9ad4:1f3e59d6:745cf943")
+ ((${#RESULT[@]} == ${#args[@]})) || ret=$((ret + 1))
+ for ((i = 0; i < ${#RESULT[@]}; i++)); do
+ [[ ${args[$i]} == "${RESULT[$i]}" ]] || ret=$((ret + 1))
+ done
+
+ return $ret
+ )
+}
+
+test_cleanup() {
+ rm -fr -- "$TESTDIR"/*.rpm
+ return 0
+}
+
+# shellcheck disable=SC1090
+. "$testdir"/test-functions
diff --git a/test/container/Dockerfile-Arch b/test/container/Dockerfile-Arch
new file mode 100644
index 0000000..f05ce8f
--- /dev/null
+++ b/test/container/Dockerfile-Arch
@@ -0,0 +1,17 @@
+FROM docker.io/archlinux
+
+MAINTAINER https://github.com/dracutdevs/dracut
+
+# Install needed packages for the dracut CI container
+RUN pacman --noconfirm -Syu \
+ linux dash strace dhclient asciidoc cpio pigz squashfs-tools \
+ qemu btrfs-progs mdadm dmraid nfs-utils nfsidmap lvm2 nbd \
+ dhcp networkmanager multipath-tools vi tcpdump open-iscsi connman \
+ git shfmt shellcheck astyle which base-devel glibc parted ntfs-3g && yes | pacman -Scc
+
+RUN useradd -m build
+RUN su build -c 'cd && git clone https://aur.archlinux.org/perl-config-general.git && cd perl-config-general && makepkg -s --noconfirm'
+RUN pacman -U --noconfirm ~build/perl-config-general/*.pkg.tar.*
+RUN su build -c 'cd && git clone https://aur.archlinux.org/tgt.git && cd tgt && makepkg -s --noconfirm'
+RUN pacman -U --noconfirm ~build/tgt/*.pkg.tar.*
+RUN rm -fr ~build
diff --git a/test/container/Dockerfile-Debian b/test/container/Dockerfile-Debian
new file mode 100644
index 0000000..9ba3a1f
--- /dev/null
+++ b/test/container/Dockerfile-Debian
@@ -0,0 +1,67 @@
+FROM docker.io/debian:latest
+
+MAINTAINER https://github.com/dracutdevs/dracut
+
+# Install needed packages for the dracut CI container
+# Install dracut as a linux-initramfs-tool provider so that the default initramfs-tool package does not get installed
+# Uninstall initramfs-tools-core as a workaround for https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=994492
+RUN apt-get update -y -qq && apt-get upgrade -y -qq && apt-get install -y -qq --no-install-recommends dracut && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends -o Dpkg::Use-Pty=0 \
+ asciidoc \
+ astyle \
+ btrfs-progs \
+ busybox-static \
+ bzip2 \
+ ca-certificates \
+ console-setup \
+ cpio \
+ cryptsetup \
+ curl \
+ dash \
+ debhelper \
+ debhelper-compat \
+ dmraid \
+ docbook \
+ docbook-xml \
+ docbook-xsl \
+ fdisk \
+ g++ \
+ gawk \
+ git \
+ iputils-arping \
+ iputils-ping \
+ isc-dhcp-client \
+ isc-dhcp-server \
+ kmod \
+ less \
+ libdmraid-dev \
+ libkmod-dev \
+ linux-image-generic \
+ lvm2 \
+ make \
+ mdadm \
+ multipath-tools \
+ nbd-client \
+ nbd-server \
+ network-manager \
+ nfs-kernel-server \
+ ntfs-3g \
+ open-iscsi \
+ ovmf \
+ parted \
+ pigz \
+ pkg-config \
+ procps \
+ qemu-system-x86 \
+ quilt \
+ shellcheck \
+ squashfs-tools \
+ strace \
+ sudo \
+ systemd-boot-efi \
+ tcpdump \
+ tgt \
+ thin-provisioning-tools \
+ vim \
+ wget \
+ && apt-get clean && dpkg -P --force-depends dracut dracut-core initramfs-tools-core
diff --git a/test/container/Dockerfile-Fedora-latest b/test/container/Dockerfile-Fedora-latest
new file mode 100644
index 0000000..2ce3975
--- /dev/null
+++ b/test/container/Dockerfile-Fedora-latest
@@ -0,0 +1,71 @@
+FROM registry.fedoraproject.org/fedora:latest
+
+MAINTAINER https://github.com/dracutdevs/dracut
+
+# Install needed packages for the dracut CI container
+RUN dnf -y install --setopt=install_weak_deps=False \
+ asciidoc \
+ bash-completion \
+ biosdevname \
+ bluez \
+ btrfs-progs \
+ busybox \
+ bzip2 \
+ cifs-utils \
+ cryptsetup \
+ dash \
+ dbus-daemon \
+ device-mapper-multipath \
+ dhcp-client \
+ dhcp-server \
+ dmraid \
+ e2fsprogs \
+ f2fs-tools \
+ fcoe-utils \
+ fuse3 \
+ gcc \
+ git \
+ iproute \
+ iputils \
+ iscsi-initiator-utils \
+ kbd \
+ kernel \
+ kmod-devel \
+ libkcapi-hmaccalc \
+ libselinux-utils \
+ lvm2 \
+ lzop \
+ make \
+ mdadm \
+ memstrack \
+ mksh \
+ nbd \
+ ndctl \
+ NetworkManager \
+ nfs-utils \
+ ntfs-3g \
+ ntfsprogs \
+ nvme-cli \
+ parted \
+ pcsc-lite \
+ pigz \
+ qemu-system-x86-core \
+ rng-tools \
+ rpm-build \
+ sbsigntools \
+ scsi-target-utils \
+ ShellCheck \
+ shfmt \
+ squashfs-tools \
+ strace \
+ sudo \
+ systemd-boot-unsigned \
+ systemd-networkd \
+ systemd-resolved \
+ tar \
+ tcpdump \
+ tpm2-tools \
+ wget \
+ which \
+ xz \
+ && dnf -y remove dracut --noautoremove && dnf -y update && dnf clean all
diff --git a/test/container/Dockerfile-Gentoo b/test/container/Dockerfile-Gentoo
new file mode 100644
index 0000000..f2bb555
--- /dev/null
+++ b/test/container/Dockerfile-Gentoo
@@ -0,0 +1,57 @@
+ARG TAG=musl
+FROM docker.io/gentoo/portage:latest as portage
+
+# uefi stub in a separate builder
+FROM docker.io/gentoo/stage3 as efistub
+COPY --from=portage /var/db/repos/gentoo /var/db/repos/gentoo
+
+# systemd-boot
+RUN echo 'sys-apps/systemd-utils boot' > /etc/portage/package.use/systemd-utils && \
+ emerge -qv sys-apps/systemd-utils
+
+# kernel and its dependencies in a separate builder
+FROM docker.io/gentoo/stage3:$TAG as kernel
+COPY --from=portage /var/db/repos/gentoo /var/db/repos/gentoo
+# disable initramfs generation, only need the kernel image itself
+RUN echo 'sys-kernel/gentoo-kernel-bin -initramfs' > /etc/portage/package.use/kernel
+RUN emerge -qv sys-kernel/gentoo-kernel-bin
+
+FROM docker.io/gentoo/stage3:$TAG
+COPY --from=portage /var/db/repos/gentoo /var/db/repos/gentoo
+COPY --from=kernel /boot /boot
+COPY --from=kernel /lib/modules /lib/modules
+COPY --from=efistub /usr/lib/systemd/boot/efi /usr/lib/systemd/boot/efi
+ARG TAG
+
+MAINTAINER https://github.com/dracutdevs/dracut
+
+# required by sys-fs/dmraid
+RUN echo 'sys-fs/lvm2 lvm thin' > /etc/portage/package.use/lvm2
+
+# workaround for https://bugs.gentoo.org/734022 whereby Gentoo does not support NFS4 with musl
+RUN if [[ "$TAG" == 'musl' ]]; then echo 'net-fs/nfs-utils -nfsv4' > /etc/portage/package.use/nfs-utils ; fi
+
+# workaround for packages do not compile on musl
+# https://bugs.gentoo.org/713490 for tgt
+# https://bugs.gentoo.org/908587 for open-iscsi
+RUN if [[ "$TAG" != 'musl' ]]; then emerge -qv sys-block/tgt sys-block/open-iscsi ; fi
+
+# Install needed packages for the dracut CI container
+RUN emerge -qv \
+ app-arch/cpio \
+ app-emulation/qemu \
+ app-shells/dash \
+ net-fs/nfs-utils \
+ net-misc/dhcp \
+ sys-apps/busybox \
+ sys-block/nbd \
+ sys-block/parted \
+ sys-fs/btrfs-progs \
+ sys-fs/cryptsetup \
+ sys-fs/dmraid \
+ sys-fs/lvm2 \
+ sys-fs/mdadm \
+ sys-fs/multipath-tools \
+ sys-fs/ntfs3g \
+ sys-fs/squashfs-tools \
+ && rm -rf /var/cache/* /usr/share/doc/* /usr/share/man/*
diff --git a/test/container/Dockerfile-OpenSuse-latest b/test/container/Dockerfile-OpenSuse-latest
new file mode 100644
index 0000000..3e7cefc
--- /dev/null
+++ b/test/container/Dockerfile-OpenSuse-latest
@@ -0,0 +1,13 @@
+FROM registry.opensuse.org/opensuse/tumbleweed-dnf:latest
+
+MAINTAINER https://github.com/dracutdevs/dracut
+
+# Install needed packages for the dracut CI container
+RUN dnf -y install --setopt=install_weak_deps=False \
+ dash asciidoc mdadm lvm2 dmraid cryptsetup nfs-utils nbd dhcp-server \
+ strace libkmod-devel gcc bzip2 xz tar wget rpm-build make git bash-completion \
+ sudo kernel dhcp-client qemu-kvm /usr/bin/qemu-system-$(uname -m) e2fsprogs \
+ tcpdump iproute iputils kbd NetworkManager btrfsprogs tgt dbus-broker \
+ iscsiuio open-iscsi which ShellCheck shfmt procps pigz parted squashfs ntfsprogs \
+ multipath-tools util-linux-systemd systemd-boot \
+ && dnf -y remove dracut && dnf -y update && dnf clean all
diff --git a/test/container/Dockerfile-Ubuntu b/test/container/Dockerfile-Ubuntu
new file mode 100644
index 0000000..0fb7f20
--- /dev/null
+++ b/test/container/Dockerfile-Ubuntu
@@ -0,0 +1,63 @@
+FROM docker.io/ubuntu:latest
+
+MAINTAINER https://github.com/dracutdevs/dracut
+
+# Install needed packages for the dracut CI container
+# The Linux kernel is only readable by root. See https://launchpad.net/bugs/759725
+RUN apt-get update -y -qq && apt-get upgrade -y -qq && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends -o Dpkg::Use-Pty=0 \
+ asciidoc \
+ astyle \
+ btrfs-progs \
+ busybox-static \
+ bzip2 \
+ ca-certificates \
+ console-setup \
+ cpio \
+ cryptsetup \
+ curl \
+ dmraid \
+ docbook \
+ docbook-xml \
+ docbook-xsl \
+ fdisk \
+ g++ \
+ gawk \
+ git \
+ iputils-arping \
+ iputils-ping \
+ isc-dhcp-client \
+ isc-dhcp-server \
+ kmod \
+ less \
+ libdmraid-dev \
+ libkmod-dev \
+ linux-image-generic \
+ lvm2 \
+ make \
+ mdadm \
+ multipath-tools \
+ nbd-client \
+ nbd-server \
+ network-manager \
+ nfs-kernel-server \
+ ntfs-3g \
+ open-iscsi \
+ ovmf \
+ parted \
+ pigz \
+ pkg-config \
+ procps \
+ qemu-kvm \
+ shellcheck \
+ squashfs-tools \
+ strace \
+ sudo \
+ systemd \
+ tcpdump \
+ tgt \
+ thin-provisioning-tools \
+ vim \
+ wget \
+ && apt-get clean \
+ && chmod a+r /boot/vmlinu*
diff --git a/test/run-qemu b/test/run-qemu
new file mode 100755
index 0000000..41d458a
--- /dev/null
+++ b/test/run-qemu
@@ -0,0 +1,74 @@
+#!/bin/bash
+# Check which virtualization technology to use
+# We prefer kvm, kqemu, userspace in that order.
+
+export PATH=/usr/sbin:/usr/bin:/sbin:/bin
+ARCH="${ARCH-$(uname -m)}"
+QEMU_CPU="${QEMU_CPU:-max}"
+
+[[ -x /usr/bin/qemu ]] && BIN=/usr/bin/qemu && ARGS=(-cpu "$QEMU_CPU")
+(lsmod | grep -q '^kqemu ') && BIN=/usr/bin/qemu && ARGS=(-kernel-kqemu -cpu host)
+[[ -z ${NO_KVM-} && -c /dev/kvm && -x /usr/bin/kvm ]] && BIN=/usr/bin/kvm && ARGS=(-cpu host)
+[[ -z ${NO_KVM-} && -c /dev/kvm && -x /usr/bin/qemu-kvm ]] && BIN=/usr/bin/qemu-kvm && ARGS=(-cpu host)
+[[ -z ${NO_KVM-} && -c /dev/kvm && -x /usr/libexec/qemu-kvm ]] && BIN=/usr/libexec/qemu-kvm && ARGS=(-cpu host)
+[[ -x "/usr/bin/qemu-system-${ARCH}" ]] && BIN="/usr/bin/qemu-system-${ARCH}" && ARGS=(-cpu "$QEMU_CPU")
+[[ -z ${NO_KVM-} && -c /dev/kvm && -x "/usr/bin/qemu-system-${ARCH}" ]] && BIN="/usr/bin/qemu-system-${ARCH}" && ARGS=(-enable-kvm -cpu host)
+
+[[ $BIN ]] || {
+ echo "Could not find a working KVM or QEMU to test with!" >&2
+ echo "Please install kvm or qemu." >&2
+ exit 1
+}
+
+case "$ARCH" in
+ aarch64 | arm64)
+ ARGS+=(-M "virt,gic-version=max")
+ ;;
+ amd64 | i?86 | x86_64)
+ ARGS+=(-M q35)
+ ;;
+ arm | armhf | armv7l)
+ ARGS+=(-M virt)
+ ;;
+ ppc64el | ppc64le)
+ ARGS+=(-M "cap-ccf-assist=off,cap-cfpc=broken,cap-ibs=broken,cap-sbbc=broken")
+ ;;
+esac
+
+# Provide rng device sourcing the hosts /dev/urandom and other standard parameters
+ARGS+=(-smp 2 -m 1024 -nodefaults -vga none -display none -no-reboot -device virtio-rng-pci)
+
+if ! [[ $* == *-daemonize* ]] && ! [[ $* == *-daemonize* ]]; then
+ ARGS+=(-serial stdio)
+fi
+
+KVERSION=${KVERSION-$(uname -r)}
+
+VMLINUZ="/lib/modules/${KVERSION}/vmlinuz"
+if ! [ -f "$VMLINUZ" ]; then
+ VMLINUZ="/lib/modules/${KVERSION}/vmlinux"
+fi
+
+if ! [ -f "$VMLINUZ" ]; then
+ [[ -f /etc/machine-id ]] && read -r MACHINE_ID < /etc/machine-id
+
+ if [[ $MACHINE_ID ]] && { [[ -d /boot/${MACHINE_ID} ]] || [[ -L /boot/${MACHINE_ID} ]]; }; then
+ VMLINUZ="/boot/${MACHINE_ID}/$KVERSION/linux"
+ elif [ -f "/boot/vmlinuz-${KVERSION}" ]; then
+ VMLINUZ="/boot/vmlinuz-${KVERSION}"
+ elif [ -f "/boot/vmlinux-${KVERSION}" ]; then
+ VMLINUZ="/boot/vmlinux-${KVERSION}"
+ else
+ echo "Could not find a Linux kernel version $KVERSION to test with!" >&2
+ echo "Please install linux." >&2
+ exit 1
+ fi
+fi
+
+# only set -kernel if -initrd is specified
+if [[ $* == *-initrd* ]]; then
+ ARGS+=(-kernel "$VMLINUZ")
+fi
+
+echo "${0##*/}: $BIN ${ARGS[*]@Q} ${*@Q}"
+exec "$BIN" "${ARGS[@]}" "$@"
diff --git a/test/test-functions b/test/test-functions
new file mode 100644
index 0000000..f76d930
--- /dev/null
+++ b/test/test-functions
@@ -0,0 +1,206 @@
+#!/bin/bash
+PATH=/usr/sbin:/usr/bin:/sbin:/bin
+export PATH
+
+# shellcheck disable=SC1090
+[[ -e .testdir${TEST_RUN_ID:+-$TEST_RUN_ID} ]] && . .testdir${TEST_RUN_ID:+-$TEST_RUN_ID}
+if [[ -z $TESTDIR ]] || [[ ! -d $TESTDIR ]]; then
+ TESTDIR=$(mktemp -d -p "/var/tmp" -t dracut-test.XXXXXX)
+fi
+echo "TESTDIR=\"$TESTDIR\"" > .testdir${TEST_RUN_ID:+-$TEST_RUN_ID}
+export TESTDIR
+
+KVERSION=${KVERSION-$(uname -r)}
+
+[ -z "$USE_NETWORK" ] && USE_NETWORK="network-legacy"
+
+if [[ -z $basedir ]]; then basedir="$(realpath ../..)"; fi
+
+DRACUT=${DRACUT-${basedir}/dracut.sh}
+PKGLIBDIR=${PKGLIBDIR-$basedir}
+
+test_dracut() {
+ TEST_DRACUT_ARGS+=" --local --no-hostonly --no-early-microcode --add test --kver $KVERSION"
+
+ # shellcheck disable=SC2162
+ IFS=' ' read -a TEST_DRACUT_ARGS_ARRAY <<< "$TEST_DRACUT_ARGS"
+
+ "$DRACUT" "$@" \
+ --kernel-cmdline "panic=1 oops=panic softlockup_panic=1 systemd.crash_reboot selinux=0 console=ttyS0,115200n81 $DEBUGFAIL" \
+ "${TEST_DRACUT_ARGS_ARRAY[@]}" || return 1
+}
+
+command -v test_check &> /dev/null || test_check() {
+ :
+}
+
+command -v test_cleanup &> /dev/null || test_cleanup() {
+ :
+}
+
+# terminal sequence to set color to a 'success' color (currently: green)
+function SETCOLOR_SUCCESS() { echo -en '\033[0;32m'; }
+# terminal sequence to set color to a 'failure' color (currently: red)
+function SETCOLOR_FAILURE() { echo -en '\033[0;31m'; }
+# terminal sequence to set color to a 'warning' color (currently: yellow)
+function SETCOLOR_WARNING() { echo -en '\033[0;33m'; }
+# terminal sequence to reset to the default color.
+function SETCOLOR_NORMAL() { echo -en '\033[0;39m'; }
+
+COLOR_SUCCESS='\033[0;32m'
+COLOR_FAILURE='\033[0;31m'
+COLOR_WARNING='\033[0;33m'
+COLOR_NORMAL='\033[0;39m'
+
+check_root() {
+ if ((EUID != 0)); then
+ SETCOLOR_FAILURE
+ echo "Tests must be run as root! Please use 'sudo'."
+ SETCOLOR_NORMAL
+ exit 1
+ fi
+}
+
+# generate qemu arguments for named raw disks
+#
+# qemu_add_drive_args <index> <args> <filename> <id-name> [<bootindex>]
+#
+# index: name of the index variable (set to 0 at start)
+# args: name of the argument array variable (set to () at start)
+# filename: filename of the raw disk image
+# id-name: name of the disk in /dev/disk/by-id -> /dev/disk/by-id/ata-disk_$name
+# size: optional file size in MiB (0 implies size is not set)
+# bootindex: optional bootindex number
+#
+# to be used later with `qemu … "${args[@]}" …`
+# The <index> variable will be incremented each time the function is called.
+#
+# can't be easier than this :-/
+#
+# # EXAMPLES
+# ```
+# declare -a disk_args=()
+# declare -i disk_index=0
+# qemu_add_drive_args disk_index disk_args "$TESTDIR"/root.ext3 root 0 1
+# qemu_add_drive_args disk_index disk_args "$TESTDIR"/client.img client
+# qemu_add_drive_args disk_index disk_args "$TESTDIR"/iscsidisk2.img iscsidisk2
+# qemu_add_drive_args disk_index disk_args "$TESTDIR"/iscsidisk3.img iscsidisk3
+# qemu "${disk_args[@]}"
+# ```
+qemu_add_drive_args() {
+ local index=${!1}
+ local file=$3
+ local name=${4:-$index}
+ local size=${5:-0}
+ local bootindex=$6
+
+ if [ "${size}" -ne 0 ]; then
+ dd if=/dev/zero of="${file}" bs=1MiB count="${size}"
+ fi
+
+ eval "${2}"'+=(' \
+ -drive "if=none,format=raw,file=${file},id=drive-sata${index}" \
+ -device "ide-hd,bus=ide.${index},drive=drive-sata${index},id=sata${index},${bootindex:+bootindex=$bootindex,}model=disk,serial=${name}" \
+ ')'
+
+ # shellcheck disable=SC2219
+ let "${1}++"
+}
+
+test_marker_reset() {
+ dd if=/dev/zero of="$TESTDIR"/marker.img bs=1MiB count=1
+}
+
+test_marker_check() {
+ local marker=${1:-dracut-root-block-success}
+ local file=${2:-marker.img}
+
+ grep -U --binary-files=binary -F -m 1 -q "$marker" "$TESTDIR/$file"
+ return $?
+}
+
+while (($# > 0)); do
+ case $1 in
+ --run)
+ check_root
+ echo "TEST RUN: $TEST_DESCRIPTION"
+ test_check && test_run
+ exit $?
+ ;;
+ --setup)
+ check_root
+ echo "TEST SETUP: $TEST_DESCRIPTION"
+ test_check && test_setup
+ exit $?
+ ;;
+ --clean)
+ echo "TEST CLEANUP: $TEST_DESCRIPTION"
+ test_cleanup
+ rm -fr -- "$TESTDIR"
+ rm -f -- .testdir${TEST_RUN_ID:+-$TEST_RUN_ID}
+ exit $?
+ ;;
+ --all)
+ check_root
+ if ! test_check 2 &> test${TEST_RUN_ID:+-$TEST_RUN_ID}.log; then
+ echo -e "TEST: $TEST_DESCRIPTION " "$COLOR_WARNING" "[SKIPPED]" "$COLOR_NORMAL"
+ exit 0
+ else
+ echo -e "TEST: $TEST_DESCRIPTION " "$COLOR_SUCCESS" "[STARTED]" "$COLOR_NORMAL"
+ fi
+ if [[ $V == "1" ]]; then
+ set -o pipefail
+ (
+ test_setup && test_run
+ ret=$?
+ test_cleanup
+ if ((ret != 0)) && [[ -f "$TESTDIR"/server.log ]]; then
+ mv "$TESTDIR"/server.log ./server${TEST_RUN_ID:+-$TEST_RUN_ID}.log
+ fi
+ rm -fr -- "$TESTDIR"
+ rm -f -- .testdir${TEST_RUN_ID:+-$TEST_RUN_ID}
+ exit $ret
+ ) < /dev/null 2>&1 | tee "test${TEST_RUN_ID:+-$TEST_RUN_ID}.log"
+ elif [[ $V == "2" ]]; then
+ set -o pipefail
+ (
+ test_setup && test_run
+ ret=$?
+ test_cleanup
+ if ((ret != 0)) && [[ -f "$TESTDIR"/server.log ]]; then
+ mv "$TESTDIR"/server.log ./server${TEST_RUN_ID:+-$TEST_RUN_ID}.log
+ fi
+ rm -fr -- "$TESTDIR"
+ rm -f -- .testdir${TEST_RUN_ID:+-$TEST_RUN_ID}
+ exit $ret
+ ) < /dev/null 2>&1 | "$basedir/logtee" "test${TEST_RUN_ID:+-$TEST_RUN_ID}.log"
+ else
+ (
+ test_setup && test_run
+ ret=$?
+ test_cleanup
+ rm -fr -- "$TESTDIR"
+ rm -f -- .testdir${TEST_RUN_ID:+-$TEST_RUN_ID}
+ exit $ret
+ ) < /dev/null > test${TEST_RUN_ID:+-$TEST_RUN_ID}.log 2>&1
+ fi
+ ret=$?
+ set +o pipefail
+ if [ $ret -eq 0 ]; then
+ rm -- test${TEST_RUN_ID:+-$TEST_RUN_ID}.log
+ echo -e "TEST: $TEST_DESCRIPTION " "$COLOR_SUCCESS" "[OK]" "$COLOR_NORMAL"
+ else
+ echo -e "TEST: $TEST_DESCRIPTION " "$COLOR_FAILURE" "[FAILED]" "$COLOR_NORMAL"
+ if [ "$V" == "2" ]; then
+ tail -c 1048576 "$(pwd)/server${TEST_RUN_ID:+-$TEST_RUN_ID}.log" "$(pwd)/test${TEST_RUN_ID:+-$TEST_RUN_ID}.log"
+ echo -e "TEST: $TEST_DESCRIPTION " "$COLOR_FAILURE" "[FAILED]" "$COLOR_NORMAL"
+ else
+ echo "see $(pwd)/test${TEST_RUN_ID:+-$TEST_RUN_ID}.log"
+ fi
+ fi
+ exit $ret
+ ;;
+ *) break ;;
+ esac
+ shift
+done