[libvirt] [PATCH v3 4/6] qemu_process: Use common processes mgmt funcs for all QMP query types

Chris Venteicher cventeic at redhat.com
Thu Sep 27 21:26:43 UTC 2018


Generalized QEMU process management functions supporting all forms of
QMP queries including but no limited to capabilities queries.

QEMU process instances can be maintained and used for multiple different QMP
queries, of the same or different types, as required to complete a
specific libvirt task.

Support concurrent QEMU process instances for QMP queries,
using the same or different qemu binaries.

All process mgmt functions are removed from qemu_capabilities and
re-implemented in qemu_process in a generic, non capabilities specific,
manner consistent with existing qemu_process mgmt functions.

Concept of qmp_query domain is used to contain process state through the
an entire process lifecycle similar to how a VM domains contain process
state in existing process functions.

Monitor callbacks were not used in original process management code for
capabilities and are eliminated in new generalized process code for QMP
queries.

QMP exchange to exit capabilities negotiation mode and enter command mode
is the sole responsibility of the QMP query process code.

Signed-off-by: Chris Venteicher <cventeic at redhat.com>
---
 src/qemu/qemu_capabilities.c | 304 +++++---------------------
 src/qemu/qemu_monitor.c      |   4 +-
 src/qemu/qemu_process.c      | 398 +++++++++++++++++++++++++++++++++++
 src/qemu/qemu_process.h      |  35 +++
 tests/qemucapabilitiestest.c |   7 +
 5 files changed, 489 insertions(+), 259 deletions(-)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index d38530ca80..5b1e4f1bbd 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -47,6 +47,7 @@
 #define __QEMU_CAPSPRIV_H_ALLOW__
 #include "qemu_capspriv.h"
 #include "qemu_qapi.h"
+#include "qemu_process.h"
 
 #include <fcntl.h>
 #include <sys/stat.h>
@@ -3960,18 +3961,6 @@ virQEMUCapsIsValid(void *data,
 }
 
 
-static void virQEMUCapsMonitorNotify(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
-                                     virDomainObjPtr vm ATTRIBUTE_UNUSED,
-                                     void *opaque ATTRIBUTE_UNUSED)
-{
-}
-
-static qemuMonitorCallbacks callbacks = {
-    .eofNotify = virQEMUCapsMonitorNotify,
-    .errorNotify = virQEMUCapsMonitorNotify,
-};
-
-
 /**
  * virQEMUCapsInitQMPArch:
  * @qemuCaps: QEMU capabilities
@@ -4067,13 +4056,6 @@ virQEMUCapsInitQMPMonitor(virQEMUCapsPtr qemuCaps,
 
     /* @mon is supposed to be locked by callee */
 
