[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 4/4] Implement code to attach to external QEMU instances.



Given a PID, the QEMU driver reads /proc/$PID/cmdline and
/proc/$PID/environ to get the configuration. This is fed
into the ARGV->XML convertor to build an XML configuration
for the process.

/proc/$PID/exe is resolved to identify the full command
binary path

After checking for name/uuid uniqueness, an attempt is
made to connect to the monitor socket. If successful
then 'info status' and 'info kvm' are issued to determine
whether the CPUs are running and if KVM is enabled.

* src/qemu/qemu_command.h, src/qemu/qemu_command.c: Add
  qemuParseCommandLinePid to extract XML from a PID. Also
  extract monitor configuration from command line
* src/qemu/qemu_driver.c: Implement virDomainQemuAttach
* src/qemu/qemu_process.h, src/qemu/qemu_process.c: Add
  qemuProcessAttach to connect to the monitor of an
  existing QEMU process
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
  src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
  src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h:
  Add APIs for invoking 'info status' and 'info kvm'
---
 src/qemu/qemu_command.c      |  252 +++++++++++++++++++++++++++++++-----------
 src/qemu/qemu_command.h      |   12 ++-
 src/qemu/qemu_driver.c       |   98 ++++++++++++++++-
 src/qemu/qemu_monitor.c      |   41 +++++++
 src/qemu/qemu_monitor.h      |    4 +
 src/qemu/qemu_monitor_json.c |   92 +++++++++++++++
 src/qemu/qemu_monitor_json.h |    4 +
 src/qemu/qemu_monitor_text.c |   43 +++++++
 src/qemu/qemu_monitor_text.h |    4 +
 src/qemu/qemu_process.c      |  192 +++++++++++++++++++++++++++++---
 src/qemu/qemu_process.h      |    7 +
 11 files changed, 666 insertions(+), 83 deletions(-)

diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 2205ed1..6c7c3ac 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -5315,84 +5315,80 @@ cleanup:
 /*
  * Tries to parse a QEMU serial/parallel device
  */
