summaryrefslogtreecommitdiffstats
path: root/recipes-containers/vcontainer
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-04-22 20:17:55 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-04-22 20:17:55 +0000
commit891c00db7ba647d0b68a929ca1ad15b0ba9dc5a1 (patch)
tree15dcbd63c1f9361118fb8532fe8d99a3c45daee7 /recipes-containers/vcontainer
parentb02e400b63849991c1590a3572e711740758583f (diff)
downloadmeta-virtualization-891c00db7ba647d0b68a929ca1ad15b0ba9dc5a1.tar.gz
vcontainer: detach background-process stdio from memres start caller
The memres start operation spawns long-running background processes (host-side idle watchdog and Xen domain monitor) that persist beyond the vrunner.sh script. These processes inherited file descriptors 0/1/2 from the parent shell without redirection. When invoked through a harness capturing output via pipes—such as pytest's subprocess.run(..., capture_output=True)—the inherited pipe write-ends kept the caller's read/communicate() operations blocked until memres stop executed, potentially for up to 30 minutes (IDLE_TIMEOUT default). The fix fully detaches stdio from three background spawners: - vrunner.sh: Watchdog subshell now redirects stdin from /dev/null, stdout/stderr to /dev/null, and uses disown - vrunner-backend-qemu.sh: Adds stdin redirection from /dev/null to existing log file redirections - vrunner-backend-xen.sh: Applies same detachment plus disown for daemon mode; redirects stdin for ephemeral-mode console reader From: Tim Orling <tim.orling@konsulko.com> Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/vcontainer')
-rw-r--r--recipes-containers/vcontainer/files/vrunner-backend-qemu.sh10
-rw-r--r--recipes-containers/vcontainer/files/vrunner-backend-xen.sh16
-rwxr-xr-xrecipes-containers/vcontainer/files/vrunner.sh16
3 files changed, 35 insertions, 7 deletions
diff --git a/recipes-containers/vcontainer/files/vrunner-backend-qemu.sh b/recipes-containers/vcontainer/files/vrunner-backend-qemu.sh
index 87054876..3ea73cc7 100644
--- a/recipes-containers/vcontainer/files/vrunner-backend-qemu.sh
+++ b/recipes-containers/vcontainer/files/vrunner-backend-qemu.sh
@@ -188,10 +188,16 @@ hv_start_vm_background() {
188 local log_file="$2" 188 local log_file="$2"
189 local timeout_val="$3" 189 local timeout_val="$3"
190 190
191 # Fully detach stdio from the invoking shell. In daemon mode this
192 # process outlives vrunner.sh, and if the CLI that invoked us was
193 # wrapped by something that pipes stdout/stderr (e.g. a test harness
194 # using subprocess.run(capture_output=True)), any inherited fd here
195 # would block the parent's read/communicate() call until QEMU exits.
196 # Redirect fd 0 from /dev/null and fd 1/fd 2 to the log file.
191 if [ -n "$timeout_val" ]; then 197 if [ -n "$timeout_val" ]; then
192 timeout $timeout_val $HV_CMD $HV_OPTS -append "$kernel_append" > "$log_file" 2>&1 & 198 timeout $timeout_val $HV_CMD $HV_OPTS -append "$kernel_append" </dev/null > "$log_file" 2>&1 &
193 else 199 else
194 $HV_CMD $HV_OPTS -append "$kernel_append" > "$log_file" 2>&1 & 200 $HV_CMD $HV_OPTS -append "$kernel_append" </dev/null > "$log_file" 2>&1 &
195 fi 201 fi
196 HV_VM_PID=$! 202 HV_VM_PID=$!
197} 203}
diff --git a/recipes-containers/vcontainer/files/vrunner-backend-xen.sh b/recipes-containers/vcontainer/files/vrunner-backend-xen.sh
index 55e87bd1..f490f91c 100644
--- a/recipes-containers/vcontainer/files/vrunner-backend-xen.sh
+++ b/recipes-containers/vcontainer/files/vrunner-backend-xen.sh
@@ -397,13 +397,23 @@ hv_start_vm_background() {
397 # Monitor process: stays alive while domain exists. 397 # Monitor process: stays alive while domain exists.
398 # vcontainer-common.sh checks /proc/$pid → alive means daemon running. 398 # vcontainer-common.sh checks /proc/$pid → alive means daemon running.
399 # When domain dies (xl destroy, guest reboot), monitor exits. 399 # When domain dies (xl destroy, guest reboot), monitor exits.
400 #
401 # Detach the monitor's stdio from the invoking shell: in daemon
402 # mode this process outlives vrunner.sh, and any inherited fd
403 # would keep a pipe-wrapped caller (e.g. subprocess.run with
404 # capture_output=True) blocked in communicate() until the domain
405 # exits. Redirect fd 0/1/2 and disown.
400 local _domname="$HV_DOMNAME" 406 local _domname="$HV_DOMNAME"
401 (while xl list "$_domname" >/dev/null 2>&1; do sleep 10; done) & 407 (while xl list "$_domname" >/dev/null 2>&1; do sleep 10; done) \
408 </dev/null >/dev/null 2>&1 &
402 HV_VM_PID=$! 409 HV_VM_PID=$!
410 disown $! 2>/dev/null || true
403 else 411 else
404 # Ephemeral mode: capture guest console (hvc0) to log file 412 # Ephemeral mode: capture guest console (hvc0) to log file
405 # so the monitoring loop in vrunner.sh can see output markers 413 # so the monitoring loop in vrunner.sh can see output markers.
406 stdbuf -oL xl console "$HV_DOMNAME" >> "$log_file" 2>&1 & 414 # Detach stdin so the background reader doesn't hold the caller's
415 # fd 0 open.
416 stdbuf -oL xl console "$HV_DOMNAME" </dev/null >> "$log_file" 2>&1 &
407 _XEN_CONSOLE_PID=$! 417 _XEN_CONSOLE_PID=$!
408 log "DEBUG" "Console capture started (PID: $_XEN_CONSOLE_PID)" 418 log "DEBUG" "Console capture started (PID: $_XEN_CONSOLE_PID)"
409 fi 419 fi
diff --git a/recipes-containers/vcontainer/files/vrunner.sh b/recipes-containers/vcontainer/files/vrunner.sh
index f1fb4d2b..1744245a 100755
--- a/recipes-containers/vcontainer/files/vrunner.sh
+++ b/recipes-containers/vcontainer/files/vrunner.sh
@@ -1365,7 +1365,18 @@ if [ "$DAEMON_MODE" = "start" ]; then
1365 # Set up port forwards via backend (e.g., iptables for Xen) 1365 # Set up port forwards via backend (e.g., iptables for Xen)
1366 hv_setup_port_forwards 1366 hv_setup_port_forwards
1367 1367
1368 # Start host-side idle watchdog if timeout is set 1368 # Start host-side idle watchdog if timeout is set.
1369 #
1370 # The watchdog is a long-running background subshell that outlives
1371 # vrunner.sh itself. It MUST fully detach from the invoking shell's
1372 # stdio: when the caller (e.g. the vdkr CLI, or a test harness that
1373 # wraps vdkr in subprocess.run(capture_output=True)) reads
1374 # stdout/stderr via pipes, any inherited write-end fd in the
1375 # watchdog keeps those pipes open and blocks the caller's
1376 # communicate()/read until the daemon is stopped (up to
1377 # IDLE_TIMEOUT, default 30 minutes). Redirect all three fds so the
1378 # watchdog holds no descriptors from the caller, and disown it so
1379 # the shell's job table doesn't retain it either.
1369 if [ "$IDLE_TIMEOUT" -gt 0 ] 2>/dev/null; then 1380 if [ "$IDLE_TIMEOUT" -gt 0 ] 2>/dev/null; then
1370 ACTIVITY_FILE="$DAEMON_SOCKET_DIR/activity" 1381 ACTIVITY_FILE="$DAEMON_SOCKET_DIR/activity"
1371 touch "$ACTIVITY_FILE" 1382 touch "$ACTIVITY_FILE"
@@ -1399,7 +1410,8 @@ if [ "$DAEMON_MODE" = "start" ]; then
1399 exit 0 1410 exit 0
1400 fi 1411 fi
1401 done 1412 done
1402 ) & 1413 ) </dev/null >/dev/null 2>&1 &
1414 disown $! 2>/dev/null || true
1403 log "DEBUG" "Started host-side idle watchdog (timeout: ${IDLE_TIMEOUT}s)" 1415 log "DEBUG" "Started host-side idle watchdog (timeout: ${IDLE_TIMEOUT}s)"
1404 fi 1416 fi
1405 1417