[libvirt] [PATCH 00/23] Use a slirp helper process

From: Marc-André Lureau <marcandre.lureau@redhat.com> Hi, SLIRP networking can be running in a separate process. This allows for stricter security policies for QEMU & SLIRP, as SLIRP is notoriously not very safe (discussed on ML, various CVEs, and even the code says so explicitly in the comments), yet people rely on it for various reasons. With this series, for a network interface "user", libvirt will: - check the slirp-helper presence and capabilites (see [1]) - setup a socket pair between qemu and the helper - use -net socket - setup migration thanks to dbus-vmstate There are no changes required to domain configuration to benefit it. "guestfwd" isn't supported at this point, but it is known to be in a broken state with libvirt+qemu anyway. The dbus-vmstate is being proposed to QEMU. The libslirp-rs slirp-helper hasn't yet received a release, but I plan to make one soon, depending on the feedback. fwiw, I have been working on an alternative rust-only implementation of a slirp-helper that will also follow [1]. [1] https://gitlab.freedesktop.org/slirp/libslirp-rs/blob/master/src/bin/README.... Marc-André Lureau (23): Add .editorconfig dbus: correctly build reply message qemu: replace logCtxt with qemuDomainLogAppendMessage() qemu: reset VM id after external devices stop qemu: add socket datagram capability qemu: add dbus-vmstate capability qemu-conf: add configurable dbus-daemon location qemu-conf: add dbusStateDir qemu-security: add qemuSecurityCommandRun() qemu: add a DBus daemon helper unit qemu: add dbus-vmstate helper migration support domain-conf: add network def private data qemu: add qemuDomainNetworkPrivate qemu-conf: add configurable slirp-helper location qemu: add slirp helper unit qemu-domain: save and restore slirp state qemu: add a flag to the cookie to prevent slirp-helper setup qemu-migration: prevent migration if slirp cannot be migrated qemu-extdevice: prepare, start and stop slirp-helper qemu-command: use -net socket,fd= with slirp-helper qemu-process: prepare slirp-helper qemu-hotplug: handle hotplugging of slirp-helper tests: add slirp-helper qemuxml2argv test .editorconfig | 21 + m4/virt-driver-qemu.m4 | 10 + src/conf/domain_conf.c | 21 +- src/conf/domain_conf.h | 6 + src/qemu/Makefile.inc.am | 6 + src/qemu/libvirtd_qemu.aug | 2 + src/qemu/qemu.conf | 6 + src/qemu/qemu_alias.c | 7 + src/qemu/qemu_alias.h | 2 + src/qemu/qemu_capabilities.c | 10 + src/qemu/qemu_capabilities.h | 4 + src/qemu/qemu_command.c | 100 ++++- src/qemu/qemu_command.h | 6 +- src/qemu/qemu_conf.c | 20 +- src/qemu/qemu_conf.h | 3 + src/qemu/qemu_dbus.c | 384 ++++++++++++++++ src/qemu/qemu_dbus.h | 44 ++ src/qemu/qemu_domain.c | 206 ++++++++- src/qemu/qemu_domain.h | 27 ++ src/qemu/qemu_driver.c | 11 +- src/qemu/qemu_extdevice.c | 82 ++-- src/qemu/qemu_extdevice.h | 10 +- src/qemu/qemu_hotplug.c | 118 ++++- src/qemu/qemu_hotplug.h | 8 + src/qemu/qemu_interface.c | 27 ++ src/qemu/qemu_interface.h | 4 + src/qemu/qemu_migration.c | 62 +++ src/qemu/qemu_monitor.c | 35 +- src/qemu/qemu_monitor.h | 6 +- src/qemu/qemu_monitor_json.c | 15 + src/qemu/qemu_monitor_json.h | 5 + src/qemu/qemu_process.c | 37 +- src/qemu/qemu_process.h | 1 + src/qemu/qemu_security.c | 22 + src/qemu/qemu_security.h | 6 + src/qemu/qemu_slirp.c | 412 ++++++++++++++++++ src/qemu/qemu_slirp.h | 78 ++++ src/qemu/qemu_tpm.c | 10 +- src/qemu/qemu_tpm.h | 5 +- src/qemu/test_libvirtd_qemu.aug.in | 2 + src/util/virdbus.c | 18 +- src/util/virdbus.h | 6 +- tests/Makefile.am | 1 + .../caps_4.0.0.aarch64.xml | 1 + .../qemucapabilitiesdata/caps_4.0.0.ppc64.xml | 1 + .../caps_4.0.0.riscv32.xml | 1 + .../caps_4.0.0.riscv64.xml | 1 + .../qemucapabilitiesdata/caps_4.0.0.s390x.xml | 1 + .../caps_4.0.0.x86_64.xml | 1 + .../caps_4.1.0.x86_64.xml | 1 + .../net-user.x86_64-4.0.0.args | 34 ++ tests/qemuxml2argvtest.c | 16 + tests/testutilsqemu.h | 1 + tests/virfirewalltest.c | 9 +- tests/virpolkittest.c | 3 +- 55 files changed, 1849 insertions(+), 87 deletions(-) create mode 100644 .editorconfig create mode 100644 src/qemu/qemu_dbus.c create mode 100644 src/qemu/qemu_dbus.h create mode 100644 src/qemu/qemu_slirp.c create mode 100644 src/qemu/qemu_slirp.h create mode 100644 tests/qemuxml2argvdata/net-user.x86_64-4.0.0.args -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Consistent code style across editors. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- .editorconfig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..e766441a50 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig is a file format and collection of text editor plugins +# for maintaining consistent coding styles between different editors +# and IDEs. Most popular editors support this either natively or via +# plugin. +# +# Check https://editorconfig.org for details. + +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.c] +indent_style = space +indent_size = 4 + +[*.{rng,xml}] +indent_style = space +indent_size = 2 -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> dbus_message_new() does not construct correct replies by itself, it is recommended to use dbus_message_new_method_return() instead. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/util/virdbus.c | 18 ++++++++++++------ src/util/virdbus.h | 6 ++++-- tests/virfirewalltest.c | 9 ++++++--- tests/virpolkittest.c | 3 ++- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/util/virdbus.c b/src/util/virdbus.c index b0ac8d7055..64513eef14 100644 --- a/src/util/virdbus.c +++ b/src/util/virdbus.c @@ -1456,6 +1456,7 @@ int virDBusCreateMethod(DBusMessage **call, /** * virDBusCreateReplyV: + * @msg: the message to reply to * @reply: pointer to be filled with a method reply message * @types: type signature for following method arguments * @args: method arguments @@ -1468,13 +1469,14 @@ int virDBusCreateMethod(DBusMessage **call, * as variadic args. See virDBusCreateMethodV for a * description of this parameter. */ -int virDBusCreateReplyV(DBusMessage **reply, +int virDBusCreateReplyV(DBusMessage *msg, + DBusMessage **reply, const char *types, va_list args) { int ret = -1; - if (!(*reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN))) { + if (!(*reply = dbus_message_new_method_return(msg))) { virReportOOMError(); goto cleanup; } @@ -1493,6 +1495,7 @@ int virDBusCreateReplyV(DBusMessage **reply, /** * virDBusCreateReply: + * @msg: the message to reply to * @reply: pointer to be filled with a method reply message * @types: type signature for following method arguments * @...: method arguments @@ -1500,14 +1503,15 @@ int virDBusCreateReplyV(DBusMessage **reply, * See virDBusCreateReplyV for a description of the * behaviour of this method. */ -int virDBusCreateReply(DBusMessage **reply, +int virDBusCreateReply(DBusMessage *msg, + DBusMessage **reply, const char *types, ...) { va_list args; int ret; va_start(args, types); - ret = virDBusCreateReplyV(reply, types, args); + ret = virDBusCreateReplyV(msg, reply, types, args); va_end(args); return ret; @@ -1811,7 +1815,8 @@ int virDBusCreateMethodV(DBusMessage **call ATTRIBUTE_UNUSED, return -1; } -int virDBusCreateReplyV(DBusMessage **reply ATTRIBUTE_UNUSED, +int virDBusCreateReplyV(DBusMessage *msg ATTRIBUTE_UNUSED, + DBusMessage **reply ATTRIBUTE_UNUSED, const char *types ATTRIBUTE_UNUSED, va_list args ATTRIBUTE_UNUSED) { @@ -1820,7 +1825,8 @@ int virDBusCreateReplyV(DBusMessage **reply ATTRIBUTE_UNUSED, return -1; } -int virDBusCreateReply(DBusMessage **reply ATTRIBUTE_UNUSED, +int virDBusCreateReply(DBusMessage *msg ATTRIBUTE_UNUSED, + DBusMessage **reply ATTRIBUTE_UNUSED, const char *types ATTRIBUTE_UNUSED, ...) { virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/util/virdbus.h b/src/util/virdbus.h index 083c074d59..0303e91045 100644 --- a/src/util/virdbus.h +++ b/src/util/virdbus.h @@ -52,9 +52,11 @@ int virDBusCreateMethodV(DBusMessage **call, const char *member, const char *types, va_list args); -int virDBusCreateReply(DBusMessage **reply, +int virDBusCreateReply(DBusMessage *msg, + DBusMessage **reply, const char *types, ...); -int virDBusCreateReplyV(DBusMessage **reply, +int virDBusCreateReplyV(DBusMessage *msg, + DBusMessage **reply, const char *types, va_list args); diff --git a/tests/virfirewalltest.c b/tests/virfirewalltest.c index 7c586877d3..d2c85a27cc 100644 --- a/tests/virfirewalltest.c +++ b/tests/virfirewalltest.c @@ -150,7 +150,8 @@ VIR_MOCK_WRAP_RET_ARGS(dbus_connection_send_with_reply_and_block, if (nargs == 1 && STREQ(type, "ipv4") && STREQ(args[0], "-L")) { - if (virDBusCreateReply(&reply, + if (virDBusCreateReply(message, + &reply, "s", TEST_FILTER_TABLE_LIST) < 0) goto error; } else if (nargs == 3 && @@ -158,11 +159,13 @@ VIR_MOCK_WRAP_RET_ARGS(dbus_connection_send_with_reply_and_block, STREQ(args[0], "-t") && STREQ(args[1], "nat") && STREQ(args[2], "-L")) { - if (virDBusCreateReply(&reply, + if (virDBusCreateReply(message, + &reply, "s", TEST_NAT_TABLE_LIST) < 0) goto error; } else { - if (virDBusCreateReply(&reply, + if (virDBusCreateReply(message, + &reply, "s", "success") < 0) goto error; } diff --git a/tests/virpolkittest.c b/tests/virpolkittest.c index 94a6daae0c..598eca8803 100644 --- a/tests/virpolkittest.c +++ b/tests/virpolkittest.c @@ -123,7 +123,8 @@ VIR_MOCK_WRAP_RET_ARGS(dbus_connection_send_with_reply_and_block, VIR_FREE(cancellationId); virStringListFreeCount(details, detailslen); - if (virDBusCreateReply(&reply, + if (virDBusCreateReply(message, + &reply, "(bba&{ss})", is_authorized, is_challenge, -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Once QEMU is started, the qemuDomainLogContext is owned by it, and can no longer be used from libvirt. Instead, use qemuDomainLogAppendMessage() which will redirect the log. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_extdevice.c | 40 ++++++++++++--------------------------- src/qemu/qemu_extdevice.h | 10 +++++----- src/qemu/qemu_process.c | 2 +- src/qemu/qemu_tpm.c | 10 ++++------ src/qemu/qemu_tpm.h | 5 ++--- 5 files changed, 24 insertions(+), 43 deletions(-) diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c index a21caefaba..79d5d64951 100644 --- a/src/qemu/qemu_extdevice.c +++ b/src/qemu/qemu_extdevice.c @@ -36,39 +36,24 @@ VIR_LOG_INIT("qemu.qemu_extdevice"); int -qemuExtDeviceLogCommand(qemuDomainLogContextPtr logCtxt, +qemuExtDeviceLogCommand(virQEMUDriverPtr driver, + virDomainObjPtr vm, virCommandPtr cmd, const char *info) { - int ret = -1; - char *timestamp = NULL; - char *logline = NULL; - int logFD; + VIR_AUTOFREE(char *) timestamp = virTimeStringNow(); + VIR_AUTOFREE(char *) cmds = virCommandToString(cmd, false); - logFD = qemuDomainLogContextGetWriteFD(logCtxt); - - if ((timestamp = virTimeStringNow()) == NULL) - goto cleanup; - - if (virAsprintf(&logline, "%s: Starting external device: %s\n", - timestamp, info) < 0) - goto cleanup; - - if (safewrite(logFD, logline, strlen(logline)) < 0) - goto cleanup; - - virCommandWriteArgLog(cmd, logFD); - - ret = 0; - - cleanup: - VIR_FREE(timestamp); - VIR_FREE(logline); + if (!timestamp || !cmds) + return -1; - return ret; + return qemuDomainLogAppendMessage(driver, vm, + _("%s: Starting external device: %s\n%s\n"), + timestamp, info, cmds); } + /* * qemuExtDevicesInitPaths: * @@ -127,8 +112,7 @@ qemuExtDevicesCleanupHost(virQEMUDriverPtr driver, int qemuExtDevicesStart(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDomainLogContextPtr logCtxt) + virDomainObjPtr vm) { int ret = 0; @@ -136,7 +120,7 @@ qemuExtDevicesStart(virQEMUDriverPtr driver, return -1; if (vm->def->tpm) - ret = qemuExtTPMStart(driver, vm, logCtxt); + ret = qemuExtTPMStart(driver, vm); return ret; } diff --git a/src/qemu/qemu_extdevice.h b/src/qemu/qemu_extdevice.h index a72e05ce63..c9a3109fb6 100644 --- a/src/qemu/qemu_extdevice.h +++ b/src/qemu/qemu_extdevice.h @@ -23,10 +23,11 @@ #include "qemu_conf.h" #include "qemu_domain.h" -int qemuExtDeviceLogCommand(qemuDomainLogContextPtr logCtxt, +int qemuExtDeviceLogCommand(virQEMUDriverPtr driver, + virDomainObjPtr vm, virCommandPtr cmd, const char *info) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; int qemuExtDevicesPrepareHost(virQEMUDriverPtr driver, @@ -39,9 +40,8 @@ void qemuExtDevicesCleanupHost(virQEMUDriverPtr driver, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); int qemuExtDevicesStart(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDomainLogContextPtr logCtxt) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) + virDomainObjPtr vm) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; void qemuExtDevicesStop(virQEMUDriverPtr driver, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index aa09ef175a..411ecf17ad 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6705,7 +6705,7 @@ qemuProcessLaunch(virConnectPtr conn, if (qemuProcessGenID(vm, flags) < 0) goto cleanup; - if (qemuExtDevicesStart(driver, vm, logCtxt) < 0) + if (qemuExtDevicesStart(driver, vm) < 0) goto cleanup; VIR_DEBUG("Building emulator command line"); diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c index cc8c69433b..200a3d3391 100644 --- a/src/qemu/qemu_tpm.c +++ b/src/qemu/qemu_tpm.c @@ -754,8 +754,7 @@ qemuExtTPMCleanupHost(virDomainDefPtr def) */ static int qemuExtTPMStartEmulator(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDomainLogContextPtr logCtxt) + virDomainObjPtr vm) { int ret = -1; virCommandPtr cmd = NULL; @@ -782,7 +781,7 @@ qemuExtTPMStartEmulator(virQEMUDriverPtr driver, cfg->swtpmStateDir, shortName))) goto cleanup; - if (qemuExtDeviceLogCommand(logCtxt, cmd, "TPM Emulator") < 0) + if (qemuExtDeviceLogCommand(driver, vm, cmd, "TPM Emulator") < 0) goto cleanup; virCommandSetErrorBuffer(cmd, &errbuf); @@ -835,15 +834,14 @@ qemuExtTPMStartEmulator(virQEMUDriverPtr driver, int qemuExtTPMStart(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDomainLogContextPtr logCtxt) + virDomainObjPtr vm) { int ret = 0; virDomainTPMDefPtr tpm = vm->def->tpm; switch (tpm->type) { case VIR_DOMAIN_TPM_TYPE_EMULATOR: - ret = qemuExtTPMStartEmulator(driver, vm, logCtxt); + ret = qemuExtTPMStartEmulator(driver, vm); break; case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: case VIR_DOMAIN_TPM_TYPE_LAST: diff --git a/src/qemu/qemu_tpm.h b/src/qemu/qemu_tpm.h index 74c9924d68..0be94bac3f 100644 --- a/src/qemu/qemu_tpm.h +++ b/src/qemu/qemu_tpm.h @@ -36,9 +36,8 @@ void qemuExtTPMCleanupHost(virDomainDefPtr def) ATTRIBUTE_NONNULL(1); int qemuExtTPMStart(virQEMUDriverPtr driver, - virDomainObjPtr vm, - qemuDomainLogContextPtr logCtxt) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) + virDomainObjPtr vm) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; void qemuExtTPMStop(virQEMUDriverPtr driver, -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> pid filenames (from swtpm and other helpers from this series) are based on VM shortname, which is derived from VM id. If the id is reset to early, the state filenames will not be found. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_process.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 411ecf17ad..5156b4906c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7324,8 +7324,6 @@ void qemuProcessStop(virQEMUDriverPtr driver, qemuProcessBuildDestroyMemoryPaths(driver, vm, NULL, false); - vm->def->id = -1; - if (virAtomicIntDecAndTest(&driver->nactive) && driver->inhibitCallback) driver->inhibitCallback(false, driver->inhibitOpaque); @@ -7401,6 +7399,8 @@ void qemuProcessStop(virQEMUDriverPtr driver, qemuExtDevicesStop(driver, vm); + vm->def->id = -1; + /* Stop autodestroy in case guest is restarted */ qemuProcessAutoDestroyRemove(driver, vm); -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Datagram socket is available since qemu 4.0, commit fdec16e3c2a614e2861f3086b05d444b5d8c3406 ("net/socket: learn to talk with a unix dgram socket"). Required for slirp-helper communication. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_capabilities.c | 8 ++++++++ src/qemu/qemu_capabilities.h | 3 +++ tests/qemucapabilitiesdata/caps_4.0.0.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_4.0.0.ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml | 1 + tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml | 1 + tests/qemucapabilitiesdata/caps_4.0.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_4.0.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_4.1.0.x86_64.xml | 1 + 9 files changed, 18 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 02e84edc15..2229aea4f5 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -533,6 +533,9 @@ VIR_ENUM_IMPL(virQEMUCaps, "x86-max-cpu", "cpu-unavailable-features", "canonical-cpu-features", + + /* 335 */ + "net-socket-dgram", ); @@ -4373,6 +4376,11 @@ virQEMUCapsInitQMPVersionCaps(virQEMUCapsPtr qemuCaps) ARCH_IS_PPC64(qemuCaps->arch)) { virQEMUCapsSet(qemuCaps, QEMU_CAPS_MACHINE_PSERIES_MAX_CPU_COMPAT); } + + /* -net socket,fd= with dgram socket (for ex, with slirp helper) */ + if (qemuCaps->version >= 3001092) { + virQEMUCapsSet(qemuCaps, QEMU_CAPS_NET_SOCKET_DGRAM); + } } diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 915ba6cb2e..564e3bfe55 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -515,6 +515,9 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_CPU_UNAVAILABLE_FEATURES, /* "unavailable-features" CPU property */ QEMU_CAPS_CANONICAL_CPU_FEATURES, /* avoid CPU feature aliases */ + /* 335 */ + QEMU_CAPS_NET_SOCKET_DGRAM, /* -net socket,fd= with dgram socket */ + QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_4.0.0.aarch64.xml b/tests/qemucapabilitiesdata/caps_4.0.0.aarch64.xml index 250b7edd52..a0846b0782 100644 --- a/tests/qemucapabilitiesdata/caps_4.0.0.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_4.0.0.aarch64.xml @@ -163,6 +163,7 @@ <flag name='machine.virt.iommu'/> <flag name='bitmap-merge'/> <flag name='nbd-bitmap'/> + <flag name='net-socket-dgram'/> <version>4000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>61700758</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_4.0.0.ppc64.xml b/tests/qemucapabilitiesdata/caps_4.0.0.ppc64.xml index 24b55002a6..6ebf003f51 100644 --- a/tests/qemucapabilitiesdata/caps_4.0.0.ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_4.0.0.ppc64.xml @@ -168,6 +168,7 @@ <flag name='query-current-machine'/> <flag name='bitmap-merge'/> <flag name='nbd-bitmap'/> + <flag name='net-socket-dgram'/> <version>4000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>42900758</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml b/tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml index 230e1e7c99..8ba9e41dae 100644 --- a/tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml +++ b/tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml @@ -166,6 +166,7 @@ <flag name='query-current-machine'/> <flag name='bitmap-merge'/> <flag name='nbd-bitmap'/> + <flag name='net-socket-dgram'/> <version>4000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>0</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml b/tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml index 4b2f4cf628..dd0e1ca6f6 100644 --- a/tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml +++ b/tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml @@ -166,6 +166,7 @@ <flag name='query-current-machine'/> <flag name='bitmap-merge'/> <flag name='nbd-bitmap'/> + <flag name='net-socket-dgram'/> <version>4000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>0</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_4.0.0.s390x.xml b/tests/qemucapabilitiesdata/caps_4.0.0.s390x.xml index a1ac2587a0..9c50b1afdf 100644 --- a/tests/qemucapabilitiesdata/caps_4.0.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_4.0.0.s390x.xml @@ -131,6 +131,7 @@ <flag name='query-current-machine'/> <flag name='bitmap-merge'/> <flag name='nbd-bitmap'/> + <flag name='net-socket-dgram'/> <version>4000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>39100758</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_4.0.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_4.0.0.x86_64.xml index 716b756979..cd2f74b219 100644 --- a/tests/qemucapabilitiesdata/caps_4.0.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_4.0.0.x86_64.xml @@ -205,6 +205,7 @@ <flag name='bitmap-merge'/> <flag name='nbd-bitmap'/> <flag name='x86-max-cpu'/> + <flag name='net-socket-dgram'/> <version>4000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100758</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_4.1.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_4.1.0.x86_64.xml index 9cbf65b405..ca1d7dbbfe 100644 --- a/tests/qemucapabilitiesdata/caps_4.1.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_4.1.0.x86_64.xml @@ -207,6 +207,7 @@ <flag name='x86-max-cpu'/> <flag name='cpu-unavailable-features'/> <flag name='canonical-cpu-features'/> + <flag name='net-socket-dgram'/> <version>4000050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100759</microcodeVersion> -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> This object is being proposed to qemu upstream "Add dbus-vmstate object". It handles data migration of external processes. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 2229aea4f5..9e6390af52 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -536,6 +536,7 @@ VIR_ENUM_IMPL(virQEMUCaps, /* 335 */ "net-socket-dgram", + "dbus-vmstate", ); @@ -1124,6 +1125,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "virtio-serial-pci-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL }, { "virtio-serial-pci-non-transitional", QEMU_CAPS_VIRTIO_PCI_TRANSITIONAL }, { "max-x86_64-cpu", QEMU_CAPS_X86_MAX_CPU }, + { "dbus-vmstate", QEMU_CAPS_DBUS_VMSTATE }, }; static struct virQEMUCapsStringFlags virQEMUCapsDevicePropsVirtioBalloon[] = { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 564e3bfe55..d71198802b 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -517,6 +517,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 335 */ QEMU_CAPS_NET_SOCKET_DGRAM, /* -net socket,fd= with dgram socket */ + QEMU_CAPS_DBUS_VMSTATE, /* -object dbus-vmstate */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- m4/virt-driver-qemu.m4 | 5 +++++ src/qemu/libvirtd_qemu.aug | 1 + src/qemu/qemu.conf | 3 +++ src/qemu/qemu_conf.c | 7 ++++++- src/qemu/qemu_conf.h | 1 + src/qemu/test_libvirtd_qemu.aug.in | 1 + 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/m4/virt-driver-qemu.m4 b/m4/virt-driver-qemu.m4 index a1d05bbd7f..1bc1755620 100644 --- a/m4/virt-driver-qemu.m4 +++ b/m4/virt-driver-qemu.m4 @@ -105,6 +105,11 @@ AC_DEFUN([LIBVIRT_DRIVER_CHECK_QEMU], [ [/usr/bin:/usr/libexec]) AC_DEFINE_UNQUOTED([QEMU_PR_HELPER], ["$QEMU_PR_HELPER"], [QEMU PR helper]) + AC_PATH_PROG([QEMU_DBUS_DAEMON], [dbus-daemon], + [/usr/bin/dbus-daemon], + [/usr/bin:/usr/libexec]) + AC_DEFINE_UNQUOTED([QEMU_DBUS_DAEMON], ["$QEMU_DBUS_DAEMON"], + [QEMU dbus daemon]) ]) AC_DEFUN([LIBVIRT_DRIVER_RESULT_QEMU], [ diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index eea9094d39..ba4caabc42 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -89,6 +89,7 @@ module Libvirtd_qemu = | bool_entry "clear_emulator_capabilities" | str_entry "bridge_helper" | str_entry "pr_helper" + | str_entry "dbus_daemon" | bool_entry "set_process_name" | int_entry "max_processes" | int_entry "max_files" diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index fd2ed9dc21..26bab39123 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -823,6 +823,9 @@ # used whenever <reservations/> are enabled for SCSI LUN devices. #pr_helper = "/usr/bin/qemu-pr-helper" +# Path to the dbus-daemon +#dbus_daemon = "/usr/bin/dbus-daemon" + # User for the swtpm TPM Emulator # # Default is 'tss'; this is the same user that tcsd (TrouSerS) installs diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index e0195dac29..6f15ce2086 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -278,7 +278,8 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) } if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0 || - VIR_STRDUP(cfg->prHelperName, QEMU_PR_HELPER) < 0) + VIR_STRDUP(cfg->prHelperName, QEMU_PR_HELPER) < 0 || + VIR_STRDUP(cfg->dbusDaemonName, QEMU_DBUS_DAEMON) < 0) goto error; cfg->clearEmulatorCapabilities = true; @@ -368,6 +369,7 @@ static void virQEMUDriverConfigDispose(void *obj) VIR_FREE(cfg->hugetlbfs); VIR_FREE(cfg->bridgeHelperName); VIR_FREE(cfg->prHelperName); + VIR_FREE(cfg->dbusDaemonName); VIR_FREE(cfg->saveImageFormat); VIR_FREE(cfg->dumpImageFormat); @@ -664,6 +666,9 @@ virQEMUDriverConfigLoadProcessEntry(virQEMUDriverConfigPtr cfg, if (virConfGetValueString(conf, "pr_helper", &cfg->prHelperName) < 0) return -1; + if (virConfGetValueString(conf, "dbus_daemon", &cfg->dbusDaemonName) < 0) + return -1; + if (virConfGetValueBool(conf, "set_process_name", &cfg->setProcessName) < 0) return -1; if (virConfGetValueUInt(conf, "max_processes", &cfg->maxProcesses) < 0) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e51514a344..0799846001 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -151,6 +151,7 @@ struct _virQEMUDriverConfig { char *bridgeHelperName; char *prHelperName; + char *dbusDaemonName; bool macFilter; diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index 388ba24b8b..e135869717 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -103,6 +103,7 @@ module Test_libvirtd_qemu = } { "memory_backing_dir" = "/var/lib/libvirt/qemu/ram" } { "pr_helper" = "/usr/bin/qemu-pr-helper" } +{ "dbus_daemon" = "/usr/bin/dbus-daemon" } { "swtpm_user" = "tss" } { "swtpm_group" = "tss" } { "capability_filters" -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Location of DBus daemon state configuration, socket, pid... Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_conf.c | 8 ++++++++ src/qemu/qemu_conf.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 6f15ce2086..5f6f93d828 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -149,6 +149,10 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) "%s/run/libvirt/qemu/swtpm", LOCALSTATEDIR) < 0) goto error; + if (virAsprintf(&cfg->dbusStateDir, + "%s/run/libvirt/qemu/dbus", LOCALSTATEDIR) < 0) + goto error; + if (virAsprintf(&cfg->cacheDir, "%s/cache/libvirt/qemu", LOCALSTATEDIR) < 0) goto error; @@ -214,6 +218,9 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) if (virAsprintf(&cfg->swtpmStateDir, "%s/swtpm", cfg->stateDir) < 0) goto error; + if (virAsprintf(&cfg->dbusStateDir, "%s/dbus", cfg->stateDir) < 0) + goto error; + if (!(cfg->configBaseDir = virGetUserConfigDirectory())) goto error; @@ -331,6 +338,7 @@ static void virQEMUDriverConfigDispose(void *obj) VIR_FREE(cfg->swtpmLogDir); VIR_FREE(cfg->stateDir); VIR_FREE(cfg->swtpmStateDir); + VIR_FREE(cfg->dbusStateDir); VIR_FREE(cfg->libDir); VIR_FREE(cfg->cacheDir); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 0799846001..c87cb8bc41 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -95,6 +95,7 @@ struct _virQEMUDriverConfig { char *swtpmLogDir; char *stateDir; char *swtpmStateDir; + char *dbusStateDir; /* These two directories are ones QEMU processes use (so must match * the QEMU user/group */ char *libDir; -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Add a generic way to run a command through the security management. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_security.c | 22 ++++++++++++++++++++++ src/qemu/qemu_security.h | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/src/qemu/qemu_security.c b/src/qemu/qemu_security.c index 87209d3781..f9aa94e0a7 100644 --- a/src/qemu/qemu_security.c +++ b/src/qemu/qemu_security.c @@ -631,3 +631,25 @@ qemuSecurityRestoreSavedStateLabel(virQEMUDriverPtr driver, virSecurityManagerTransactionAbort(driver->securityManager); return ret; } + + +int +qemuSecurityCommandRun(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virCommandPtr cmd, + int *exitstatus, + int *cmdret) +{ + if (virSecurityManagerSetChildProcessLabel(driver->securityManager, + vm->def, cmd) < 0) + return -1; + + if (virSecurityManagerPreFork(driver->securityManager) < 0) + return -1; + + *cmdret = virCommandRun(cmd, exitstatus); + + virSecurityManagerPostFork(driver->securityManager); + + return 0; +} diff --git a/src/qemu/qemu_security.h b/src/qemu/qemu_security.h index 68e377f418..8cf4ab0721 100644 --- a/src/qemu/qemu_security.h +++ b/src/qemu/qemu_security.h @@ -101,6 +101,12 @@ int qemuSecurityRestoreSavedStateLabel(virQEMUDriverPtr driver, virDomainObjPtr vm, const char *savefile); +int qemuSecurityCommandRun(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virCommandPtr cmd, + int *exitstatus, + int *cmdret); + /* Please note that for these APIs there is no wrapper yet. Do NOT blindly add * new APIs here. If an API can touch a file add a proper wrapper instead. */ -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Add a unit to start & stop a private dbus-daemon. The daemon is meant to be started on demand, and associated with a QEMU process. It is stopped when the QEMU process is stopped. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/Makefile.inc.am | 4 + src/qemu/qemu_dbus.c | 370 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_dbus.h | 40 +++++ src/qemu/qemu_domain.c | 10 ++ src/qemu/qemu_domain.h | 5 + src/qemu/qemu_process.c | 6 + tests/Makefile.am | 1 + 7 files changed, 436 insertions(+) create mode 100644 src/qemu/qemu_dbus.c create mode 100644 src/qemu/qemu_dbus.h diff --git a/src/qemu/Makefile.inc.am b/src/qemu/Makefile.inc.am index 254ba07dc0..248f3970c9 100644 --- a/src/qemu/Makefile.inc.am +++ b/src/qemu/Makefile.inc.am @@ -58,6 +58,8 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_qapi.h \ qemu/qemu_tpm.c \ qemu/qemu_tpm.h \ + qemu/qemu_dbus.c \ + qemu/qemu_dbus.h \ $(NULL) @@ -81,6 +83,7 @@ libvirt_driver_qemu_impl_la_CFLAGS = \ $(LIBNL_CFLAGS) \ $(SELINUX_CFLAGS) \ $(XDR_CFLAGS) \ + $(DBUS_CFLAGS) \ -I$(srcdir)/access \ -I$(srcdir)/conf \ -I$(srcdir)/secret \ @@ -92,6 +95,7 @@ libvirt_driver_qemu_impl_la_LIBADD = \ $(GNUTLS_LIBS) \ $(LIBNL_LIBS) \ $(SELINUX_LIBS) \ + $(DBUS_LIBS) \ $(LIBXML_LIBS) \ $(NULL) libvirt_driver_qemu_impl_la_SOURCES = $(QEMU_DRIVER_SOURCES) diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c new file mode 100644 index 0000000000..bf24c85910 --- /dev/null +++ b/src/qemu/qemu_dbus.c @@ -0,0 +1,370 @@ +/* + * qemu_dbus.c: QEMU dbus daemon + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "qemu_extdevice.h" +#include "qemu_dbus.h" +#include "qemu_security.h" + +#include "viralloc.h" +#include "virlog.h" +#include "virstring.h" +#include "virtime.h" +#include "virpidfile.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("qemu.dbus"); + + +int +qemuDBusPrepareHost(virQEMUDriverPtr driver) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + + return virDirCreate(cfg->dbusStateDir, 0770, cfg->user, cfg->group, + VIR_DIR_CREATE_ALLOW_EXIST); +} + + +static char * +qemuDBusCreatePidFilename(const char *stateDir, + const char *shortName) +{ + VIR_AUTOFREE(char *) name = NULL; + + if (virAsprintf(&name, "%s-dbus", shortName) < 0) + return NULL; + + return virPidFileBuildPath(stateDir, name); +} + + +static char * +qemuDBusCreateFilename(const char *stateDir, + const char *shortName, + const char *ext) +{ + VIR_AUTOFREE(char *) name = NULL; + + if (virAsprintf(&name, "%s-dbus", shortName) < 0) + return NULL; + + return virFileBuildPath(stateDir, name, ext); +} + + +static char * +qemuDBusCreateSocketPath(virQEMUDriverConfigPtr cfg, + const char *shortName) +{ + return qemuDBusCreateFilename(cfg->dbusStateDir, shortName, ".sock"); +} + + +char * +qemuDBusGetAddress(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + VIR_AUTOFREE(char *) shortName = virDomainDefGetShortName(vm->def); + VIR_AUTOFREE(char *) path = qemuDBusCreateSocketPath(cfg, shortName); + char *ret; + + ignore_value(virAsprintf(&ret, "unix:path=%s", path)); + + return ret; +} + + +static int +qemuDBusGetPid(const char *binPath, + const char *stateDir, + const char *shortName, + pid_t *pid) +{ + VIR_AUTOFREE(char *) pidfile = qemuDBusCreatePidFilename(stateDir, shortName); + + if (!pidfile) + return -ENOMEM; + + return virPidFileReadPathIfAlive(pidfile, pid, binPath); +} + + +static int +qemuDBusWriteConfig(const char *filename, const char *path) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + VIR_AUTOFREE(char *) config = NULL; + + virBufferAddLit(&buf, "<!DOCTYPE busconfig PUBLIC \"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN\"\n"); + virBufferAddLit(&buf, " \"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd\">\n"); + virBufferAddLit(&buf, "<busconfig>\n"); + virBufferAdjustIndent(&buf, 2); + + virBufferAddLit(&buf, "<type>org.libvirt.qemu</type>\n"); + + /* This may become useful: */ + /* virBufferAddLit(&buf, "<servicedir>" DATADIR "/dbus-1/libvirt-qemu-services</servicedir>\n"); */ + + virBufferAsprintf(&buf, "<listen>unix:path=%s</listen>\n", path); + + virBufferAddLit(&buf, "<auth>EXTERNAL</auth>\n"); + + virBufferAddLit(&buf, "<policy context='default'>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAddLit(&buf, "<!-- Allow everything to be sent -->\n"); + virBufferAddLit(&buf, "<allow send_destination='*' eavesdrop='true'/>\n"); + virBufferAddLit(&buf, "<!-- Allow everything to be received -->\n"); + virBufferAddLit(&buf, "<allow eavesdrop='true'/>\n"); + virBufferAddLit(&buf, "<!-- Allow anyone to own anything -->\n"); + virBufferAddLit(&buf, "<allow own='*'/>\n"); + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</policy>\n"); + + virBufferAddLit(&buf, "<include if_selinux_enabled='yes' selinux_root_relative='yes'>contexts/dbus_contexts</include>\n"); + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</busconfig>\n"); + + if (virBufferCheckError(&buf) < 0) + return -1; + + config = virBufferContentAndReset(&buf); + + return virFileWriteStr(filename, config, 0600); +} + + +void +qemuDBusStop(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + VIR_AUTOFREE(char *) shortName = NULL; + VIR_AUTOFREE(char *) pidfile = NULL; + VIR_AUTOFREE(char *) configfile = NULL; + virErrorPtr orig_err; + int rc; + pid_t pid; + + shortName = virDomainDefGetShortName(vm->def); + pidfile = qemuDBusCreatePidFilename(cfg->dbusStateDir, shortName); + configfile = qemuDBusCreateFilename(cfg->dbusStateDir, shortName, ".conf"); + + if (!shortName || !pidfile || !configfile) + return; + + rc = qemuDBusGetPid(cfg->dbusDaemonName, cfg->dbusStateDir, shortName, &pid); + if (rc == 0 && pid != (pid_t)-1) { + char ebuf[1024]; + + VIR_DEBUG("Killing dbus-daemon process %lld", (long long)pid); + if (virProcessKill(pid, SIGTERM) < 0 && errno != ESRCH) + VIR_ERROR(_("Failed to kill process %lld: %s"), + (long long)pid, + virStrerror(errno, ebuf, sizeof(ebuf))); + } + + virErrorPreserveLast(&orig_err); + if (virPidFileForceCleanupPath(pidfile) < 0) { + VIR_WARN("Unable to kill dbus-daemon process"); + } else { + if (unlink(pidfile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale pidfile %s"), + pidfile); + } + } + if (unlink(configfile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale configfile %s"), + pidfile); + } + virErrorRestore(&orig_err); + + VIR_FREE(pidfile); + + priv->dbusDaemonRunning = false; +} + + +int +qemuDBusStart(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + VIR_AUTOPTR(virCommand) cmd = NULL; + VIR_AUTOFREE(char *) shortName = NULL; + VIR_AUTOFREE(char *) pidfile = NULL; + VIR_AUTOFREE(char *) configfile = NULL; + VIR_AUTOFREE(char *) sockpath = NULL; + virTimeBackOffVar timebackoff; + const unsigned long long timeout = 500 * 1000; /* ms */ + int errfd = -1; + int cmdret = 0; + int exitstatus = 0; + + if (priv->dbusDaemonRunning) + return 0; + + /* cleanup */ + qemuDBusStop(driver, vm); + + cmd = virCommandNew(cfg->dbusDaemonName); + shortName = virDomainDefGetShortName(vm->def); + pidfile = qemuDBusCreatePidFilename(cfg->dbusStateDir, shortName); + configfile = qemuDBusCreateFilename(cfg->dbusStateDir, shortName, ".conf"); + sockpath = qemuDBusCreateSocketPath(cfg, shortName); + + if (!cmd || !pidfile || !configfile || !sockpath) + return -1; + + if (qemuDBusWriteConfig(configfile, sockpath) < 0) { + virReportSystemError(errno, _("Failed to write '%s'"), configfile); + return -1; + } + + if (qemuSecurityDomainSetPathLabel(driver, vm, configfile, true) < 0) + return -1; + + virCommandClearCaps(cmd); + virCommandSetPidFile(cmd, pidfile); + virCommandSetErrorFD(cmd, &errfd); + virCommandDaemonize(cmd); + virCommandAddArgFormat(cmd, "--config-file=%s", configfile); + + if (qemuExtDeviceLogCommand(driver, vm, cmd, "DBus") < 0) + return -1; + + if (qemuSecurityCommandRun(driver, vm, cmd, + &exitstatus, &cmdret) < 0) + return -1; + + if (cmdret < 0 || exitstatus != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not start 'vhost-user-gpu'. exitstatus: %d"), + exitstatus); + return -1; + } + + if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) + return -1; + while (virTimeBackOffWait(&timebackoff)) { + pid_t pid; + + if (qemuDBusGetPid(cfg->dbusDaemonName, cfg->dbusStateDir, shortName, &pid) < 0) + continue; + + if (pid == (pid_t)-1) + break; + + if (virFileExists(sockpath)) + break; + } + + if (!virFileExists(sockpath)) { + char errbuf[1024] = { 0 }; + + if (saferead(errfd, errbuf, sizeof(errbuf) - 1) < 0) { + virReportSystemError(errno, "%s", _("dbus-daemon died unexpectedly")); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("dbus-daemon died and reported: %s"), errbuf); + } + + return -1; + } + + if (qemuSecurityDomainSetPathLabel(driver, vm, sockpath, true) < 0) + return -1; + + priv->dbusDaemonRunning = true; + + return 0; +} + + +int +qemuDBusSetupCgroup(virQEMUDriverPtr driver, + virDomainDefPtr def, + virCgroupPtr cgroup) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + VIR_AUTOFREE(char *) shortName = virDomainDefGetShortName(def); + pid_t pid; + int rc; + + rc = qemuDBusGetPid(cfg->dbusDaemonName, cfg->dbusStateDir, shortName, &pid); + if (rc < 0 || (rc == 0 && pid == (pid_t)-1)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not get process id of dbus-daemon")); + return -1; + } + + if (virCgroupAddProcess(cgroup, pid) < 0) + return -1; + + return 0; +} + + +int +qemuDBusConnect(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + VIR_AUTOFREE(char *) addr = qemuDBusGetAddress(driver, vm); + DBusConnection *conn = NULL; + DBusError error; + int ret = -1; + + if (!priv->dbusDaemonRunning || priv->dbusConn) + return 0; + + dbus_error_init(&error); + conn = dbus_connection_open(addr, &error); + if (!conn) { + virReportError(VIR_ERR_DBUS_SERVICE, _("failed to connect to DBus: %s"), + error.message ? : _("unknown error")); + goto end; + } + + if (!dbus_bus_register(conn, &error)) { + virReportError(VIR_ERR_DBUS_SERVICE, _("failed to register to DBus: %s"), + error.message ? : _("unknown error")); + goto end; + } + + priv->dbusConn = conn; + conn = NULL; + ret = 0; + +end: + if (conn) { + dbus_connection_unref(conn); + } + dbus_error_free(&error); + return ret; +} diff --git a/src/qemu/qemu_dbus.h b/src/qemu/qemu_dbus.h new file mode 100644 index 0000000000..8728824bd7 --- /dev/null +++ b/src/qemu/qemu_dbus.h @@ -0,0 +1,40 @@ +/* + * qemu_dbus.h: QEMU dbus daemon + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "qemu_conf.h" +#include "qemu_domain.h" + +int qemuDBusPrepareHost(virQEMUDriverPtr driver); + +char *qemuDBusGetAddress(virQEMUDriverPtr driver, + virDomainObjPtr vm); + +int qemuDBusConnect(virQEMUDriverPtr driver, + virDomainObjPtr vm); + +int qemuDBusStart(virQEMUDriverPtr driver, + virDomainObjPtr vm); + +void qemuDBusStop(virQEMUDriverPtr driver, + virDomainObjPtr vm); + +int qemuDBusSetupCgroup(virQEMUDriverPtr driver, + virDomainDefPtr def, + virCgroupPtr cgroup); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 6225ac23e2..192aceb605 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2051,6 +2051,11 @@ qemuDomainObjPrivateDataClear(qemuDomainObjPrivatePtr priv) qemuDomainObjResetJob(priv); qemuDomainObjResetAsyncJob(priv); + + if (priv->dbusConn) { + dbus_connection_unref(priv->dbusConn); + priv->dbusConn = NULL; + } } @@ -2479,6 +2484,9 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf, virDomainChrTypeToString(priv->monConfig->type)); } + if (priv->dbusDaemonRunning) + virBufferAddLit(buf, "<dbusDaemon/>\n"); + if (priv->namespaces) { ssize_t ns = -1; @@ -2910,6 +2918,8 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, goto error; } + priv->dbusDaemonRunning = virXPathBoolean("boolean(./dbusDaemon)", ctxt) > 0; + if ((node = virXPathNode("./namespaces", ctxt))) { xmlNodePtr next; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 3eea8b0f96..9d14163c21 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -39,6 +39,7 @@ #include "logging/log_manager.h" #include "virdomainmomentobjlist.h" #include "virenum.h" +#include "virdbus.h" #define QEMU_DOMAIN_FORMAT_LIVE_FLAGS \ (VIR_DOMAIN_XML_SECURE) @@ -386,6 +387,10 @@ struct _qemuDomainObjPrivate { /* true if global -mem-prealloc appears on cmd line */ bool memPrealloc; + + /* true if dbus-daemon is running */ + bool dbusDaemonRunning; + DBusConnection *dbusConn; }; #define QEMU_DOMAIN_PRIVATE(vm) \ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 5156b4906c..5e8184b0e2 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -57,6 +57,7 @@ #include "qemu_security.h" #include "qemu_extdevice.h" #include "qemu_firmware.h" +#include "qemu_dbus.h" #include "cpu/cpu.h" #include "cpu/cpu_x86.h" @@ -6452,6 +6453,9 @@ qemuProcessPrepareHost(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + if (qemuDBusPrepareHost(driver) < 0) + goto cleanup; + if (qemuPrepareNVRAM(cfg, vm) < 0) goto cleanup; @@ -7399,6 +7403,8 @@ void qemuProcessStop(virQEMUDriverPtr driver, qemuExtDevicesStop(driver, vm); + qemuDBusStop(driver, vm); + vm->def->id = -1; /* Stop autodestroy in case guest is restarted */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 115afa1c1a..85d0d8f614 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -42,6 +42,7 @@ AM_CFLAGS = \ $(APPARMOR_CFLAGS) \ $(YAJL_CFLAGS) \ $(XDR_CFLAGS) \ + $(DBUS_CFLAGS) \ $(WARN_CFLAGS) AM_LDFLAGS = \ -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Helper processes may have their state migrated with QEMU data stream thanks to the proposed object "dbus-vmstate". libvirt knows what helpers should be migrated. The "dbus-vmstate" is added when required, and given the list of helper Ids that must be present during migration (save & load). See QEMU "Add dbus-vmstate object" patch for further documentation on the helper expected behaviour. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_alias.c | 7 +++ src/qemu/qemu_alias.h | 2 + src/qemu/qemu_command.c | 65 ++++++++++++++++++++++++++++ src/qemu/qemu_command.h | 3 ++ src/qemu/qemu_dbus.c | 14 ++++++ src/qemu/qemu_dbus.h | 4 ++ src/qemu/qemu_domain.c | 8 ++++ src/qemu/qemu_domain.h | 5 +++ src/qemu/qemu_hotplug.c | 82 ++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 8 ++++ src/qemu/qemu_migration.c | 51 ++++++++++++++++++++++ src/qemu/qemu_monitor.c | 22 ++++++++++ src/qemu/qemu_monitor.h | 3 ++ src/qemu/qemu_monitor_json.c | 15 +++++++ src/qemu/qemu_monitor_json.h | 5 +++ 15 files changed, 294 insertions(+) diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 585cc972ba..a901932034 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -843,3 +843,10 @@ qemuDomainGetUnmanagedPRAlias(const char *parentalias) return ret; } + + +const char * +qemuDomainGetDBusVMStateAlias(void) +{ + return "dbus-vmstate0"; +} diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index aaac09a1d1..e3492116c5 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -95,3 +95,5 @@ char *qemuAliasChardevFromDevAlias(const char *devAlias) const char *qemuDomainGetManagedPRAlias(void); char *qemuDomainGetUnmanagedPRAlias(const char *parentalias); + +const char *qemuDomainGetDBusVMStateAlias(void); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 688dc324c6..040e8e2b12 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -24,6 +24,7 @@ #include "qemu_command.h" #include "qemu_hostdev.h" #include "qemu_capabilities.h" +#include "qemu_dbus.h" #include "qemu_interface.h" #include "qemu_alias.h" #include "qemu_security.h" @@ -10406,6 +10407,67 @@ qemuBuildManagedPRCommandLine(virCommandPtr cmd, } +static virJSONValuePtr +qemuBuildDBusVMStateInfoPropsInternal(const char *alias, + const char *addr) +{ + virJSONValuePtr ret = NULL; + + if (qemuMonitorCreateObjectProps(&ret, + "dbus-vmstate", alias, + "s:addr", addr, NULL) < 0) + return NULL; + + return ret; +} + + +virJSONValuePtr +qemuBuildDBusVMStateInfoProps(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + VIR_AUTOFREE(char *) addr = qemuDBusGetAddress(driver, vm); + + if (!addr) + return NULL; + + return qemuBuildDBusVMStateInfoPropsInternal(qemuDomainGetDBusVMStateAlias(), + addr); +} + + +static int +qemuBuildDBusVMStateCommandLine(virCommandPtr cmd, + virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + VIR_AUTOCLEAN(virBuffer) buf = VIR_BUFFER_INITIALIZER; + VIR_AUTOPTR(virJSONValue) props = NULL; + qemuDomainObjPrivatePtr priv = QEMU_DOMAIN_PRIVATE(vm); + + if (virStringListLength((const char **)priv->dbusVMStateIds) == 0) + return 0; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) { + VIR_INFO("dbus-vmstate object is not supported by this QEMU binary"); + return 0; + } + + if (!(props = qemuBuildDBusVMStateInfoProps(driver, vm))) + return -1; + + if (virQEMUBuildObjectCommandlineFromJSON(&buf, props) < 0) + return -1; + + virCommandAddArg(cmd, "-object"); + virCommandAddArgBuffer(cmd, &buf); + + priv->dbusVMState = true; + + return 0; +} + + /** * qemuBuildCommandLineValidate: * @@ -10650,6 +10712,9 @@ qemuBuildCommandLine(virQEMUDriverPtr driver, if (qemuBuildMasterKeyCommandLine(cmd, priv) < 0) goto error; + if (qemuBuildDBusVMStateCommandLine(cmd, driver, vm) < 0) + goto error; + if (qemuBuildManagedPRCommandLine(cmd, def, priv) < 0) goto error; diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 8695832c16..8bb941e2bc 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -58,6 +58,9 @@ virCommandPtr qemuBuildCommandLine(virQEMUDriverPtr driver, virJSONValuePtr qemuBuildPRManagerInfoProps(virStorageSourcePtr src); virJSONValuePtr qemuBuildPRManagedManagerInfoProps(qemuDomainObjPrivatePtr priv); +virJSONValuePtr qemuBuildDBusVMStateInfoProps(virQEMUDriverPtr driver, + virDomainObjPtr vm); + /* Generate the object properties for a secret */ int qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo, virJSONValuePtr *propsret); diff --git a/src/qemu/qemu_dbus.c b/src/qemu/qemu_dbus.c index bf24c85910..729be09069 100644 --- a/src/qemu/qemu_dbus.c +++ b/src/qemu/qemu_dbus.c @@ -368,3 +368,17 @@ end: dbus_error_free(&error); return ret; } + + +int +qemuDBusVMStateAdd(virDomainObjPtr vm, const char *id) +{ + return virStringListAdd(&QEMU_DOMAIN_PRIVATE(vm)->dbusVMStateIds, id); +} + + +void +qemuDBusVMStateRemove(virDomainObjPtr vm, const char *id) +{ + virStringListRemove(&QEMU_DOMAIN_PRIVATE(vm)->dbusVMStateIds, id); +} diff --git a/src/qemu/qemu_dbus.h b/src/qemu/qemu_dbus.h index 8728824bd7..e86134b2a1 100644 --- a/src/qemu/qemu_dbus.h +++ b/src/qemu/qemu_dbus.h @@ -38,3 +38,7 @@ void qemuDBusStop(virQEMUDriverPtr driver, int qemuDBusSetupCgroup(virQEMUDriverPtr driver, virDomainDefPtr def, virCgroupPtr cgroup); + +int qemuDBusVMStateAdd(virDomainObjPtr vm, const char *id); + +void qemuDBusVMStateRemove(virDomainObjPtr vm, const char *id); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 192aceb605..b7fcaab186 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2056,6 +2056,9 @@ qemuDomainObjPrivateDataClear(qemuDomainObjPrivatePtr priv) dbus_connection_unref(priv->dbusConn); priv->dbusConn = NULL; } + + virStringListFree(priv->dbusVMStateIds); + priv->dbusVMStateIds = NULL; } @@ -2487,6 +2490,9 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf, if (priv->dbusDaemonRunning) virBufferAddLit(buf, "<dbusDaemon/>\n"); + if (priv->dbusVMState) + virBufferAddLit(buf, "<dbusVMState/>\n"); + if (priv->namespaces) { ssize_t ns = -1; @@ -2920,6 +2926,8 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, priv->dbusDaemonRunning = virXPathBoolean("boolean(./dbusDaemon)", ctxt) > 0; + priv->dbusVMState = virXPathBoolean("boolean(./dbusVMState)", ctxt) > 0; + if ((node = virXPathNode("./namespaces", ctxt))) { xmlNodePtr next; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 9d14163c21..20c5fbec09 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -391,6 +391,11 @@ struct _qemuDomainObjPrivate { /* true if dbus-daemon is running */ bool dbusDaemonRunning; DBusConnection *dbusConn; + + /* list of Ids to migrate */ + char **dbusVMStateIds; + /* true if -object dbus-vmstate was added */ + bool dbusVMState; }; #define QEMU_DOMAIN_PRIVATE(vm) \ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 7e9c1a1649..ccff9a79d7 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -325,6 +325,88 @@ qemuDomainChangeMediaLegacy(virQEMUDriverPtr driver, } +/** + * qemuHotplugAttachDBusVMState: + * @driver: QEMU driver object + * @vm: domain object + * @asyncJob: asynchronous job identifier + * + * Add -object dbus-vmstate if necessary. + * + * Returns: 0 on success, -1 on error. + */ +int +qemuHotplugAttachDBusVMState(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + VIR_AUTOPTR(virJSONValue) props = NULL; + int ret; + + if (priv->dbusVMState) + return 0; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) { + VIR_INFO("dbus-vmstate object is not supported by this QEMU binary"); + return 0; + } + + if (!(props = qemuBuildDBusVMStateInfoProps(driver, vm))) + return -1; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return -1; + + ret = qemuMonitorAddObject(priv->mon, &props, NULL); + + if (ret == 0) + priv->dbusVMState = true; + + if (qemuDomainObjExitMonitor(driver, vm) < 0) + return -1; + + return ret; +} + + +/** + * qemuHotplugRemoveDBusVMState: + * @driver: QEMU driver object + * @vm: domain object + * @asyncJob: asynchronous job identifier + * + * Remove -object dbus-vmstate from @vm if the configuration does not require + * it any more. + * + * Returns: 0 on success, -1 on error. + */ +int +qemuHotplugRemoveDBusVMState(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int ret; + + if (!priv->dbusVMState) + return 0; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return -1; + + ret = qemuMonitorDelObject(priv->mon, qemuDomainGetDBusVMStateAlias()); + + if (ret == 0) + priv->dbusVMState = false; + + if (qemuDomainObjExitMonitor(driver, vm) < 0) + return -1; + + return ret; +} + + /** * qemuHotplugAttachManagedPR: * @driver: QEMU driver object diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 896e6c7b98..10896976be 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -150,3 +150,11 @@ int qemuDomainSetVcpuInternal(virQEMUDriverPtr driver, virDomainDefPtr persistentDef, virBitmapPtr vcpus, bool state); + +int qemuHotplugAttachDBusVMState(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob); + +int qemuHotplugRemoveDBusVMState(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainAsyncJob asyncJob); diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 2436f5051b..a11c6f1a9f 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1112,10 +1112,18 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver, bool remote, unsigned int flags) { + qemuDomainObjPrivatePtr priv = vm->privateData; int nsnapshots; int pauseReason; size_t i; + if (virStringListLength((const char **)priv->dbusVMStateIds) && + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot migrate this domain without dbus-vmstate support")); + return false; + } + /* perform these checks only when migrating to remote hosts */ if (remote) { nsnapshots = virDomainSnapshotObjListNum(vm->snapshots, NULL, 0); @@ -1846,8 +1854,14 @@ qemuMigrationDstRun(virQEMUDriverPtr driver, if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) return -1; + rv = qemuMonitorSetDBusVMStateIdList(priv->mon, + (const char **)priv->dbusVMStateIds); + if (rv < 0) + goto exit_monitor; + rv = qemuMonitorMigrateIncoming(priv->mon, uri); + exit_monitor: if (qemuDomainObjExitMonitor(driver, vm) < 0 || rv < 0) goto cleanup; @@ -3352,6 +3366,37 @@ qemuMigrationSrcContinue(virQEMUDriverPtr driver, } +static int +qemuMigrationSetDBusVMState(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + if (virStringListLength((const char **)priv->dbusVMStateIds) > 0) { + int rv; + + if (qemuHotplugAttachDBusVMState(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + return -1; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + return -1; + + rv = qemuMonitorSetDBusVMStateIdList(priv->mon, + (const char **)priv->dbusVMStateIds); + + if (qemuDomainObjExitMonitor(driver, vm) < 0) + rv = -1; + + return rv; + } else { + if (qemuHotplugRemoveDBusVMState(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + return -1; + } + + return 0; +} + + static int qemuMigrationSrcRun(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -3502,6 +3547,9 @@ qemuMigrationSrcRun(virQEMUDriverPtr driver, } } + if (qemuMigrationSetDBusVMState(driver, vm) < 0) + goto exit_monitor; + /* Before EnterMonitor, since already qemuProcessStopCPUs does that */ if (!(flags & VIR_MIGRATE_LIVE) && virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING) { @@ -5207,6 +5255,9 @@ qemuMigrationSrcToFile(virQEMUDriverPtr driver, virDomainObjPtr vm, char *errbuf = NULL; virErrorPtr orig_err = NULL; + if (qemuMigrationSetDBusVMState(driver, vm) < 0) + return -1; + /* Increase migration bandwidth to unlimited since target is a file. * Failure to change migration speed is not fatal. */ if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) == 0) { diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 731be2e5a6..ea57bad9f5 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -27,6 +27,7 @@ #include <unistd.h> #include <fcntl.h> +#include "qemu_alias.h" #include "qemu_monitor.h" #include "qemu_monitor_text.h" #include "qemu_monitor_json.h" @@ -2478,6 +2479,27 @@ qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, } +int +qemuMonitorSetDBusVMStateIdList(qemuMonitorPtr mon, + const char **list) +{ + VIR_AUTOFREE(char *) path = NULL; + + VIR_DEBUG("list=%p", list); + + if (virStringListLength(list) == 0) + return 0; + + if (virAsprintf(&path, "/objects/%s", + qemuDomainGetDBusVMStateAlias()) < 0) + return -1; + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSetDBusVMStateIdList(mon, path, list); +} + + int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, unsigned long bandwidth) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c41428b048..fcb029f97f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -685,6 +685,9 @@ int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, size_t length, const char *path); +int qemuMonitorSetDBusVMStateIdList(qemuMonitorPtr mon, + const char **list); + int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, unsigned long bandwidth); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 8723ff49c7..239005dbad 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2304,6 +2304,21 @@ qemuMonitorJSONSetMemoryStatsPeriod(qemuMonitorPtr mon, } +int +qemuMonitorJSONSetDBusVMStateIdList(qemuMonitorPtr mon, + const char *vmstatepath, + const char **list) +{ + VIR_AUTOFREE(char *) str = virStringListJoin(list, ","); + qemuMonitorJSONObjectProperty prop = { + .type = QEMU_MONITOR_OBJECT_PROPERTY_STRING, + .val.str = str, + }; + + return qemuMonitorJSONSetObjectProperty(mon, vmstatepath, "id-list", &prop); +} + + /* qemuMonitorJSONQueryBlock: * @mon: Monitor pointer * diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index d0b519c88e..1ed2b005e4 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -605,3 +605,8 @@ int qemuMonitorJSONMergeBitmaps(qemuMonitorPtr mon, int qemuMonitorJSONDeleteBitmap(qemuMonitorPtr mon, const char *node, const char *bitmap); + +int qemuMonitorJSONSetDBusVMStateIdList(qemuMonitorPtr mon, + const char *vmstatepath, + const char **list) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/conf/domain_conf.c | 21 ++++++++++++++++++++- src/conf/domain_conf.h | 6 ++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 3323c9a5b1..9e868b5337 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2451,6 +2451,7 @@ virDomainNetDefFree(virDomainNetDefPtr def) if (!def) return; virDomainNetDefClear(def); + virObjectUnref(def->privateData); VIR_FREE(def); } @@ -11434,7 +11435,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, VIR_AUTOFREE(char *) trustGuestRxFilters = NULL; VIR_AUTOFREE(char *) vhost_path = NULL; - if (VIR_ALLOC(def) < 0) + if (!(def = virDomainNetDefNew(xmlopt))) return NULL; ctxt->node = node; @@ -14312,6 +14313,24 @@ virDomainGraphicsDefNew(virDomainXMLOptionPtr xmlopt) } +virDomainNetDefPtr +virDomainNetDefNew(virDomainXMLOptionPtr xmlopt) +{ + virDomainNetDefPtr def = NULL; + + if (VIR_ALLOC(def) < 0) + return NULL; + + if (xmlopt && xmlopt->privateData.networkNew && + !(def->privateData = xmlopt->privateData.networkNew())) { + VIR_FREE(def); + def = NULL; + } + + return def; +} + + /* Parse the XML definition for a graphics device */ static virDomainGraphicsDefPtr virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index c1b5fc1337..ebe9ac183e 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1019,6 +1019,7 @@ struct _virDomainNetDef { unsigned int mtu; virNetDevCoalescePtr coalesce; virDomainVirtioOptionsPtr virtio; + virObjectPtr privateData; }; typedef enum { @@ -2708,6 +2709,7 @@ struct _virDomainXMLPrivateDataCallbacks { virDomainXMLPrivateDataNewFunc chrSourceNew; virDomainXMLPrivateDataNewFunc vsockNew; virDomainXMLPrivateDataNewFunc graphicsNew; + virDomainXMLPrivateDataNewFunc networkNew; virDomainXMLPrivateDataFormatFunc format; virDomainXMLPrivateDataParseFunc parse; /* following function shall return a pointer which will be used as the @@ -2891,6 +2893,10 @@ virDomainChrDefPtr virDomainChrDefNew(virDomainXMLOptionPtr xmlopt); virDomainGraphicsDefPtr virDomainGraphicsDefNew(virDomainXMLOptionPtr xmlopt); + +virDomainNetDefPtr +virDomainNetDefNew(virDomainXMLOptionPtr xmlopt); + virDomainDefPtr virDomainDefNew(void); void virDomainObjAssignDef(virDomainObjPtr domain, -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_domain.c | 39 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 12 ++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index b7fcaab186..825c23aa26 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1272,6 +1272,44 @@ qemuDomainGraphicsPrivateDispose(void *obj) } +static virClassPtr qemuDomainNetworkPrivateClass; +static void qemuDomainNetworkPrivateDispose(void *obj); + + +static int +qemuDomainNetworkPrivateOnceInit(void) +{ + if (!VIR_CLASS_NEW(qemuDomainNetworkPrivate, virClassForObject())) + return -1; + + return 0; +} + + +VIR_ONCE_GLOBAL_INIT(qemuDomainNetworkPrivate); + + +static virObjectPtr +qemuDomainNetworkPrivateNew(void) +{ + qemuDomainNetworkPrivatePtr priv; + + if (qemuDomainNetworkPrivateInitialize() < 0) + return NULL; + + if (!(priv = virObjectNew(qemuDomainNetworkPrivateClass))) + return NULL; + + return (virObjectPtr) priv; +} + + +static void +qemuDomainNetworkPrivateDispose(void *obj ATTRIBUTE_UNUSED) +{ +} + + /* qemuDomainSecretPlainSetup: * @secinfo: Pointer to secret info * @usageType: The virSecretUsageType @@ -3089,6 +3127,7 @@ virDomainXMLPrivateDataCallbacks virQEMUDriverPrivateDataCallbacks = { .chrSourceNew = qemuDomainChrSourcePrivateNew, .vsockNew = qemuDomainVsockPrivateNew, .graphicsNew = qemuDomainGraphicsPrivateNew, + .networkNew = qemuDomainNetworkPrivateNew, .parse = qemuDomainObjPrivateXMLParse, .format = qemuDomainObjPrivateXMLFormat, .getParseOpaque = qemuDomainObjPrivateXMLGetParseOpaque, diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 20c5fbec09..7ab44d3c43 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -511,6 +511,18 @@ struct _qemuDomainGraphicsPrivate { }; +#define QEMU_DOMAIN_NETWORK_PRIVATE(dev) \ + ((qemuDomainNetworkPrivatePtr) (dev)->privateData) + +typedef struct _qemuDomainNetworkPrivate qemuDomainNetworkPrivate; +typedef qemuDomainNetworkPrivate *qemuDomainNetworkPrivatePtr; +struct _qemuDomainNetworkPrivate { + virObject parent; + + bool tmp_to_be_larger_than_parent; +}; + + typedef enum { QEMU_PROCESS_EVENT_WATCHDOG = 0, QEMU_PROCESS_EVENT_GUESTPANIC, -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> A slirp helper is a process that provides user-mode networking through a unix domain socket. It is expected to follow the following specification: https://gitlab.freedesktop.org/slirp/libslirp-rs/blob/master/src/bin/README.... Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- m4/virt-driver-qemu.m4 | 5 +++++ src/qemu/libvirtd_qemu.aug | 1 + src/qemu/qemu.conf | 3 +++ src/qemu/qemu_conf.c | 5 +++++ src/qemu/qemu_conf.h | 1 + src/qemu/test_libvirtd_qemu.aug.in | 1 + 6 files changed, 16 insertions(+) diff --git a/m4/virt-driver-qemu.m4 b/m4/virt-driver-qemu.m4 index 1bc1755620..b050411b7b 100644 --- a/m4/virt-driver-qemu.m4 +++ b/m4/virt-driver-qemu.m4 @@ -110,6 +110,11 @@ AC_DEFUN([LIBVIRT_DRIVER_CHECK_QEMU], [ [/usr/bin:/usr/libexec]) AC_DEFINE_UNQUOTED([QEMU_DBUS_DAEMON], ["$QEMU_DBUS_DAEMON"], [QEMU dbus daemon]) + AC_PATH_PROG([QEMU_SLIRP_HELPER], [slirp-helper], + [/usr/bin/slirp-helper], + [/usr/bin:/usr/libexec]) + AC_DEFINE_UNQUOTED([QEMU_SLIRP_HELPER], ["$QEMU_SLIRP_HELPER"], + [QEMU slirp helper]) ]) AC_DEFUN([LIBVIRT_DRIVER_RESULT_QEMU], [ diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index ba4caabc42..e989fa8623 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -90,6 +90,7 @@ module Libvirtd_qemu = | str_entry "bridge_helper" | str_entry "pr_helper" | str_entry "dbus_daemon" + | str_entry "slirp_helper" | bool_entry "set_process_name" | int_entry "max_processes" | int_entry "max_files" diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 26bab39123..b8408b2f79 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -823,6 +823,9 @@ # used whenever <reservations/> are enabled for SCSI LUN devices. #pr_helper = "/usr/bin/qemu-pr-helper" +# Path to the SLIRP networking helper. +#slirp_helper = "/usr/bin/slirp-helper" + # Path to the dbus-daemon #dbus_daemon = "/usr/bin/dbus-daemon" diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 5f6f93d828..a986feecea 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -286,6 +286,7 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool privileged) if (VIR_STRDUP(cfg->bridgeHelperName, QEMU_BRIDGE_HELPER) < 0 || VIR_STRDUP(cfg->prHelperName, QEMU_PR_HELPER) < 0 || + VIR_STRDUP(cfg->slirpHelperName, QEMU_SLIRP_HELPER) < 0 || VIR_STRDUP(cfg->dbusDaemonName, QEMU_DBUS_DAEMON) < 0) goto error; @@ -377,6 +378,7 @@ static void virQEMUDriverConfigDispose(void *obj) VIR_FREE(cfg->hugetlbfs); VIR_FREE(cfg->bridgeHelperName); VIR_FREE(cfg->prHelperName); + VIR_FREE(cfg->slirpHelperName); VIR_FREE(cfg->dbusDaemonName); VIR_FREE(cfg->saveImageFormat); @@ -674,6 +676,9 @@ virQEMUDriverConfigLoadProcessEntry(virQEMUDriverConfigPtr cfg, if (virConfGetValueString(conf, "pr_helper", &cfg->prHelperName) < 0) return -1; + if (virConfGetValueString(conf, "slirp_helper", &cfg->slirpHelperName) < 0) + return -1; + if (virConfGetValueString(conf, "dbus_daemon", &cfg->dbusDaemonName) < 0) return -1; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index c87cb8bc41..003851d2a7 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -153,6 +153,7 @@ struct _virQEMUDriverConfig { char *bridgeHelperName; char *prHelperName; char *dbusDaemonName; + char *slirpHelperName; bool macFilter; diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qemu.aug.in index e135869717..1bb3de3edc 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -103,6 +103,7 @@ module Test_libvirtd_qemu = } { "memory_backing_dir" = "/var/lib/libvirt/qemu/ram" } { "pr_helper" = "/usr/bin/qemu-pr-helper" } +{ "slirp_helper" = "/usr/bin/slirp-helper" } { "dbus_daemon" = "/usr/bin/dbus-daemon" } { "swtpm_user" = "tss" } { "swtpm_group" = "tss" } -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> The unit provides the functions associated with a slirp-helper: - probing / checking capabilities - opening the socketpair - starting / stoping the helper - registering for dbus-vmstate migration Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/Makefile.inc.am | 2 + src/qemu/qemu_domain.h | 4 + src/qemu/qemu_slirp.c | 412 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_slirp.h | 78 ++++++++ 4 files changed, 496 insertions(+) create mode 100644 src/qemu/qemu_slirp.c create mode 100644 src/qemu/qemu_slirp.h diff --git a/src/qemu/Makefile.inc.am b/src/qemu/Makefile.inc.am index 248f3970c9..e09206cb53 100644 --- a/src/qemu/Makefile.inc.am +++ b/src/qemu/Makefile.inc.am @@ -56,6 +56,8 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_security.h \ qemu/qemu_qapi.c \ qemu/qemu_qapi.h \ + qemu/qemu_slirp.c \ + qemu/qemu_slirp.h \ qemu/qemu_tpm.c \ qemu/qemu_tpm.h \ qemu/qemu_dbus.c \ diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 7ab44d3c43..a4463f96a3 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -511,6 +511,10 @@ struct _qemuDomainGraphicsPrivate { }; +typedef struct _qemuSlirp qemuSlirp; +typedef struct _qemuSlirp *qemuSlirpPtr; + + #define QEMU_DOMAIN_NETWORK_PRIVATE(dev) \ ((qemuDomainNetworkPrivatePtr) (dev)->privateData) diff --git a/src/qemu/qemu_slirp.c b/src/qemu/qemu_slirp.c new file mode 100644 index 0000000000..6600f508db --- /dev/null +++ b/src/qemu/qemu_slirp.c @@ -0,0 +1,412 @@ +/* + * qemu_slirp.c: QEMU Slirp support + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "qemu_dbus.h" +#include "qemu_extdevice.h" +#include "qemu_security.h" +#include "qemu_slirp.h" +#include "viralloc.h" +#include "virenum.h" +#include "virerror.h" +#include "virjson.h" +#include "virlog.h" +#include "virpidfile.h" +#include "virstring.h" +#include "virtime.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("qemu.slirp"); + +VIR_ENUM_IMPL(qemuSlirpFeature, + QEMU_SLIRP_FEATURE_LAST, + "", + "ipv4", + "ipv6", + "tftp", + "dbus-address", + "migrate", + "restrict", + "exit-with-parent", +); + + +void +qemuSlirpFree(qemuSlirpPtr slirp) +{ + VIR_FORCE_CLOSE(slirp->fd[0]); + VIR_FORCE_CLOSE(slirp->fd[1]); + virBitmapFree(slirp->features); + VIR_FREE(slirp); +} + + +void +qemuSlirpSetFeature(qemuSlirpPtr slirp, + qemuSlirpFeature feature) +{ + ignore_value(virBitmapSetBit(slirp->features, feature)); +} + + +bool +qemuSlirpHasFeature(const qemuSlirpPtr slirp, + qemuSlirpFeature feature) +{ + return virBitmapIsBitSet(slirp->features, feature); +} + + +qemuSlirpPtr +qemuSlirpNew(void) +{ + qemuSlirpPtr slirp = NULL; + + if (VIR_ALLOC(slirp) < 0) + return NULL; + + slirp->pid = (pid_t)-1; + slirp->fd[0] = slirp->fd[1] = -1; + slirp->features = virBitmapNew(QEMU_SLIRP_FEATURE_LAST); + + return slirp; +} + + +qemuSlirpPtr +qemuSlirpNewForHelper(const char *helper) +{ + VIR_AUTOPTR(qemuSlirp) slirp = NULL; + VIR_AUTOPTR(virCommand) cmd = NULL; + VIR_AUTOFREE(char *) output = NULL; + VIR_AUTOPTR(virJSONValue) doc = NULL; + virJSONValuePtr featuresJSON; + size_t i, nfeatures; + + if (!helper) + return NULL; + + slirp = qemuSlirpNew(); + if (!slirp) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to allocate slirp for '%s'"), helper); + return NULL; + } + + cmd = virCommandNewArgList(helper, "--print-capabilities", NULL); + virCommandSetOutputBuffer(cmd, &output); + if (virCommandRun(cmd, NULL) < 0) + return NULL; + + if (!(doc = virJSONValueFromString(output)) || + !(featuresJSON = virJSONValueObjectGetArray(doc, "features"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to parse json capabilities '%s'"), + helper); + return NULL; + } + + nfeatures = virJSONValueArraySize(featuresJSON); + for (i = 0; i < nfeatures; i++) { + virJSONValuePtr item = virJSONValueArrayGet(featuresJSON, i); + const char *tmpStr = virJSONValueGetString(item); + int tmp; + + if ((tmp = qemuSlirpFeatureTypeFromString(tmpStr)) <= 0) { + VIR_WARN("unknown slirp feature %s", tmpStr); + continue; + } + + qemuSlirpSetFeature(slirp, tmp); + } + + VIR_RETURN_PTR(slirp); +} + + +static char * +qemuSlirpCreatePidFilename(const char *stateDir, + const char *shortName, + const char *alias) +{ + VIR_AUTOFREE(char *) name = NULL; + + if (virAsprintf(&name, "%s-%s-slirp", shortName, alias) < 0) + return NULL; + + return virPidFileBuildPath(stateDir, name); +} + + +static int +qemuSlirpGetPid(const char *binPath, + const char *stateDir, + const char *shortName, + const char *alias, + pid_t *pid) +{ + VIR_AUTOFREE(char *) pidfile = qemuSlirpCreatePidFilename(stateDir, shortName, alias); + if (!pidfile) + return -ENOMEM; + + return virPidFileReadPathIfAlive(pidfile, pid, binPath); +} + + +int +qemuSlirpOpen(qemuSlirpPtr slirp, + virQEMUDriverPtr driver, + virDomainDefPtr def) +{ + int rc, pair[2] = { -1, -1 }; + + if (qemuSecuritySetSocketLabel(driver->securityManager, def) < 0) + goto error; + + rc = socketpair(AF_UNIX, SOCK_DGRAM, 0, pair); + + if (qemuSecurityClearSocketLabel(driver->securityManager, def) < 0) + goto error; + + if (rc < 0) { + virReportSystemError(errno, "%s", _("failed to create socketpair")); + goto error; + } + + slirp->fd[0] = pair[0]; + slirp->fd[1] = pair[1]; + + return 0; + +error: + VIR_FORCE_CLOSE(pair[0]); + VIR_FORCE_CLOSE(pair[1]); + return -1; +} + + +int +qemuSlirpGetFD(qemuSlirpPtr slirp) +{ + int fd = slirp->fd[0]; + slirp->fd[0] = -1; + return fd; +} + + +static char * +qemuSlirpGetDBusVMStateId(virDomainNetDefPtr net) +{ + char macstr[VIR_MAC_STRING_BUFLEN] = ""; + char *id = NULL; + + /* can't use alias, because it's not stable across restarts */ + if (virAsprintf(&id, "slirp-%s", virMacAddrFormat(&net->mac, macstr)) < 0) + return NULL; + + return id; +} + + +void +qemuSlirpStop(qemuSlirpPtr slirp, + virDomainObjPtr vm, + virQEMUDriverPtr driver, + virDomainNetDefPtr net) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + VIR_AUTOFREE(char *) pidfile = NULL; + VIR_AUTOFREE(char *) shortName = virDomainDefGetShortName(vm->def); + VIR_AUTOFREE(char *) id = qemuSlirpGetDBusVMStateId(net); + virErrorPtr orig_err; + int rc; + pid_t pid; + + if (!(pidfile = qemuSlirpCreatePidFilename( + cfg->stateDir, shortName, net->info.alias))) { + VIR_WARN("Unable to construct slirp pidfile path"); + return; + } + + qemuDBusVMStateRemove(vm, id); + + rc = qemuSlirpGetPid(cfg->slirpHelperName, + cfg->stateDir, shortName, net->info.alias, &pid); + if (rc == 0 && pid != (pid_t)-1) { + char ebuf[1024]; + + VIR_DEBUG("Killing slirp process %lld", (long long)pid); + if (virProcessKill(pid, SIGKILL) < 0 && errno != ESRCH) + VIR_ERROR(_("Failed to kill process %lld: %s"), + (long long)pid, + virStrerror(errno, ebuf, sizeof(ebuf))); + } + + virErrorPreserveLast(&orig_err); + if (virPidFileForceCleanupPath(pidfile) < 0) { + VIR_WARN("Unable to kill slirp process"); + } else { + if (unlink(pidfile) < 0 && + errno != ENOENT) { + virReportSystemError(errno, + _("Unable to remove stale pidfile %s"), + pidfile); + } + } + virErrorRestore(&orig_err); + slirp->pid = 0; +} + + +int +qemuSlirpStart(qemuSlirpPtr slirp, + virDomainObjPtr vm, + virQEMUDriverPtr driver, + virDomainNetDefPtr net, + qemuProcessIncomingDefPtr incoming) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + VIR_AUTOPTR(virCommand) cmd = NULL; + VIR_AUTOFREE(char *) cmdstr = NULL; + VIR_AUTOFREE(char *) addr = NULL; + VIR_AUTOFREE(char *) pidfile = NULL; + VIR_AUTOFREE(char *) shortName = virDomainDefGetShortName(vm->def); + size_t i; + virTimeBackOffVar timebackoff; + const unsigned long long timeout = 500 * 1000; /* ms */ + pid_t pid; + int cmdret = 0, exitstatus = 0; + + if (incoming && + !qemuSlirpHasFeature(slirp, QEMU_SLIRP_FEATURE_MIGRATE)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The slirp-helper doesn't support migration")); + } + + if (!(pidfile = qemuSlirpCreatePidFilename( + cfg->stateDir, shortName, net->info.alias))) + return -1; + + if (!(cmd = virCommandNew(cfg->slirpHelperName))) + return -1; + + virCommandClearCaps(cmd); + virCommandSetPidFile(cmd, pidfile); + virCommandDaemonize(cmd); + + virCommandAddArgFormat(cmd, "--fd=%d", slirp->fd[1]); + virCommandPassFD(cmd, slirp->fd[1], + VIR_COMMAND_PASS_FD_CLOSE_PARENT); + slirp->fd[1] = -1; + + for (i = 0; i < net->guestIP.nips; i++) { + const virNetDevIPAddr *ip = net->guestIP.ips[i]; + const char *opt = ""; + + if (!(addr = virSocketAddrFormat(&ip->address))) + return -1; + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) + opt = "--net"; + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) + opt = "--prefix-ipv6"; + + virCommandAddArgFormat(cmd, "%s=%s", opt, addr); + VIR_FREE(addr); + + if (ip->prefix) { + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) { + virSocketAddr netmask; + VIR_AUTOFREE(char *) netmaskStr = NULL; + + if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to translate prefix %d to netmask"), + ip->prefix); + return -1; + } + if (!(netmaskStr = virSocketAddrFormat(&netmask))) + return -1; + virCommandAddArgFormat(cmd, "--mask=%s", netmaskStr); + } + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) + virCommandAddArgFormat(cmd, "--prefix-length-ipv6=%u", ip->prefix); + } + } + + if (qemuSlirpHasFeature(slirp, + QEMU_SLIRP_FEATURE_DBUS_ADDRESS)) { + VIR_AUTOFREE(char *) id = qemuSlirpGetDBusVMStateId(net); + VIR_AUTOFREE(char *) dbus_addr = qemuDBusGetAddress(driver, vm); + + if (qemuDBusStart(driver, vm) < 0) + return -1; + + virCommandAddArgFormat(cmd, "--dbus-id=%s", id); + + virCommandAddArgFormat(cmd, "--dbus-address=%s", dbus_addr); + + if (qemuSlirpHasFeature(slirp, + QEMU_SLIRP_FEATURE_MIGRATE)) { + if (qemuDBusVMStateAdd(vm, id) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to register slirp migration")); + return -1; + } + if (incoming) { + virCommandAddArg(cmd, "--dbus-incoming"); + } + } + } + + if (qemuSlirpHasFeature(slirp, + QEMU_SLIRP_FEATURE_EXIT_WITH_PARENT)) { + virCommandAddArg(cmd, "--exit-with-parent"); + } + + if (qemuExtDeviceLogCommand(driver, vm, cmd, "slirp") < 0) + return -1; + + if (qemuSecurityCommandRun(driver, vm, cmd, + &exitstatus, &cmdret) < 0) + return -1; + + if (cmdret < 0 || exitstatus != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not start 'slirp'. exitstatus: %d"), exitstatus); + return -1; + } + + /* check that the helper has written its pid into the file */ + if (virTimeBackOffStart(&timebackoff, 1, timeout) < 0) + return -1; + while (virTimeBackOffWait(&timebackoff)) { + int rc = qemuSlirpGetPid(cfg->slirpHelperName, + cfg->stateDir, shortName, net->info.alias, &pid); + if (rc < 0) + continue; + if (rc == 0 && pid == (pid_t)-1) + return -1; + break; + } + + slirp->pid = pid; + return 0; +} diff --git a/src/qemu/qemu_slirp.h b/src/qemu/qemu_slirp.h new file mode 100644 index 0000000000..78a01b9dd0 --- /dev/null +++ b/src/qemu/qemu_slirp.h @@ -0,0 +1,78 @@ +/* + * qemu_slirp.h: QEMU Slirp support + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "qemu_conf.h" +#include "qemu_domain.h" +#include "qemu_process.h" +#include "vircommand.h" + +typedef enum { + QEMU_SLIRP_FEATURE_NONE = 0, + QEMU_SLIRP_FEATURE_IPV4, + QEMU_SLIRP_FEATURE_IPV6, + QEMU_SLIRP_FEATURE_TFTP, + QEMU_SLIRP_FEATURE_DBUS_ADDRESS, + QEMU_SLIRP_FEATURE_MIGRATE, + QEMU_SLIRP_FEATURE_RESTRICT, + QEMU_SLIRP_FEATURE_EXIT_WITH_PARENT, + + QEMU_SLIRP_FEATURE_LAST, +} qemuSlirpFeature; + +VIR_ENUM_DECL(qemuSlirpFeature); + + +struct _qemuSlirp { + int fd[2]; + virBitmapPtr features; + pid_t pid; +}; + + +qemuSlirpPtr qemuSlirpNew(void); + +qemuSlirpPtr qemuSlirpNewForHelper(const char *helper); + +void qemuSlirpFree(qemuSlirpPtr slirp); + +void qemuSlirpSetFeature(qemuSlirpPtr slirp, + qemuSlirpFeature feature); + +bool qemuSlirpHasFeature(const qemuSlirpPtr slirp, + qemuSlirpFeature feature); + +int qemuSlirpOpen(qemuSlirpPtr slirp, + virQEMUDriverPtr driver, + virDomainDefPtr def); + +int qemuSlirpStart(qemuSlirpPtr slirp, + virDomainObjPtr vm, + virQEMUDriverPtr driver, + virDomainNetDefPtr net, + qemuProcessIncomingDefPtr incoming); + +void qemuSlirpStop(qemuSlirpPtr slirp, + virDomainObjPtr vm, + virQEMUDriverPtr driver, + virDomainNetDefPtr net); + +int qemuSlirpGetFD(qemuSlirpPtr slirp); + +VIR_DEFINE_AUTOPTR_FUNC(qemuSlirp, qemuSlirpFree); -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Save & restore the slirp helper PID associated with a network interface & the probed features. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_domain.c | 137 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 2 +- 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 825c23aa26..bc34cdb3bf 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -31,6 +31,7 @@ #include "qemu_migration.h" #include "qemu_migration_params.h" #include "qemu_security.h" +#include "qemu_slirp.h" #include "qemu_extdevice.h" #include "viralloc.h" #include "virlog.h" @@ -1307,6 +1308,11 @@ qemuDomainNetworkPrivateNew(void) static void qemuDomainNetworkPrivateDispose(void *obj ATTRIBUTE_UNUSED) { + qemuDomainNetworkPrivatePtr priv = obj; + + if (priv->slirp) { + qemuSlirpFree(priv->slirp); + } } @@ -2500,6 +2506,63 @@ qemuDomainObjPrivateXMLFormatJob(virBufferPtr buf, } +static bool +qemuDomainHasSlirp(virDomainObjPtr vm) +{ + size_t i; + + for (i = 0; i < vm->def->nnets; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + + if (QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp) + return true; + } + + return false; +} + + +static int +qemuDomainObjPrivateXMLFormatSlirp(virBufferPtr buf, + virDomainObjPtr vm) +{ + size_t i; + + if (!qemuDomainHasSlirp(vm)) + return 0; + + virBufferAddLit(buf, "<slirp>\n"); + virBufferAdjustIndent(buf, 2); + + for (i = 0; i < vm->def->nnets; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + qemuSlirpPtr slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; + size_t j; + + if (!slirp) + continue; + + virBufferAsprintf(buf, "<helper alias='%s' pid='%d'>\n", + net->info.alias, slirp->pid); + + virBufferAdjustIndent(buf, 2); + for (j = 0; j < QEMU_SLIRP_FEATURE_LAST; j++) { + if (qemuSlirpHasFeature(slirp, j)) { + virBufferAsprintf(buf, "<feature name='%s'/>\n", + qemuSlirpFeatureTypeToString(j)); + } + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</helper>\n"); + } + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</slirp>\n"); + + + return 0; +} + static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, virDomainObjPtr vm) @@ -2608,6 +2671,9 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf, if (qemuDomainObjPrivateXMLFormatBlockjobs(buf, vm) < 0) return -1; + if (qemuDomainObjPrivateXMLFormatSlirp(buf, vm) < 0) + return -1; + return 0; } @@ -2915,6 +2981,46 @@ qemuDomainObjPrivateXMLParseJob(virDomainObjPtr vm, } +static int +qemuDomainObjPrivateXMLParseSlirpFeatures(xmlXPathContextPtr ctxt, + size_t h, + qemuSlirpPtr slirp) +{ + VIR_AUTOFREE(char *) path = NULL; + VIR_AUTOFREE(xmlNodePtr *) nodes = NULL; + size_t i; + int n; + + if (virAsprintf(&path, "./slirp/helper[%ld]/feature", h + 1) < 0) + return -1; + + if ((n = virXPathNodeSet(path, ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("failed to parse slirp-helper features")); + return -1; + } + + for (i = 0; i < n; i++) { + VIR_AUTOFREE(char *) str = virXMLPropString(nodes[i], "name"); + int feature; + + if (!str) + continue; + + feature = qemuSlirpFeatureTypeFromString(str); + if (feature < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown slirp feature %s"), str); + return -1; + } + + qemuSlirpSetFeature(slirp, feature); + } + + return 0; +} + + static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, virDomainObjPtr vm, @@ -3057,6 +3163,37 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, } VIR_FREE(nodes); + if ((n = virXPathNodeSet("./slirp/helper", ctxt, &nodes)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse slirp helper list")); + goto error; + } + if (n > 0) { + for (i = 0; i < n; i++) { + VIR_AUTOFREE(char *) alias = virXMLPropString(nodes[i], "alias"); + VIR_AUTOFREE(char *) pid = virXMLPropString(nodes[i], "pid"); + VIR_AUTOPTR(qemuSlirp) slirp = qemuSlirpNew(); + virDomainDeviceDef dev; + + if (!alias || !pid || !slirp || + virStrToLong_i(pid, NULL, 10, &slirp->pid) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to parse slirp helper list")); + goto error; + } + + if (virDomainDefFindDevice(vm->def, alias, &dev, true) < 0 || + dev.type != VIR_DOMAIN_DEVICE_NET) + goto error; + + if (qemuDomainObjPrivateXMLParseSlirpFeatures(ctxt, i, slirp) < 0) + goto error; + + VIR_STEAL_PTR(QEMU_DOMAIN_NETWORK_PRIVATE(dev.data.net)->slirp, slirp); + } + } + VIR_FREE(nodes); + if (qemuDomainObjPrivateXMLParseAutomaticPlacement(ctxt, priv, driver) < 0) goto error; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index a4463f96a3..d35c800064 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -523,7 +523,7 @@ typedef qemuDomainNetworkPrivate *qemuDomainNetworkPrivatePtr; struct _qemuDomainNetworkPrivate { virObject parent; - bool tmp_to_be_larger_than_parent; + qemuSlirpPtr slirp; }; -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> For VM started and migrated/saved without slirp-helpers, let's prevent the automatic setup (as it would fail to migrate otherwise). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_domain.c | 12 ++++++++++-- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 11 +++++++++-- src/qemu/qemu_process.c | 6 ++++-- src/qemu/qemu_process.h | 1 + 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index bc34cdb3bf..bb719fc3c4 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -13962,7 +13962,7 @@ qemuDomainSaveCookieDispose(void *obj) qemuDomainSaveCookiePtr -qemuDomainSaveCookieNew(virDomainObjPtr vm ATTRIBUTE_UNUSED) +qemuDomainSaveCookieNew(virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainSaveCookiePtr cookie = NULL; @@ -13976,7 +13976,10 @@ qemuDomainSaveCookieNew(virDomainObjPtr vm ATTRIBUTE_UNUSED) if (priv->origCPU && !(cookie->cpu = virCPUDefCopy(vm->def->cpu))) goto error; - VIR_DEBUG("Save cookie %p, cpu=%p", cookie, cookie->cpu); + cookie->hasSlirpHelper = qemuDomainHasSlirp(vm); + + VIR_DEBUG("Save cookie %p, cpu=%p, hasSlirpHelper=%d", + cookie, cookie->cpu, cookie->hasSlirpHelper); return cookie; @@ -14002,6 +14005,8 @@ qemuDomainSaveCookieParse(xmlXPathContextPtr ctxt ATTRIBUTE_UNUSED, &cookie->cpu) < 0) goto error; + cookie->hasSlirpHelper = virXPathBoolean("boolean(./hasSlirpHelper)", ctxt) > 0; + *obj = (virObjectPtr) cookie; return 0; @@ -14021,6 +14026,9 @@ qemuDomainSaveCookieFormat(virBufferPtr buf, virCPUDefFormatBufFull(buf, cookie->cpu, NULL) < 0) return -1; + if (cookie->hasSlirpHelper) + virBufferAddLit(buf, "<hasSlirpHelper/>\n"); + return 0; } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index d35c800064..9a56a90a9a 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -560,6 +560,7 @@ struct _qemuDomainSaveCookie { virObject parent; virCPUDefPtr cpu; + bool hasSlirpHelper; }; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9c05ab4ad1..dda9389776 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6788,6 +6788,7 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virQEMUSaveHeaderPtr header = &data->header; qemuDomainSaveCookiePtr cookie = NULL; + unsigned int flags; if (virSaveCookieParseString(data->cookie, (virObjectPtr *)&cookie, virDomainXMLOptionGetSaveCookie(driver->xmlopt)) < 0) @@ -6819,11 +6820,14 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, qemuDomainFixupCPUs(vm, &cookie->cpu) < 0) goto cleanup; + flags = VIR_QEMU_PROCESS_START_PAUSED | VIR_QEMU_PROCESS_START_GEN_VMID; + if (!cookie->hasSlirpHelper) + flags |= VIR_QEMU_PROCESS_START_NO_SLIRP; + if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL, asyncJob, "stdio", *fd, path, NULL, VIR_NETDEV_VPORT_PROFILE_OP_RESTORE, - VIR_QEMU_PROCESS_START_PAUSED | - VIR_QEMU_PROCESS_START_GEN_VMID) == 0) + flags) == 0) restored = true; if (intermediatefd != -1) { @@ -16298,6 +16302,9 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, start_flags |= VIR_QEMU_PROCESS_START_PAUSED; + if (!cookie->hasSlirpHelper) + start_flags |= VIR_QEMU_PROCESS_START_NO_SLIRP; + /* Transitions 2, 3, 5, 6, 8, 9 */ /* When using the loadvm monitor command, qemu does not know * whether to pause or run the reverted domain, and just stays diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 5e8184b0e2..93e06104f4 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6680,7 +6680,8 @@ qemuProcessLaunch(virConnectPtr conn, VIR_QEMU_PROCESS_START_PAUSED | VIR_QEMU_PROCESS_START_AUTODESTROY | VIR_QEMU_PROCESS_START_NEW | - VIR_QEMU_PROCESS_START_GEN_VMID, -1); + VIR_QEMU_PROCESS_START_GEN_VMID | + VIR_QEMU_PROCESS_START_NO_SLIRP, -1); cfg = virQEMUDriverGetConfig(driver); @@ -7092,7 +7093,8 @@ qemuProcessStart(virConnectPtr conn, virCheckFlagsGoto(VIR_QEMU_PROCESS_START_COLD | VIR_QEMU_PROCESS_START_PAUSED | VIR_QEMU_PROCESS_START_AUTODESTROY | - VIR_QEMU_PROCESS_START_GEN_VMID, cleanup); + VIR_QEMU_PROCESS_START_GEN_VMID | + VIR_QEMU_PROCESS_START_NO_SLIRP, cleanup); if (!migrateFrom && !snapshot) flags |= VIR_QEMU_PROCESS_START_NEW; diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index 1d62319092..09ef124838 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -81,6 +81,7 @@ typedef enum { VIR_QEMU_PROCESS_START_GEN_VMID = 1 << 5, /* Generate a new VMID */ VIR_QEMU_PROCESS_START_STANDALONE = 1 << 6, /* Require CLI args to be usable standalone, ie no FD passing and the like */ + VIR_QEMU_PROCESS_START_NO_SLIRP = 1 << 7, /* do not setup slirp-helpers */ } qemuProcessStartFlags; int qemuProcessStart(virConnectPtr conn, -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_migration.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index a11c6f1a9f..35fadfec44 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -39,6 +39,7 @@ #include "qemu_hotplug.h" #include "qemu_blockjob.h" #include "qemu_security.h" +#include "qemu_slirp.h" #include "qemu_block.h" #include "domain_audit.h" @@ -1146,7 +1147,17 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver, _("cannot migrate domain with I/O error")); return false; } + } + + for (i = 0; i < vm->def->nnets; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + qemuSlirpPtr slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; + if (slirp && !qemuSlirpHasFeature(slirp, QEMU_SLIRP_FEATURE_MIGRATE)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("a slirp-helper cannot be migrated")); + return false; + } } /* following checks don't make sense for offline migration */ -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> If a slirp-helper is associated with a network interface, prepare/start/stop the process via qemu-extdevice. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_extdevice.c | 46 ++++++++++++++++++++++++++++++++++----- src/qemu/qemu_extdevice.h | 8 ++++--- src/qemu/qemu_process.c | 4 ++-- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c index 79d5d64951..2a6cb12215 100644 --- a/src/qemu/qemu_extdevice.c +++ b/src/qemu/qemu_extdevice.c @@ -23,6 +23,7 @@ #include "qemu_extdevice.h" #include "qemu_domain.h" #include "qemu_tpm.h" +#include "qemu_slirp.h" #include "viralloc.h" #include "virlog.h" @@ -87,14 +88,24 @@ qemuExtDevicesInitPaths(virQEMUDriverPtr driver, */ int qemuExtDevicesPrepareHost(virQEMUDriverPtr driver, - virDomainDefPtr def) + virDomainObjPtr vm) { - int ret = 0; + virDomainDefPtr def = vm->def; + size_t i; - if (def->tpm) - ret = qemuExtTPMPrepareHost(driver, def); + if (def->tpm && + qemuExtTPMPrepareHost(driver, def) < 0) + return -1; - return ret; + for (i = 0; i < def->nnets; i++) { + virDomainNetDefPtr net = def->nets[i]; + qemuSlirpPtr slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; + + if (slirp && qemuSlirpOpen(slirp, driver, def) < 0) + return -1; + } + + return 0; } @@ -112,9 +123,12 @@ qemuExtDevicesCleanupHost(virQEMUDriverPtr driver, int qemuExtDevicesStart(virQEMUDriverPtr driver, - virDomainObjPtr vm) + virDomainObjPtr vm, + qemuProcessIncomingDefPtr incoming) { + virDomainDefPtr def = vm->def; int ret = 0; + size_t i; if (qemuExtDevicesInitPaths(driver, vm->def) < 0) return -1; @@ -122,6 +136,15 @@ qemuExtDevicesStart(virQEMUDriverPtr driver, if (vm->def->tpm) ret = qemuExtTPMStart(driver, vm); + for (i = 0; i < def->nnets; i++) { + virDomainNetDefPtr net = def->nets[i]; + qemuSlirpPtr slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; + + if (slirp && + qemuSlirpStart(slirp, vm, driver, net, incoming) < 0) + return -1; + } + return ret; } @@ -130,11 +153,22 @@ void qemuExtDevicesStop(virQEMUDriverPtr driver, virDomainObjPtr vm) { + virDomainDefPtr def = vm->def; + size_t i; + if (qemuExtDevicesInitPaths(driver, vm->def) < 0) return; if (vm->def->tpm) qemuExtTPMStop(driver, vm); + + for (i = 0; i < def->nnets; i++) { + virDomainNetDefPtr net = def->nets[i]; + qemuSlirpPtr slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; + + if (slirp) + qemuSlirpStop(slirp, vm, driver, net); + } } diff --git a/src/qemu/qemu_extdevice.h b/src/qemu/qemu_extdevice.h index c9a3109fb6..39502b57a6 100644 --- a/src/qemu/qemu_extdevice.h +++ b/src/qemu/qemu_extdevice.h @@ -22,6 +22,7 @@ #include "qemu_conf.h" #include "qemu_domain.h" +#include "qemu_process.h" int qemuExtDeviceLogCommand(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -31,7 +32,7 @@ int qemuExtDeviceLogCommand(virQEMUDriverPtr driver, ATTRIBUTE_RETURN_CHECK; int qemuExtDevicesPrepareHost(virQEMUDriverPtr driver, - virDomainDefPtr def) + virDomainObjPtr vm) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; @@ -40,8 +41,9 @@ void qemuExtDevicesCleanupHost(virQEMUDriverPtr driver, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); int qemuExtDevicesStart(virQEMUDriverPtr driver, - virDomainObjPtr vm) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) + virDomainObjPtr vm, + qemuProcessIncomingDefPtr incoming) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; void qemuExtDevicesStop(virQEMUDriverPtr driver, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 93e06104f4..d067223a0e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6535,7 +6535,7 @@ qemuProcessPrepareHost(virQEMUDriverPtr driver, goto cleanup; VIR_DEBUG("Preparing external devices"); - if (qemuExtDevicesPrepareHost(driver, vm->def) < 0) + if (qemuExtDevicesPrepareHost(driver, vm) < 0) goto cleanup; if (qemuProcessPrepareSEVGuestInput(vm) < 0) @@ -6710,7 +6710,7 @@ qemuProcessLaunch(virConnectPtr conn, if (qemuProcessGenID(vm, flags) < 0) goto cleanup; - if (qemuExtDevicesStart(driver, vm) < 0) + if (qemuExtDevicesStart(driver, vm, incoming) < 0) goto cleanup; VIR_DEBUG("Building emulator command line"); -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> If a slirp-helper is associated with a network interface (after probing & preparing succesfully), pass the socket fd to QEMU and use "-net socket,fd=". Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_command.c | 35 +++++++++++++++++++++++++++++------ src/qemu/qemu_command.h | 3 ++- src/qemu/qemu_hotplug.c | 4 +++- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 040e8e2b12..e15d19eb25 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -28,6 +28,7 @@ #include "qemu_interface.h" #include "qemu_alias.h" #include "qemu_security.h" +#include "qemu_slirp.h" #include "qemu_block.h" #include "cpu/cpu.h" #include "dirname.h" @@ -4038,7 +4039,8 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, char **tapfd, size_t tapfdSize, char **vhostfd, - size_t vhostfdSize) + size_t vhostfdSize, + const char *slirpfd) { bool is_tap = false; virBuffer buf = VIR_BUFFER_INITIALIZER; @@ -4109,6 +4111,12 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, break; case VIR_DOMAIN_NET_TYPE_USER: + if (slirpfd) { + virBufferAsprintf(&buf, "socket,fd=%s,", + slirpfd); + break; + } + virBufferAddLit(&buf, "user,"); for (i = 0; i < net->guestIP.nips; i++) { const virNetDevIPAddr *ip = net->guestIP.ips[i]; @@ -8654,10 +8662,10 @@ qemuInterfaceVhostuserConnect(virQEMUDriverPtr driver, static int qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, + virDomainObjPtr vm, virLogManagerPtr logManager, virSecurityManagerPtr secManager, virCommandPtr cmd, - virDomainDefPtr def, virDomainNetDefPtr net, virQEMUCapsPtr qemuCaps, unsigned int bootindex, @@ -8666,6 +8674,7 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, size_t *nnicindexes, int **nicindexes) { + virDomainDefPtr def = vm->def; int ret = -1; char *nic = NULL; char *host = NULL; @@ -8676,9 +8685,11 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, size_t vhostfdSize = 0; char **tapfdName = NULL; char **vhostfdName = NULL; + VIR_AUTOFREE(char *) slirpfdName = NULL; virDomainNetType actualType = virDomainNetGetActualType(net); virNetDevBandwidthPtr actualBandwidth; bool requireNicdev = false; + qemuSlirpPtr slirp; size_t i; @@ -8904,6 +8915,16 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, goto cleanup; } + slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; + if (slirp && !standalone) { + int slirpfd = qemuSlirpGetFD(slirp); + virCommandPassFD(cmd, slirpfd, + VIR_COMMAND_PASS_FD_CLOSE_PARENT); + if (virAsprintf(&slirpfdName, "%d", slirpfd) < 0) + goto cleanup; + } + + for (i = 0; i < tapfdSize; i++) { if (qemuSecuritySetTapFDLabel(driver->securityManager, def, tapfd[i]) < 0) @@ -8928,7 +8949,8 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, if (!(host = qemuBuildHostNetStr(net, driver, tapfdName, tapfdSize, - vhostfdName, vhostfdSize))) + vhostfdName, vhostfdSize, + slirpfdName))) goto cleanup; virCommandAddArgList(cmd, "-netdev", host, NULL); @@ -8996,10 +9018,10 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, */ static int qemuBuildNetCommandLine(virQEMUDriverPtr driver, + virDomainObjPtr vm, virLogManagerPtr logManager, virSecurityManagerPtr secManager, virCommandPtr cmd, - virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virNetDevVPortProfileOp vmop, bool standalone, @@ -9010,6 +9032,7 @@ qemuBuildNetCommandLine(virQEMUDriverPtr driver, size_t i; int last_good_net = -1; virErrorPtr originalError = NULL; + virDomainDefPtr def = vm->def; if (def->nnets) { unsigned int bootNet = 0; @@ -9025,7 +9048,7 @@ qemuBuildNetCommandLine(virQEMUDriverPtr driver, for (i = 0; i < def->nnets; i++) { virDomainNetDefPtr net = def->nets[i]; - if (qemuBuildInterfaceCommandLine(driver, logManager, secManager, cmd, def, net, + if (qemuBuildInterfaceCommandLine(driver, vm, logManager, secManager, cmd, net, qemuCaps, bootNet, vmop, standalone, nnicindexes, nicindexes) < 0) @@ -10817,7 +10840,7 @@ qemuBuildCommandLine(virQEMUDriverPtr driver, if (qemuBuildFSDevCommandLine(cmd, def, qemuCaps) < 0) goto error; - if (qemuBuildNetCommandLine(driver, logManager, secManager, cmd, def, + if (qemuBuildNetCommandLine(driver, vm, logManager, secManager, cmd, qemuCaps, vmop, standalone, nnicindexes, nicindexes, &bootHostdevNet) < 0) goto error; diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 8bb941e2bc..71afacbc0e 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -90,7 +90,8 @@ char *qemuBuildHostNetStr(virDomainNetDefPtr net, char **tapfd, size_t tapfdSize, char **vhostfd, - size_t vhostfdSize); + size_t vhostfdSize, + const char *slirpfd); /* Current, best practice */ char *qemuBuildNicDevStr(virDomainDefPtr def, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ccff9a79d7..6e390c6ce4 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1142,6 +1142,7 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDeviceDef dev = { VIR_DOMAIN_DEVICE_NET, { .net = net } }; virErrorPtr originalError = NULL; + VIR_AUTOFREE(char *) slirpfdName = NULL; char **tapfdName = NULL; int *tapfd = NULL; size_t tapfdSize = 0; @@ -1380,7 +1381,8 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, if (!(netstr = qemuBuildHostNetStr(net, driver, tapfdName, tapfdSize, - vhostfdName, vhostfdSize))) + vhostfdName, vhostfdSize, + slirpfdName))) goto cleanup; qemuDomainObjEnterMonitor(driver, vm); -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> When the network interface is of "user" type, and QEMU has the "-net socket,fd=" datagram support, call qemuInterfacePrepareSlirp() to probe and associate a slirp-helper with the interface. The usage of automated slirp-helper can be prevented with VIR_QEMU_PROCESS_START_NO_SLIRP (in particular when resuming a VM that didn't start with slirp-helper before). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_interface.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_interface.h | 4 ++++ src/qemu/qemu_process.c | 17 ++++++++++++++--- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c index c8effa68f4..e58f464d87 100644 --- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -603,6 +603,33 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def, } +qemuSlirpPtr +qemuInterfacePrepareSlirp(virQEMUDriverPtr driver, + virDomainNetDefPtr net) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + VIR_AUTOPTR(qemuSlirp) slirp = NULL; + size_t i; + + if (!(slirp = qemuSlirpNewForHelper(cfg->slirpHelperName))) + return NULL; + + for (i = 0; i < net->guestIP.nips; i++) { + const virNetDevIPAddr *ip = net->guestIP.ips[i]; + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET) && + !qemuSlirpHasFeature(slirp, QEMU_SLIRP_FEATURE_IPV4)) + return NULL; + + if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6) && + !qemuSlirpHasFeature(slirp, QEMU_SLIRP_FEATURE_IPV6)) + return NULL; + } + + VIR_RETURN_PTR(slirp); +} + + /** * qemuInterfaceOpenVhostNet: * @def: domain definition diff --git a/src/qemu/qemu_interface.h b/src/qemu/qemu_interface.h index 5a2f87e532..0464b903d7 100644 --- a/src/qemu/qemu_interface.h +++ b/src/qemu/qemu_interface.h @@ -24,6 +24,7 @@ #include "domain_conf.h" #include "qemu_conf.h" #include "qemu_domain.h" +#include "qemu_slirp.h" int qemuInterfaceStartDevice(virDomainNetDefPtr net); int qemuInterfaceStartDevices(virDomainDefPtr def); @@ -54,3 +55,6 @@ int qemuInterfaceOpenVhostNet(virDomainDefPtr def, virDomainNetDefPtr net, int *vhostfd, size_t *vhostfdSize); + +qemuSlirpPtr qemuInterfacePrepareSlirp(virQEMUDriverPtr driver, + virDomainNetDefPtr net); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index d067223a0e..856460f0b6 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -5619,8 +5619,12 @@ qemuProcessInit(virQEMUDriverPtr driver, * qemuProcessNetworkPrepareDevices */ static int -qemuProcessNetworkPrepareDevices(virDomainDefPtr def) +qemuProcessNetworkPrepareDevices(virQEMUDriverPtr driver, + virDomainObjPtr vm, + unsigned int flags) { + virDomainDefPtr def = vm->def; + qemuDomainObjPrivatePtr priv = vm->privateData; int ret = -1; size_t i; virConnectPtr conn = NULL; @@ -5665,7 +5669,14 @@ qemuProcessNetworkPrepareDevices(virDomainDefPtr def) } if (virDomainHostdevInsert(def, hostdev) < 0) goto cleanup; - } + } else if (actualType == VIR_DOMAIN_NET_TYPE_USER && + !(flags & VIR_QEMU_PROCESS_START_NO_SLIRP) && + virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NET_SOCKET_DGRAM)) { + qemuSlirpPtr slirp = qemuInterfacePrepareSlirp(driver, net); + + QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp = slirp; + } + } ret = 0; cleanup: @@ -6468,7 +6479,7 @@ qemuProcessPrepareHost(virQEMUDriverPtr driver, * will need to be setup. */ VIR_DEBUG("Preparing network devices"); - if (qemuProcessNetworkPrepareDevices(vm->def) < 0) + if (qemuProcessNetworkPrepareDevices(driver, vm, flags) < 0) goto cleanup; /* Must be run before security labelling */ -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- src/qemu/qemu_hotplug.c | 32 +++++++++++++++++++++++++++++--- src/qemu/qemu_monitor.c | 13 ++++++++++--- src/qemu/qemu_monitor.h | 3 ++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6e390c6ce4..cb4665ab01 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1143,6 +1143,7 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, virDomainDeviceDef dev = { VIR_DOMAIN_DEVICE_NET, { .net = net } }; virErrorPtr originalError = NULL; VIR_AUTOFREE(char *) slirpfdName = NULL; + int slirpfd = -1; char **tapfdName = NULL; int *tapfd = NULL; size_t tapfdSize = 0; @@ -1322,7 +1323,25 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, break; case VIR_DOMAIN_NET_TYPE_USER: - /* No preparation needed. */ + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_NET_SOCKET_DGRAM)) { + qemuSlirpPtr slirp = qemuInterfacePrepareSlirp(driver, net); + + if (!slirp) + break; + + QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp = slirp; + + if (qemuSlirpOpen(slirp, driver, vm->def) < 0 || + qemuSlirpStart(slirp, vm, driver, net, NULL) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Failed to start slirp")); + goto cleanup; + } + + slirpfd = qemuSlirpGetFD(slirp); + if (virAsprintf(&slirpfdName, "slirpfd-%s", net->info.alias) < 0) + goto cleanup; + } break; case VIR_DOMAIN_NET_TYPE_SERVER: @@ -1398,7 +1417,8 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, if (qemuMonitorAddNetdev(priv->mon, netstr, tapfd, tapfdName, tapfdSize, - vhostfd, vhostfdName, vhostfdSize) < 0) { + vhostfd, vhostfdName, vhostfdSize, + slirpfd, slirpfdName) < 0) { ignore_value(qemuDomainObjExitMonitor(driver, vm)); virDomainAuditNet(vm, NULL, net, "attach", false); goto try_remove; @@ -1513,6 +1533,7 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, VIR_FREE(charDevAlias); virObjectUnref(conn); virDomainCCWAddressSetFree(ccwaddrs); + VIR_FORCE_CLOSE(slirpfd); return ret; @@ -1523,6 +1544,8 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, virErrorPreserveLast(&originalError); if (virAsprintf(&netdev_name, "host%s", net->info.alias) >= 0) { qemuDomainObjEnterMonitor(driver, vm); + if (QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp) + qemuSlirpStop(QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp, vm, driver, net); if (charDevPlugged && qemuMonitorDetachCharDev(priv->mon, charDevAlias) < 0) VIR_WARN("Failed to remove associated chardev %s", charDevAlias); @@ -2208,7 +2231,7 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver, if (guestfwd) { if (qemuMonitorAddNetdev(priv->mon, devstr, - NULL, NULL, 0, NULL, NULL, 0) < 0) + NULL, NULL, 0, NULL, NULL, 0, -1, NULL) < 0) goto exit_monitor; } else { if (qemuMonitorAddDevice(priv->mon, devstr) < 0) @@ -4669,6 +4692,9 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver, } } + if (QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp) + qemuSlirpStop(QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp, vm, driver, net); + if (qemuDomainObjExitMonitor(driver, vm) < 0) goto cleanup; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index ea57bad9f5..66d046bfe0 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2853,15 +2853,17 @@ int qemuMonitorAddNetdev(qemuMonitorPtr mon, const char *netdevstr, int *tapfd, char **tapfdName, int tapfdSize, - int *vhostfd, char **vhostfdName, int vhostfdSize) + int *vhostfd, char **vhostfdName, int vhostfdSize, + int slirpfd, char *slirpfdName) { int ret = -1; size_t i = 0, j = 0; VIR_DEBUG("netdevstr=%s tapfd=%p tapfdName=%p tapfdSize=%d" - "vhostfd=%p vhostfdName=%p vhostfdSize=%d", + "vhostfd=%p vhostfdName=%p vhostfdSize=%d" + "slirpfd=%d slirpfdName=%s", netdevstr, tapfd, tapfdName, tapfdSize, - vhostfd, vhostfdName, vhostfdSize); + vhostfd, vhostfdName, vhostfdSize, slirpfd, slirpfdName); QEMU_CHECK_MONITOR(mon); @@ -2874,6 +2876,11 @@ qemuMonitorAddNetdev(qemuMonitorPtr mon, goto cleanup; } + if (slirpfd != -1) { + if (qemuMonitorSendFileHandle(mon, slirpfdName, slirpfd) < 0) + goto cleanup; + } + ret = qemuMonitorJSONAddNetdev(mon, netdevstr); cleanup: diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index fcb029f97f..af20e92506 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -832,7 +832,8 @@ int qemuMonitorRemoveFd(qemuMonitorPtr mon, int fdset, int fd); int qemuMonitorAddNetdev(qemuMonitorPtr mon, const char *netdevstr, int *tapfd, char **tapfdName, int tapfdSize, - int *vhostfd, char **vhostfdName, int vhostfdSize); + int *vhostfd, char **vhostfdName, int vhostfdSize, + int slirpfd, char *slirpfdName); int qemuMonitorRemoveNetdev(qemuMonitorPtr mon, const char *alias); -- 2.22.0.214.g8dca754b1e

