Hi
On Thu, Mar 24, 2022 at 11:26 PM Jonathon Jongsma <jjongsma(a)redhat.com> 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(a)redhat.com>
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(a)redhat.com>
---
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