From: Michal Privoznik <mprivozn@redhat.com> 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 <mprivozn@redhat.com> --- 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 418386317d..fbfaafc1bb 100644 --- a/src/hypervisor/qemu_agent.c +++ b/src/hypervisor/qemu_agent.c @@ -2843,3 +2843,122 @@ qemuAgentInterfaceFormatParams(virDomainInterfacePtr *ifaces, } } } + + +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 def6f983d4..35d2057bac 100644 --- a/src/hypervisor/qemu_agent.h +++ b/src/hypervisor/qemu_agent.h @@ -218,3 +218,24 @@ void qemuAgentInterfaceFormatParams(virDomainInterfacePtr *ifaces, int nifaces, virTypedParamList *list); + +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 02bd505749..0bd8c6f781 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1726,6 +1726,7 @@ qemuAgentFSThaw; qemuAgentFSTrim; qemuAgentGetDisks; qemuAgentGetFSInfo; +qemuAgentGetGuestDeviceInfo; qemuAgentGetHostname; qemuAgentGetInterfaces; qemuAgentGetLoadAvg; @@ -1734,6 +1735,7 @@ qemuAgentGetTime; qemuAgentGetTimezone; qemuAgentGetUsers; qemuAgentGetVCPUs; +qemuAgentGuestDeviceInfoFree; qemuAgentInterfaceFormatParams; qemuAgentNotifyClose; qemuAgentNotifyEvent; -- 2.54.0