-static virDomainChrDefPtr
-qemuParseCommandLineChr(const char *val)
+static int
+qemuParseCommandLineChr(virDomainChrSourceDefPtr source,
+                        const char *val)
 {
-    virDomainChrDefPtr def;
-
-    if (!(def = virDomainChrDefNew()))
-        goto error;
-
     if (STREQ(val, "null")) {
-        def->source.type = VIR_DOMAIN_CHR_TYPE_NULL;
+        source->type = VIR_DOMAIN_CHR_TYPE_NULL;
     } else if (STREQ(val, "vc")) {
-        def->source.type = VIR_DOMAIN_CHR_TYPE_VC;
+        source->type = VIR_DOMAIN_CHR_TYPE_VC;
     } else if (STREQ(val, "pty")) {
-        def->source.type = VIR_DOMAIN_CHR_TYPE_PTY;
+        source->type = VIR_DOMAIN_CHR_TYPE_PTY;
     } else if (STRPREFIX(val, "file:")) {
-        def->source.type = VIR_DOMAIN_CHR_TYPE_FILE;
-        def->source.data.file.path = strdup(val+strlen("file:"));
-        if (!def->source.data.file.path)
+        source->type = VIR_DOMAIN_CHR_TYPE_FILE;
+        source->data.file.path = strdup(val+strlen("file:"));
+        if (!source->data.file.path)
             goto no_memory;
     } else if (STRPREFIX(val, "pipe:")) {
-        def->source.type = VIR_DOMAIN_CHR_TYPE_PIPE;
-        def->source.data.file.path = strdup(val+strlen("pipe:"));
-        if (!def->source.data.file.path)
+        source->type = VIR_DOMAIN_CHR_TYPE_PIPE;
+        source->data.file.path = strdup(val+strlen("pipe:"));
+        if (!source->data.file.path)
             goto no_memory;
     } else if (STREQ(val, "stdio")) {
-        def->source.type = VIR_DOMAIN_CHR_TYPE_STDIO;
+        source->type = VIR_DOMAIN_CHR_TYPE_STDIO;
     } else if (STRPREFIX(val, "udp:")) {
         const char *svc1, *host2, *svc2;
-        def->source.type = VIR_DOMAIN_CHR_TYPE_UDP;
+        source->type = VIR_DOMAIN_CHR_TYPE_UDP;
         val += strlen("udp:");
         svc1 = strchr(val, ':');
         host2 = svc1 ? strchr(svc1, '@') : NULL;
         svc2 = host2 ? strchr(host2, ':') : NULL;
 
         if (svc1)
-            def->source.data.udp.connectHost = strndup(val, svc1-val);
+            source->data.udp.connectHost = strndup(val, svc1-val);
         else
-            def->source.data.udp.connectHost = strdup(val);
+            source->data.udp.connectHost = strdup(val);
 
-        if (!def->source.data.udp.connectHost)
+        if (!source->data.udp.connectHost)
             goto no_memory;
 
         if (svc1) {
             svc1++;
             if (host2)
-                def->source.data.udp.connectService = strndup(svc1, host2-svc1);
+                source->data.udp.connectService = strndup(svc1, host2-svc1);
             else
-                def->source.data.udp.connectService = strdup(svc1);
+                source->data.udp.connectService = strdup(svc1);
 
-            if (!def->source.data.udp.connectService)
+            if (!source->data.udp.connectService)
                 goto no_memory;
         }
 
         if (host2) {
             host2++;
             if (svc2)
-                def->source.data.udp.bindHost = strndup(host2, svc2-host2);
+                source->data.udp.bindHost = strndup(host2, svc2-host2);
             else
-                def->source.data.udp.bindHost = strdup(host2);
+                source->data.udp.bindHost = strdup(host2);
 
-            if (!def->source.data.udp.bindHost)
+            if (!source->data.udp.bindHost)
                 goto no_memory;
         }
         if (svc2) {
             svc2++;
-            def->source.data.udp.bindService = strdup(svc2);
-            if (!def->source.data.udp.bindService)
+            source->data.udp.bindService = strdup(svc2);
+            if (!source->data.udp.bindService)
                 goto no_memory;
         }
     } else if (STRPREFIX(val, "tcp:") ||
                STRPREFIX(val, "telnet:")) {
         const char *opt, *svc;
-        def->source.type = VIR_DOMAIN_CHR_TYPE_TCP;
+        source->type = VIR_DOMAIN_CHR_TYPE_TCP;
         if (STRPREFIX(val, "tcp:")) {
             val += strlen("tcp:");
         } else {
             val += strlen("telnet:");
-            def->source.data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
+            source->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
         }
         svc = strchr(val, ':');
         if (!svc) {
@@ -5402,38 +5398,38 @@ qemuParseCommandLineChr(const char *val)
         }
         opt = strchr(svc, ',');
         if (opt && strstr(opt, "server"))
-            def->source.data.tcp.listen = true;
+            source->data.tcp.listen = true;
 
-        def->source.data.tcp.host = strndup(val, svc-val);
-        if (!def->source.data.tcp.host)
+        source->data.tcp.host = strndup(val, svc-val);
+        if (!source->data.tcp.host)
             goto no_memory;
         svc++;
         if (opt) {
-            def->source.data.tcp.service = strndup(svc, opt-svc);
+            source->data.tcp.service = strndup(svc, opt-svc);
         } else {
-            def->source.data.tcp.service = strdup(svc);
+            source->data.tcp.service = strdup(svc);
         }
-        if (!def->source.data.tcp.service)
+        if (!source->data.tcp.service)
             goto no_memory;
     } else if (STRPREFIX(val, "unix:")) {
         const char *opt;
         val += strlen("unix:");
         opt = strchr(val, ',');
-        def->source.type = VIR_DOMAIN_CHR_TYPE_UNIX;
+        source->type = VIR_DOMAIN_CHR_TYPE_UNIX;
         if (opt) {
             if (strstr(opt, "listen"))
-                def->source.data.nix.listen = true;
-            def->source.data.nix.path = strndup(val, opt-val);
+                source->data.nix.listen = true;
+            source->data.nix.path = strndup(val, opt-val);
         } else {
-            def->source.data.nix.path = strdup(val);
+            source->data.nix.path = strdup(val);
         }
-        if (!def->source.data.nix.path)
+        if (!source->data.nix.path)
             goto no_memory;
 
     } else if (STRPREFIX(val, "/dev")) {
-        def->source.type = VIR_DOMAIN_CHR_TYPE_DEV;
-        def->source.data.file.path = strdup(val);
-        if (!def->source.data.file.path)
+        source->type = VIR_DOMAIN_CHR_TYPE_DEV;
+        source->data.file.path = strdup(val);
+        if (!source->data.file.path)
             goto no_memory;
     } else {
         qemuReportError(VIR_ERR_INTERNAL_ERROR,
@@ -5441,13 +5437,12 @@ qemuParseCommandLineChr(const char *val)
         goto error;
     }
 
-    return def;
+    return 0;
 
 no_memory:
     virReportOOMError();
 error:
-    virDomainChrDefFree(def);
-    return NULL;
+    return -1;
 }
 
 
@@ -5621,7 +5616,9 @@ error:
  */
 virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
                                      const char **progenv,
-                                     const char **progargv)
+                                     const char **progargv,
+                                     virDomainChrSourceDefPtr *monConfig,
+                                     bool *monJSON)
 {
     virDomainDefPtr def;
     int i;
@@ -5634,6 +5631,11 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
     int nvirtiodisk = 0;
     qemuDomainCmdlineDefPtr cmd;
 
+    if (monConfig)
+        *monConfig = NULL;
+    if (monJSON)
+        *monJSON = false;
+
     if (!progargv[0]) {
         qemuReportError(VIR_ERR_INTERNAL_ERROR,
                         "%s", _("no emulator path found"));
@@ -5750,10 +5752,12 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
                                         _("cannot parse VNC port '%s'"), tmp+1);
                         goto error;
                     }
