From b680ff9dd8396a79b20d26966d3016884ddbb450 Mon Sep 17 00:00:00 2001 From: Kroese Date: Fri, 17 Jul 2026 19:16:30 +0200 Subject: [PATCH] feat: Preserve graceful shutdown for interactive console (#1289) --- src/entry.sh | 9 ++++--- src/network.sh | 4 +-- src/power.sh | 71 +++++++++++++++++++++++++++++++++++++++++++++----- src/serial.sh | 40 +++++++++++++++++++--------- src/utils.sh | 8 ++++++ 5 files changed, 107 insertions(+), 25 deletions(-) diff --git a/src/entry.sh b/src/entry.sh index ae91cc3..a00198f 100755 --- a/src/entry.sh +++ b/src/entry.sh @@ -32,14 +32,17 @@ if ! enabled "$SHUTDOWN"; then exec "${cmd[@]}" ${ARGS:+ $ARGS} fi -if [ ! -t 1 ] || [ ! -c /dev/tty ]; then +if ! interactive; then "${cmd[@]}" ${ARGS:+ $ARGS} & else - "${cmd[@]}" ${ARGS:+ $ARGS} /dev/tty & + startConsole + setsid -w "${cmd[@]}" ${ARGS:+ $ARGS} &- 2>/dev/null || true - exec 40>&- 2>/dev/null || true + exec 30<&- || : + exec 40<&- || : ip link set "$TAP" down promisc off &> /dev/null || : ip link delete "$TAP" &> /dev/null || : diff --git a/src/power.sh b/src/power.sh index 4469ede..c8094f4 100644 --- a/src/power.sh +++ b/src/power.sh @@ -10,14 +10,18 @@ set -Eeuo pipefail API_CMD=6 API_HOST="127.0.0.1:$COM_PORT" -# Configure QEMU for graceful shutdown +SHUTDOWN_SKIP=0 +SHUTDOWN_SIGNAL=0 QEMU_END="$QEMU_DIR/qemu.end" +CONSOLE_PID="$QEMU_DIR/console.pid" +CONSOLE_SOCKET="$QEMU_DIR/console.sock" _trap() { local func="$1"; shift local sig + TRAP_PID=$BASHPID for sig; do @@ -88,7 +92,7 @@ forceKillQemu() { cleanupHelpers() { - local pids=( "${HOST_PID:-}" "${WSD_PID:-}" \ + local pids=( "${HOST_PID:-}" "${WSD_PID:-}" "${CONSOLE_PID:-}" \ "${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" ) mKill "${pids[@]}" @@ -98,6 +102,47 @@ cleanupHelpers() { return 0 } +startConsole() { + + local cnt=0 + local pid="" + + rm -f -- "$CONSOLE_SOCKET" "$CONSOLE_PID" + + if ! stty -icanon -echo isig -ixon min 1 time 0 /dev/tty + ) & + + pid=$! + echo "$pid" > "$CONSOLE_PID" + + while [ ! -S "$CONSOLE_SOCKET" ]; do + + if ! isAlive "$pid"; then + rm -f -- "$CONSOLE_PID" + error "Serial console relay exited unexpectedly!" + return 1 + fi + + sleep 0.02 + cnt=$((cnt + 1)) + + if (( cnt > 100 )); then + error "Failed to start serial console relay!" + return 1 + fi + + done + + return 0 +} + finish() { local reason=$1 @@ -198,7 +243,7 @@ waitForShutdown() { local name="$APP" local slp - while (( cnt <= wait_until )); do + while (( cnt <= wait_until && SHUTDOWN_SKIP == 0 )); do sleep 1 & slp=$! @@ -216,7 +261,7 @@ waitForShutdown() { info "Waiting for $name to shut down... ($cnt/$wait_until)" fi - wait "$slp" + wait "$slp" || : (( cnt++ )) done @@ -235,21 +280,29 @@ graceful_shutdown() { code=$(signalCode "$sig") if [ -f "$QEMU_END" ]; then - echo && info "Received $1 signal while already shutting down..." + + if (( code == 130 && SHUTDOWN_SIGNAL == code )); then + SHUTDOWN_SKIP=1 + echo && info "Received SIGINT again, forcing shutdown..." return + fi + + echo && info "Received $sig signal while already shutting down..." return fi set +e start=$SECONDS + SHUTDOWN_SIGNAL=$code + touch "$QEMU_END" - echo && info "Received $1 signal, sending shutdown command..." + echo && info "Received $sig signal, sending shutdown command..." if ! readQemuPid pid; then warn "QEMU PID file ($QEMU_PID) does not exist?" finish "$code" fi - if ! isAlive "$pid"; then + if [ -z "$pid" ] || ! isAlive "$pid"; then warn "QEMU process with PID $pid does not exist?" finish "$code" fi @@ -264,6 +317,10 @@ graceful_shutdown() { ! enabled "$SHUTDOWN" && return 0 [ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT" +if interactive; then + _trap graceful_shutdown SIGINT +fi + _trap graceful_shutdown SIGTERM SIGHUP SIGABRT SIGQUIT return 0 diff --git a/src/serial.sh b/src/serial.sh index 547e7d9..5a2a078 100644 --- a/src/serial.sh +++ b/src/serial.sh @@ -52,27 +52,25 @@ buildHostArguments() { startHostBinary() { - local pid="" + local pid if enabled "$HOST_DEBUG"; then - - { - set -x - ./host.bin "${HOST_ARGS[@]}" & - pid=$! - { set +x; } 2>/dev/null - } 2>&1 - - echo "$pid" > "$HOST_PID" + set -x + ./host.bin "${HOST_ARGS[@]}" & + { set +x; } 2>/dev/null + pid=$! echo else ./host.bin "${HOST_ARGS[@]}" >/dev/null & - echo "$!" > "$HOST_PID" + pid=$! fi + echo "$pid" > "$HOST_PID" + return 0 } + waitForPort() { local port="$1" @@ -90,13 +88,29 @@ waitForPort() { configureSerialPorts() { - # Configure serial ports - SERIAL_OPTS="-serial mon:stdio \ + if enabled "${SHUTDOWN:-Y}" && interactive; then + + CONSOLE_SOCKET="$QEMU_DIR/console.sock" + MONITOR_SOCKET="$QEMU_DIR/monitor.sock" + + SERIAL_OPTS="-chardev socket,id=console0,path=$CONSOLE_SOCKET,reconnect-ms=1000 \ + -serial chardev:console0 \ + -chardev socket,id=monitor0,path=$MONITOR_SOCKET,server=on,wait=off \ + -mon chardev=monitor0,mode=readline" + + else + + SERIAL_OPTS="-serial mon:stdio" + + fi + + SERIAL_OPTS+=" \ -device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \ -chardev socket,id=charchannel0,host=127.0.0.1,port=$CHR_PORT,reconnect=10 \ -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel" return 0 + } validateHostMac diff --git a/src/utils.sh b/src/utils.sh index 4d3cd64..3ca719f 100644 --- a/src/utils.sh +++ b/src/utils.sh @@ -7,6 +7,14 @@ info () { printf "%b%s%b" "\E[1;34m❯ \E[1;36m" "${1:-}" "\E[0m\n"; } error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: ${1:-}" "\E[0m\n" >&2; } warn () { printf "%b%s%b" "\E[1;31m❯ " "Warning: ${1:-}" "\E[0m\n" >&2; } +interactive() { + + [ -t 1 ] && + [ -c /dev/tty ] && + : 2>/dev/null /dev/tty + +} + strip() { local value="${1:-}"