Skip to content

Commit

Permalink
test: add a couple of tests for systemd-pstore
Browse files Browse the repository at this point in the history
Based on:
    - 6858e32d730fd5574eaa3d7fbf4cb12aacaea336
    - edea0d6ac57610b7af603b833b19a846327e3638

Related: #2190151
  • Loading branch information
mrc0mmand authored and systemd-rhel-bot committed May 4, 2023
1 parent 7a76b2a commit 06b0704
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 1 deletion.
1 change: 1 addition & 0 deletions test/TEST-74-AUX-UTILS/Makefile
48 changes: 48 additions & 0 deletions test/TEST-74-AUX-UTILS/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="Tests for auxiliary utilities"

. $TEST_BASE_DIR/test-functions

test_setup() {
create_empty_image
mkdir -p $TESTDIR/root
mount ${LOOPDEV}p1 $TESTDIR/root

(
LOG_LEVEL=5
eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)

setup_basic_environment

# mask some services that we do not want to run in these tests
ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service

# setup the testsuite service
cat >$initdir/etc/systemd/system/testsuite.service <<EOF
[Unit]
Description=Testsuite service
[Service]
ExecStart=/bin/bash -x /testsuite.sh
Type=oneshot
StandardOutput=tty
StandardError=tty
NotifyAccess=all
EOF
cp testsuite*.sh $initdir/

setup_testsuite
) || return 1
setup_nspawn_root

ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
}

do_test "$@"
218 changes: 218 additions & 0 deletions test/TEST-74-AUX-UTILS/testsuite.pstore.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail

if systemd-detect-virt -cq; then
echo "Running in a container, skipping the systemd-pstore test..."
exit 0
fi

DUMMY_DMESG_1="$(mktemp)"
cat >"$DUMMY_DMESG_1" <<\EOF
6,17159,5340096332127,-;usb 1-4: USB disconnect, device number 124
6,17160,5340109662397,-;input: WH-1000XM3 (AVRCP) as /devices/virtual/input/input293
6,17161,5343126458360,-;loop0: detected capacity change from 0 to 3145728
6,17162,5343126766065,-; loop0: p1 p2
6,17163,5343126815038,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
6,17164,5343158037334,-;EXT4-fs (loop0p1): unmounting filesystem.
6,17165,5343158072598,-;loop0: detected capacity change from 0 to 3145728
6,17166,5343158073563,-; loop0: p1 p2
6,17167,5343158074325,-; loop0: p1 p2
6,17168,5343158140859,-;EXT4-fs (loop0p1): mounted filesystem with ordered data mode. Quota mode: none.
6,17169,5343158182977,-;EXT4-fs (loop0p1): unmounting filesystem.
6,17170,5343158700241,-;loop0: detected capacity change from 0 to 3145728
6,17171,5343158700439,-; loop0: p1 p2
6,17172,5343158701120,-; loop0: p1 p2
EOF

DUMMY_DMESG_2="$(mktemp)"
cat >"$DUMMY_DMESG_2" <<\EOF
Nechť již hříšné saxofony ďáblů rozezvučí síň úděsnými tóny waltzu, tanga a quickstepu.
Příliš žluťoučký kůň úpěl ďábelské ódy.
Zvlášť zákeřný učeň s ďolíčky běží podél zóny úlů.
Vyciď křišťálový nůž, ó učiň úděsné líbivým!
Loď čeří kýlem tůň obzvlášť v Grónské úžině
Ó, náhlý déšť již zvířil prach a čilá laň teď běží s houfcem gazel k úkrytům.
Vypätá dcéra grófa Maxwella s IQ nižším ako kôň núti čeľaď hrýzť hŕbu jabĺk.
Kŕdeľ šťastných ďatľov učí pri ústí Váhu mĺkveho koňa obhrýzať kôru a žrať čerstvé mäso.
Stróż pchnął kość w quiz gędźb vel fax myjń.
Portez ce vieux whisky au juge blond qui fume!
EOF

file_count() { find "${1:?}" -type f | wc -l; }
file_size() { wc -l <"${1:?}"; }
random_efi_timestamp() { printf "%0.10d" "$((1000000000 + RANDOM))"; }