From: Marc-André Lureau <marcandre.lureau@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- .../net-user.x86_64-4.0.0.args | 34 +++++++++++++++++++ tests/qemuxml2argvtest.c | 16 +++++++++ tests/testutilsqemu.h | 1 + 3 files changed, 51 insertions(+) create mode 100644 tests/qemuxml2argvdata/net-user.x86_64-4.0.0.args diff --git a/tests/qemuxml2argvdata/net-user.x86_64-4.0.0.args b/tests/qemuxml2argvdata/net-user.x86_64-4.0.0.args new file mode 100644 index 0000000000..ef4b77be92 --- /dev/null +++ b/tests/qemuxml2argvdata/net-user.x86_64-4.0.0.args @@ -0,0 +1,34 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/tmp/lib/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-i686 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object secret,id=masterKey0,format=raw,file=/tmp/lib/domain--1-QEMUGuest1/master-key.aes \ +-machine pc-i440fx-4.0,accel=tcg,usb=off,dump-guest-core=off \ +-m 214 \ +-overcommit mem-lock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-no-acpi \ +-boot strict=on \ +-device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 \ +-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ +-device ide-hd,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0,bootindex=1 \ +-netdev socket,fd=42,id=hostnet0 \ +-device rtl8139,netdev=hostnet0,id=net0,mac=00:11:22:33:44:55,bus=pci.0,addr=0x2 \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 0ac128be00..1436a36b03 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -17,6 +17,7 @@ # include "qemu/qemu_domain.h" # include "qemu/qemu_migration.h" # include "qemu/qemu_process.h" +# include "qemu/qemu_slirp.h" # include "datatypes.h" # include "conf/storage_conf.h" # include "cpu/cpu_map.h" @@ -398,6 +399,7 @@ testCheckExclusiveFlags(int flags) FLAG_FIPS | FLAG_REAL_CAPS | FLAG_SKIP_LEGACY_CPUS | + FLAG_SLIRP_HELPER | 0, -1); VIR_EXCLUSIVE_FLAGS_RET(FLAG_REAL_CAPS, FLAG_SKIP_LEGACY_CPUS, -1); @@ -526,6 +528,19 @@ testCompareXMLToArgv(const void *data) } } + if (flags & FLAG_SLIRP_HELPER) { + for (i = 0; i < vm->def->nnets; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + + if (net->type == VIR_DOMAIN_NET_TYPE_USER && + virQEMUCapsGet(info->qemuCaps, QEMU_CAPS_NET_SOCKET_DGRAM)) { + qemuSlirpPtr slirp = qemuSlirpNew(); + slirp->fd[0] = 42; + QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp = slirp; + } + } + } + if (!(cmd = qemuProcessCreatePretendCmd(&driver, vm, migrateURI, (flags & FLAG_FIPS), false, VIR_QEMU_PROCESS_START_COLD))) { @@ -1280,6 +1295,7 @@ mymain(void) DO_TEST_FAILURE("net-vhostuser-fail", QEMU_CAPS_VHOSTUSER_MULTIQUEUE); DO_TEST("net-user", NONE); + DO_TEST_CAPS_ARCH_VER_FULL("net-user", "x86_64", "4.0.0", ARG_FLAGS, FLAG_SLIRP_HELPER); DO_TEST("net-user-addr", NONE); DO_TEST("net-virtio", NONE); DO_TEST("net-virtio-device", diff --git a/tests/testutilsqemu.h b/tests/testutilsqemu.h index 0632141d68..a2f7bfcc26 100644 --- a/tests/testutilsqemu.h +++ b/tests/testutilsqemu.h @@ -51,6 +51,7 @@ typedef enum { FLAG_FIPS = 1 << 2, FLAG_REAL_CAPS = 1 << 3, FLAG_SKIP_LEGACY_CPUS = 1 << 4, + FLAG_SLIRP_HELPER = 1 << 5, } testQemuInfoFlags; struct testQemuInfo { -- 2.22.0.214.g8dca754b1e
participants (1)
-
marcandre.lureau@redhat.com