-                    vnc->data.vnc.listenAddr = strndup(val, tmp-val);
-                    if (!vnc->data.vnc.listenAddr) {
-                        VIR_FREE(vnc);
-                        goto no_memory;
+                    if (tmp != val) {
+                        vnc->data.vnc.listenAddr = strndup(val, tmp-val);
+                        if (!vnc->data.vnc.listenAddr) {
+                            VIR_FREE(vnc);
+                            goto no_memory;
+                        }
                     }
                     vnc->data.vnc.port += 5900;
                     vnc->data.vnc.autoport = 0;
@@ -5972,7 +5976,11 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
             WANT_VALUE();
             if (STRNEQ(val, "none")) {
                 virDomainChrDefPtr chr;
-                if (!(chr = qemuParseCommandLineChr(val)))
+
+                if (!(chr = virDomainChrDefNew()))
+                    goto error;
+
+                if (qemuParseCommandLineChr(&chr->source, val) < 0)
                     goto error;
                 if (VIR_REALLOC_N(def->serials, def->nserials+1) < 0) {
                     virDomainChrDefFree(chr);
@@ -5986,7 +5994,11 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
             WANT_VALUE();
             if (STRNEQ(val, "none")) {
                 virDomainChrDefPtr chr;
-                if (!(chr = qemuParseCommandLineChr(val)))
+
+                if (!(chr = virDomainChrDefNew()))
+                    goto error;
+
+                if (qemuParseCommandLineChr(&chr->source, val) < 0)
                     goto error;
                 if (VIR_REALLOC_N(def->parallels, def->nparallels+1) < 0) {
                     virDomainChrDefFree(chr);
@@ -6169,7 +6181,17 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
             /* ignore, used via restore/migrate APIs */
         } else if (STREQ(arg, "-monitor")) {
             WANT_VALUE();
-            /* ignore, used internally by libvirt */
+            if (monConfig) {
+                virDomainChrSourceDefPtr chr;
+
+                if (VIR_ALLOC(chr) < 0)
+                    goto no_memory;
+
+                if (qemuParseCommandLineChr(chr, val) < 0)
+                    goto error;
+
+                *monConfig = chr;
+            }
         } else if (STREQ(arg, "-S")) {
             /* ignore, always added by libvirt */
         } else {
@@ -6310,11 +6332,6 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
 
     VIR_FREE(nics);
 
-    if (!def->name) {
-        if (!(def->name = strdup("unnamed")))
-            goto no_memory;
-    }
-
     if (virDomainDefAddImplicitControllers(def) < 0)
         goto error;
 
@@ -6333,12 +6350,18 @@ error:
     VIR_FREE(cmd);
     virDomainDefFree(def);
     VIR_FREE(nics);
+    if (monConfig) {
+        virDomainChrSourceDefFree(*monConfig);
+        *monConfig = NULL;
+    }
     return NULL;
 }
 
 
 virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps,
-                                           const char *args)
+                                           const char *args,
+                                           virDomainChrSourceDefPtr *monConfig,
+                                           bool *monJSON)
 {
     const char **progenv = NULL;
     const char **progargv = NULL;
@@ -6348,7 +6371,7 @@ virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps,
     if (qemuStringToArgvEnv(args, &progenv, &progargv) < 0)
         goto cleanup;
 
-    def = qemuParseCommandLine(caps, progenv, progargv);
+    def = qemuParseCommandLine(caps, progenv, progargv, monConfig, monJSON);
 
 cleanup:
     for (i = 0 ; progargv && progargv[i] ; i++)
@@ -6361,3 +6384,106 @@ cleanup:
 
     return def;
 }
+
+
+static int qemuParseProcFileStrings(int pid,
+                                    const char *name,
+                                    const char ***list)
+{
+    char *path = NULL;
+    int ret = -1;
+    char *data = NULL;
+    ssize_t len;
+    char *tmp;
+    size_t nstr = 0;
+    const char **str = NULL;
+    int i;
+
+    if (virAsprintf(&path, "/proc/%d/%s", pid, name) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if ((len = virFileReadAll(path, 1024*128, &data)) < 0)
+        goto cleanup;
+
+    tmp = data;
+    while (tmp < (data + len)) {
+        if (VIR_EXPAND_N(str, nstr, 1) < 0) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        if (!(str[nstr-1] = strdup(tmp))) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        /* Skip arg */
+        tmp += strlen(tmp);
+        /* Skip \0 separator */
+        tmp++;
+    }
+
+    if (VIR_EXPAND_N(str, nstr, 1) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    str[nstr-1] = NULL;
+
+    ret = nstr-1;
+    *list = str;
+
+cleanup:
+    if (ret < 0) {
+        for (i = 0 ; str && str[i] ; i++)
+            VIR_FREE(str[i]);
+        VIR_FREE(str);
+    }
+    VIR_FREE(data);
+    VIR_FREE(path);
+    return ret;
+}
+
+virDomainDefPtr qemuParseCommandLinePid(virCapsPtr caps,
+                                        int pid,
+                                        virDomainChrSourceDefPtr *monConfig,
+                                        bool *monJSON)
+{
+    virDomainDefPtr def = NULL;
+    const char **progargv = NULL;
+    const char **progenv = NULL;
+    char *exepath = NULL;
+    char *emulator;
+    int i;
+
+    if (qemuParseProcFileStrings(pid, "cmdline", &progargv) < 0 ||
+        qemuParseProcFileStrings(pid, "environ", &progenv) < 0)
+        goto cleanup;
+
+    if (!(def = qemuParseCommandLine(caps, progenv, progargv, monConfig, monJSON)))
+        goto cleanup;
+
+    if (virAsprintf(&exepath, "/proc/%d/exe", pid) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (virFileResolveLink(exepath, &emulator) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to resolve %s"), exepath);
+        goto cleanup;
+    }
+    VIR_FREE(def->emulator);
+    def->emulator = emulator;
+
+cleanup:
+    VIR_FREE(exepath);
+    for (i = 0 ; progargv && progargv[i] ; i++)
+        VIR_FREE(progargv[i]);
+    VIR_FREE(progargv);
+    for (i = 0 ; progenv && progenv[i] ; i++)
+        VIR_FREE(progenv[i]);
+    VIR_FREE(progenv);
+    return def;
+}
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 528031d..e64ff8b 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -140,9 +140,17 @@ int qemudCanonicalizeMachine(struct qemud_driver *driver,
 
 virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
                                      const char **progenv,
-                                     const char **progargv);
+                                     const char **progargv,
+                                     virDomainChrSourceDefPtr *monConfig,
+                                     bool *monJSON);
 virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps,
-                                           const char *args);
+                                           const char *args,
+                                           virDomainChrSourceDefPtr *monConfig,
+                                           bool *monJSON);
+virDomainDefPtr qemuParseCommandLinePid(virCapsPtr caps,
+                                        int pid,
+                                        virDomainChrSourceDefPtr *monConfig,
+                                        bool *monJSON);
 
 int qemuDomainAssignPCIAddresses(virDomainDefPtr def);
 qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 24447b9..a995d94 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3357,11 +3357,17 @@ static char *qemuDomainXMLFromNative(virConnectPtr conn,
     }
 
     qemuDriverLock(driver);
-    def = qemuParseCommandLineString(driver->caps, config);
+    def = qemuParseCommandLineString(driver->caps, config, NULL, NULL);
     qemuDriverUnlock(driver);
     if (!def)
         goto cleanup;
 
+    if (!def->name &&
+        !(def->name = strdup("unnamed"))) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
     xml = virDomainDefFormat(def, VIR_DOMAIN_XML_INACTIVE);
 
 cleanup:
@@ -3586,7 +3592,7 @@ qemudCanonicalizeMachineFromInfo(virDomainDefPtr def,
         if (!machine->canonical)
             continue;
 
-        if (STRNEQ(def->os.machine, machine->name))
+        if (def->os.machine && STRNEQ(def->os.machine, machine->name))
             continue;
 
         if (!(*canonical = strdup(machine->canonical))) {
@@ -3613,7 +3619,7 @@ qemudCanonicalizeMachineDirect(virDomainDefPtr def, char **canonical)
         if (!machines[i]->canonical)
             continue;
 
-        if (STRNEQ(def->os.machine, machines[i]->name))
+        if (def->os.machine && STRNEQ(def->os.machine, machines[i]->name))
             continue;
 
         *canonical = machines[i]->canonical;
@@ -7008,6 +7014,90 @@ cleanup:
 }
 
 
+static virDomainPtr qemuDomainAttach(virConnectPtr conn,
+                                     int pid,
+                                     unsigned int flags)
+{
+    struct qemud_driver *driver = conn->privateData;
+    virDomainObjPtr vm = NULL;
+    virDomainDefPtr def = NULL;
+    virDomainPtr dom = NULL;
+    virDomainChrSourceDefPtr monConfig = NULL;
+    bool monJSON = false;
+
+    virCheckFlags(0, NULL);
+
+    qemuDriverLock(driver);
+
+    if (!(def = qemuParseCommandLinePid(driver->caps, pid, &monConfig, &monJSON)))
+        goto cleanup;
+
+    if (!monConfig) {
+        qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                        _("No monitor connection for pid %d"),
+                        pid);
+        goto cleanup;
+    }
+    if (monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
+        qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                        _("Cannot connect to monitor connection of type '%s' for pid %d"),
+                        virDomainChrTypeToString(monConfig->type), pid);
+        goto cleanup;
+    }
+
+    if (!(def->name) &&
+        virAsprintf(&def->name, "attach-pid-%d", pid) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0)
+        goto cleanup;
+
+    if (qemudCanonicalizeMachine(driver, def) < 0)
+        goto cleanup;
+
+    if (qemuDomainAssignPCIAddresses(def) < 0)
+        goto cleanup;
+
+    if (!(vm = virDomainAssignDef(driver->caps,
+                                  &driver->domains,
+                                  def, false)))
+        goto cleanup;
+
+    def = NULL;
+
+    if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
+        goto cleanup;
+
+    if (qemuProcessAttach(conn, driver, vm, pid, monConfig, monJSON) < 0) {
+        monConfig = NULL;
+        goto endjob;
+    }
+
+    monConfig = NULL;
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) dom->id = vm->def->id;
+
+endjob:
+    if (qemuDomainObjEndJob(vm) == 0) {
+        vm = NULL;
+        goto cleanup;
+    }
+
+cleanup:
+    if (def)
+        virDomainDefFree(def);
+    if (monConfig)
+        virDomainChrSourceDefFree(monConfig);
+    if (vm)
+        virDomainObjUnlock(vm);
+    qemuDriverUnlock(driver);
+    return dom;
+}
+
+
 static int
 qemuDomainOpenConsole(virDomainPtr dom,
                       const char *devname,
@@ -7193,7 +7283,7 @@ static virDriver qemuDriver = {
     qemuDomainRevertToSnapshot, /* domainRevertToSnapshot */
     qemuDomainSnapshotDelete, /* domainSnapshotDelete */
     qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */
-    NULL, /* qemuDomainAttach */
+    qemuDomainAttach, /* qemuDomainAttach */
     qemuDomainOpenConsole, /* domainOpenConsole */
 };
 
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 2d28f8d..2aad555 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1019,6 +1019,47 @@ int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
     return ret;
 }
 
