]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vmspawn: Redirect QEMU's stdin/stdout/stderr to the PTY
authorDaan De Meyer <daan@amutable.com>
Sun, 5 Apr 2026 20:51:37 +0000 (20:51 +0000)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 6 Apr 2026 11:58:31 +0000 (13:58 +0200)
When a PTY is allocated for the console, QEMU's own stdio file
descriptors were still inherited directly from vmspawn, meaning any
output QEMU writes to stdout/stderr (e.g. warnings) would bypass the
PTY forwarder and go straight to the terminal. Similarly, QEMU could
read directly from the terminal's stdin.

Fix this by opening the PTY slave side and passing it as stdio_fds to
the fork call with FORK_REARRANGE_STDIO, so that all of QEMU's I/O
goes through the PTY and is properly forwarded.

src/vmspawn/vmspawn.c

index 96ca4cee91e9b71c9640ca2acf7b6eb4d9667020..ee2c49b778fb7c4edcbf82716f98ac54f1e0a5d0 100644 (file)
@@ -3517,12 +3517,20 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                 log_debug("Executing: %s", joined);
         }
 
+        _cleanup_close_ int child_pty = -EBADF;
+        if (master >= 0) {
+                child_pty = pty_open_peer(master, O_RDWR|O_CLOEXEC|O_NOCTTY);
+                if (child_pty < 0)
+                        return log_error_errno(child_pty, "Failed to open PTY slave: %m");
+        }
+
         _cleanup_(pidref_done) PidRef child_pidref = PIDREF_NULL;
         r = pidref_safe_fork_full(
                         qemu_binary,
-                        /* stdio_fds= */ NULL,
+                        child_pty >= 0 ? (const int[]) { child_pty, child_pty, child_pty } : NULL,
                         pass_fds, n_pass_fds,
-                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_CLOEXEC_OFF|FORK_RLIMIT_NOFILE_SAFE,
+                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_CLOEXEC_OFF|FORK_RLIMIT_NOFILE_SAFE|
+                        (child_pty >= 0 ? FORK_REARRANGE_STDIO : 0),
                         &child_pidref);
         if (r < 0)
                 return r;
@@ -3539,6 +3547,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         }
 
         /* Close relevant fds we passed to qemu in the parent. We don't need them anymore. */
+        child_pty = safe_close(child_pty);
         child_vsock_fd = safe_close(child_vsock_fd);
         tap_fd = safe_close(tap_fd);