# The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER,
# record id, a 64-bit number.
#
# Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg.

prepare_efi_logs() {
local file="${1:?}"
local timestamp="${2:?}"
local chunk count filename

# For the EFI backend, the 3 least significant digits of record id encodes a
# "count" number, the next 2 least significant digits for the dmesg part
# (chunk) number, and the remaining digits as the timestamp. See
# linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write().
count="$(file_size "$file")"
chunk=0
# The sed in the process substitution below just reverses the file
while read -r line; do
filename="$(printf "dmesg-efi-%0.10d%0.2d%0.3d" "$timestamp" "$chunk" "$count")"
echo "$line" >"/sys/fs/pstore/$filename"
chunk=$((chunk + 1))
done < <(sed '1!G;h;$!d' "$file")

if [[ "$chunk" -eq 0 ]]; then
echo >&2 "No dmesg-efi files were created"
exit 1
fi
}

prepare_erst_logs() {
local file="${1:?}"
local start_id="${2:?}"
local id filename

# For the ERST backend, the record is a monotonically increasing number, seeded as
# a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer().
id="$start_id"
# The sed in the process substitution below just reverses the file
while read -r line; do
filename="$(printf "dmesg-erst-%0.16d" "$id")"
echo "$line" >"/sys/fs/pstore/$filename"
id=$((id + 1))
done < <(sed '1!G;h;$!d' "$file")

if [[ "$id" -eq "$start_id" ]]; then
echo >&2 "No dmesg-erst files were created"
exit 1
fi

# ID of the last dmesg file will be the ID of the erst subfolder
echo "$((id - 1))"
}

