diff --git a/.github/scripts/test_sysupgrade.sh b/.github/scripts/test_sysupgrade.sh index 4323ffe7d2..7df617ea55 100755 --- a/.github/scripts/test_sysupgrade.sh +++ b/.github/scripts/test_sysupgrade.sh @@ -4721,6 +4721,26 @@ else bad "reboot_system must release the watchdog before it reboots (rel=$relw reboot=$rbtw)" fi +# A boot that never finished: init reads SIGQUIT only after rcS exits, so only +# init's own rcS is ended, and a process merely named like it is left alone. +RB="$SB/rbproc"; rm -rf "$RB" +mkdir -p "$RB/700" "$RB/701" "$RB/702" +printf '/bin/sh\0/etc/init.d/rcS\0' > "$RB/700/cmdline"; printf 'PPid:\t1\n' > "$RB/700/status" +printf '/bin/sh\0/etc/init.d/rcS\0' > "$RB/701/cmdline"; printf 'PPid:\t700\n' > "$RB/701/status" +printf '/bin/sh\0/root/led.sh\0' > "$RB/702/cmdline"; printf 'PPid:\t1\n' > "$RB/702/status" +rb_out=$(WDOG_PROC="$RB" dash -c ' + busybox() { shift; echo "KILLED $*"; } + echo_c() { :; } + '"$(sed -n '/^release_boot_scripts()/,/^}/p' "$SRC")"' + release_boot_scripts' 3>/dev/null) +[ "$rb_out" = "KILLED -KILL 700" ] || [ "$rb_out" = "KILLED 700" ] \ + && ok "a stuck boot: only init's own rcS is ended before the hand-off" \ + || bad "release_boot_scripts ended the wrong processes: [$rb_out]" +rm -rf "$RB" +grep -q '^ release_boot_scripts$' "$SRC" \ + && ok "hand_off_pid1 releases the boot scripts before signalling init" \ + || bad "hand_off_pid1 must call release_boot_scripts" + grep -q '^WDOG=${WDOG:-/dev/watchdog}' "$SRC" && grep -q '^WDOG_PROC=${WDOG_PROC:-/proc}' "$SRC" \ && ok "both watchdog paths are overridable, so this suite never scans the real /proc" \ || bad "WDOG and WDOG_PROC must be overridable" diff --git a/general/overlay/usr/sbin/sysupgrade b/general/overlay/usr/sbin/sysupgrade index a0382124db..c943cf9cd1 100755 --- a/general/overlay/usr/sbin/sysupgrade +++ b/general/overlay/usr/sbin/sysupgrade @@ -1,6 +1,6 @@ #!/bin/sh # OpenIPC.org | 2025 -scr_version=1.0.83 +scr_version=1.0.84 args="$@" LOCK_FILE=/tmp/sysupgrade.lock @@ -2672,6 +2672,27 @@ stage_handoff() { [ -x "$RAM_ROOT/sbin/init" ] } +# Let init get past a boot that never finished. +# +# busybox init looks at its signals only between inittab's stages, and the +# ::sysinit: stage lasts until rcS exits. A foreground loop in /etc/rc.local +# (S99 runs it from rcS) therefore holds SIGQUIT unread for as long as the +# camera is up: the hand-off waits out handoff_wait, reboots with nothing +# written, and every later attempt does the same. init is waiting on rcS's pid +# alone, so ending rcS lets it reach the signal; what rcS left running is killed +# by the hand-off along with everything else. +release_boot_scripts() { + local p cmd + for p in "$WDOG_PROC"/[0-9]*; do + cmd=$(tr '\0' ' ' < "$p/cmdline" 2>&3) || continue + case "$cmd" in *'/etc/init.d/rcS'*) ;; *) continue ;; esac + [ "1" = "$(awk '/^PPid:/ { print $2; exit }' "$p/status" 2>&3)" ] || continue + echo_c 33 "The boot scripts have not finished (often a command in /etc/rc.local running in the foreground), and PID 1 cannot take over until they do: ending them." + busybox kill -KILL "${p##*/}" 2>&3 + done + return 0 +} + # Hand PID 1 to the ramfs, and never come back. # # pivot_root has already moved PID 1's root and cwd here along with ours. What @@ -2700,6 +2721,7 @@ hand_off_pid1() { fi echo_c 33 "Progress also goes to the serial console. The camera reboots by itself when it is done." sync + release_boot_scripts busybox kill -QUIT 1 sleep "${handoff_wait:-30}" echo_c 31 "PID 1 did not hand over; rebooting (nothing was flashed)"