Hi On Thu, Mar 24, 2022 at 11:26 PM Jonathon Jongsma <jjongsma@xxxxxxxxxx> wrote: > > Add the ability to configure a qemu-vdagent in guest domains. This > device is similar to the spice vdagent channel except that qemu handles > the spice-vdagent protocol messages itself rather than routing them over > a spice protocol channel. > > The qemu-vdagent device has two notable configuration options which > determine whether qemu will handle particular vdagent features: > 'clipboard' and 'mouse'. > > The 'clipboard' option allows qemu to synchronize its internal clipboard > manager with the guest clipboard, which enables client<->guest clipboard > synchronization for non-spice guests such as vnc. > > The 'mouse' option allows absolute mouse positioning to be sent over the > vdagent channel rather than using a usb or virtio tablet device. > > Sample configuration: > <channel type='qemu-vdagent'> > <target type='virtio' name='com.redhat.spice.0'/> > <source> > <clipboard copypaste='yes'/> > <mouse mode='client'/> > </source> > </channel> > > Signed-off-by: Jonathon Jongsma <jjongsma@xxxxxxxxxx> I guess you could have added some domain XML tests here, but the next patch covers it, so Reviewed-by: Marc-André Lureau <marcandre.lureau@xxxxxxxxxx> > --- > docs/formatdomain.rst | 23 ++++++++++++ > src/conf/domain_conf.c | 62 ++++++++++++++++++++++++++++++- > src/conf/domain_conf.h | 20 ++++++---- > src/conf/domain_validate.c | 1 + > src/conf/schemas/domaincommon.rng | 51 ++++++++++++++++--------- > src/qemu/qemu_command.c | 3 ++ > src/qemu/qemu_monitor_json.c | 1 + > src/qemu/qemu_process.c | 1 + > src/qemu/qemu_validate.c | 1 + > src/security/security_apparmor.c | 2 + > src/security/security_dac.c | 2 + > tests/testutilsqemu.c | 1 + > 12 files changed, 140 insertions(+), 28 deletions(-) > > diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst > index e492532004..75339c2fda 100644 > --- a/docs/formatdomain.rst > +++ b/docs/formatdomain.rst > @@ -6662,6 +6662,29 @@ types have different ``target`` attributes. > ``name='com.redhat.spice.0'``. The optional ``address`` element can tie the > channel to a particular ``type='virtio-serial'`` controller. :since:`Since > 0.8.8` > +``qemu-vdagent`` > + Paravirtualized qemu vdagent channel. This channel implements the SPICE > + vdagent protocol, but is handled internally by qemu and therefore does not > + require a SPICE graphics device. Like the spicevmc channel, the ``target`` > + element must be present, with attribute ``type='virtio'``; an optional > + attribute ``name`` controls how the guest will have access to the channel, > + and defaults to ``name='com.redhat.spice.0'``. The optional ``address`` > + element can tie the channel to a particular ``type='virtio-serial'`` > + controller. Certain vdagent protocol features can by enabled or disabled > + using the ``source`` element. > + > + Copy & Paste functionality is set by the ``clipboard`` element. It is > + disabled by default, and can be enabled by setting the ``copypaste`` > + property to ``yes``. This allows the guest's clipboard to be synchronized > + with the qemu clipboard manager. This can enable copy and paste between a > + guest and a client when using a VNC `graphics device <#elementsGraphics>`__ > + (when using a VNC client that supports the copy/paste feature) or other > + graphics types that support the qemu clipboard manager. > + > + Mouse mode is set by the ``mouse`` element, setting its ``mode`` attribute > + to one of ``server`` or ``client``. If no mode is specified, the qemu > + default will be used (client mode). > + :since:`Since 8.2.0` > > :anchor:`<a id="elementsCharHostInterface"/>` > > diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c > index 6f9954638c..3b97de89f4 100644 > --- a/src/conf/domain_conf.c > +++ b/src/conf/domain_conf.c > @@ -713,6 +713,7 @@ VIR_ENUM_IMPL(virDomainChr, > "spicevmc", > "spiceport", > "nmdm", > + "qemu-vdagent", > ); > > VIR_ENUM_IMPL(virDomainChrTcpProtocol, > @@ -2698,6 +2699,7 @@ virDomainChrSourceDefGetPath(virDomainChrSourceDef *chr) > case VIR_DOMAIN_CHR_TYPE_STDIO: > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_SPICEPORT: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > return NULL; > } > @@ -2807,6 +2809,11 @@ virDomainChrSourceDefCopy(virDomainChrSourceDef *dest, > dest->data.spiceport.channel = g_strdup(src->data.spiceport.channel); > break; > > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > + dest->data.qemuVdagent.clipboard = src->data.qemuVdagent.clipboard; > + dest->data.qemuVdagent.mouse = src->data.qemuVdagent.mouse; > + break; > + > case VIR_DOMAIN_CHR_TYPE_NULL: > case VIR_DOMAIN_CHR_TYPE_VC: > case VIR_DOMAIN_CHR_TYPE_STDIO: > @@ -2888,6 +2895,10 @@ virDomainChrSourceDefIsEqual(const virDomainChrSourceDef *src, > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > return src->data.spicevmc == tgt->data.spicevmc; > > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > + return src->data.qemuVdagent.clipboard == tgt->data.qemuVdagent.clipboard && > + src->data.qemuVdagent.mouse == tgt->data.qemuVdagent.mouse; > + > case VIR_DOMAIN_CHR_TYPE_NULL: > case VIR_DOMAIN_CHR_TYPE_VC: > case VIR_DOMAIN_CHR_TYPE_STDIO: > @@ -11244,6 +11255,33 @@ virDomainChrSourceDefParseLog(virDomainChrSourceDef *def, > } > > > +static int > +virDomainChrSourceDefParseQemuVdagent(virDomainChrSourceDef *def, > + xmlNodePtr source, > + xmlXPathContextPtr ctxt) > +{ > + xmlNodePtr cur; > + VIR_XPATH_NODE_AUTORESTORE(ctxt) > + > + ctxt->node = source; > + if ((cur = virXPathNode("./clipboard", ctxt))) { > + if (virXMLPropTristateBool(cur, "copypaste", > + VIR_XML_PROP_REQUIRED, > + &def->data.qemuVdagent.clipboard) < 0) > + return -1; > + } > + if ((cur = virXPathNode("./mouse", ctxt))) { > + if (virXMLPropEnum(cur, "mode", > + virDomainMouseModeTypeFromString, > + VIR_XML_PROP_REQUIRED | VIR_XML_PROP_NONZERO, > + &def->data.qemuVdagent.mouse) < 0) > + return -1; > + } > + > + return 0; > +} > + > + > /* Parse the source half of the XML definition for a character device, > * where node is the first element of node->children of the parent > * element. def->type must already be valid. > @@ -11325,6 +11363,12 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDef *def, > def->data.nmdm.slave = virXMLPropString(sources[0], "slave"); > break; > > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > + if (virDomainChrSourceDefParseQemuVdagent(def, sources[0], ctxt) < 0) > + goto error; > + > + break; > + > case VIR_DOMAIN_CHR_TYPE_LAST: > case VIR_DOMAIN_CHR_TYPE_NULL: > case VIR_DOMAIN_CHR_TYPE_VC: > @@ -24996,6 +25040,22 @@ virDomainChrSourceDefFormat(virBuffer *buf, > /* nada */ > break; > > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > + if (def->data.qemuVdagent.mouse != VIR_DOMAIN_MOUSE_MODE_DEFAULT || > + def->data.qemuVdagent.clipboard != VIR_TRISTATE_BOOL_ABSENT) { > + virBufferAddLit(buf, "<source>\n"); > + virBufferAdjustIndent(buf, 2); > + if (def->data.qemuVdagent.clipboard != VIR_TRISTATE_BOOL_ABSENT) > + virBufferEscapeString(buf, "<clipboard copypaste='%s'/>\n", > + virTristateBoolTypeToString(def->data.qemuVdagent.clipboard)); > + if (def->data.qemuVdagent.mouse != VIR_DOMAIN_MOUSE_MODE_DEFAULT) > + virBufferEscapeString(buf, "<mouse mode='%s'/>\n", > + virDomainMouseModeTypeToString(def->data.qemuVdagent.mouse)); > + virBufferAdjustIndent(buf, -2); > + virBufferAddLit(buf, "</source>\n"); > + } > + break; > + > case VIR_DOMAIN_CHR_TYPE_PTY: > case VIR_DOMAIN_CHR_TYPE_DEV: > case VIR_DOMAIN_CHR_TYPE_FILE: > @@ -25081,7 +25141,6 @@ virDomainChrSourceDefFormat(virBuffer *buf, > virBufferEscapeString(buf, "<source channel='%s'/>\n", > def->data.spiceport.channel); > break; > - > } > > if (def->logfile) { > @@ -25211,7 +25270,6 @@ virDomainChrTargetDefFormat(virBuffer *buf, > return 0; > } > > - > static int > virDomainChrDefFormat(virBuffer *buf, > virDomainChrDef *def, > diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h > index 2b00099431..484fc08f81 100644 > --- a/src/conf/domain_conf.h > +++ b/src/conf/domain_conf.h > @@ -1239,6 +1239,7 @@ typedef enum { > VIR_DOMAIN_CHR_TYPE_SPICEVMC, > VIR_DOMAIN_CHR_TYPE_SPICEPORT, > VIR_DOMAIN_CHR_TYPE_NMDM, > + VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT, > > VIR_DOMAIN_CHR_TYPE_LAST > } virDomainChrType; > @@ -1266,6 +1267,13 @@ struct _virDomainChrSourceReconnectDef { > unsigned int timeout; > }; > > +typedef enum { > + VIR_DOMAIN_MOUSE_MODE_DEFAULT = 0, > + VIR_DOMAIN_MOUSE_MODE_SERVER, > + VIR_DOMAIN_MOUSE_MODE_CLIENT, > + > + VIR_DOMAIN_MOUSE_MODE_LAST > +} virDomainMouseMode; > > /* The host side information for a character device. */ > struct _virDomainChrSourceDef { > @@ -1307,6 +1315,10 @@ struct _virDomainChrSourceDef { > struct { > char *channel; > } spiceport; > + struct { > + virDomainMouseMode mouse; > + virTristateBool clipboard; > + } qemuVdagent; > } data; > char *logfile; > virTristateSwitch logappend; > @@ -1843,14 +1855,6 @@ typedef enum { > VIR_DOMAIN_GRAPHICS_SPICE_ZLIB_COMPRESSION_LAST > } virDomainGraphicsSpiceZlibCompression; > > -typedef enum { > - VIR_DOMAIN_MOUSE_MODE_DEFAULT = 0, > - VIR_DOMAIN_MOUSE_MODE_SERVER, > - VIR_DOMAIN_MOUSE_MODE_CLIENT, > - > - VIR_DOMAIN_MOUSE_MODE_LAST > -} virDomainMouseMode; > - > typedef enum { > VIR_DOMAIN_GRAPHICS_SPICE_STREAMING_MODE_DEFAULT = 0, > VIR_DOMAIN_GRAPHICS_SPICE_STREAMING_MODE_FILTER, > diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c > index d6869e8fd8..c0eb2490a8 100644 > --- a/src/conf/domain_validate.c > +++ b/src/conf/domain_validate.c > @@ -849,6 +849,7 @@ virDomainChrSourceDefValidate(const virDomainChrSourceDef *src_def, > case VIR_DOMAIN_CHR_TYPE_VC: > case VIR_DOMAIN_CHR_TYPE_STDIO: > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > break; > > diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng > index 34bccee2f5..60aad8cabe 100644 > --- a/src/conf/schemas/domaincommon.rng > +++ b/src/conf/schemas/domaincommon.rng > @@ -3965,23 +3965,10 @@ > </element> > </optional> > <optional> > - <element name="clipboard"> > - <attribute name="copypaste"> > - <ref name="virYesNo"/> > - </attribute> > - <empty/> > - </element> > + <ref name="clipboard"/> > </optional> > <optional> > - <element name="mouse"> > - <attribute name="mode"> > - <choice> > - <value>server</value> > - <value>client</value> > - </choice> > - </attribute> > - <empty/> > - </element> > + <ref name="mousemode"/> > </optional> > <optional> > <element name="filetransfer"> > @@ -4070,6 +4057,25 @@ > </element> > </define> > > + <define name="clipboard"> > + <element name="clipboard"> > + <attribute name="copypaste"> > + <ref name="virYesNo"/> > + </attribute> > + <empty/> > + </element> > + </define> > + <define name="mousemode"> > + <element name="mouse"> > + <attribute name="mode"> > + <choice> > + <value>server</value> > + <value>client</value> > + </choice> > + </attribute> > + <empty/> > + </element> > + </define> > <define name="listenElements"> > <zeroOrMore> > <element name="listen"> > @@ -4470,6 +4476,7 @@ > <value>spicevmc</value> > <value>spiceport</value> > <value>nmdm</value> > + <value>qemu-vdagent</value> > </choice> > </define> > > @@ -4555,9 +4562,17 @@ > <optional> > <ref name="reconnect"/> > </optional> > - <zeroOrMore> > - <ref name="devSeclabel"/> > - </zeroOrMore> > + <interleave> > + <zeroOrMore> > + <ref name="devSeclabel"/> > + </zeroOrMore> > + <optional> > + <ref name="clipboard"/> > + </optional> > + <optional> > + <ref name="mousemode"/> > + </optional> > + </interleave> > </element> > </zeroOrMore> > <optional> > diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c > index 7834aece35..1eef9fb6d0 100644 > --- a/src/qemu/qemu_command.c > +++ b/src/qemu/qemu_command.c > @@ -1390,6 +1390,7 @@ qemuBuildChardevStr(const virDomainChrSourceDef *dev, > break; > > case VIR_DOMAIN_CHR_TYPE_NMDM: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > default: > break; > @@ -1473,6 +1474,7 @@ qemuBuildChardevCommand(virCommand *cmd, > case VIR_DOMAIN_CHR_TYPE_UDP: > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_SPICEPORT: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > break; > > case VIR_DOMAIN_CHR_TYPE_NMDM: > @@ -8613,6 +8615,7 @@ qemuInterfaceVhostuserConnect(virCommand *cmd, > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_SPICEPORT: > case VIR_DOMAIN_CHR_TYPE_NMDM: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > virReportError(VIR_ERR_INTERNAL_ERROR, > _("vhost-user type '%s' not supported"), > diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c > index d5622bd6d9..1ac5377449 100644 > --- a/src/qemu/qemu_monitor_json.c > +++ b/src/qemu/qemu_monitor_json.c > @@ -6818,6 +6818,7 @@ qemuMonitorJSONAttachCharDevGetProps(const char *chrID, > case VIR_DOMAIN_CHR_TYPE_PIPE: > case VIR_DOMAIN_CHR_TYPE_STDIO: > case VIR_DOMAIN_CHR_TYPE_NMDM: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > virReportError(VIR_ERR_OPERATION_FAILED, > _("Hotplug unsupported for char device type '%s'"), > virDomainChrTypeToString(chr->type)); > diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c > index 1ed60917ea..423a10738d 100644 > --- a/src/qemu/qemu_process.c > +++ b/src/qemu/qemu_process.c > @@ -6834,6 +6834,7 @@ qemuProcessPrepareHostBackendChardevOne(virDomainDeviceDef *dev, > case VIR_DOMAIN_CHR_TYPE_TCP: > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_SPICEPORT: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > break; > > case VIR_DOMAIN_CHR_TYPE_FILE: { > diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c > index e0708b8a76..5ad3d914dc 100644 > --- a/src/qemu/qemu_validate.c > +++ b/src/qemu/qemu_validate.c > @@ -1991,6 +1991,7 @@ qemuValidateDomainChrSourceDef(const virDomainChrSourceDef *def, > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_SPICEPORT: > case VIR_DOMAIN_CHR_TYPE_NMDM: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > break; > } > diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c > index 8f7acba980..55c0193940 100644 > --- a/src/security/security_apparmor.c > +++ b/src/security/security_apparmor.c > @@ -1021,6 +1021,7 @@ AppArmorSetChardevLabel(virSecurityManager *mgr, > case VIR_DOMAIN_CHR_TYPE_TCP: > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_NMDM: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > ret = 0; > break; > @@ -1083,6 +1084,7 @@ AppArmorSetNetdevLabel(virSecurityManager *mgr, > case VIR_DOMAIN_CHR_TYPE_TCP: > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_NMDM: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > ret = 0; > break; > diff --git a/src/security/security_dac.c b/src/security/security_dac.c > index e9e316551e..5b840f4225 100644 > --- a/src/security/security_dac.c > +++ b/src/security/security_dac.c > @@ -1555,6 +1555,7 @@ virSecurityDACSetChardevLabelHelper(virSecurityManager *mgr, > case VIR_DOMAIN_CHR_TYPE_TCP: > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_NMDM: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > break; > } > @@ -1639,6 +1640,7 @@ virSecurityDACRestoreChardevLabelHelper(virSecurityManager *mgr, > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_SPICEPORT: > case VIR_DOMAIN_CHR_TYPE_NMDM: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > case VIR_DOMAIN_CHR_TYPE_LAST: > break; > } > diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c > index fe8908f533..10fdeacaa2 100644 > --- a/tests/testutilsqemu.c > +++ b/tests/testutilsqemu.c > @@ -1042,6 +1042,7 @@ testQemuPrepareHostBackendChardevOne(virDomainDeviceDef *dev, > case VIR_DOMAIN_CHR_TYPE_TCP: > case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > case VIR_DOMAIN_CHR_TYPE_SPICEPORT: > + case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: > break; > > case VIR_DOMAIN_CHR_TYPE_FILE: > -- > 2.35.1 >