+
+int qemuMonitorGetState(qemuMonitorPtr mon,
+                        int *state)
+{
+    int ret;
+    VIR_DEBUG("mon=%p", mon);
+
+    if (!mon) {
+        qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+                        _("monitor must not be NULL"));
+        return -1;
+    }
+
+    if (mon->json)
+        ret = qemuMonitorJSONGetState(mon, state);
+    else
+        ret = qemuMonitorTextGetState(mon, state);
+    return ret;
+}
+
+
+int qemuMonitorGetVirtType(qemuMonitorPtr mon,
+                           int *virtType)
+{
+    int ret;
+    VIR_DEBUG("mon=%p", mon);
+
+    if (!mon) {
+        qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+                        _("monitor must not be NULL"));
+        return -1;
+    }
+
+    if (mon->json)
+        ret = qemuMonitorJSONGetVirtType(mon, virtType);
+    else
+        ret = qemuMonitorTextGetVirtType(mon, virtType);
+    return ret;
+}
+
+
 int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon,
                               unsigned long *currmem)
 {
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index c90219b..a69a583 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -183,6 +183,10 @@ int qemuMonitorSystemPowerdown(qemuMonitorPtr mon);
 
 int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
                           int **pids);
+int qemuMonitorGetState(qemuMonitorPtr mon,
+                        int *state);
+int qemuMonitorGetVirtType(qemuMonitorPtr mon,
+                           int *virtType);
 int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon,
                               unsigned long *currmem);
 int qemuMonitorGetMemoryStats(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 20a78e1..5f39c98 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -978,6 +978,98 @@ int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
 }
 
 
