From dbd7d3b7ee05f99772274c07138b6929f586856c Mon Sep 17 00:00:00 2001 From: AI Dev Date: Sun, 8 Nov 2026 12:47:40 +0000 Subject: [PATCH] sysupgrade: a boot that never finished no longer blocks the update busybox init reads its signals only between inittab stages, and the sysinit stage lasts until rcS exits. A foreground loop in /etc/rc.local keeps rcS alive, so the SIGQUIT that hands PID 1 to the RAM root is never read. The updater waits 30 s and reboots with nothing written, and every later attempt does the same. Just before the signal, say so and end PID 1's rcS child. init is waiting on that pid alone, so it moves on and takes the signal. What rcS left running would have been killed by the hand-off anyway. The 30 s guard and its reboot stay as the backstop. --- .github/scripts/test_sysupgrade.sh | 20 ++++++++++++++++++++ general/overlay/usr/sbin/sysupgrade | 24 +++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) 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)"