summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-02-18 18:20:16 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-02-26 01:05:01 +0000
commit6fe3f4cd1a1f408e0ef6b52b793927984449c9f6 (patch)
treebe2aefe4f3dd6142bf4f918034264d0e2a298234
parentba23ccd3390b7fbebfed641ebfcd978a0ba406dd (diff)
downloadmeta-virtualization-6fe3f4cd1a1f408e0ef6b52b793927984449c9f6.tar.gz
vxn: fix terminal mode hang and enable interactive container support
The containerd shim's Create RPC hung indefinitely because go-runc captures the OCI runtime's stdout via a pipe, and cmd.Wait() blocks until all holders of the pipe's write end close it. The background monitor subshell inherited this pipe fd and held it open, preventing the shim from ever proceeding to ReceiveMaster() or calling Start. Fix by closing inherited stdout/stderr in the terminal-mode monitor with exec >/dev/null before entering the domain poll loop. Non-terminal mode is unaffected because the shim configures IO via FIFO dup2, where cmd.Wait() only waits for process exit. Additional changes for terminal mode support: - vxn-sendtty: set PTY to raw mode (cfmakeraw) before sending fd - vxn-oci-runtime: wait up to 5s for xenconsoled PTY, capture sendtty return code, write persistent debug file to /root/vxn-tty-debug, log every runtime invocation, remove stale debug logging - vxn-init.sh: add [vxn] diagnostic markers for terminal visibility, suppress kernel console messages early in interactive mode - vcontainer-preinit.sh: suppress kernel messages in quiet mode Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
-rw-r--r--recipes-containers/vcontainer/files/vcontainer-preinit.sh4
-rwxr-xr-xrecipes-containers/vcontainer/files/vxn-init.sh13
-rw-r--r--recipes-containers/vcontainer/files/vxn-oci-runtime54
-rw-r--r--recipes-containers/vcontainer/files/vxn-sendtty.c13
4 files changed, 67 insertions, 17 deletions
diff --git a/recipes-containers/vcontainer/files/vcontainer-preinit.sh b/recipes-containers/vcontainer/files/vcontainer-preinit.sh
index 7d4e7072..7c0fa590 100644
--- a/recipes-containers/vcontainer/files/vcontainer-preinit.sh
+++ b/recipes-containers/vcontainer/files/vcontainer-preinit.sh
@@ -35,6 +35,10 @@ log() {
35 [ "$QUIET" = "0" ] && echo "$@" 35 [ "$QUIET" = "0" ] && echo "$@"
36} 36}
37 37
38# Suppress kernel console messages in interactive mode so they don't
39# pollute the terminal (loop device messages bypass loglevel=0)
40[ "$QUIET" = "1" ] && { dmesg -n 1 2>/dev/null || true; }
41
38log "=== vcontainer preinit (squashfs) ===" 42log "=== vcontainer preinit (squashfs) ==="
39 43
40# Wait for block devices to appear 44# Wait for block devices to appear
diff --git a/recipes-containers/vcontainer/files/vxn-init.sh b/recipes-containers/vcontainer/files/vxn-init.sh
index 4b9b630a..c22fa8ea 100755
--- a/recipes-containers/vcontainer/files/vxn-init.sh
+++ b/recipes-containers/vcontainer/files/vxn-init.sh
@@ -343,9 +343,10 @@ exec_in_container() {
343 if [ "$RUNTIME_INTERACTIVE" = "1" ]; then 343 if [ "$RUNTIME_INTERACTIVE" = "1" ]; then
344 # Interactive mode: connect stdin/stdout directly 344 # Interactive mode: connect stdin/stdout directly
345 export TERM=linux 345 export TERM=linux
346 printf '\r\033[K' 346 dmesg -n 1 2>/dev/null || true
347 echo "[vxn] chroot: $rootfs $cmd" > /dev/console 2>/dev/null
347 if [ "$use_sh" = "true" ]; then 348 if [ "$use_sh" = "true" ]; then
348 chroot "$rootfs" /bin/sh -c "cd '$workdir' 2>/dev/null; $cmd" 349 chroot "$rootfs" /bin/sh -c "cd '$workdir' 2>/dev/null; exec $cmd"
349 else 350 else
350 chroot "$rootfs" $cmd 351 chroot "$rootfs" $cmd
351 fi 352 fi
@@ -630,6 +631,13 @@ mount_base_filesystems
630# Check for quiet boot mode 631# Check for quiet boot mode
631check_quiet_boot 632check_quiet_boot
632 633
634# Interactive mode: suppress kernel console messages early (before mounts
635# that trigger loop device messages) and emit markers visible through PTY
636if [ "$QUIET_BOOT" = "1" ]; then
637 dmesg -n 1 2>/dev/null || true
638 echo "[vxn] init" > /dev/console 2>/dev/null
639fi
640
633log "=== vxn Init ===" 641log "=== vxn Init ==="
634log "Version: $VCONTAINER_VERSION" 642log "Version: $VCONTAINER_VERSION"
635 643
@@ -720,6 +728,7 @@ else
720 fi 728 fi
721 729
722 # Execute in container rootfs 730 # Execute in container rootfs
731 [ "$QUIET_BOOT" = "1" ] && echo "[vxn] exec: $EXEC_CMD" > /dev/console 2>/dev/null
723 exec_in_container "$CONTAINER_ROOT" "$EXEC_CMD" 732 exec_in_container "$CONTAINER_ROOT" "$EXEC_CMD"
724fi 733fi
725 734
diff --git a/recipes-containers/vcontainer/files/vxn-oci-runtime b/recipes-containers/vcontainer/files/vxn-oci-runtime
index 6158cddd..57144aea 100644
--- a/recipes-containers/vcontainer/files/vxn-oci-runtime
+++ b/recipes-containers/vcontainer/files/vxn-oci-runtime
@@ -169,14 +169,6 @@ cmd_create() {
169 [ -f "$bundle/config.json" ] || die "create: $bundle/config.json not found" 169 [ -f "$bundle/config.json" ] || die "create: $bundle/config.json not found"
170 170
171 log "CREATE: id=$container_id bundle=$bundle console_socket=$console_socket" 171 log "CREATE: id=$container_id bundle=$bundle console_socket=$console_socket"
172 # Debug: log what the shim gives us
173 log " DEBUG: fd0=$(readlink /proc/$$/fd/0 2>/dev/null) fd1=$(readlink /proc/$$/fd/1 2>/dev/null) fd2=$(readlink /proc/$$/fd/2 2>/dev/null)"
174 log " DEBUG: bundle dir: $(ls -F $bundle/ 2>/dev/null | tr '\n' ' ')"
175 local _taskdir
176 _taskdir=$(dirname "$bundle")
177 log " DEBUG: task dir ($bundle): $(ls -F $bundle/ 2>/dev/null | tr '\n' ' ')"
178 log " DEBUG: parent dir ($_taskdir): $(ls -F $_taskdir/ 2>/dev/null | tr '\n' ' ')"
179 log " DEBUG: all pipes: $(find /run -type p 2>/dev/null | tr '\n' ' ')"
180 172
181 detect_arch 173 detect_arch
182 174
@@ -283,10 +275,17 @@ XENEOF
283 275
284 log " Domain $domname created (paused)" 276 log " Domain $domname created (paused)"
285 277
286 # Get domid and read Xen console PTY from xenstore 278 # Get domid and read Xen console PTY from xenstore.
279 # xenconsoled may not have created the PTY yet — wait for it.
287 local domid pty_path 280 local domid pty_path
288 domid=$(xl domid "$domname" 2>/dev/null) || die "create: failed to get domid for $domname" 281 domid=$(xl domid "$domname" 2>/dev/null) || die "create: failed to get domid for $domname"
289 pty_path=$(xenstore-read "/local/domain/$domid/console/tty" 2>/dev/null) || true 282 pty_path=""
283 local _try
284 for _try in 1 2 3 4 5 6 7 8 9 10; do
285 pty_path=$(xenstore-read "/local/domain/$domid/console/tty" 2>/dev/null) || true
286 [ -n "$pty_path" ] && break
287 sleep 0.5
288 done
290 log " domid=$domid pty=$pty_path" 289 log " domid=$domid pty=$pty_path"
291 290
292 if [ -n "$pty_path" ]; then 291 if [ -n "$pty_path" ]; then
@@ -294,14 +293,32 @@ XENEOF
294 fi 293 fi
295 294
296 # Terminal mode: send PTY fd to shim via console-socket (SCM_RIGHTS) 295 # Terminal mode: send PTY fd to shim via console-socket (SCM_RIGHTS)
296 local sendtty_rc="-"
297 if [ -n "$console_socket" ] && [ -n "$pty_path" ]; then 297 if [ -n "$console_socket" ] && [ -n "$pty_path" ]; then
298 if command -v vxn-sendtty >/dev/null 2>&1; then 298 if command -v vxn-sendtty >/dev/null 2>&1; then
299 vxn-sendtty "$console_socket" "$pty_path" \ 299 vxn-sendtty "$console_socket" "$pty_path"
300 || log " WARNING: vxn-sendtty failed (socket=$console_socket pty=$pty_path)" 300 sendtty_rc=$?
301 log " Sent PTY fd to console-socket" 301 [ "$sendtty_rc" -ne 0 ] && log " WARNING: vxn-sendtty failed (rc=$sendtty_rc)"
302 log " Sent PTY fd to console-socket (rc=$sendtty_rc)"
302 else 303 else
304 sendtty_rc="missing"
303 log " WARNING: vxn-sendtty not found, cannot send PTY to shim" 305 log " WARNING: vxn-sendtty not found, cannot send PTY to shim"
304 fi 306 fi
307 elif [ -n "$console_socket" ] && [ -z "$pty_path" ]; then
308 sendtty_rc="no-pty"
309 log " WARNING: no PTY path from xenstore, cannot send to shim"
310 fi
311
312 # Write terminal debug to persistent storage (survives reboot)
313 if [ -n "$console_socket" ]; then
314 cat > /root/vxn-tty-debug 2>/dev/null <<DBGEOF
315domid=$domid
316pty=$pty_path
317console_socket=$console_socket
318sendtty_rc=$sendtty_rc
319terminal=$terminal
320domname=$domname
321DBGEOF
305 fi 322 fi
306 323
307 # Persistent log dir — survives container deletion by shim 324 # Persistent log dir — survives container deletion by shim
@@ -349,8 +366,12 @@ XENEOF
349 done < "$_logdir/console.log" 366 done < "$_logdir/console.log"
350 fi 367 fi
351 else 368 else
352 # Terminal mode: shim owns PTY — just wait for domain death 369 # Terminal mode: shim owns PTY — just wait for domain death.
353 while xl list "$_dn" >/dev/null 2>&1; do sleep 2; done 370 # Close inherited stdout/stderr: go-runc captures runtime stdout
371 # via a pipe and cmd.Wait() blocks until EOF. If we hold the
372 # pipe's write end open, Create never returns to the shim.
373 exec >/dev/null 2>/dev/null
374 while xl list "$_dn" >/dev/null 2>&1; do sleep 0.5; done
354 fi 375 fi
355 ) & 376 ) &
356 local monitor_pid=$! 377 local monitor_pid=$!
@@ -627,6 +648,9 @@ done
627command="${1:-}" 648command="${1:-}"
628shift || true 649shift || true
629 650
651# Log every invocation for debugging (before command dispatch)
652log "INVOKE: cmd=$command args=$* root=$RUNTIME_ROOT"
653
630case "$command" in 654case "$command" in
631 create) cmd_create "$@" ;; 655 create) cmd_create "$@" ;;
632 start) cmd_start "$@" ;; 656 start) cmd_start "$@" ;;
diff --git a/recipes-containers/vcontainer/files/vxn-sendtty.c b/recipes-containers/vcontainer/files/vxn-sendtty.c
index a253b129..b1257100 100644
--- a/recipes-containers/vcontainer/files/vxn-sendtty.c
+++ b/recipes-containers/vcontainer/files/vxn-sendtty.c
@@ -19,6 +19,7 @@
19#include <string.h> 19#include <string.h>
20#include <unistd.h> 20#include <unistd.h>
21#include <fcntl.h> 21#include <fcntl.h>
22#include <termios.h>
22#include <sys/socket.h> 23#include <sys/socket.h>
23#include <sys/un.h> 24#include <sys/un.h>
24 25
@@ -44,6 +45,18 @@ int main(int argc, char *argv[])
44 return 1; 45 return 1;
45 } 46 }
46 47
48 /* Set PTY to raw mode for direct terminal I/O.
49 * Without this, the PTY slave's line discipline (echo, canonical mode)
50 * buffers input and echoes characters, preventing the shim from
51 * bridging the terminal correctly. */
52 {
53 struct termios tio;
54 if (tcgetattr(pty_fd, &tio) == 0) {
55 cfmakeraw(&tio);
56 tcsetattr(pty_fd, TCSANOW, &tio);
57 }
58 }
59
47 sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); 60 sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
48 if (sock_fd < 0) { 61 if (sock_fd < 0) {
49 perror("socket"); 62 perror("socket");