+int qemuMonitorJSONGetState(qemuMonitorPtr mon,
+                            int *state)
+{
+    int ret;
+    virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-status",
+                                                     NULL);
+    virJSONValuePtr reply = NULL;
+
+    *state = VIR_DOMAIN_RUNNING;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+    if (ret == 0) {
+        virJSONValuePtr data;
+        int r;
+
+        if (!(data = virJSONValueObjectGet(reply, "return"))) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("info status reply was missing return data"));
+            ret = -1;
+            goto cleanup;
+        }
+
+        if ((r = virJSONValueObjectGetBoolean(data, "running")) < 0) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("info status reply missing 'running' field"));
+            ret = -1;
+            goto cleanup;
+        }
+        if (!r)
+            *state = VIR_DOMAIN_PAUSED;
+    }
+
+cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONGetVirtType(qemuMonitorPtr mon,
+                               int *virtType)
+{
+    int ret;
+    virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-kvm",
+                                                     NULL);
+    virJSONValuePtr reply = NULL;
+
+    *virtType = VIR_DOMAIN_VIRT_QEMU;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+    if (ret == 0) {
+        virJSONValuePtr data;
+        int r;
+
+        if (!(data = virJSONValueObjectGet(reply, "return"))) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("info kvm reply was missing return data"));
+            ret = -1;
+            goto cleanup;
+        }
+
+        if ((r = virJSONValueObjectGetBoolean(data, "enabled")) < 0) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("info kvm reply missing 'running' field"));
+            ret = -1;
+            goto cleanup;
+        }
+        if (r)
+            *virtType = VIR_DOMAIN_VIRT_KVM;
+    }
+
+cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
 /*
  * Returns: 0 if balloon not supported, +1 if balloon query worked
  * or -1 on failure
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 086f0e1..e2b5002 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -51,6 +51,10 @@ int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon);
 
 int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
                               int **pids);
+int qemuMonitorJSONGetState(qemuMonitorPtr mon,
+                            int *state);
+int qemuMonitorJSONGetVirtType(qemuMonitorPtr mon,
+                               int *virtType);
 int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon,
                                   unsigned long *currmem);
 int qemuMonitorJSONGetMemoryStats(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 53781c8..711b7e2 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -468,6 +468,49 @@ error:
     return 0;
 }
 
+
+int qemuMonitorTextGetState(qemuMonitorPtr mon,
+                            int *state)
+{
+    char *reply = NULL;
+
+    *state = VIR_DOMAIN_RUNNING;
+
+    if (qemuMonitorHMPCommand(mon, "info status", &reply) < 0) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        "%s", _("could not query CPU state"));
+        return -1;
+    }
+
+    if (strstr(reply, "paused"))
+        *state = VIR_DOMAIN_PAUSED;
+
+    VIR_FREE(reply);
+    return 0;
+}
+
+
+int qemuMonitorTextGetVirtType(qemuMonitorPtr mon,
+                               int *virtType)
+{
+    char *reply = NULL;
+
+    *virtType = VIR_DOMAIN_VIRT_QEMU;
+
+    if (qemuMonitorHMPCommand(mon, "info kvm", &reply) < 0) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                        "%s", _("could not query kvm status"));
+        return -1;
+    }
+
+    if (strstr(reply, "enabled"))
+        *virtType = VIR_DOMAIN_VIRT_KVM;
+
+    VIR_FREE(reply);
+    return 0;
+}
+
+
 static int parseMemoryStat(char **text, unsigned int tag,
                            const char *search, virDomainMemoryStatPtr stat)
 {
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 0838a2b..cea609c 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -48,6 +48,10 @@ int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon);
 
 int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
                               int **pids);
+int qemuMonitorTextGetState(qemuMonitorPtr mon,
+                            int *state);
+int qemuMonitorTextGetVirtType(qemuMonitorPtr mon,
+                               int *virtType);
 int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon,
                                   unsigned long *currmem);
 int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 3460000..5f6980b 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -54,6 +54,7 @@
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
 #define START_POSTFIX ": starting up\n"
+#define ATTACH_POSTFIX ": attaching\n"
 #define SHUTDOWN_POSTFIX ": shutting down\n"
 
 /**
@@ -1040,29 +1041,32 @@ qemuProcessReadLogFD(int logfd, char *buf, int maxlen, int off)
     tmpbuf[ret] = '\0';
 }
 
+
 static int
 qemuProcessWaitForMonitor(struct qemud_driver* driver,
                           virDomainObjPtr vm, off_t pos)
 {
-    char *buf;
+    char *buf = NULL;
     size_t buf_size = 4096; /* Plenty of space to get startup greeting */
