]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: Introduce unified chardev backend config generator
authorPeter Krempa <pkrempa@redhat.com>
Thu, 9 Dec 2021 08:29:37 +0000 (09:29 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Thu, 19 Sep 2024 08:30:14 +0000 (10:30 +0200)
Similarly to how we approach the generators for
-device/-object/-blockdev/-netdev rewrite the generator of -chardev to
be unified with the generator for the monitor.

Unfortunately with -chardev it will be a bit more quirky when compared
to the others as the generator itself will need to know whether it
generates command line output or not as a few field names change and data
is nested differently.

This first step adds the generator and uses it only for command line
generation. This was possible to achieve without changing any of the
output in tests.

In further patches the same generator will then be used also in the
monitor code replacing both.

As basis for the generator I took the monitor code but modified it to
have the same field order as the commandline code and extended it
further to support all backend types, even those which are not
hotpluggable.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
src/qemu/meson.build
src/qemu/qemu_chardev.c [new file with mode: 0644]
src/qemu/qemu_chardev.h [new file with mode: 0644]
src/qemu/qemu_command.c

index 57356451e42de6ca9a347f389985af48b800a848..1d904bbc6834e5159773e7c1bbfd7a3ec4474266 100644 (file)
@@ -6,6 +6,7 @@ qemu_driver_sources = [
   'qemu_blockjob.c',
   'qemu_capabilities.c',
   'qemu_cgroup.c',
+  'qemu_chardev.c',
   'qemu_checkpoint.c',
   'qemu_command.c',
   'qemu_conf.c',
diff --git a/src/qemu/qemu_chardev.c b/src/qemu/qemu_chardev.c
new file mode 100644 (file)
index 0000000..b3ed112
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <config.h>
+
+#include "qemu_capabilities.h"
+#include "qemu_domain.h"
+#include "qemu_fd.h"
+
+#include "vircommand.h"
+#include "virlog.h"
+#include "virqemu.h"
+
+#include "domain_conf.h"
+
+#include "qemu_chardev.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+VIR_LOG_INIT("qemu.qemu_command");
+
+static int
+qemuChardevBackendAddSocketAddressInet(virJSONValue **backendData,
+                                       const char *backendFieldName,
+                                       bool commandline,
+                                       const char *commandlinePrefix,
+                                       const char *host,
+                                       const char *port)
+{
+    if (commandline) {
+        g_autofree char *hostField = NULL;
+        g_autofree char *portField = NULL;
+
+        if (!commandlinePrefix) {
+            hostField = g_strdup("s:host");
+            portField = g_strdup("s:port");
+        } else {
+            hostField = g_strdup_printf("s:%saddr", commandlinePrefix);
+            portField = g_strdup_printf("s:%sport", commandlinePrefix);
+        }
+
+        if (virJSONValueObjectAdd(backendData,
+                                  hostField, host,
+                                  portField, port,
+                                  NULL) < 0)
+            return -1;
+    } else {
+        g_autoptr(virJSONValue) addr = NULL;
+        g_autoptr(virJSONValue) data = NULL;
+        g_autofree char *datafield = g_strdup_printf("a:%s", backendFieldName);
+
+        if (virJSONValueObjectAdd(&data,
+                                  "s:host", host,
+                                  "s:port", port,
+                                  NULL) < 0)
+            return -1;
+
+        if (virJSONValueObjectAdd(&addr,
+                                  "s:type", "inet",
+                                  "a:data", &data,
+                                  NULL) < 0)
+            return -1;
+
+        if (virJSONValueObjectAdd(backendData,
+                                  datafield, &addr,
+                                  NULL) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+qemuChardevBackendAddSocketAddressFD(virJSONValue **backendData,
+                                     const char *backendFieldName,
+                                     bool commandline,
+                                     const char *fdname)
+{
+    if (commandline) {
+        if (virJSONValueObjectAdd(backendData,
+                                  "s:fd", fdname,
+                                  NULL) < 0)
+            return -1;
+    } else {
+        g_autoptr(virJSONValue) addr = NULL;
+        g_autoptr(virJSONValue) data = NULL;
+        g_autofree char *datafield = g_strdup_printf("a:%s", backendFieldName);
+
+        if (virJSONValueObjectAdd(&data, "s:str", fdname, NULL) < 0)
+            return -1;
+
+        if (virJSONValueObjectAdd(&addr,
+                                  "s:type", "fd",
+                                  "a:data", &data, NULL) < 0)
+            return -1;
+
+        if (virJSONValueObjectAdd(backendData,
+                                  datafield, &addr,
+                                  NULL) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+qemuChardevBackendAddSocketAddressUNIX(virJSONValue **backendData,
+                                       const char *backendFieldName,
+                                       bool commandline,
+                                       const char *path)
+{
+    if (commandline) {
+        if (virJSONValueObjectAdd(backendData,
+                                  "s:path", path,
+                                  NULL) < 0)
+            return -1;
+    } else {
+        g_autoptr(virJSONValue) addr = NULL;
+        g_autoptr(virJSONValue) data = NULL;
+        g_autofree char *datafield = g_strdup_printf("a:%s", backendFieldName);
+
+        if (virJSONValueObjectAdd(&data, "s:path", path, NULL) < 0)
+            return -1;
+
+        if (virJSONValueObjectAdd(&addr,
+                                  "s:type", "unix",
+                                  "a:data", &data, NULL) < 0)
+            return -1;
+
+        if (virJSONValueObjectAdd(backendData,
+                                  datafield, &addr,
+                                  NULL) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+
+int
+qemuChardevGetBackendProps(const virDomainChrSourceDef *chr,
+                           bool commandline,
+                           const char *alias,
+                           const char **backendType,
+                           virJSONValue **props)
+{
+    qemuDomainChrSourcePrivate *chrSourcePriv = QEMU_DOMAIN_CHR_SOURCE_PRIVATE(chr);
+    const char *dummy = NULL;
+
+    if (!backendType)
+        backendType = &dummy;
+
+    *props = NULL;
+
+    switch ((virDomainChrType)chr->type) {
+    case VIR_DOMAIN_CHR_TYPE_NULL:
+    case VIR_DOMAIN_CHR_TYPE_VC:
+    case VIR_DOMAIN_CHR_TYPE_PTY:
+    case VIR_DOMAIN_CHR_TYPE_STDIO:
+        *backendType = virDomainChrTypeToString(chr->type);
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_FILE: {
+        const char *path = chr->data.file.path;
+        virTristateSwitch append = chr->data.file.append;
+        const char *pathfield = "s:out";
+
+        if (commandline)
+            pathfield = "s:path";
+
+        *backendType = "file";
+
+        if (chrSourcePriv->sourcefd) {
+            path = qemuFDPassGetPath(chrSourcePriv->sourcefd);
+            append = VIR_TRISTATE_SWITCH_ON;
+        }
+
+        if (virJSONValueObjectAdd(props,
+                                  pathfield, path,
+                                  "T:append", append,
+                                  NULL) < 0)
+            return -1;
+    }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_PIPE:
+    case VIR_DOMAIN_CHR_TYPE_DEV: {
+        const char *pathField = "s:device";
+
+        if (commandline)
+            pathField = "s:path";
+
+        if (chr->type == VIR_DOMAIN_CHR_TYPE_PIPE) {
+            *backendType = "pipe";
+        } else {
+            if (STRPREFIX(alias, "charparallel"))
+                *backendType = "parallel";
+            else
+                *backendType = "serial";
+        }
+
+        if (virJSONValueObjectAdd(props,
+                                  pathField, chr->data.file.path,
+                                  NULL) < 0)
+            return -1;
+    }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_UNIX: {
+        virTristateBool waitval = VIR_TRISTATE_BOOL_ABSENT;
+        virTristateBool server = VIR_TRISTATE_BOOL_ABSENT;
+        int reconnect = -1;
+
+        *backendType = "socket";
+
+        if (!commandline)
+            server = VIR_TRISTATE_BOOL_NO;
+
+        if (chr->data.nix.listen) {
+            server = VIR_TRISTATE_BOOL_YES;
+
+            if (!chrSourcePriv->wait)
+                waitval = VIR_TRISTATE_BOOL_NO;
+        }
+
+        if (chrSourcePriv->directfd) {
+            if (qemuChardevBackendAddSocketAddressFD(props, "addr",
+                                                     commandline,
+                                                     qemuFDPassDirectGetPath(chrSourcePriv->directfd)) < 0)
+                return -1;
+        } else {
+            if (qemuChardevBackendAddSocketAddressUNIX(props, "addr",
+                                                       commandline,
+                                                       chr->data.nix.path) < 0)
+                return -1;
+
+            if (chr->data.nix.reconnect.enabled == VIR_TRISTATE_BOOL_YES)
+                reconnect = chr->data.nix.reconnect.timeout;
+            else if (chr->data.nix.reconnect.enabled == VIR_TRISTATE_BOOL_NO)
+                reconnect = 0;
+        }
+
+        if (virJSONValueObjectAdd(props,
+                                  "T:server", server,
+                                  "T:wait", waitval,
+                                  "k:reconnect", reconnect,
+                                  NULL) < 0)
+            return -1;
+    }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_TCP: {
+        virTristateBool waitval = VIR_TRISTATE_BOOL_ABSENT;
+        virTristateBool telnet = VIR_TRISTATE_BOOL_ABSENT;
+        virTristateBool server = VIR_TRISTATE_BOOL_ABSENT;
+        int reconnect = -1;
+
+        *backendType = "socket";
+
+        if (!commandline) {
+            server = VIR_TRISTATE_BOOL_NO;
+            telnet = VIR_TRISTATE_BOOL_NO;
+        }
+
+        if (chr->data.tcp.listen) {
+            server = VIR_TRISTATE_BOOL_YES;
+
+            if (!chrSourcePriv->wait)
+                waitval = VIR_TRISTATE_BOOL_NO;
+        }
+
+        if (chr->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET)
+            telnet = VIR_TRISTATE_BOOL_YES;
+
+        if (chr->data.tcp.reconnect.enabled == VIR_TRISTATE_BOOL_YES)
+            reconnect = chr->data.tcp.reconnect.timeout;
+        else if (chr->data.tcp.reconnect.enabled == VIR_TRISTATE_BOOL_NO)
+            reconnect = 0;
+
+        if (qemuChardevBackendAddSocketAddressInet(props, "addr",
+                                                   commandline, NULL,
+                                                   chr->data.tcp.host,
+                                                   chr->data.tcp.service) < 0)
+            return -1;
+
+        if (virJSONValueObjectAdd(props,
+                                  "T:telnet", telnet,
+                                  "T:server", server,
+                                  "T:wait", waitval,
+                                  "k:reconnect", reconnect,
+                                  "S:tls-creds", chrSourcePriv->tlsCredsAlias,
+                                  NULL) < 0)
+            return -1;
+    }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_UDP:
+        *backendType = "udp";
+
+        if (qemuChardevBackendAddSocketAddressInet(props, "remote",
+                                                   commandline, NULL,
+                                                   NULLSTR_EMPTY(chr->data.udp.connectHost),
+                                                   chr->data.udp.connectService) < 0)
+            return -1;
+
+        if (commandline || chr->data.udp.bindHost || chr->data.udp.bindService) {
+            const char *bindHost = NULLSTR_EMPTY(chr->data.udp.bindHost);
+            const char *bindService = chr->data.udp.bindService;
+
+            if (!bindService)
+                bindService = "0";
+
+            if (qemuChardevBackendAddSocketAddressInet(props, "local",
+                                                       commandline, "local",
+                                                       bindHost, bindService) < 0)
+                return -1;
+        }
+
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_SPICEVMC: {
+        const char *typeField = "s:type";
+
+        *backendType = "spicevmc";
+
+        if (commandline)
+            typeField = "s:name";
+
+        if (virJSONValueObjectAdd(props,
+                                  typeField, virDomainChrSpicevmcTypeToString(chr->data.spicevmc),
+                                  NULL) < 0)
+            return -1;
+    }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT: {
+        virTristateBool mouse = VIR_TRISTATE_BOOL_ABSENT;
+
+        *backendType = "qemu-vdagent";
+
+        switch (chr->data.qemuVdagent.mouse) {
+            case VIR_DOMAIN_MOUSE_MODE_CLIENT:
+                mouse = VIR_TRISTATE_BOOL_YES;
+                break;
+            case VIR_DOMAIN_MOUSE_MODE_SERVER:
+                mouse = VIR_TRISTATE_BOOL_NO;
+                break;
+            case VIR_DOMAIN_MOUSE_MODE_DEFAULT:
+                break;
+            case VIR_DOMAIN_MOUSE_MODE_LAST:
+            default:
+                virReportEnumRangeError(virDomainMouseMode,
+                                        chr->data.qemuVdagent.mouse);
+                return -1;
+        }
+
+        if (commandline) {
+            if (virJSONValueObjectAdd(props,
+                                      "s:name", "vdagent",
+                                      NULL) < 0)
+                return -1;
+        }
+
+        if (virJSONValueObjectAdd(props,
+                                  "T:clipboard", chr->data.qemuVdagent.clipboard,
+                                  "T:mouse", mouse,
+                                  NULL) < 0)
+            return -1;
+        break;
+    }
+
+    case VIR_DOMAIN_CHR_TYPE_DBUS:
+        *backendType = "dbus";
+
+        if (virJSONValueObjectAdd(props,
+                                  "s:name", chr->data.dbus.channel,
+                                  NULL) < 0)
+            return -1;
+
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_SPICEPORT: {
+        const char *channelField = "s:fqdn";
+
+        *backendType = "spiceport";
+
+        if (commandline)
+            channelField = "s:name";
+
+        if (virJSONValueObjectAdd(props,
+                                  channelField, chr->data.spiceport.channel,
+                                  NULL) < 0)
+            return -1;
+    }
+        break;
+
+
+    case VIR_DOMAIN_CHR_TYPE_NMDM:
+    case VIR_DOMAIN_CHR_TYPE_LAST:
+    default:
+        virReportEnumRangeError(virDomainChrType, chr->type);
+        return -1;
+    }
+
+    if (chr->logfile) {
+        const char *path = chr->logfile;
+        virTristateSwitch append = chr->logappend;
+
+        if (chrSourcePriv->logfd) {
+            path = qemuFDPassGetPath(chrSourcePriv->logfd);
+            append = VIR_TRISTATE_SWITCH_ON;
+        }
+
+        if (virJSONValueObjectAdd(props,
+                                  "s:logfile", path,
+                                  "T:logappend", append,
+                                  NULL) < 0)
+            return -1;
+    }
+
+    if (!commandline) {
+        /* The 'chardev-add' QMP command uses two extra layers of wrapping in
+         * comparison to what the '-chardev' command syntax has */
+        g_autoptr(virJSONValue) backend = g_steal_pointer(props);
+        g_autoptr(virJSONValue) backendWrap = NULL;
+
+        /* the 'data' field of the wrapper below must be present per QMP schema */
+        if (!backend)
+            backend = virJSONValueNewObject();
+
+        if (virJSONValueObjectAdd(&backendWrap,
+                                  "s:type", *backendType,
+                                  "a:data", &backend,
+                                  NULL) < 0)
+            return -1;
+
+        /* We now replace the value in the variable we're about to return */
+        if (virJSONValueObjectAdd(props,
+                                  "s:id", alias,
+                                  "a:backend", &backendWrap,
+                                  NULL) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+
+int
+qemuChardevBuildCommandline(virCommand *cmd,
+                            const virDomainChrSourceDef *dev,
+                            const char *charAlias,
+                            virQEMUCaps *qemuCaps)
+{
+    g_autoptr(virJSONValue) props = NULL;
+    g_autofree char *arg = NULL;
+    /* BEWARE: '-chardev' is not yet accepting JSON syntax.
+     * QEMU_CAPS_CHARDEV_JSON is asserted just from tests */
+    bool useJSON = virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV_JSON);
+    const char *backendType = NULL;
+
+    if (qemuChardevGetBackendProps(dev, !useJSON, charAlias, &backendType, &props) < 0)
+        return -1;
+
+    if (useJSON) {
+        if (!(arg = virJSONValueToString(props, false)))
+            return -1;
+    } else {
+        g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+        virBufferAsprintf(&buf, "%s,id=%s", backendType, charAlias);
+
+        if (props) {
+            virBufferAddLit(&buf, ",");
+
+            if (virQEMUBuildCommandLineJSON(props, &buf, NULL, NULL) < 0)
+                return -1;
+        }
+
+        arg = virBufferContentAndReset(&buf);
+    }
+
+    virCommandAddArgList(cmd, "-chardev", arg, NULL);
+    return 0;
+}
diff --git a/src/qemu/qemu_chardev.h b/src/qemu/qemu_chardev.h
new file mode 100644 (file)
index 0000000..0381e0d
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include "domain_conf.h"
+#include "qemu_capabilities.h"
+#include "vircommand.h"
+
+int
+qemuChardevBuildCommandline(virCommand *cmd,
+                            const virDomainChrSourceDef *dev,
+                            const char *charAlias,
+                            virQEMUCaps *qemuCaps);
+
+int
+qemuChardevGetBackendProps(const virDomainChrSourceDef *chr,
+                           bool commandline,
+                           const char *alias,
+                           const char **backendType,
+                           virJSONValue **props);
index 1b992d8eed7a8134fe1c8ea4ecd8370e235d6e60..c2d65645c4385483a0e4890b796863be499226ac 100644 (file)
@@ -31,6 +31,7 @@
 #include "qemu_slirp.h"
 #include "qemu_block.h"
 #include "qemu_fd.h"
+#include "qemu_chardev.h"
 #include "viralloc.h"
 #include "virlog.h"
 #include "virarch.h"
@@ -1292,203 +1293,6 @@ qemuBuildTLSx509CommandLine(virCommand *cmd,
 }
 
 
-static void
-qemuBuildChrChardevReconnectStr(virBuffer *buf,
-                                const virDomainChrSourceReconnectDef *def)
-{
-    if (def->enabled == VIR_TRISTATE_BOOL_YES) {
-        virBufferAsprintf(buf, ",reconnect=%u", def->timeout);
-    } else if (def->enabled == VIR_TRISTATE_BOOL_NO) {
-        virBufferAddLit(buf, ",reconnect=0");
-    }
-}
-
-
-static char *
-qemuBuildChardevStr(const virDomainChrSourceDef *dev,
-                    const char *charAlias)
-{
-
-    qemuDomainChrSourcePrivate *chrSourcePriv = QEMU_DOMAIN_CHR_SOURCE_PRIVATE(dev);
-    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
-    const char *path;
-    virTristateSwitch append;
-
-    switch ((virDomainChrType) dev->type) {
-    case VIR_DOMAIN_CHR_TYPE_NULL:
-        virBufferAsprintf(&buf, "null,id=%s", charAlias);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_VC:
-        virBufferAsprintf(&buf, "vc,id=%s", charAlias);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_PTY:
-        virBufferAsprintf(&buf, "pty,id=%s", charAlias);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_DEV: {
-        const char *backend = "serial";
-
-        if (STRPREFIX(charAlias, "charparallel"))
-            backend = "parallel";
-
-        virBufferAsprintf(&buf, "%s,id=%s,path=", backend, charAlias);
-        virQEMUBuildBufferEscapeComma(&buf, dev->data.file.path);
-        break;
-    }
-
-    case VIR_DOMAIN_CHR_TYPE_FILE:
-        virBufferAsprintf(&buf, "file,id=%s", charAlias);
-        path = dev->data.file.path;
-        append = dev->data.file.append;
-
-        if (chrSourcePriv->sourcefd) {
-            path = qemuFDPassGetPath(chrSourcePriv->sourcefd);
-            append = VIR_TRISTATE_SWITCH_ON;
-        }
-
-        virBufferAddLit(&buf, ",path=");
-        virQEMUBuildBufferEscapeComma(&buf, path);
-        if (append != VIR_TRISTATE_SWITCH_ABSENT) {
-            virBufferAsprintf(&buf, ",append=%s",
-                              virTristateSwitchTypeToString(append));
-        }
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_PIPE:
-        virBufferAsprintf(&buf, "pipe,id=%s,path=", charAlias);
-        virQEMUBuildBufferEscapeComma(&buf, dev->data.file.path);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_STDIO:
-        virBufferAsprintf(&buf, "stdio,id=%s", charAlias);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_UDP: {
-        const char *connectHost = dev->data.udp.connectHost;
-        const char *bindHost = dev->data.udp.bindHost;
-        const char *bindService = dev->data.udp.bindService;
-
-        if (connectHost == NULL)
-            connectHost = "";
-        if (bindHost == NULL)
-            bindHost = "";
-        if (bindService == NULL)
-            bindService = "0";
-
-        virBufferAsprintf(&buf,
-                          "udp,id=%s,host=%s,port=%s,localaddr=%s,"
-                          "localport=%s",
-                          charAlias,
-                          connectHost,
-                          dev->data.udp.connectService,
-                          bindHost, bindService);
-        break;
-    }
-
-    case VIR_DOMAIN_CHR_TYPE_TCP:
-        virBufferAsprintf(&buf,
-                          "socket,id=%s,host=%s,port=%s",
-                          charAlias,
-                          dev->data.tcp.host,
-                          dev->data.tcp.service);
-
-        if (dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET)
-            virBufferAddLit(&buf, ",telnet=on");
-
-        if (dev->data.tcp.listen) {
-            virBufferAddLit(&buf, ",server=on");
-            if (!chrSourcePriv->wait)
-                virBufferAddLit(&buf, ",wait=off");
-        }
-
-        qemuBuildChrChardevReconnectStr(&buf, &dev->data.tcp.reconnect);
-
-        if (chrSourcePriv->tlsCredsAlias)
-            virBufferAsprintf(&buf, ",tls-creds=%s", chrSourcePriv->tlsCredsAlias);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_UNIX:
-        virBufferAsprintf(&buf, "socket,id=%s", charAlias);
-        if (chrSourcePriv->directfd) {
-            virBufferAsprintf(&buf, ",fd=%s", qemuFDPassDirectGetPath(chrSourcePriv->directfd));
-        } else {
-            virBufferAddLit(&buf, ",path=");
-            virQEMUBuildBufferEscapeComma(&buf, dev->data.nix.path);
-        }
-
-        if (dev->data.nix.listen) {
-            virBufferAddLit(&buf, ",server=on");
-            if (!chrSourcePriv->wait)
-                virBufferAddLit(&buf, ",wait=off");
-        }
-
-        qemuBuildChrChardevReconnectStr(&buf, &dev->data.nix.reconnect);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
-        virBufferAsprintf(&buf, "spicevmc,id=%s,name=%s", charAlias,
-                          virDomainChrSpicevmcTypeToString(dev->data.spicevmc));
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
-        virBufferAsprintf(&buf, "spiceport,id=%s,name=%s", charAlias,
-                          dev->data.spiceport.channel);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
-        virBufferAsprintf(&buf, "qemu-vdagent,id=%s,name=vdagent",
-                          charAlias);
-        if (dev->data.qemuVdagent.clipboard != VIR_TRISTATE_BOOL_ABSENT)
-            virBufferAsprintf(&buf, ",clipboard=%s",
-                              virTristateSwitchTypeToString(dev->data.qemuVdagent.clipboard));
-        switch (dev->data.qemuVdagent.mouse) {
-            case VIR_DOMAIN_MOUSE_MODE_CLIENT:
-                virBufferAddLit(&buf, ",mouse=on");
-                break;
-            case VIR_DOMAIN_MOUSE_MODE_SERVER:
-                virBufferAddLit(&buf, ",mouse=off");
-                break;
-            case VIR_DOMAIN_MOUSE_MODE_DEFAULT:
-            case VIR_DOMAIN_MOUSE_MODE_LAST:
-            default:
-                break;
-        }
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_DBUS:
-        virBufferAsprintf(&buf, "dbus,id=%s,name=%s", charAlias,
-                          dev->data.dbus.channel);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_NMDM:
-    case VIR_DOMAIN_CHR_TYPE_LAST:
-    default:
-        break;
-    }
-
-    if (dev->logfile) {
-        path = dev->logfile;
-        append = dev->logappend;
-
-        if (chrSourcePriv->logfd) {
-            path = qemuFDPassGetPath(chrSourcePriv->logfd);
-            append = VIR_TRISTATE_SWITCH_ON;
-        }
-
-        virBufferAddLit(&buf, ",logfile=");
-        virQEMUBuildBufferEscapeComma(&buf, path);
-        if (append != VIR_TRISTATE_SWITCH_ABSENT) {
-            virBufferAsprintf(&buf, ",logappend=%s",
-                              virTristateSwitchTypeToString(append));
-        }
-    }
-
-    return virBufferContentAndReset(&buf);
-}
-
-
 static int
 qemuBuildChardevCommand(virCommand *cmd,
                         const virDomainChrSourceDef *dev,
@@ -1564,11 +1368,9 @@ qemuBuildChardevCommand(virCommand *cmd,
 
     qemuFDPassTransferCommand(chrSourcePriv->logfd, cmd);
 
-    if (!(charstr = qemuBuildChardevStr(dev, charAlias)))
+    if (qemuChardevBuildCommandline(cmd, dev, charAlias, qemuCaps) < 0)
         return -1;
 
-    virCommandAddArgList(cmd, "-chardev", charstr, NULL);
-
     qemuDomainChrSourcePrivateClearFDPass(chrSourcePriv);
 
     return 0;