Merge pull request #31357 from keszybz/cleanups-vmspawn

Cleanups vmspawn
This commit is contained in:
Luca Boccassi 2024-02-19 16:45:42 +00:00 committed by GitHub
commit 4086a16742
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 213 additions and 208 deletions

View file

@ -32,14 +32,15 @@
<refsect1>
<title>Description</title>
<para><command>systemd-vmspawn</command> may be used to start a virtual machine from an OS image. In many ways it is similar to <citerefentry
project='man-pages'><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, but it
project='man-pages'><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>, but
launches a full virtual machine instead of using namespaces.</para>
<para>File descriptors for <filename>/dev/kvm</filename> and <filename>/dev/vhost-vsock</filename> can be
passed to <command>systemd-vmspawn</command> via systemd's native socket passing interface (see
<citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
details about the precise protocol used and the order in which the file descriptors are passed), these
fds must be passed with the names <literal>kvm</literal> and <literal>vhost-vsock</literal> respectively.</para>
file descriptors must be passed with the names <literal>kvm</literal> and <literal>vhost-vsock</literal>
respectively.</para>
<para>Note: on Ubuntu/Debian derivatives systemd-vmspawn requires the user to be in the <literal>kvm</literal> group to use the VSock options.</para>
</refsect1>
@ -76,6 +77,7 @@
<listitem><para>Directory to use as file system root for the virtual machine.</para>
<para>One of either <option>--directory=</option> or <option>--image=</option> must be specified.</para>
<para>Note: If mounting a non-root owned directory you may require <option>--private-users=</option>
to map into the user's subuid namespace.</para>
@ -120,129 +122,145 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--qemu-kvm=</option><replaceable>BOOL</replaceable></term>
<varlistentry>
<term><option>--qemu-kvm=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Configures whether to use KVM. If the option is not specified KVM support will be
detected automatically. If true, KVM is always used, and if false, KVM is never used.</para>
<listitem><para>Configures whether to use KVM. If the option is not specified KVM support will be
detected automatically. If true, KVM is always used, and if false, KVM is never used.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--qemu-vsock=</option><replaceable>BOOL</replaceable></term>
<varlistentry>
<term><option>--qemu-vsock=</option><replaceable>BOOL</replaceable></term>
<listitem>
<para>Configure whether to use VSock networking.</para>
<para>If the option is not specified VSock support will be detected automatically.
If yes is specified VSocks are always used, and vice versa if no is set VSocks are never used.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<listitem>
<para>Configure whether to use VSock networking.</para>
<varlistentry>
<term><option>--vsock-cid=</option><replaceable>CID</replaceable></term>
<para>If the option is not specified VSock support will be detected automatically.
If yes is specified VSocks are always used, and vice versa if no is set VSocks are never used.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<listitem>
<para>Configure vmspawn to use a specific CID for the guest.</para>
<para>If the option is not specified or an empty argument is supplied the guest will be assigned a random CID.</para>
<para>Valid CIDs are in the range <constant>3</constant> to <constant>4294967294</constant> (<constant>0xFFFF_FFFE</constant>).
CIDs outside of this range are reserved.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--vsock-cid=</option><replaceable>CID</replaceable></term>
<varlistentry>
<term><option>--tpm=</option><replaceable>BOOL</replaceable></term>
<listitem>
<para>Configure vmspawn to use a specific CID for the guest.</para>
<listitem>
<para>Configure whether to use qemu with a virtual TPM or not.</para>
<para>If the option is not specified vmspawn will detect the presence of <citerefentry project='man-pages'>
<refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry> and use it if available.
If yes is specified <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is always used, and vice versa if no is set <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle>
<manvolnum>8</manvolnum></citerefentry> is never used.</para>
<para>Note: the virtual TPM used may change in future.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<para>If the option is not specified or an empty argument is supplied the guest will be assigned a random CID.</para>
<varlistentry>
<term><option>--linux=</option><replaceable>PATH</replaceable></term>
<para>Valid CIDs are in the range <constant>3</constant> to <constant>4294967294</constant> (<constant>0xFFFF_FFFE</constant>).
CIDs outside of this range are reserved.</para>
<listitem>
<para>Set the linux kernel image to use for direct kernel boot.</para>
<para>If no kernel was installed into the image then the image will fail to boot.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--initrd=</option><replaceable>PATH</replaceable></term>
<varlistentry>
<term><option>--tpm=</option><replaceable>BOOL</replaceable></term>
<listitem>
<para>Set the initrd to use for direct kernel boot.</para>
<para>If the linux kernel supplied is a UKI then this argument is not required.</para>
<para>If the option is specified multiple times vmspawn will merge the initrds together.</para>
<para>If no initrd was installed into the image then the image will fail to boot.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<listitem>
<para>Configure whether to use qemu with a virtual TPM or not.</para>
<varlistentry>
<term><option>--qemu-gui</option></term>
<para>If the option is not specified vmspawn will detect the presence of <citerefentry project='man-pages'>
<refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry> and use it if available.
If yes is specified <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is always used, and vice versa if no is set <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle>
<manvolnum>8</manvolnum></citerefentry> is never used.</para>
<listitem><para>Start QEMU in graphical mode.</para>
<para>Note: the virtual TPM used may change in future.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<term><option>--network-tap</option></term>
<varlistentry>
<term><option>--linux=</option><replaceable>PATH</replaceable></term>
<listitem>
<para>Create a TAP device to network with the virtual machine.</para>
<para>
Note: root privileges are required to use TAP networking.
Additionally requires a correctly setup
<listitem>
<para>Set the linux kernel image to use for direct kernel boot.</para>
<para>If no kernel was installed into the image then the image will fail to boot.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--initrd=</option><replaceable>PATH</replaceable></term>
<listitem>
<para>Set the initrd to use for direct kernel boot.</para>
<para>If the linux kernel supplied is a UKI then this argument is not required.</para>
<para>If the option is specified multiple times vmspawn will merge the initrds together.</para>
<para>If no initrd was installed into the image then the image will fail to boot.</para>
<xi:include href="version-info.xml" xpointer="v256"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--qemu-gui</option></term>
<listitem><para>Start QEMU in graphical mode.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<term><option>--network-tap</option></term>
<listitem>
<para>Create a TAP device to network with the virtual machine.</para>
<para>Note: root privileges are required to use TAP networking.
Additionally,
<citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
to be running on the host to ensure the host interface is correctly configured.
The relevant <literal>.network</literal> file can be found at <filename>/usr/lib/systemd/network/80-vm-vt.network</filename>.
</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
must be running and correctly set up on the host to provision the host interface. The relevant
<literal>.network</literal> file can be found at
<filename>/usr/lib/systemd/network/80-vm-vt.network</filename>.
</para>
<varlistentry>
<term><option>--network-user-mode</option></term>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
<listitem><para>Use user mode networking with QEMU.</para>
<varlistentry>
<term><option>--network-user-mode</option></term>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<listitem><para>Use user mode networking with QEMU.</para>
<varlistentry>
<term><option>--firmware=</option><replaceable>PATH</replaceable></term>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<listitem><para>Takes an absolute path, or a relative path beginning with
<filename>./</filename>. Specifies a JSON firmware definition file, which allows selecting the
firmware to boot in the VM. If not specified a suitable firmware is automatically discovered. If the
special string <literal>list</literal> is specified lists all discovered firmwares.</para>
<varlistentry>
<term><option>--firmware=</option><replaceable>PATH</replaceable></term>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<listitem><para>Takes an absolute path, or a relative path beginning with
<filename>./</filename>. Specifies a JSON firmware definition file, which allows selecting the
firmware to boot in the VM. If not specified a suitable firmware is automatically discovered. If the
special string <literal>list</literal> is specified lists all discovered firmwares.</para>
<varlistentry>
<term><option>--secure-boot=</option><replaceable>BOOL</replaceable></term>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<listitem><para>Configure whether to search for firmware which supports Secure Boot.</para>
<para>If the option is not specified the first firmware which is detected will be used.
If the option is set to yes then the first firmware with Secure Boot support will be selected.
If no is specified then the first firmware without Secure Boot will be selected.</para>
<varlistentry>
<term><option>--secure-boot=</option><replaceable>BOOL</replaceable></term>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<listitem><para>Configure whether to search for firmware which supports Secure Boot.</para>
<para>If the option is not specified the first firmware which is detected will be used.
If the option is set to yes then the first firmware with Secure Boot support will be selected.
If no is specified then the first firmware without Secure Boot will be selected.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
</variablelist>
</refsect2>
@ -259,6 +277,7 @@
(for example in tools like
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
and similar).</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
</varlistentry>
@ -319,10 +338,11 @@
<varlistentry>
<term><option>--forward-journal=</option><replaceable>FILE|DIR</replaceable></term>
<listitem><para>Forward the virtual machine's journal entries to the host.</para>
<para><citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>
Is currently used to receive the guest VM's forwarded journal entries. For more information on the semantics
of supplying a file vs a directory here see <option>-o</option>/<option>--output</option> in
<listitem><para>Forward the virtual machine's journal to the host.
<citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is currently used to receive the guest VM's forwarded journal entries. This option determines where
this journal is saved on the host and has the same semantics as
<option>-o</option>/<option>--output</option> described in
<citerefentry><refentrytitle>systemd-journal-remote</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v256"/>