-    int logfd;
+    int logfd = -1;
     int ret = -1;
     virHashTablePtr paths = NULL;
     qemuDomainObjPrivatePtr priv;
 
-    if ((logfd = qemuProcessLogReadFD(driver->logDir, vm->def->name, pos)) < 0)
-        return -1;
+    if (pos != -1) {
+        if ((logfd = qemuProcessLogReadFD(driver->logDir, vm->def->name, pos)) < 0)
+            return -1;
 
-    if (VIR_ALLOC_N(buf, buf_size) < 0) {
-        virReportOOMError();
-        return -1;
-    }
+        if (VIR_ALLOC_N(buf, buf_size) < 0) {
+            virReportOOMError();
+            return -1;
+        }
 
-    if (qemuProcessReadLogOutput(vm, logfd, buf, buf_size,
-                                 qemuProcessFindCharDevicePTYs,
-                                 "console", 30) < 0)
-        goto closelog;
+        if (qemuProcessReadLogOutput(vm, logfd, buf, buf_size,
+                                     qemuProcessFindCharDevicePTYs,
+                                     "console", 30) < 0)
+            goto closelog;
+    }
 
     VIR_DEBUG("Connect monitor to %p '%s'", vm, vm->def->name);
     if (qemuConnectMonitor(driver, vm) < 0) {
@@ -1099,8 +1103,6 @@ cleanup:
         ret = -1;
     }
 
