From a8c2aa9e2fdde65939300e83f3782237d300e614 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 23 Mar 2026 22:00:03 +0100 Subject: [PATCH] vmspawn: Add headless console support --- man/systemd-vmspawn.xml | 12 +++++++----- shell-completion/bash/systemd-vmspawn | 2 +- src/vmspawn/vmspawn-settings.c | 1 + src/vmspawn/vmspawn-settings.h | 1 + src/vmspawn/vmspawn.c | 11 +++++++++-- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 331c7c16fd6..23ecc51bac3 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -658,11 +658,13 @@ Configures how to set up the console of the VM. Takes one of interactive, read-only, native, - gui. Defaults to interactive. interactive - provides an interactive terminal interface to the VM. read-only is similar, but - is strictly read-only, i.e. does not accept any input from the user. native also - provides a TTY-based interface, but uses qemu native implementation (which means the qemu monitor - is available). gui shows the qemu graphical UI. + gui, headless. Defaults to interactive. + interactive provides an interactive terminal interface to the VM. + read-only is similar, but is strictly read-only, i.e. does not accept any input + from the user. native also provides a TTY-based interface, but uses qemu native + implementation (which means the qemu monitor is available). gui shows the qemu + graphical UI. headless runs the VM without any console, which is useful for + automated or scripted usage. diff --git a/shell-completion/bash/systemd-vmspawn b/shell-completion/bash/systemd-vmspawn index b17586de145..08e92e0a4b5 100644 --- a/shell-completion/bash/systemd-vmspawn +++ b/shell-completion/bash/systemd-vmspawn @@ -56,7 +56,7 @@ _systemd_vmspawn() { elif __contains_word "$prev" ${OPTS[SSH_KEY]}; then comps='dsa ecdsa ecdsa-sk ed25519 ed25519-sk rsa' elif __contains_word "$prev" ${OPTS[CONSOLE]}; then - comps='interactive native gui' + comps='interactive native gui read-only headless' elif __contains_word "$prev" ${OPTS[IMAGE_FORMAT]}; then comps='raw qcow2' elif __contains_word "$prev" ${OPTS[ARG]}; then diff --git a/src/vmspawn/vmspawn-settings.c b/src/vmspawn/vmspawn-settings.c index 46dda4bfc32..1c4bc102b94 100644 --- a/src/vmspawn/vmspawn-settings.c +++ b/src/vmspawn/vmspawn-settings.c @@ -24,6 +24,7 @@ static const char *const console_mode_table[_CONSOLE_MODE_MAX] = { [CONSOLE_READ_ONLY] = "read-only", [CONSOLE_NATIVE] = "native", [CONSOLE_GUI] = "gui", + [CONSOLE_HEADLESS] = "headless", }; DEFINE_STRING_TABLE_LOOKUP(console_mode, ConsoleMode); diff --git a/src/vmspawn/vmspawn-settings.h b/src/vmspawn/vmspawn-settings.h index ee937c993ac..1d59db6418b 100644 --- a/src/vmspawn/vmspawn-settings.h +++ b/src/vmspawn/vmspawn-settings.h @@ -27,6 +27,7 @@ typedef enum ConsoleMode { CONSOLE_READ_ONLY, /* ptyfwd, but in read-only mode */ CONSOLE_NATIVE, /* qemu's native TTY handling */ CONSOLE_GUI, /* qemu's graphical UI */ + CONSOLE_HEADLESS, /* no console */ _CONSOLE_MODE_MAX, _CONSOLE_MODE_INVALID = -EINVAL, } ConsoleMode; diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index a65f879449e..d41a9203891 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -260,7 +260,7 @@ static int help(void) { " --pass-ssh-key=BOOL Create an SSH key to access the VM\n" " --ssh-key-type=TYPE Choose what type of SSH key to pass\n" "\n%3$sInput/Output:%4$s\n" - " --console=MODE Console mode (interactive, native, gui)\n" + " --console=MODE Console mode (interactive, native, gui, read-only, headless)\n" " --background=COLOR Set ANSI color for background\n" "\n%3$sCredentials:%4$s\n" " --set-credential=ID:VALUE\n" @@ -2365,6 +2365,13 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { "-mon", "console"); break; + case CONSOLE_HEADLESS: + r = strv_extend_many( + &cmdline, + "-nographic", + "-nodefaults"); + break; + default: assert_not_reached(); } @@ -2641,7 +2648,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { return log_oom(); } - if (arg_console_mode != CONSOLE_GUI) { + if (!IN_SET(arg_console_mode, CONSOLE_GUI, CONSOLE_HEADLESS)) { r = strv_prepend(&arg_kernel_cmdline_extra, "console=hvc0"); if (r < 0) return log_oom();