From: Michal Privoznik <[email protected]>

QEMU agent has 'guest-get-info` command. Even though it's
currently implemented only for Windows, it shows some interesting
information from inside the guest, like device drivers, their
versions, and so on.

Looking at the command definition in qga/qapi-schema.json the
only non-optional field in returned data is 'driver-name'. The
rest is optional. Although looking at the current implementation
either all fields are set or device is ignored completely.
Nevertheless, our code should follow QMP schema.

New qemuAgentGuestDeviceInfo structure is introduced among with
qemuAgentGetGuestDeviceInfo() function which parses reply from
agent and fills the structure. Optional fields are either set to
NULL or -1, if missing.

Signed-off-by: Michal Privoznik <[email protected]>
---
 src/hypervisor/qemu_agent.c | 119 ++++++++++++++++++++++++++++++++++++
 src/hypervisor/qemu_agent.h |  21 +++++++
 src/libvirt_private.syms    |   2 +
 3 files changed, 142 insertions(+)

diff --git a/src/hypervisor/qemu_agent.c b/src/hypervisor/qemu_agent.c
index e549947fbf..e11507326f 100644
--- a/src/hypervisor/qemu_agent.c
+++ b/src/hypervisor/qemu_agent.c
@@ -2681,3 +2681,122 @@ qemuAgentFSInfoFormat(qemuAgentFSInfo **agentinfo,
     }
     return ret;
 }
+
+
+void
+qemuAgentGuestDeviceInfoFree(qemuAgentGuestDeviceInfo *info)
+{
+    if (!info)
+        return;
+
+    g_free(info->driverName);
+    g_free(info->driverVersion);
+    g_free(info->pci);
+    g_free(info);
+}
+
+
+int
+qemuAgentGetGuestDeviceInfo(qemuAgent *agent,
+                            qemuAgentGuestDeviceInfo ***info,
+                            bool report_unsupported)
+{
+    g_autoptr(virJSONValue) cmd = NULL;
+    g_autoptr(virJSONValue) reply = NULL;
+    virJSONValue *data = NULL;
+    size_t ndata;
+    size_t i;
+    int rc;
+
+    if (!(cmd = qemuAgentMakeCommand("guest-get-devices", NULL)))
+        return -1;
+
+    if ((rc = qemuAgentCommandFull(agent, cmd, &reply, agent->timeout,
+                                   report_unsupported)) < 0)
+        return rc;
+
+    if (!(data = virJSONValueObjectGetArray(reply, "return"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("qemu agent didn't return an array of devices"));
+        return -1;
+    }
+
+    ndata = virJSONValueArraySize(data);
+
+    *info = g_new0(qemuAgentGuestDeviceInfo *, ndata);
+
+    for (i = 0; i < ndata; i++) {
+        g_autoptr(qemuAgentGuestDeviceInfo) oneInfo = NULL;
+        virJSONValue *entry = virJSONValueArrayGet(data, i);
+        virJSONValue *dDate = NULL;
+        virJSONValue *idObj = NULL;
+
+        if (!entry) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("array element missing in guest-get-devices 
return value"));
+            goto error;
+        }
+
+        oneInfo = g_new0(qemuAgentGuestDeviceInfo, 1);
+
+        oneInfo->driverName = g_strdup(virJSONValueObjectGetString(entry, 
"driver-name"));
+        if (!oneInfo->driverName) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("'driver-name' missing in reply of 
guest-get-devices"));
+            goto error;
+        }
+
+        if ((dDate = virJSONValueObjectGet(entry, "driver-date"))) {
+            if (virJSONValueGetNumberLong(dDate, &oneInfo->driverDate) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("malformed 'driver-date' in reply of 
guest-get-devices"));
+                goto error;
+            }
+        } else {
+            oneInfo->driverDate = -1;
+        }
+
+        oneInfo->driverVersion = g_strdup(virJSONValueObjectGetString(entry, 
"driver-version"));
+
+        if ((idObj = virJSONValueObjectGet(entry, "id"))) {
+            const char *type = NULL;
+
+            if (!(type = virJSONValueObjectGetString(idObj, "type"))) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("missing 'type' in reply of 
guest-get-devices"));
+                goto error;
+            }
+
+            if (STREQ("pci", type)) {
+                g_autofree qemuAgentGuestDeviceInfoPCI *pci = NULL;
+
+                pci = g_new0(qemuAgentGuestDeviceInfoPCI, 1);
+
+                if (virJSONValueObjectGetNumberUint(idObj, "vendor-id", 
&pci->vendorID) < 0) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("missing or malformed 'vendor-id' in 
reply of guest-get-devices"));
+                    goto error;
+                }
+
+                if (virJSONValueObjectGetNumberUint(idObj, "device-id", 
&pci->deviceID) < 0) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("missing or malformed 'device-id' in 
reply of guest-get-devices"));
+                    goto error;
+                }
+
+                oneInfo->pci = g_steal_pointer(&pci);
+            }
+        }
+
+        (*info)[i] = g_steal_pointer(&oneInfo);
+    }
+
+    return ndata;
+
+ error:
+    for (i = 0; i < ndata; i++) {
+        qemuAgentGuestDeviceInfoFree((*info)[i]);
+    }
+    g_clear_pointer(info, g_free);
+    return -1;
+}
diff --git a/src/hypervisor/qemu_agent.h b/src/hypervisor/qemu_agent.h
index 3dbc3baec1..c55cfeb99b 100644
--- a/src/hypervisor/qemu_agent.h
+++ b/src/hypervisor/qemu_agent.h
@@ -201,3 +201,24 @@ int qemuAgentFSInfoFormat(qemuAgentFSInfo **agentinfo,
                           int nagentinfo,
                           virDomainDef *vmdef,
                           virDomainFSInfoPtr **info);
+
+typedef struct _qemuAgentGuestDeviceInfoPCI qemuAgentGuestDeviceInfoPCI;
+struct _qemuAgentGuestDeviceInfoPCI {
+    unsigned int vendorID;
+    unsigned int deviceID;
+};
+
+typedef struct _qemuAgentGuestDeviceInfo qemuAgentGuestDeviceInfo;
+struct _qemuAgentGuestDeviceInfo {
+    char *driverName;
+    long long driverDate;
+    char *driverVersion;
+    qemuAgentGuestDeviceInfoPCI *pci;
+};
+
+void qemuAgentGuestDeviceInfoFree(qemuAgentGuestDeviceInfo *info);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuAgentGuestDeviceInfo, 
qemuAgentGuestDeviceInfoFree);
+
+int qemuAgentGetGuestDeviceInfo(qemuAgent *agent,
+                                qemuAgentGuestDeviceInfo ***info,
+                                bool report_unsupported);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c76e5cb08a..9d1bac27f5 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1724,6 +1724,7 @@ qemuAgentFSThaw;
 qemuAgentFSTrim;
 qemuAgentGetDisks;
 qemuAgentGetFSInfo;
+qemuAgentGetGuestDeviceInfo;
 qemuAgentGetHostname;
 qemuAgentGetInterfaces;
 qemuAgentGetLoadAvg;
@@ -1732,6 +1733,7 @@ qemuAgentGetTime;
 qemuAgentGetTimezone;
 qemuAgentGetUsers;
 qemuAgentGetVCPUs;
+qemuAgentGuestDeviceInfoFree;
 qemuAgentNotifyClose;
 qemuAgentNotifyEvent;
 qemuAgentOpen;
-- 
2.54.0

Reply via email to