diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 9be305f3e6..993c65e50b 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -6608,6 +6608,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:``
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index a0dd255bd8..298ae183ce 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -715,6 +715,7 @@ VIR_ENUM_IMPL(virDomainChr,
"spicevmc",
"spiceport",
"nmdm",
+ "qemu-vdagent",
);
VIR_ENUM_IMPL(virDomainChrTcpProtocol,
@@ -2702,6 +2703,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;
}
@@ -2811,6 +2813,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:
@@ -2892,6 +2899,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:
@@ -11220,6 +11231,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.
@@ -11301,6 +11339,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, "\n");
+ virBufferAdjustIndent(buf, 2);
+ if (def->data.qemuVdagent.clipboard != VIR_TRISTATE_BOOL_ABSENT)
+ virBufferEscapeString(buf, "\n",
+ virTristateBoolTypeToString(def->data.qemuVdagent.clipboard));
+ if (def->data.qemuVdagent.mouse != VIR_DOMAIN_MOUSE_MODE_DEFAULT)
+ virBufferEscapeString(buf, "\n",
+ virDomainMouseModeTypeToString(def->data.qemuVdagent.mouse));
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "\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, "\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 cc67bc453c..8a48646160 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1241,6 +1241,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;
@@ -1268,6 +1269,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 {
@@ -1309,6 +1317,10 @@ struct _virDomainChrSourceDef {
struct {
char *channel;
} spiceport;
+ struct {
+ virDomainMouseMode mouse;
+ virTristateBool clipboard;
+ } qemuVdagent;
} data;
char *logfile;
virTristateSwitch logappend;
@@ -1845,14 +1857,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 b9cb50ed31..18eb8d697d 100644
--- a/src/conf/domain_validate.c
+++ b/src/conf/domain_validate.c
@@ -920,6 +920,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 8afb0dadd4..2544864eb4 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -4009,23 +4009,10 @@
-
-
-
-
-
-
+
-
-
-
- server
- client
-
-
-
-
+
@@ -4114,6 +4101,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ server
+ client
+
+
+
+
+
@@ -4514,6 +4520,7 @@
spicevmcspiceportnmdm
+ qemu-vdagent
@@ -4599,9 +4606,17 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index f5e253740a..54f46c60ee 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -1423,6 +1423,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;
@@ -1506,6 +1507,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:
@@ -8641,6 +8643,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 9e611e93e8..95bcbc08ef 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -6824,6 +6824,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 b0b00eb0a2..2b16298942 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -6826,6 +6826,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 b576efe375..4a0ccf4b2b 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -1997,6 +1997,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 d57f982c37..7f6d9ee06f 100644
--- a/tests/testutilsqemu.c
+++ b/tests/testutilsqemu.c
@@ -1056,6 +1056,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: