On Fri, Aug 14, 2026 at 13:47:48 +0200, Michal Privoznik via Devel wrote:
> 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 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;
Please add the unit as a comment so it's obvious. You parse it as-is
from qemu so the value I suppose is 'nanoseconds since epoch' if the
docs are correct.
> + char *driverVersion;
> + qemuAgentGuestDeviceInfoPCI *pci;
> +};