Blob: scripts/check-c3.exp
Raw · Blame
#!/usr/bin/expect -f
if {$argc != 2} {
puts stderr "usage: check-c3.exp RUNNER MODE"
exit 2
}
set runner [file normalize [lindex $argv 0]]
set mode [lindex $argv 1]
if {$mode ni {enabled disabled recovery missing cycle}} {
puts stderr "invalid C3 check mode: $mode"
exit 2
}
set timeout 45
proc fail {message} {
puts stderr "C3 check failed: $message"
exit 1
}
proc await_exact {value description} {
expect {
-exact $value {
return
}
timeout {
fail "timed out waiting for $description"
}
eof {
fail "QEMU exited while waiting for $description"
}
}
}
proc await_regexp {value description} {
expect {
-re $value {
return
}
timeout {
fail "timed out waiting for $description"
}
eof {
fail "QEMU exited while waiting for $description"
}
}
}
proc await_prompt {} {
send -- "\r"
await_exact "mouse:~# " "the tcsh prompt"
}
proc sendline {line} {
send -- $line
send -- "\r"
}
proc await_clean_exit {action} {
expect {
eof {
set result [wait]
set status [lindex $result 3]
if {$status != 0} {
fail "QEMU exited with status $status after $action"
}
}
timeout {
fail "QEMU did not exit after $action"
}
}
}
proc poweroff {} {
send -- "kill -TERM 1\r"
await_exact "cheesed: received poweroff request" "the poweroff request"
await_exact "cheesed: OpenRC shutdown transition complete" "the shutdown transition"
await_clean_exit "poweroff"
}
log_user 0
spawn -noecho $runner
await_exact "cheesed: C3 bootstrap complete; cheesed is PID 1" "PID 1 confirmation"
if {$mode eq "recovery"} {
await_regexp {required service console_login is not started: status command exited with status [0-9]+; entering recovery console} "the required-service failure"
await_regexp {cheesed: started emergency shell as PID [0-9]+} "the recovery shell"
await_exact "mouse:~# " "the recovery prompt"
sendline {sh -c 'set -- `cat /proc/$PPID/stat`; cat /proc/$4/comm'}
await_exact "cheesed\r" "the recovery shell parent"
await_exact "mouse:~# " "the prompt after the recovery parent check"
poweroff
puts "C3 required-service recovery check passed"
exit 0
}
if {$mode eq "disabled"} {
await_exact "cheesed: enabled optional services: (none)" "disabled ports policy"
} else {
await_exact "cheesed: enabled optional services: mouse_echo" "enabled ports policy"
}
if {$mode eq "missing"} {
await_exact "Service 'mouse_echo' needs non existent service 'absent_port_dependency'" "the missing-dependency diagnostic"
}
await_exact "Starting console_login" "the required console service"
if {$mode eq "cycle"} {
await_exact "ERROR: cannot start cycle_peer as mouse_echo would not start" "the dependency-cycle diagnostic"
}
await_exact "mouse login: root (automatic login)" "the automatic root login"
await_exact "MOUSE 0.1.0 (x86_64)" "the login banner"
if {$mode eq "enabled"} {
await_exact "cheesed: OpenRC default transition complete" "the default transition"
} elseif {$mode eq "disabled"} {
await_exact "cheesed: OpenRC default transition complete" "the default transition"
} else {
await_exact "cheesed: OpenRC default transition complete" "the default transition after rejecting the optional service"
await_regexp {optional service mouse_echo failed: status command exited with status [0-9]+} "the optional-service failure"
}
await_prompt
sendline {id}
await_exact "uid=0(root) gid=0(root) groups=0(root)\r" "the root login identity"
await_exact "mouse:~# " "the prompt after the identity check"
sendline {echo $LOGNAME $USER $SHELL}
await_exact "root root /bin/tcsh\r" "the login environment"
await_exact "mouse:~# " "the prompt after the environment check"
sendline {tty}
await_exact "/dev/ttyS0\r" "the serial login tty"
await_exact "mouse:~# " "the prompt after the tty check"
sendline {sh -c 'set -- `cat /proc/$PPID/stat`; cat /proc/$4/comm'}
await_exact "supervise-daemo\r" "the supervised login parent"
await_exact "mouse:~# " "the prompt after the login parent check"
sendline {sh -c 'test "$(cat /proc/1/comm)" = cheesed && ! command -v rc-update >/dev/null 2>&1'; echo BASE_$status}
await_exact "BASE_0\r" "cheesed as PID 1 with no rc-update command"
await_exact "mouse:~# " "the prompt after the base contract check"
if {$mode eq "missing"} {
sendline {sh -c 'test ! -e /run/mouse-echo.unexpected'; echo ORDERED_$status}
await_exact "ORDERED_0\r" "no partial start after a missing dependency"
await_exact "mouse:~# " "the prompt after the missing-dependency check"
poweroff
puts "C3 missing-dependency check passed"
exit 0
}
if {$mode eq "cycle"} {
sendline {sh -c 'test ! -e /run/mouse-echo.unexpected && test ! -e /run/cycle-peer.unexpected'; echo ORDERED_$status}
await_exact "ORDERED_0\r" "no partial start after a dependency cycle"
await_exact "mouse:~# " "the prompt after the dependency-cycle check"
poweroff
puts "C3 dependency-cycle check passed"
exit 0
}
sendline {service console_login status}
await_exact "status: started" "the required console service status"
await_exact "mouse:~# " "the prompt after the console status"
if {$mode eq "disabled"} {
sendline {sh -c 'test ! -e /run/openrc/runlevels/current/default/mouse_echo'; echo DISABLED_$status}
await_exact "DISABLED_0\r" "the absent ports runlevel member"
await_exact "mouse:~# " "the prompt after the disabled membership check"
sendline {sh -c 'cksum /etc/rc.conf > /run/rc.conf.cksum'; service mouse_echo start}
await_exact "Starting mouse_echo" "the runtime-only ports service start"
await_exact "mouse:~# " "the prompt after the runtime start"
sendline {sh -c 'test "$(cksum /etc/rc.conf)" = "$(cat /run/rc.conf.cksum)"'; echo POLICY_$status}
await_exact "POLICY_0\r" "unchanged persistent policy"
await_exact "mouse:~# " "the prompt after the persistent-policy check"
sendline {service mouse_echo stop}
await_exact "Stopping mouse_echo" "the runtime-only ports service stop"
await_exact "mouse:~# " "the prompt after the runtime stop"
poweroff
puts "C3 disabled ports-service check passed"
exit 0
}
sendline {sh -c 'test -L /run/openrc/runlevels/current/default/mouse_echo'; echo ENABLED_$status}
await_exact "ENABLED_0\r" "the generated ports runlevel member"
await_exact "mouse:~# " "the prompt after the enabled membership check"
sendline {sh -c 'i=0; while [ "$i" -lt 4 ]; do child=`cat /run/openrc/options/mouse_echo/child_pid`; kill -KILL "$child"; i=$((i + 1)); sleep 1; done'; echo EXHAUST_$status}
await_exact "EXHAUST_0\r" "four supervised ports-service crashes"
await_exact "mouse:~# " "the prompt after exhausting respawns"
sendline {service mouse_echo status; echo FAILED_STATUS_$status}
await_exact "status: failed" "the failed ports-service status"
await_exact "FAILED_STATUS_32\r" "the failed status exit code"
await_exact "mouse:~# " "the prompt after failed status"
sendline {service mouse_echo restart}
await_exact "Starting mouse_echo" "administrative ports-service recovery"
await_exact "mouse:~# " "the prompt after administrative recovery"
sendline {set stubborn=`cat /run/openrc/options/mouse_echo/child_pid`; service mouse_echo stop}
await_exact "Stopping mouse_echo" "the forced ports-service stop"
await_exact "mouse:~# " "the prompt after the forced stop"
sendline {sh -c 'test -f /run/mouse-echo.term'; echo TERM_SEEN_$status}
await_exact "TERM_SEEN_0\r" "the ports daemon observing SIGTERM"
await_exact "mouse:~# " "the prompt after the SIGTERM check"
sendline {sh -c '! kill -0 "$1" 2>/dev/null' sh "$stubborn"; echo FORCED_GONE_$status}
await_exact "FORCED_GONE_0\r" "SIGKILL escalation for the ports daemon"
await_exact "mouse:~# " "the prompt after forced termination"
sendline {service mouse_echo start}
await_exact "Starting mouse_echo" "the final ports-service start"
await_exact "mouse:~# " "the prompt after the final start"
sendline {exit}
await_exact "logout\r" "the first login session ending"
await_exact "mouse login: root (automatic login)" "the respawned console login"
await_exact "MOUSE 0.1.0 (x86_64)" "the respawned login banner"
await_exact "mouse:~# " "the respawned tcsh prompt"
sendline {sh -c 'for stat in /proc/[0-9]*/stat; do set -- $(cat "$stat"); test "$3" != Z || exit 1; done'; echo ZOMBIES_$status}
await_exact "ZOMBIES_0\r" "the absence of zombie processes"
await_exact "mouse:~# " "the prompt after the zombie check"
sendline {kill -TERM 1}
await_exact "cheesed: received poweroff request" "the poweroff request"
await_exact "Stopping mouse_echo" "the ports service stopping first"
await_exact "Stopping console_login" "the console service stopping after its dependent"
await_exact "cheesed: OpenRC shutdown transition complete" "the shutdown transition"
await_clean_exit "poweroff"
puts "C3 enabled base-integration check passed"