View file

@ -1155,7 +1155,7 @@ static void server_dispatch_message_real(
else
journal_uid = 0;
server_forward_socket(s, iovec, n, priority);
(void) server_forward_socket(s, iovec, n, priority);
server_write_to_journal(s, journal_uid, iovec, n, priority);
}
@ -2488,16 +2488,12 @@ static void server_load_credentials(Server *s) {
assert(s);
/* if we already have a forward address from config don't load the credential */
if (s->forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC) {
log_debug("Socket forward address already set not loading journal.forward_to_socket");
return;
}
if (s->forward_to_socket.sockaddr.sa.sa_family != AF_UNSPEC)
return log_debug("Socket forward address already set not loading journal.forward_to_socket");
r = read_credential("journal.forward_to_socket", &data, NULL);
if (r < 0) {
log_debug_errno(r, "Failed to read credential journal.forward_to_socket, ignoring: %m");
return;
}
if (r < 0)
return (void) log_debug_errno(r, "Failed to read credential journal.forward_to_socket, ignoring: %m");
r = socket_address_parse(&s->forward_to_socket, data);
if (r < 0)

View file

@ -13,42 +13,41 @@
#include "socket-util.h"
#include "sparse-endian.h"
void server_open_forward_socket(Server *s) {
static int server_open_forward_socket(Server *s) {
_cleanup_close_ int socket_fd = -EBADF;
const SocketAddress *addr;
int family;
assert(s);
/* nop if there is nothing to do */
if (s->forward_to_socket.sockaddr.sa.sa_family == AF_UNSPEC || s->namespace || s->forward_socket_fd >= 0)
return;
/* Noop if there is nothing to do. */
if (s->forward_to_socket.sockaddr.sa.sa_family == AF_UNSPEC || s->namespace)
return 0;
/* All ready, nothing to do. */
if (s->forward_socket_fd >= 0)
return 1;
addr = &s->forward_to_socket;
family = socket_address_family(addr);
if (!IN_SET(family, AF_UNIX, AF_INET, AF_INET6, AF_VSOCK)) {
log_debug("Unsupported socket type for forward socket: %d", family);
return;
}
if (!IN_SET(family, AF_UNIX, AF_INET, AF_INET6, AF_VSOCK))
return log_debug_errno(SYNTHETIC_ERRNO(ESOCKTNOSUPPORT),
"Unsupported socket type for forward socket: %d", family);
socket_fd = socket(family, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (socket_fd < 0) {
log_debug_errno(errno, "Failed to create forward socket, ignoring: %m");
return;
}
if (socket_fd < 0)
return log_debug_errno(errno, "Failed to create forward socket, ignoring: %m");
if (connect(socket_fd, &addr->sockaddr.sa, addr->size) < 0) {
log_debug_errno(errno, "Failed to connect to remote address for forwarding, ignoring: %m");
return;
}
if (connect(socket_fd, &addr->sockaddr.sa, addr->size) < 0)
return log_debug_errno(errno, "Failed to connect to remote address for forwarding, ignoring: %m");
s->forward_socket_fd = TAKE_FD(socket_fd);
log_debug("Successfully connected to remote address for forwarding");
log_debug("Successfully connected to remote address for forwarding.");
return 1;
}
static inline bool must_serialise(struct iovec iov) {
static inline bool must_serialize(struct iovec iov) {
/* checks an iovec of the form FIELD=VALUE to see if VALUE needs binary safe serialisation:
* See https://systemd.io/JOURNAL_EXPORT_FORMATS/#journal-export-format for more information
* on binary safe serialisation for the journal export format */
@ -58,42 +57,40 @@ static inline bool must_serialise(struct iovec iov) {
const uint8_t *s = iov.iov_base;
bool before_value = true;
FOREACH_ARRAY(c, s, iov.iov_len) {
FOREACH_ARRAY(c, s, iov.iov_len)
if (before_value)
before_value = *c != (uint8_t)'=';
else if (*c < (uint8_t)' ' && *c != (uint8_t)'\t')
return true;
}
return false;
}
void server_forward_socket(
int server_forward_socket(
Server *s,
const struct iovec *iovec,
size_t n_iovec,
int priority) {
_cleanup_free_ struct iovec *iov_alloc = NULL;
struct iovec *iov = NULL;
_cleanup_free_ struct iovec *iov_alloc = NULL;
struct iovec *iov;
_cleanup_free_ le64_t *len_alloc = NULL;
le64_t *len = NULL;
le64_t *len;
int r;
assert(s);
assert(iovec);
assert(n_iovec > 0);
if (LOG_PRI(priority) > s->max_level_socket)
return;
return 0;
server_open_forward_socket(s);
r = server_open_forward_socket(s);
if (r <= 0)
return r;
/* if we failed to open a socket just return */
if (s->forward_socket_fd < 0)
return;
/* we need a newline after each iovec + 4 for each we have to serialise in a binary safe way
* +1 for the final __REALTIME_TIMESTAMP metadata field */
/* We need a newline after each iovec + 4 for each we have to serialize in a binary safe way
* + 1 for the final __REALTIME_TIMESTAMP metadata field. */
size_t n = n_iovec * 5 + 1;
if (n < ALLOCA_MAX / (sizeof(struct iovec) + sizeof(le64_t)) / 2) {
@ -101,18 +98,14 @@ void server_forward_socket(
len = newa(le64_t, n_iovec);
} else {
iov_alloc = new(struct iovec, n);
if (!iov_alloc) {
log_oom();
return;
}
if (!iov_alloc)
return log_oom();
iov = iov_alloc;
len_alloc = new(le64_t, n_iovec);
if (!len_alloc) {
log_oom();
return;
}
if (!len_alloc)
return log_oom();
len = len_alloc;
}
@ -120,15 +113,14 @@ void server_forward_socket(
struct iovec nl = IOVEC_MAKE_STRING("\n");
size_t iov_idx = 0, len_idx = 0;
FOREACH_ARRAY(i, iovec, n_iovec) {
if (must_serialise(*i)) {
if (must_serialize(*i)) {
const uint8_t *c;
c = memchr(i->iov_base, '=', i->iov_len);
/* this should never happen */
if (_unlikely_(!c || c == i->iov_base)) {
log_error("Found invalid journal field, refusing to forward.");
return;
}
if (_unlikely_(!c || c == i->iov_base))
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG),
"Found invalid journal field, refusing to forward.");
/* write the field name */
iov[iov_idx++] = IOVEC_MAKE(i->iov_base, c - (uint8_t*) i->iov_base);
@ -147,7 +139,8 @@ void server_forward_socket(
iov[iov_idx++] = nl;
}
/* synthesise __REALTIME_TIMESTAMP as the last argument so systemd-journal-upload can receive these export messages */
/* Synthesise __REALTIME_TIMESTAMP as the last argument so systemd-journal-upload can receive these
* export messages. */
char buf[sizeof("__REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t) + 2];
xsprintf(buf, "__REALTIME_TIMESTAMP="USEC_FMT"\n\n", now(CLOCK_REALTIME));
iov[iov_idx++] = IOVEC_MAKE_STRING(buf);
@ -155,8 +148,10 @@ void server_forward_socket(
if (writev(s->forward_socket_fd, iov, iov_idx) < 0) {
log_debug_errno(errno, "Failed to forward log message over socket: %m");
/* if we failed to send once we will probably fail again so wait for a new connection to
* establish before attempting to forward again */
/* If we failed to send once we will probably fail again so wait for a new connection to
* establish before attempting to forward again. */
s->forward_socket_fd = safe_close(s->forward_socket_fd);
}
return 0;
}

View file

@ -4,5 +4,4 @@
#include "journald-server.h"
#include "socket-util.h"
void server_forward_socket(Server *s, const struct iovec *iovec, size_t n, int priority);
void server_open_forward_socket(Server *s);
int server_forward_socket(Server *s, const struct iovec *iovec, size_t n, int priority);

View file

@ -55,33 +55,33 @@ TEST(config_compress) {
}
#define _FORWARD_TO_SOCKET_PARSE_CHECK_FAILS(str, addr, varname) \
do { \
SocketAddress varname = {}; \
do { \
SocketAddress varname = {}; \
config_parse_forward_to_socket("", "", 0, "", 0, "", 0, str, \
&varname, NULL); \
assert_se(socket_address_verify(&varname, true) < 0); \
&varname, NULL); \
assert_se(socket_address_verify(&varname, true) < 0); \
} while (0)
#define FORWARD_TO_SOCKET_PARSE_CHECK_FAILS(str) \
_FORWARD_TO_SOCKET_PARSE_CHECK_FAILS(str, addr, conf##__COUNTER__)
#define _FORWARD_TO_SOCKET_PARSE_CHECK(str, addr, varname) \
do { \
SocketAddress varname = {}; \
do { \
SocketAddress varname = {}; \
config_parse_forward_to_socket("", "", 0, "", 0, "", 0, str, \
&varname, NULL); \
buf = mfree(buf); \
buf2 = mfree(buf2); \
socket_address_print(&varname, &buf);\
socket_address_print(&addr, &buf2);\
&varname, NULL); \
buf = mfree(buf); \
buf2 = mfree(buf2); \
socket_address_print(&varname, &buf); \
socket_address_print(&addr, &buf2); \
log_info("\"%s\" parsed as \"%s\", should be \"%s\"", str, buf, buf2); \
log_info("socket_address_verify(&addr, false) = %d", socket_address_verify(&addr, false)); \
log_info("socket_address_verify(&varname, false) = %d", socket_address_verify(&varname, false)); \
log_info("socket_address_family(&addr) = %d", socket_address_family(&addr)); \
log_info("socket_address_family(&varname) = %d", socket_address_family(&varname)); \
log_info("addr.size = %u", addr.size); \
log_info("varname.size = %u", varname.size); \
assert_se(socket_address_equal(&varname, &addr)); \
log_info("addr.size = %u", addr.size); \
log_info("varname.size = %u", varname.size); \
assert_se(socket_address_equal(&varname, &addr)); \
} while (0)
#define FORWARD_TO_SOCKET_PARSE_CHECK(str, addr) \

View file

@ -112,52 +112,47 @@ static int help(void) {
printf("%1$s [OPTIONS...] [ARGUMENTS...]\n\n"
"%5$sSpawn a command or OS in a virtual machine.%6$s\n\n"
" -h --help Show this help\n"
" --version Print version string\n"
" -q --quiet Do not show status information\n"
" --no-pager Do not pipe output into a pager\n"
" -h --help Show this help\n"
" --version Print version string\n"
" -q --quiet Do not show status information\n"
" --no-pager Do not pipe output into a pager\n"
"\n%3$sImage:%4$s\n"
" -D --directory=PATH Root directory for the container\n"
" -i --image=PATH Root file system disk image (or device node) for\n"
" the virtual machine\n"
" -D --directory=PATH Root directory for the VM\n"
" -i --image=FILE|DEVICE Root file system disk image or device for the VM\n"
"\n%3$sHost Configuration:%4$s\n"
" --qemu-smp=SMP Configure guest's SMP settings\n"
" --qemu-mem=MEM Configure guest's RAM size\n"
" --qemu-kvm=BOOL Configure whether to use KVM or not\n"
" --qemu-vsock=BOOL Configure whether to use qemu with a vsock or not\n"
" --vsock-cid= Specify the CID to use for the qemu guest's vsock\n"
" --tpm=BOOL Configure whether to use a virtual TPM or not\n"
" --linux=PATH Specify the linux kernel for direct kernel boot\n"
" --initrd=PATH Specify the initrd for direct kernel boot\n"
" --qemu-gui Start QEMU in graphical mode\n"
" -n --network-tap Create a TAP device for networking with QEMU.\n"
" --network-user-mode Use user mode networking with QEMU.\n"
" --secure-boot=BOOL Configure whether to search for firmware which\n"
" supports Secure Boot\n"
" --firmware=PATH|list Select firmware definition file (or list available)\n"
" --qemu-smp=SMP Configure guest's SMP settings\n"
" --qemu-mem=MEM Configure guest's RAM size\n"
" --qemu-kvm=BOOL Enable use of KVM\n"
" --qemu-vsock=BOOL Override autodetection of VSock support in QEMU\n"
" --vsock-cid=CID Specify the CID to use for the qemu guest's VSock\n"
" --tpm=BOOL Enable use of a virtual TPM\n"
" --linux=PATH Specify the linux kernel for direct kernel boot\n"
" --initrd=PATH Specify the initrd for direct kernel boot\n"
" --qemu-gui Start QEMU in graphical mode\n"
" -n --network-tap Create a TAP device for networking with QEMU\n"
" --network-user-mode Use user mode networking with QEMU\n"
" --secure-boot=BOOL Enable searching for firmware supporting SecureBoot\n"
" --firmware=PATH|list Select firmware definition file (or list available)\n"
"\n%3$sSystem Identity:%4$s\n"
" -M --machine=NAME Set the machine name for the virtual machine\n"
" -M --machine=NAME Set the machine name for the VM\n"
"\n%3$sUser Namespacing:%4$s\n"
" --private-users=UIDBASE[:NUIDS]\n"
" Configure the UID/GID range to map into the\n"
" virtiofsd namespace\n"
" Configure the UID/GID range to map into the\n"
" virtiofsd namespace\n"
"\n%3$sMounts:%4$s\n"
" --bind=SOURCE[:TARGET]\n"
" Mount a file or directory from the host into\n"
" the VM.\n"
" Mount a file or directory from the host into the VM\n"
" --bind-ro=SOURCE[:TARGET]\n"
" Similar, but creates a read-only mount\n"
" Mount a file or directory, but read-only\n"
"\n%3$sIntegration:%4$s\n"
" --forward-journal=FILE|DIR\n"
" Forward the virtual machine's journal entries to\n"
" the host.\n"
" Forward the VM's journal to the host\n"
"\n%3$sCredentials:%4$s\n"
" --set-credential=ID:VALUE\n"
" Pass a credential with literal value to the\n"
" virtual machine\n"
" Pass a credential with literal value to the VM\n"
" --load-credential=ID:PATH\n"
" Load credential to pass to the virtual machine from\n"
" file or AF_UNIX stream socket.\n"
" Load credential for the VM from file or AF_UNIX\n"
" stream socket.\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,