-    if (qemuMonitorSetCapabilities(mon) < 0) {
-        VIR_DEBUG("Failed to set monitor capabilities %s",
-                  virGetLastErrorMessage());
-        ret = 0;
-        goto cleanup;
-    }
-
     if (qemuMonitorGetVersion(mon,
                               &major, &minor, &micro,
                               &package) < 0) {
@@ -4247,13 +4229,6 @@ virQEMUCapsInitQMPMonitorTCG(virQEMUCapsPtr qemuCaps ATTRIBUTE_UNUSED,
 {
     int ret = -1;
 
-    if (qemuMonitorSetCapabilities(mon) < 0) {
-        VIR_DEBUG("Failed to set monitor capabilities %s",
-                  virGetLastErrorMessage());
-        ret = 0;
-        goto cleanup;
-    }
-
     if (virQEMUCapsProbeQMPCPUDefinitions(qemuCaps, mon, true) < 0)
         goto cleanup;
 
@@ -4266,251 +4241,75 @@ virQEMUCapsInitQMPMonitorTCG(virQEMUCapsPtr qemuCaps ATTRIBUTE_UNUSED,
 }
 
 
-typedef struct _virQEMUCapsInitQMPCommand virQEMUCapsInitQMPCommand;
-typedef virQEMUCapsInitQMPCommand *virQEMUCapsInitQMPCommandPtr;
-struct _virQEMUCapsInitQMPCommand {
-    char *binary;
-    uid_t runUid;
-    gid_t runGid;
-    char **qmperr;
-    char *monarg;
-    char *monpath;
-    char *pidfile;
-    virCommandPtr cmd;
-    qemuMonitorPtr mon;
-    virDomainChrSourceDef config;
-    pid_t pid;
-    virDomainObjPtr vm;
-};
-
-
-static void
-virQEMUCapsInitQMPCommandAbort(virQEMUCapsInitQMPCommandPtr cmd)
-{
-    if (cmd->mon)
-        virObjectUnlock(cmd->mon);
-    qemuMonitorClose(cmd->mon);
-    cmd->mon = NULL;
-
-    virCommandAbort(cmd->cmd);
-    virCommandFree(cmd->cmd);
-    cmd->cmd = NULL;
-
-    if (cmd->monpath)
-        unlink(cmd->monpath);
-
-    virDomainObjEndAPI(&cmd->vm);
-
-    if (cmd->pid != 0) {
-        char ebuf[1024];
-
-        VIR_DEBUG("Killing QMP caps process %lld", (long long)cmd->pid);
-        if (virProcessKill(cmd->pid, SIGKILL) < 0 && errno != ESRCH)
-            VIR_ERROR(_("Failed to kill process %lld: %s"),
-                      (long long)cmd->pid,
-                      virStrerror(errno, ebuf, sizeof(ebuf)));
-
-        VIR_FREE(*cmd->qmperr);
-    }
-    if (cmd->pidfile)
-        unlink(cmd->pidfile);
-    cmd->pid = 0;
-}
-
-
-static void
-virQEMUCapsInitQMPCommandFree(virQEMUCapsInitQMPCommandPtr cmd)
-{
-    if (!cmd)
-        return;
-
-    virQEMUCapsInitQMPCommandAbort(cmd);
-    VIR_FREE(cmd->binary);
-    VIR_FREE(cmd->monpath);
-    VIR_FREE(cmd->monarg);
-    VIR_FREE(cmd->pidfile);
-    VIR_FREE(cmd);
-}
-
-
-static virQEMUCapsInitQMPCommandPtr
-virQEMUCapsInitQMPCommandNew(char *binary,
-                             const char *libDir,
-                             uid_t runUid,
-                             gid_t runGid,
-                             char **qmperr)
-{
-    virQEMUCapsInitQMPCommandPtr cmd = NULL;
-
-    if (VIR_ALLOC(cmd) < 0)
-        goto error;
-
-    if (VIR_STRDUP(cmd->binary, binary) < 0)
-        goto error;
-
-    cmd->runUid = runUid;
-    cmd->runGid = runGid;
-    cmd->qmperr = qmperr;
-
-    /* the ".sock" sufix is important to avoid a possible clash with a qemu
-     * domain called "capabilities"
-     */
-    if (virAsprintf(&cmd->monpath, "%s/%s", libDir,
-                    "capabilities.monitor.sock") < 0)
-        goto error;
-    if (virAsprintf(&cmd->monarg, "unix:%s,server,nowait", cmd->monpath) < 0)
-        goto error;
-
-    /* ".pidfile" suffix is used rather than ".pid" to avoid a possible clash
-     * with a qemu domain called "capabilities"
-     * Normally we'd use runDir for pid files, but because we're using
-     * -daemonize we need QEMU to be allowed to create them, rather
-     * than libvirtd. So we're using libDir which QEMU can write to
-     */
-    if (virAsprintf(&cmd->pidfile, "%s/%s", libDir, "capabilities.pidfile") < 0)
-        goto error;
-
-    virPidFileForceCleanupPath(cmd->pidfile);
-
-    cmd->config.type = VIR_DOMAIN_CHR_TYPE_UNIX;
-    cmd->config.data.nix.path = cmd->monpath;
-    cmd->config.data.nix.listen = false;
-
-    return cmd;
-
- error:
-    virQEMUCapsInitQMPCommandFree(cmd);
-    return NULL;
-}
-
-
-/* Returns -1 on fatal error,
- *          0 on success,
- *          1 when probing QEMU failed
- */
 static int
-virQEMUCapsInitQMPCommandRun(virQEMUCapsInitQMPCommandPtr cmd,
-                             bool forceTCG)
+virQEMUCapsInitQMP(virQEMUCapsPtr qemuCaps,
+                   const char *libDir,
+                   uid_t runUid,
+                   gid_t runGid)
 {
-    virDomainXMLOptionPtr xmlopt = NULL;
-    const char *machine;
-    int status = 0;
     int ret = -1;
+    virDomainObjPtr dom = NULL;
+    virDomainObjPtr dom_kvm = NULL;
+    qemuMonitorPtr mon = NULL;
+    qemuMonitorPtr mon_kvm = NULL;
+    bool forceTCG = false;
 
-    if (forceTCG)
-        machine = "none,accel=tcg";
-    else
-        machine = "none,accel=kvm:tcg";
-
-    VIR_DEBUG("Try to probe capabilities of '%s' via QMP, machine %s",
-              cmd->binary, machine);
+    VIR_DEBUG("qemuCaps->binary=%s, libDir=%s, runUid=%u, runGid=%u ",
+              NULLSTR(qemuCaps->binary), NULLSTR(libDir), runUid, runGid);
 
-    /*
-     * We explicitly need to use -daemonize here, rather than
-     * virCommandDaemonize, because we need to synchronize
-     * with QEMU creating its monitor socket API. Using
-     * daemonize guarantees control won't return to libvirt
-     * until the socket is present.
-     */
-    cmd->cmd = virCommandNewArgList(cmd->binary,
-                                    "-S",
-                                    "-no-user-config",
-                                    "-nodefaults",
-                                    "-nographic",
-                                    "-machine", machine,
-                                    "-qmp", cmd->monarg,
-                                    "-pidfile", cmd->pidfile,
-                                    "-daemonize",
-                                    NULL);
-    virCommandAddEnvPassCommon(cmd->cmd);
-    virCommandClearCaps(cmd->cmd);
-    virCommandSetGID(cmd->cmd, cmd->runGid);
-    virCommandSetUID(cmd->cmd, cmd->runUid);
-
-    virCommandSetErrorBuffer(cmd->cmd, cmd->qmperr);
-
-    /* Log, but otherwise ignore, non-zero status.  */
-    if (virCommandRun(cmd->cmd, &status) < 0)
+    if (!(dom = qmpDomainObjNew(qemuCaps->binary, forceTCG, libDir, runUid, runGid)))
         goto cleanup;
 
-    if (status != 0) {
-        VIR_DEBUG("QEMU %s exited with status %d: %s",
-                  cmd->binary, status, *cmd->qmperr);
-        goto ignore;
-    }
+    if (qemuProcessStartQmp(dom) < 0)
+        goto cleanup;
 
-    if (virPidFileReadPath(cmd->pidfile, &cmd->pid) < 0) {
-        VIR_DEBUG("Failed to read pidfile %s", cmd->pidfile);
-        goto ignore;
+    if (!(mon = QMP_DOMAIN_MONITOR(dom))) {
+        ret = 0; /* Failure probing QEMU not considered error case */
+        goto cleanup;
     }
 
-    if (!(xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL, NULL, NULL)) ||
-        !(cmd->vm = virDomainObjNew(xmlopt)))
+    /* Pull capabilities from QEMU */
+    if (virQEMUCapsInitQMPMonitor(qemuCaps, mon) < 0)
         goto cleanup;
 
-    cmd->vm->pid = cmd->pid;
-
-    if (!(cmd->mon = qemuMonitorOpen(cmd->vm, &cmd->config, true, true,
-                                     0, &callbacks, NULL)))
-        goto ignore;
-
-    virObjectLock(cmd->mon);
-
-    ret = 0;
-
- cleanup:
-    if (!cmd->mon)
-        virQEMUCapsInitQMPCommandAbort(cmd);
-    virObjectUnref(xmlopt);
-
-    return ret;
-
- ignore:
-    ret = 1;
-    goto cleanup;
-}
-
-
-static int
-virQEMUCapsInitQMP(virQEMUCapsPtr qemuCaps,
-                   const char *libDir,
-                   uid_t runUid,
-                   gid_t runGid,
-                   char **qmperr)
-{
-    virQEMUCapsInitQMPCommandPtr cmd = NULL;
-    int ret = -1;
-    int rc;
+    /* Pull capabilities again if KVM supported */
+    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) {
 
-    if (!(cmd = virQEMUCapsInitQMPCommandNew(qemuCaps->binary, libDir,
-                                             runUid, runGid, qmperr)))
-        goto cleanup;
+        qemuProcessStopQmp(dom);
+        virDomainObjEndAPI(&dom);
 
-    if ((rc = virQEMUCapsInitQMPCommandRun(cmd, false)) != 0) {
-        if (rc == 1)
-            ret = 0;
-        goto cleanup;
-    }
+        forceTCG = true;
+        dom_kvm = qmpDomainObjNew(qemuCaps->binary, forceTCG, libDir, runUid, runGid);
 
-    if (virQEMUCapsInitQMPMonitor(qemuCaps, cmd->mon) < 0)
-        goto cleanup;
+        if (qemuProcessStartQmp(dom_kvm) < 0)
+            goto cleanup;
 
-    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) {
-        virQEMUCapsInitQMPCommandAbort(cmd);
-        if ((rc = virQEMUCapsInitQMPCommandRun(cmd, true)) != 0) {
-            if (rc == 1)
-                ret = 0;
+        if (!(mon_kvm = QMP_DOMAIN_MONITOR(dom_kvm))) {
+            ret = 0; /* Failure probing QEMU not considered error case */
             goto cleanup;
         }
 
-        if (virQEMUCapsInitQMPMonitorTCG(qemuCaps, cmd->mon) < 0)
+        if (virQEMUCapsInitQMPMonitorTCG(qemuCaps, mon_kvm) < 0)
             goto cleanup;
     }
 
     ret = 0;
 
  cleanup:
-    virQEMUCapsInitQMPCommandFree(cmd);
+    if (!mon) {
+        char *err = QMP_DOMAIN_ERROR(dom) ? QMP_DOMAIN_ERROR(dom) : _("unknown error");
+
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to probe QEMU binary with QMP: %s"), err);
+    }
+
+    qemuProcessStopQmp(dom);
+    virDomainObjEndAPI(&dom);
+    qemuProcessStopQmp(dom_kvm);
+    virDomainObjEndAPI(&dom_kvm);
+
+    VIR_DEBUG("ret=%i", ret);
+
     return ret;
 }
 
@@ -4546,7 +4345,6 @@ virQEMUCapsNewForBinaryInternal(virArch hostArch,
 {
     virQEMUCapsPtr qemuCaps;
     struct stat sb;
-    char *qmperr = NULL;
 
     if (!(qemuCaps = virQEMUCapsNew()))
         goto error;
@@ -4573,15 +4371,8 @@ virQEMUCapsNewForBinaryInternal(virArch hostArch,
         goto error;
     }
 
-    if (virQEMUCapsInitQMP(qemuCaps, libDir, runUid, runGid, &qmperr) < 0) {
-        virQEMUCapsLogProbeFailure(binary);
-        goto error;
-    }
-
-    if (!qemuCaps->usedQMP) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Failed to probe QEMU binary with QMP: %s"),
-                       qmperr ? qmperr : _("unknown error"));
+    if (virQEMUCapsInitQMP(qemuCaps, libDir, runUid, runGid) < 0 ||
+        !qemuCaps->usedQMP) {
         virQEMUCapsLogProbeFailure(binary);
         goto error;
     }
@@ -4600,7 +4391,6 @@ virQEMUCapsNewForBinaryInternal(virArch hostArch,
     }
 
  cleanup:
-    VIR_FREE(qmperr);
     return qemuCaps;
 
  error:
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index c64b3ad38a..507710d241 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -813,12 +813,12 @@ qemuMonitorOpenInternal(virDomainObjPtr vm,
 {
     qemuMonitorPtr mon;
 
-    if (!cb->eofNotify) {
+    if (cb && !cb->eofNotify) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("EOF notify callback must be supplied"));
         return NULL;
     }
-    if (!cb->errorNotify) {
+    if (cb && !cb->errorNotify) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("Error notify callback must be supplied"));
         return NULL;
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 29b0ba1590..c8a34fc37e 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -8068,3 +8068,401 @@ qemuProcessReconnectAll(virQEMUDriverPtr driver)
     struct qemuProcessReconnectData data = {.driver = driver};
     virDomainObjListForEach(driver->domains, qemuProcessReconnectHelper, &data);
 }
+
+
+static void *
+qmpDomainObjPrivateAlloc(void *opaque)
+{
+    qmpDomainObjPrivatePtr priv;
+
+    if (VIR_ALLOC(priv) < 0)
+        return NULL;
+
+    VIR_DEBUG("priv=%p opaque=%p", priv, opaque);
+
+    return priv;
+}
+
+
+static void
+qmpDomainObjPrivateFree(void *data)
+{
+    qmpDomainObjPrivatePtr priv = data;
+
+    VIR_DEBUG("priv=%p, priv->mon=%p", priv, (priv ? priv->mon : NULL));
+
+    if (!priv)
+        return;
+
+    /* This should never be non-NULL if we get here, but just in case... */
+    if (priv->mon) {
+        VIR_ERROR(_("Unexpected QEMU monitor still active during domain deletion"));
+        qemuMonitorClose(priv->mon);
+        priv->mon = NULL;
+    }
+
+    VIR_FREE(priv->libDir);
+    VIR_FREE(priv->monpath);
+    VIR_FREE(priv->monarg);
+    VIR_FREE(priv->pidfile);
+    VIR_FREE(priv->qmperr);
+    VIR_FREE(priv);
+}
+
+/**
+ * qmpDomainObjNew:
+ * @binary: Qemu binary
+ * @forceTCG: Force TCG mode if true
+ * @libDir: Directory for process and connection artifacts
+ * @runUid: UserId for Qemu Process
+ * @runGid: GroupId for Qemu Process
+ *
+ * Allocate and initialize domain structure encapsulating
+ * QEMU Process state and monitor connection to QEMU
+ * for completing QMP Queries.
+ */
+virDomainObjPtr
+qmpDomainObjNew(const char *binary,
+                bool forceTCG,
+                const char *libDir,
+                uid_t runUid, gid_t runGid)
+{
+    virDomainXMLOptionPtr xmlopt = NULL;
+    virDomainObjPtr ret = NULL;
+    virDomainObjPtr dom = NULL;
+    qmpDomainObjPrivatePtr priv = NULL;
+
+    virDomainXMLPrivateDataCallbacks callbacks = {
+        .alloc = qmpDomainObjPrivateAlloc,
+        .free = qmpDomainObjPrivateFree,
+    };
+
+    VIR_DEBUG("exec=%s, libDir=%s, runUid=%u, runGid=%u ",
+              NULLSTR(binary), NULLSTR(libDir), runUid, runGid);
+
+    xmlopt = virDomainXMLOptionNew(NULL,
+                                   &callbacks,
+                                   NULL, NULL, NULL);
+
+    if (!xmlopt ||
+        !(dom = virDomainObjNew(xmlopt)))
+        goto cleanup;
+
+    if (VIR_ALLOC(dom->def) < 0 ||
+        VIR_STRDUP(dom->def->name, "QmpQuery") < 0 ||
+        VIR_STRDUP(dom->def->emulator, binary) < 0)
+        goto cleanup;
+
+    priv = QMP_DOMAIN_PRIVATE(dom);
+
+    priv->forceTCG = forceTCG;
+
+    if (VIR_STRDUP(priv->libDir, libDir) < 0)
+        goto cleanup;
+
+    priv->runUid = runUid;
+    priv->runGid = runGid;
+
+    VIR_STEAL_PTR(ret, dom);
+
+ cleanup:
+    virDomainObjEndAPI(&dom);
+    return ret;
+}
+
+
+/* Initialize configuration and paths prior to starting QEMU
+ */
+static int
+qemuProcessInitQmp(virDomainObjPtr dom)
+{
+    int ret = -1;
+    qmpDomainObjPrivatePtr priv = QMP_DOMAIN_PRIVATE(dom);
+
+    /* Allow multiple QEMU Procs and make clashes very unlikely */
+    unsigned int seed = time(NULL);
+    dom->def->id = rand_r(&seed) % 1000000;
+
+    VIR_DEBUG("Beginning VM startup process"
+              "dom=%p name=%s emulator=%s, id=%d",
+              dom, dom->def->name, dom->def->emulator, dom->def->id);
+
+    /* the ".sock" sufix is important to avoid a possible clash with a qemu
+     * domain called "capabilities"
+     */
+    if (virAsprintf(&priv->monpath, "%s/%s.%d.%s",
+                    priv->libDir, dom->def->name, dom->def->id,
+                    "monitor.sock") < 0)
+        goto cleanup;
+
+    if (virAsprintf(&priv->monarg, "unix:%s,server,nowait", priv->monpath) < 0)
+        goto cleanup;
+
+    /* Normally we'd use runDir for pid files, but because we're using
+     * -daemonize we need QEMU to be allowed to create them, rather
+     * than libvirtd. So we're using libDir which QEMU can write to
+     */
+    if (virAsprintf(&priv->pidfile, "%s/%s.%d.%s",
+                    priv->libDir, dom->def->name, dom->def->id,
+                    "pidfile") < 0)
+        goto cleanup;
+
+    virPidFileForceCleanupPath(priv->pidfile);
+
+    ret = 0;
+
+ cleanup:
+    VIR_DEBUG("ret=%i", ret);
+    return ret;
+}
+
+
+/* Launch QEMU Process
+ *
+ * Returns -1 on fatal error,
+ *          0 on success,
+ *          1 when probing QEMU failed
+ */
+static int
+qemuProcessLaunchQmp(virDomainObjPtr dom)
+{
+    const char *machine;
+    int status = 0;
+    int ret = -1;
+
+    qmpDomainObjPrivatePtr priv = QMP_DOMAIN_PRIVATE(dom);
+
+    VIR_DEBUG("dom=%p, emulator=%s, id=%d",
+              dom, NULLSTR(dom->def->emulator), dom->def->id);
+
+    if (priv->forceTCG) {
+        machine = "none,accel=tcg";
+    } else {
+        machine = "none,accel=kvm:tcg";
+    }
+
+    VIR_DEBUG("Try to probe capabilities of '%s' via QMP, machine %s",
+              NULLSTR(dom->def->emulator), machine);
+
+    /* We explicitly need to use -daemonize here, rather than
+     * virCommandDaemonize, because we need to synchronize
+     * with QEMU creating its monitor socket API. Using
+     * daemonize guarantees control won't return to libvirt
+     * until the socket is present.
+     */
+    priv->cmd = virCommandNewArgList(dom->def->emulator,
+                                     "-S",
+                                     "-no-user-config",
+                                     "-nodefaults",
+                                     "-nographic",
+                                     "-machine", machine,
+                                     "-qmp", priv->monarg,
+                                     "-pidfile", priv->pidfile,
+                                     "-daemonize",
+                                     NULL);
+
+    virCommandAddEnvPassCommon(priv->cmd);
+    virCommandClearCaps(priv->cmd);
+    virCommandSetGID(priv->cmd, priv->runGid);
+    virCommandSetUID(priv->cmd, priv->runUid);
+
+    virCommandSetErrorBuffer(priv->cmd, &(priv->qmperr));
+
+    if (virCommandRun(priv->cmd, &status) < 0) {
+        VIR_DEBUG("QEMU %s dom=%p name=%s failed to spawn",
+                  dom->def->emulator, dom, dom->def->name);
+        goto cleanup;
+    }
+
+    /* Log, but otherwise ignore, non-zero status. */
+    if (status != 0) {
+        VIR_DEBUG("QEMU %s exited with status %d: %s",
+                  dom->def->emulator, status, priv->qmperr);
+        goto ignore;
+    }
+
+    if (virPidFileReadPath(priv->pidfile, &dom->pid) < 0) {
+        VIR_DEBUG("Failed to read pidfile %s", priv->pidfile);
+        goto ignore;
+    }
+
+    ret = 0;
+
+ cleanup:
+    return ret;
+
+ ignore:
+    ret = 1;
+    goto cleanup;
+}
+
+
+/* Connect Monitor to QEMU Process
+ *
+ * Returns -1 on fatal error,
+ *          0 on success,
+ *          1 when probing QEMU failed
+ */
+static int
+qemuConnectMonitorQmp(virDomainObjPtr dom)
+{
+    int ret = 0;
+
+    qemuMonitorPtr mon = NULL;
+    virDomainChrSourceDef monConfig;
+    unsigned long long timeout = 0;
+    bool retry = true;
+    bool enableJson = true;
+    qemuMonitorCallbacksPtr monCallbacks = NULL;
+
+    qmpDomainObjPrivatePtr priv = QMP_DOMAIN_PRIVATE(dom);
+
+    VIR_DEBUG("emulator=%s, dom->pid=%lld",
+              dom->def->emulator, (long long)dom->pid);
+
+    priv->mon = NULL;
+
+    if (!dom->pid)
+        goto ignore;
+
+    monConfig.type = VIR_DOMAIN_CHR_TYPE_UNIX;
+    monConfig.data.nix.path = priv->monpath;
+    monConfig.data.nix.listen = false;
+
+    /* Hold an extra reference because we can't allow dom to be
+     * deleted until the monitor gets its own reference. */
+    virObjectRef(dom);
+    virObjectUnlock(dom);
+
+    mon = qemuMonitorOpen(dom,
+                          &monConfig,
+                          enableJson,
+                          retry,
+                          timeout,
+                          monCallbacks,
+                          NULL);
+
+    virObjectLock(dom);
+    virObjectUnref(dom);
+
+    if (!mon) {
+        VIR_INFO("Failed to connect monitor for %s", dom->def->name);
+        goto ignore;
+    }
+
+    VIR_STEAL_PTR(priv->mon, mon);
+
+    /* Exit capabilities negotiation mode and enter QEMU command mode
+     * by issuing qmp_capabilities command to QEMU */
+    if (qemuMonitorSetCapabilities(priv->mon) < 0) {
+        VIR_DEBUG("Failed to set monitor capabilities %s",
+                  virGetLastErrorMessage());
+        goto ignore;
+    }
+
+    ret = 0;
+
+ cleanup:
+    VIR_DEBUG("ret=%i", ret);
+    return ret;
+
+ ignore:
+    ret = 1;
+    goto cleanup;
+}
+
+
+/**
+ * qemuProcessStartQmp:
+ * @dom: Qmp Specific Domain storing Process and Connection State
+ *
+ * Start and connect to QEMU binary so QMP queries can be made.
+ *
+ * Usage:
+ *   dom = qmpDomainObjNew(binary, forceTCG, libDir, runUid, runGid);
+ *   qemuProcessStartQmp(dom);
+ *   mon = QMP_DOMAIN_MONITOR(dom);
+ *   ** Send QMP Queries to QEMU using monitor **
+ *   qemuProcessStopQmp(dom);
+ *   virDomainObjEndAPI(&dom);
+ *
+ * QEMU probing failure is not an error case (negative return value.)
+ * Check monitor before using.
+ *
+ * Resources remain allocated until qemuProcessStopQmp and virDomainObjEndAPI
+ * are called (even in failure cases.)  Error string (dom->priv->qmperr)
+ * remains valid until virDomainObjEndAPI(&dom) is called.
+ */
+int
+qemuProcessStartQmp(virDomainObjPtr dom)
+{
+    int ret = -1;
+
+    VIR_DEBUG("dom=%p name=%s emulator=%s",
+              dom, dom->def->name, dom->def->emulator);
+
+    if (qemuProcessInitQmp(dom) < 0)
+        goto stop;
+
+    if (qemuProcessLaunchQmp(dom) < 0)
+        goto stop;
+
+    if (qemuConnectMonitorQmp(dom) < 0)
+        goto stop;
+
+    ret = 0;
+
+ cleanup:
+    VIR_DEBUG("ret=%i", ret);
+    return ret;
+
+ stop:
+    qemuProcessStopQmp(dom);
+    goto cleanup;
+}
+
+/**
+ * qemuProcessStop:
+ * @dom: Qmp Specific Domain storing Process and Connection State
+ *
+ * Stop Monitor Connection and QEMU Process
+ */
+void qemuProcessStopQmp(virDomainObjPtr dom)
+{
+    if (!dom)
+        return;
+
+    qmpDomainObjPrivatePtr priv = QMP_DOMAIN_PRIVATE(dom);
+
+    VIR_DEBUG("Shutting down dom=%p name=%s emulator=%s id->%d pid=%lld",
+              dom, dom->def->name, dom->def->emulator, dom->def->id,
+              (long long)dom->pid);
+
+    if (priv->mon) {
+        virObjectUnlock(priv->mon);
+        qemuMonitorClose(priv->mon);
+        priv->mon = NULL;
+    }
+
+    virCommandAbort(priv->cmd);
+    virCommandFree(priv->cmd);
+    priv->cmd = NULL;
+
+    if (priv->monpath)
+        ignore_value(unlink(priv->monpath));
+
+    if (dom->pid != 0) {
+        char ebuf[1024];
+
+        VIR_DEBUG("Killing QMP caps process %lld", (long long)dom->pid);
+        if (virProcessKill(dom->pid, SIGKILL) < 0 && errno != ESRCH)
+            VIR_ERROR(_("Failed to kill process %lld: %s"),
+                      (long long)dom->pid,
+                      virStrerror(errno, ebuf, sizeof(ebuf)));
+    }
+
+    if (priv->pidfile)
+        unlink(priv->pidfile);
+
+    dom->pid = 0;
+}
diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h
index c2f7c2b5d2..7c5fd32538 100644
--- a/src/qemu/qemu_process.h
+++ b/src/qemu/qemu_process.h
@@ -74,6 +74,36 @@ int qemuProcessBeginJob(virQEMUDriverPtr driver,
 void qemuProcessEndJob(virQEMUDriverPtr driver,
                        virDomainObjPtr vm);
 
+typedef struct _qmpDomainObjPrivate qmpDomainObjPrivate;
+typedef qmpDomainObjPrivate *qmpDomainObjPrivatePtr;
+struct _qmpDomainObjPrivate {
+    char *libDir;
+    uid_t runUid;
+    gid_t runGid;
+    char *qmperr;  /* qemu proc stderr */
+    char *monarg;  // "unix:{priv->monpath},server,nowait"
+    char *monpath; // {libDir}/capabilities.monitor.sock
+    char *pidfile;
+    virCommandPtr cmd;
+    qemuMonitorPtr mon;
+    bool forceTCG;
+};
+
+virDomainObjPtr qmpDomainObjNew(const char *binary,
+                                bool forceTCG,
+                                const char *libDir,
+                                uid_t runUid,
+                                gid_t runGid);
+
+#define QMP_DOMAIN_PRIVATE(dom) \
+    ((qmpDomainObjPrivatePtr) (dom)->privateData)
+
+#define QMP_DOMAIN_MONITOR(dom) \
+   ((qemuMonitorPtr) (QMP_DOMAIN_PRIVATE(dom) ? QMP_DOMAIN_PRIVATE(dom)->mon : NULL))
+
+#define QMP_DOMAIN_ERROR(dom) \
+   ((char *) (QMP_DOMAIN_PRIVATE(dom) ? QMP_DOMAIN_PRIVATE(dom)->qmperr: NULL))
+
 typedef enum {
     VIR_QEMU_PROCESS_START_COLD         = 1 << 0,
     VIR_QEMU_PROCESS_START_PAUSED       = 1 << 1,
@@ -97,6 +127,9 @@ int qemuProcessStart(virConnectPtr conn,
                      virNetDevVPortProfileOp vmop,
                      unsigned int flags);
 
+int
+qemuProcessStartQmp(virDomainObjPtr dom);
+
 virCommandPtr qemuProcessCreatePretendCmd(virQEMUDriverPtr driver,
                                           virDomainObjPtr vm,
                                           const char *migrateURI,
@@ -155,6 +188,8 @@ void qemuProcessStop(virQEMUDriverPtr driver,
                      qemuDomainAsyncJob asyncJob,
                      unsigned int flags);
 
+void qemuProcessStopQmp(virDomainObjPtr dom);
+
 int qemuProcessAttach(virConnectPtr conn,
                       virQEMUDriverPtr driver,
                       virDomainObjPtr vm,
diff --git a/tests/qemucapabilitiestest.c b/tests/qemucapabilitiestest.c
index 8fe5a55e1d..ea4671739b 100644
--- a/tests/qemucapabilitiestest.c
+++ b/tests/qemucapabilitiestest.c
@@ -58,6 +58,9 @@ testQemuCaps(const void *opaque)
     if (!(mon = qemuMonitorTestNewFromFileFull(repliesFile, &data->driver, NULL)))
         goto cleanup;
 
+    if (qemuMonitorSetCapabilities(qemuMonitorTestGetMonitor(mon)) < 0)
+        goto cleanup;
+
     if (!(capsActual = virQEMUCapsNew()) ||
         virQEMUCapsInitQMPMonitor(capsActual,
                                   qemuMonitorTestGetMonitor(mon)) < 0)
@@ -65,6 +68,10 @@ testQemuCaps(const void *opaque)
 
     if (virQEMUCapsGet(capsActual, QEMU_CAPS_KVM)) {
         qemuMonitorResetCommandID(qemuMonitorTestGetMonitor(mon));
+
+        if (qemuMonitorSetCapabilities(qemuMonitorTestGetMonitor(mon)) < 0)
+            goto cleanup;
+
         if (virQEMUCapsInitQMPMonitorTCG(capsActual,
                                          qemuMonitorTestGetMonitor(mon)) < 0)
             goto cleanup;
-- 
2.17.1




More information about the libvir-list mailing list