diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 95fc57408b..90485b7e1b 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -32,14 +32,15 @@ Description systemd-vmspawn may be used to start a virtual machine from an OS image. In many ways it is similar to systemd-nspawn1, but it + project='man-pages'>systemd-nspawn1, but launches a full virtual machine instead of using namespaces. File descriptors for /dev/kvm and /dev/vhost-vsock can be passed to systemd-vmspawn via systemd's native socket passing interface (see sd_listen_fds3 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 kvm and vhost-vsock respectively. + file descriptors must be passed with the names kvm and vhost-vsock + respectively. Note: on Ubuntu/Debian derivatives systemd-vmspawn requires the user to be in the kvm group to use the VSock options. @@ -76,6 +77,7 @@ Directory to use as file system root for the virtual machine. One of either or must be specified. + Note: If mounting a non-root owned directory you may require to map into the user's subuid namespace. @@ -120,129 +122,145 @@ - - BOOL + + BOOL - 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. + 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. - - + + - - BOOL + + BOOL - - Configure whether to use VSock networking. - 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. - - - + + Configure whether to use VSock networking. - - CID + 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. + + + - - Configure vmspawn to use a specific CID for the guest. - If the option is not specified or an empty argument is supplied the guest will be assigned a random CID. - Valid CIDs are in the range 3 to 4294967294 (0xFFFF_FFFE). - CIDs outside of this range are reserved. - - - + + CID - - BOOL + + Configure vmspawn to use a specific CID for the guest. - - Configure whether to use qemu with a virtual TPM or not. - If the option is not specified vmspawn will detect the presence of - swtpm8 and use it if available. - If yes is specified swtpm8 - is always used, and vice versa if no is set swtpm - 8 is never used. - Note: the virtual TPM used may change in future. - - - + If the option is not specified or an empty argument is supplied the guest will be assigned a random CID. - - PATH + Valid CIDs are in the range 3 to 4294967294 (0xFFFF_FFFE). + CIDs outside of this range are reserved. - - Set the linux kernel image to use for direct kernel boot. - If no kernel was installed into the image then the image will fail to boot. - - - + + + - - PATH + + BOOL - - Set the initrd to use for direct kernel boot. - If the linux kernel supplied is a UKI then this argument is not required. - If the option is specified multiple times vmspawn will merge the initrds together. - If no initrd was installed into the image then the image will fail to boot. - - - + + Configure whether to use qemu with a virtual TPM or not. - - + If the option is not specified vmspawn will detect the presence of + swtpm8 and use it if available. + If yes is specified swtpm8 + is always used, and vice versa if no is set swtpm + 8 is never used. - Start QEMU in graphical mode. + Note: the virtual TPM used may change in future. - - + + + - - - + + PATH - - Create a TAP device to network with the virtual machine. - - Note: root privileges are required to use TAP networking. - Additionally requires a correctly setup + + Set the linux kernel image to use for direct kernel boot. + + If no kernel was installed into the image then the image will fail to boot. + + + + + + + PATH + + + Set the initrd to use for direct kernel boot. + + If the linux kernel supplied is a UKI then this argument is not required. + + If the option is specified multiple times vmspawn will merge the initrds together. + + If no initrd was installed into the image then the image will fail to boot. + + + + + + + + + Start QEMU in graphical mode. + + + + + + + + + + Create a TAP device to network with the virtual machine. + + Note: root privileges are required to use TAP networking. + Additionally, systemd-networkd8 - to be running on the host to ensure the host interface is correctly configured. - The relevant .network file can be found at /usr/lib/systemd/network/80-vm-vt.network. - - - - + must be running and correctly set up on the host to provision the host interface. The relevant + .network file can be found at + /usr/lib/systemd/network/80-vm-vt.network. + - - + + + - Use user mode networking with QEMU. + + - - + Use user mode networking with QEMU. - - PATH + + - Takes an absolute path, or a relative path beginning with - ./. 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 list is specified lists all discovered firmwares. + + PATH - - + Takes an absolute path, or a relative path beginning with + ./. 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 list is specified lists all discovered firmwares. - - BOOL + + - Configure whether to search for firmware which supports Secure Boot. - 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. + + BOOL - - + Configure whether to search for firmware which supports Secure Boot. + + 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. + + + @@ -259,6 +277,7 @@ (for example in tools like machinectl1 and similar). + @@ -319,10 +338,11 @@ FILE|DIR - Forward the virtual machine's journal entries to the host. - systemd-journal-remote8 - 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 / in + Forward the virtual machine's journal to the host. + systemd-journal-remote8 + 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 + / described in systemd-journal-remote8. diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 6bcc37a8ab..a8c186dc20 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -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) diff --git a/src/journal/journald-socket.c b/src/journal/journald-socket.c index 6073caea02..24c8f05d45 100644 --- a/src/journal/journald-socket.c +++ b/src/journal/journald-socket.c @@ -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; } diff --git a/src/journal/journald-socket.h b/src/journal/journald-socket.h index cb156d73fa..db8ba68223 100644 --- a/src/journal/journald-socket.h +++ b/src/journal/journald-socket.h @@ -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); diff --git a/src/journal/test-journald-config.c b/src/journal/test-journald-config.c index cd13ac197d..900b1db44a 100644 --- a/src/journal/test-journald-config.c +++ b/src/journal/test-journald-config.c @@ -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) \ diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 5e582079c7..07ea2abd3a 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -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,