feat: Preserve graceful shutdown for interactive console (#1289)

This commit is contained in:
Kroese
2026-07-17 19:16:30 +02:00
committed by GitHub
parent 5c889b272d
commit b680ff9dd8
5 changed files with 107 additions and 25 deletions
+6 -3
View File
@@ -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 >/dev/tty &
startConsole
setsid -w "${cmd[@]}" ${ARGS:+ $ARGS} </dev/null &
fi
pid=$!
rc=0
wait $! || rc=$?
wait "$pid" || rc=$?
[ -f "$QEMU_END" ] && exit "$rc"
sleep 1 & wait $!
+2 -2
View File
@@ -1568,8 +1568,8 @@ closeInterfaces() {
local pids=( "$PASST_PID" "$DNSMASQ_PID" )
mKill "${pids[@]}"
exec 30>&- 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 || :
+64 -7
View File
@@ -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; then
error "Failed to configure serial console terminal!"
return 1
fi
(
trap '' INT QUIT
exec nc -lU "$CONSOLE_SOCKET" </dev/tty >/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
+27 -13
View File
@@ -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
+8
View File
@@ -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 >/dev/tty
}
strip() {
local value="${1:-}"