prepare_pstore_config() {
local storage="${1:?}"
local unlink="${2:?}"

systemctl stop systemd-pstore

rm -fr /sys/fs/pstore/* /var/lib/systemd/pstore/*

mkdir -p /run/systemd/pstore.conf.d
cat >"/run/systemd/pstore.conf.d/99-test.conf" <<EOF
[PStore]
Storage=$storage
Unlink=$unlink
EOF

systemd-analyze cat-config systemd/pstore.conf | grep "$storage"
systemd-analyze cat-config systemd/pstore.conf | grep "$unlink"
}

start_pstore() {
JOURNAL_CURSOR="$(journalctl -q -n 0 --show-cursor | awk '{ print $3 }')"
systemctl start systemd-pstore
journalctl --sync
}

# To avoid having to depend on the VM providing the pstore, let's simulate
# it using a simple bind mount
PSTORE_DIR="$(mktemp -d)"
mount --bind "${PSTORE_DIR:?}" "/sys/fs/pstore"

# systemd-pstore is a no-op with Storage=none
for unlink in yes no; do
: "Backend: N/A; Storage: none; Unlink: $unlink"
timestamp="$(random_efi_timestamp)"
prepare_pstore_config "none" "$unlink"
prepare_efi_logs "$DUMMY_DMESG_1" "$timestamp"
old_count="$(file_count /sys/fs/pstore/)"
start_pstore
[[ "$(file_count /sys/fs/pstore)" -ge "$old_count" ]]
[[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]

: "Backend: EFI; Storage: external; Unlink: $unlink"
timestamp="$(random_efi_timestamp)"
prepare_pstore_config "external" "$unlink"
prepare_efi_logs "$DUMMY_DMESG_1" "$timestamp"
[[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
start_pstore
[[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
[[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
# We always log to journal
diff "$DUMMY_DMESG_1" <(journalctl -o json --after-cursor="${JOURNAL_CURSOR:?}" | jq -r 'select(.FILE) | .FILE' | sed '/^$/d')
filename="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp" "$(file_size "$DUMMY_DMESG_1")")"
diff "$DUMMY_DMESG_1" "$filename"

: "Backend: EFI; Storage: external; Unlink: $unlink; multiple dmesg files"
timestamp_1="$(random_efi_timestamp)"
timestamp_2="$((timestamp_1 + 1))"
prepare_pstore_config "external" "$unlink"
prepare_efi_logs "$DUMMY_DMESG_1" "$timestamp_1"
prepare_efi_logs "$DUMMY_DMESG_2" "$timestamp_2"
# Add one "random" (non-dmesg) file as well
echo "hello world" >/sys/fs/pstore/foo.bar
[[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
start_pstore
[[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
[[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
filename_1="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp_1" "$(file_size "$DUMMY_DMESG_1")")"
diff "$DUMMY_DMESG_1" "$filename_1"
filename_2="$(printf "/var/lib/systemd/pstore/%s/%0.3d/dmesg.txt" "$timestamp_2" "$(file_size "$DUMMY_DMESG_2")")"
diff "$DUMMY_DMESG_2" "$filename_2"
grep "hello world" "/var/lib/systemd/pstore/foo.bar"

: "Backend: EFI; Storage: journal; Unlink: $unlink"
timestamp="$(random_efi_timestamp)"
prepare_pstore_config "journal" "$unlink"
prepare_efi_logs "$DUMMY_DMESG_1" "$timestamp"
[[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
start_pstore
[[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
[[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
diff "$DUMMY_DMESG_1" <(journalctl -o json --after-cursor="${JOURNAL_CURSOR:?}" | jq -r 'select(.FILE) | .FILE' | sed '/^$/d')

: "Backend: ERST; Storage: external; Unlink: $unlink"
prepare_pstore_config "external" "$unlink"
last_id="$(prepare_erst_logs "$DUMMY_DMESG_1" 0)"
[[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
start_pstore
[[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
[[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
# We always log to journal
diff "$DUMMY_DMESG_1" <(journalctl -o json --after-cursor="${JOURNAL_CURSOR:?}" | jq -r 'select(.FILE) | .FILE' | sed '/^$/d')
filename="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id")"
diff "$DUMMY_DMESG_1" "$filename"

: "Backend: ERST; Storage: external; Unlink: $unlink; multiple dmesg files"
prepare_pstore_config "external" "$unlink"
last_id_1="$(prepare_erst_logs "$DUMMY_DMESG_1" 0)"
last_id_2="$(prepare_erst_logs "$DUMMY_DMESG_2" "$((last_id_1 + 10))")"
# Add one "random" (non-dmesg) file as well
echo "hello world" >/sys/fs/pstore/foo.bar
[[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
start_pstore
[[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
[[ "$(file_count /var/lib/systemd/pstore/)" -ne 0 ]]
filename_1="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id_1")"
diff "$DUMMY_DMESG_1" "$filename_1"
filename_2="$(printf "/var/lib/systemd/pstore/%0.16d/dmesg.txt" "$last_id_2")"
diff "$DUMMY_DMESG_2" "$filename_2"
grep "hello world" "/var/lib/systemd/pstore/foo.bar"

: "Backend: ERST; Storage: journal; Unlink: $unlink"
prepare_pstore_config "journal" "$unlink"
last_id="$(prepare_erst_logs "$DUMMY_DMESG_1" 0)"
[[ "$unlink" == yes ]] && exp_count=0 || exp_count="$(file_count /sys/fs/pstore/)"
start_pstore
[[ "$(file_count /sys/fs/pstore)" -ge "$exp_count" ]]
[[ "$(file_count /var/lib/systemd/pstore/)" -eq 0 ]]
diff "$DUMMY_DMESG_1" <(journalctl -o json --after-cursor="${JOURNAL_CURSOR:?}" | jq -r 'select(.FILE) | .FILE' | sed '/^$/d')
done
14 changes: 14 additions & 0 deletions test/TEST-74-AUX-UTILS/testsuite.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail

: >/failed

for script in "${0%.sh}".*.sh; do
echo "Running $script"
"./$script"
done

touch /testok
rm /failed
2 changes: 1 addition & 1 deletion test/test-functions
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fi

PATH_TO_INIT=$ROOTLIBDIR/systemd

BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs env mktemp mountpoint useradd userdel timeout"
BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs env mktemp mountpoint useradd userdel timeout jq wc awk diff"
DEBUGTOOLS="df free ls stty cat ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find"

STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
Expand Down

0 comments on commit 06b0704

Please sign in to comment.