-    VIR_FREE(buf);
-
 closelog:
     if (VIR_CLOSE(logfd) < 0) {
         char ebuf[1024];
@@ -1108,6 +1110,8 @@ closelog:
                  virStrerror(errno, ebuf, sizeof ebuf));
     }
 
+    VIR_FREE(buf);
+
     return ret;
 }
 
@@ -2625,3 +2629,163 @@ retry:
         virFreeError(orig_err);
     }
 }
+
+
+int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED,
+                      struct qemud_driver *driver,
+                      virDomainObjPtr vm,
+                      int pid,
+                      virDomainChrSourceDefPtr monConfig,
+                      bool monJSON)
+{
+    char ebuf[1024];
+    int logfile = -1;
+    char *timestamp;
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    VIR_DEBUG0("Beginning VM attach process");
+
+    if (virDomainObjIsActive(vm)) {
+        qemuReportError(VIR_ERR_OPERATION_INVALID,
+                        "%s", _("VM is already active"));
+        return -1;
+    }
+
+    /* Do this upfront, so any part of the startup process can add
+     * runtime state to vm->def that won't be persisted. This let's us
+     * report implicit runtime defaults in the XML, like vnc listen/socket
+     */
+    VIR_DEBUG0("Setting current domain def as transient");
+    if (virDomainObjSetDefTransient(driver->caps, vm, true) < 0)
+        goto cleanup;
+
+    vm->def->id = driver->nextvmid++;
+
+    if (virFileMakePath(driver->logDir) != 0) {
+        virReportSystemError(errno,
+                             _("cannot create log directory %s"),
+                             driver->logDir);
+        goto cleanup;
+    }
+
+    VIR_DEBUG0("Creating domain log file");
+    if ((logfile = qemuProcessLogFD(driver, vm->def->name, false)) < 0)
+        goto cleanup;
+
+    VIR_DEBUG0("Determining emulator version");
+    qemuCapsFree(priv->qemuCaps);
+    priv->qemuCaps = NULL;
+    if (qemuCapsExtractVersionInfo(vm->def->emulator,
+                                   vm->def->os.arch,
+                                   NULL,
+                                   &priv->qemuCaps) < 0)
+        goto cleanup;
+
+    if (VIR_ALLOC(priv->monConfig) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    VIR_DEBUG0("Preparing monitor state");
+    priv->monConfig = monConfig;
+    monConfig = NULL;
+    priv->monJSON = monJSON;
+
+    priv->gotShutdown = false;
+
+#if 0
+    /*
+     * Normally PCI addresses are assigned in the virDomainCreate
+     * or virDomainDefine methods. We might still need to assign
+     * some here to cope with the question of upgrades. Regardless
+     * we also need to populate the PCi address set cache for later
+     * use in hotplug
+     */
+    if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
+        VIR_DEBUG0("Assigning domain PCI addresses");
+        /* Populate cache with current addresses */
+        if (priv->pciaddrs) {
+            qemuDomainPCIAddressSetFree(priv->pciaddrs);
+            priv->pciaddrs = NULL;
+        }
+        if (!(priv->pciaddrs = qemuDomainPCIAddressSetCreate(vm->def)))
+            goto cleanup;
+
+
+        /* Assign any remaining addresses */
+        if (qemuAssignDevicePCISlots(vm->def, priv->pciaddrs) < 0)
+            goto cleanup;
+
+        priv->persistentAddrs = 1;
+    } else {
+        priv->persistentAddrs = 0;
+    }
+#else
+    priv->persistentAddrs = 0;
+#endif
+
+    if ((timestamp = virTimestamp()) == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    } else {
+        if (safewrite(logfile, timestamp, strlen(timestamp)) < 0 ||
+            safewrite(logfile, ATTACH_POSTFIX, strlen(ATTACH_POSTFIX)) < 0) {
+            VIR_WARN("Unable to write timestamp to logfile: %s",
+                     virStrerror(errno, ebuf, sizeof ebuf));
+        }
+
+        VIR_FREE(timestamp);
+    }
+
+    vm->pid = pid;
+
+    VIR_DEBUG0("Waiting for monitor to show up");
+    if (qemuProcessWaitForMonitor(driver, vm, -1) < 0)
+        goto cleanup;
+
+    VIR_DEBUG0("Detecting VCPU PIDs");
+    if (qemuProcessDetectVcpuPIDs(driver, vm) < 0)
+        goto cleanup;
+
+#if 0
+    /* If we have -device, then addresses are assigned explicitly.
+     * If not, then we have to detect dynamic ones here */
+    if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
+        VIR_DEBUG0("Determining domain device PCI addresses");
+        if (qemuProcessInitPCIAddresses(driver, vm) < 0)
+            goto cleanup;
+    }
+#endif
+
+    VIR_DEBUG0("Getting initial memory amount");
+    qemuDomainObjEnterMonitorWithDriver(driver, vm);
+    if (qemuMonitorGetBalloonInfo(priv->mon, &vm->def->mem.cur_balloon) < 0) {
+        qemuDomainObjExitMonitorWithDriver(driver, vm);
+        goto cleanup;
+    }
+    if (qemuMonitorGetState(priv->mon, &vm->state) < 0) {
+        qemuDomainObjExitMonitorWithDriver(driver, vm);
+        goto cleanup;
+    }
+    if (qemuMonitorGetVirtType(priv->mon, &vm->def->virtType) < 0) {
+        qemuDomainObjExitMonitorWithDriver(driver, vm);
+        goto cleanup;
+    }
+    qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+    VIR_DEBUG0("Writing domain status to disk");
+    if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+        goto cleanup;
+
+    VIR_FORCE_CLOSE(logfile);
+
+    return 0;
+
+cleanup:
+    /* We jump here if we failed to start the VM for any reason, or
+     * if we failed to initialize the now running VM. kill it off and
+     * pretend we never started it */
+    VIR_FORCE_CLOSE(logfile);
+    virDomainChrSourceDefFree(monConfig);
+    return -1;
+}
diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h
index f1ab599..eb460f4 100644
--- a/src/qemu/qemu_process.h
+++ b/src/qemu/qemu_process.h
@@ -49,4 +49,11 @@ void qemuProcessStop(struct qemud_driver *driver,
                      virDomainObjPtr vm,
                      int migrated);
 
+int qemuProcessAttach(virConnectPtr conn,
+                      struct qemud_driver *driver,
+                      virDomainObjPtr vm,
+                      int pid,
+                      virDomainChrSourceDefPtr monConfig,
+                      bool monJSON);
+
 #endif /* __QEMU_PROCESS_H__ */
-- 
1.7.4.4


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]