aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-03-19 09:53:29 +0000
committerPeter Maydell <peter.maydell@linaro.org>2019-03-19 09:53:29 +0000
commit54281eefc2597d5452720ca88a9b9c96574e1195 (patch)
tree18835552a61d81d297225b7f0533fe3be0deb9e8
parent082c0543baa6f237704c83a51658bd7f6ae316d5 (diff)
parent4eaca8de268d74ac5daaf8938abcb69d37ba2889 (diff)
downloadqemu-54281eefc2597d5452720ca88a9b9c96574e1195.zip
qemu-54281eefc2597d5452720ca88a9b9c96574e1195.tar.gz
qemu-54281eefc2597d5452720ca88a9b9c96574e1195.tar.bz2
Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2019-03-18-tag' into staging
qemu-ga patch queue for 4.0 * fix w32 build breakages with VSS enabled * fix PCI topology reporting for Windows * fix OS version reporting for Windows * add systemd info to qga schema documentation * add proper 'id' handling to QGA so it conforms to QMP spec # gpg: Signature made Mon 18 Mar 2019 16:20:32 GMT # gpg: using RSA key CEACC9E15534EBABB82D3FA03353C9CEF108B584 # gpg: issuer "mdroth@linux.vnet.ibm.com" # gpg: Good signature from "Michael Roth <flukshun@gmail.com>" [full] # gpg: aka "Michael Roth <mdroth@utexas.edu>" [full] # gpg: aka "Michael Roth <mdroth@linux.vnet.ibm.com>" [full] # Primary key fingerprint: CEAC C9E1 5534 EBAB B82D 3FA0 3353 C9CE F108 B584 * remotes/mdroth/tags/qga-pull-2019-03-18-tag: qmp: common 'id' handling & make QGA conform to QMP spec qga: process_event() simplification qga: Fix guest-get-fsinfo PCI address collection in Windows qga-win: fix VSS build breakage due to unintended gnu99 C++ flag qga-win: include glib when building VSS DLL qga-win: Adding support for Windows Server 2019 get-osinfo command qga: update docs with systemd suspend support info Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rwxr-xr-xconfigure2
-rw-r--r--monitor.c33
-rw-r--r--qapi/qmp-dispatch.c10
-rw-r--r--qga/commands-win32.c346
-rw-r--r--qga/main.c47
-rw-r--r--qga/qapi-schema.json25
-rw-r--r--qga/vss-win32/Makefile.objs4
-rw-r--r--tests/test-qga.c13
8 files changed, 288 insertions, 192 deletions
diff --git a/configure b/configure
index 7071f52..02ea221 100755
--- a/configure
+++ b/configure
@@ -4934,7 +4934,7 @@ int main(void) {
EOF
if compile_prog "" "" ; then
guest_agent_ntddscsi=yes
- libs_qga="-lsetupapi $libs_qga"
+ libs_qga="-lsetupapi -lcfgmgr32 $libs_qga"
fi
fi
diff --git a/monitor.c b/monitor.c
index 72061d5..4807bbe 100644
--- a/monitor.c
+++ b/monitor.c
@@ -250,8 +250,6 @@ QEMUBH *qmp_dispatcher_bh;
struct QMPRequest {
/* Owner of the request */
Monitor *mon;
- /* "id" field of the request */
- QObject *id;
/*
* Request object to be handled or Error to be reported
* (exactly one of them is non-null)
@@ -353,7 +351,6 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
static void qmp_request_free(QMPRequest *req)
{
- qobject_unref(req->id);
qobject_unref(req->req);
error_free(req->err);
g_free(req);
@@ -4108,18 +4105,14 @@ static int monitor_can_read(void *opaque)
* Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
* Nothing is emitted then.
*/
-static void monitor_qmp_respond(Monitor *mon, QDict *rsp, QObject *id)
+static void monitor_qmp_respond(Monitor *mon, QDict *rsp)
{
if (rsp) {
- if (id) {
- qdict_put_obj(rsp, "id", qobject_ref(id));
- }
-
qmp_send_response(mon, rsp);
}
}
-static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id)
+static void monitor_qmp_dispatch(Monitor *mon, QObject *req)
{
Monitor *old_mon;
QDict *rsp;
@@ -4144,7 +4137,7 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id)
}
}
- monitor_qmp_respond(mon, rsp, id);
+ monitor_qmp_respond(mon, rsp);
qobject_unref(rsp);
}
@@ -4208,13 +4201,15 @@ static void monitor_qmp_bh_dispatcher(void *data)
mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1;
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
if (req_obj->req) {
- trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: "");
- monitor_qmp_dispatch(mon, req_obj->req, req_obj->id);
+ QDict *qdict = qobject_to(QDict, req_obj->req);
+ QObject *id = qdict ? qdict_get(qdict, "id") : NULL;
+ trace_monitor_qmp_cmd_in_band(qobject_get_try_str(id) ?: "");
+ monitor_qmp_dispatch(mon, req_obj->req);
} else {
assert(req_obj->err);
rsp = qmp_error_response(req_obj->err);
req_obj->err = NULL;
- monitor_qmp_respond(mon, rsp, NULL);
+ monitor_qmp_respond(mon, rsp);
qobject_unref(rsp);
}
@@ -4239,8 +4234,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
qdict = qobject_to(QDict, req);
if (qdict) {
- id = qobject_ref(qdict_get(qdict, "id"));
- qdict_del(qdict, "id");
+ id = qdict_get(qdict, "id");
} /* else will fail qmp_dispatch() */
if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
@@ -4251,17 +4245,14 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
if (qdict && qmp_is_oob(qdict)) {
/* OOB commands are executed immediately */
- trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id)
- ?: "");
- monitor_qmp_dispatch(mon, req, id);
+ trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: "");
+ monitor_qmp_dispatch(mon, req);
qobject_unref(req);
- qobject_unref(id);
return;
}
req_obj = g_new0(QMPRequest, 1);
req_obj->mon = mon;
- req_obj->id = id;
req_obj->req = req;
req_obj->err = err;
@@ -4281,7 +4272,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
/*
* Put the request to the end of queue so that requests will be
- * handled in time order. Ownership for req_obj, req, id,
+ * handled in time order. Ownership for req_obj, req,
* etc. will be delivered to the handler side.
*/
assert(mon->qmp.qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 1d922e0..5f812bb 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -58,6 +58,8 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
"QMP input member 'arguments' must be an object");
return NULL;
}
+ } else if (!strcmp(arg_name, "id")) {
+ continue;
} else {
error_setg(errp, "QMP input member '%s' is unexpected",
arg_name);
@@ -165,11 +167,11 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
bool allow_oob)
{
Error *err = NULL;
- QObject *ret;
+ QDict *dict = qobject_to(QDict, request);
+ QObject *ret, *id = dict ? qdict_get(dict, "id") : NULL;
QDict *rsp;
ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
-
if (err) {
rsp = qmp_error_response(err);
} else if (ret) {
@@ -180,5 +182,9 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
rsp = NULL;
}
+ if (rsp && id) {
+ qdict_put_obj(rsp, "id", qobject_ref(id));
+ }
+
return rsp;
}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 989b93e..d40d61f 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -22,6 +22,7 @@
#include <winioctl.h>
#include <ntddscsi.h>
#include <setupapi.h>
+#include <cfgmgr32.h>
#include <initguid.h>
#endif
#include <lm.h>
@@ -485,56 +486,29 @@ static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
return win2qemu[(int)bus];
}
-/* XXX: The following function is BROKEN!
- *
- * It does not work and probably has never worked. When we query for list of
- * disks we get cryptic names like "\Device\0000001d" instead of
- * "\PhysicalDriveX" or "\HarddiskX". Whether the names can be translated one
- * way or the other for comparison is an open question.
- *
- * When we query volume names (the original version) we are able to match those
- * but then the property queries report error "Invalid function". (duh!)
- */
-
-/*
-DEFINE_GUID(GUID_DEVINTERFACE_VOLUME,
- 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2,
- 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
-*/
DEFINE_GUID(GUID_DEVINTERFACE_DISK,
0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT,
+ 0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82,
+ 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
-
-static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
+static GuestPCIAddress *get_pci_info(int number, Error **errp)
{
HDEVINFO dev_info;
SP_DEVINFO_DATA dev_info_data;
- DWORD size = 0;
+ SP_DEVICE_INTERFACE_DATA dev_iface_data;
+ HANDLE dev_file;
int i;
- char dev_name[MAX_PATH];
- char *buffer = NULL;
GuestPCIAddress *pci = NULL;
- char *name = NULL;
bool partial_pci = false;
+
pci = g_malloc0(sizeof(*pci));
pci->domain = -1;
pci->slot = -1;
pci->function = -1;
pci->bus = -1;
- if (g_str_has_prefix(guid, "\\\\.\\") ||
- g_str_has_prefix(guid, "\\\\?\\")) {
- name = g_strdup(guid + 4);
- } else {
- name = g_strdup(guid);
- }
-
- if (!QueryDosDevice(name, dev_name, ARRAY_SIZE(dev_name))) {
- error_setg_win32(errp, GetLastError(), "failed to get dos device name");
- goto out;
- }
-
dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (dev_info == INVALID_HANDLE_VALUE) {
@@ -544,90 +518,220 @@ static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
g_debug("enumerating devices");
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+ dev_iface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
- DWORD addr, bus, slot, data, size2;
- int func, dev;
- while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
- &data, (PBYTE)buffer, size,
- &size2)) {
- size = MAX(size, size2);
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- g_free(buffer);
- /* Double the size to avoid problems on
- * W2k MBCS systems per KB 888609.
- * https://support.microsoft.com/en-us/kb/259695 */
- buffer = g_malloc(size * 2);
- } else {
+ PSP_DEVICE_INTERFACE_DETAIL_DATA pdev_iface_detail_data = NULL;
+ STORAGE_DEVICE_NUMBER sdn;
+ char *parent_dev_id = NULL;
+ HDEVINFO parent_dev_info;
+ SP_DEVINFO_DATA parent_dev_info_data;
+ DWORD j;
+ DWORD size = 0;
+
+ g_debug("getting device path");
+ if (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data,
+ &GUID_DEVINTERFACE_DISK, 0,
+ &dev_iface_data)) {
+ while (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
+ pdev_iface_detail_data,
+ size, &size,
+ &dev_info_data)) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ pdev_iface_detail_data = g_malloc(size);
+ pdev_iface_detail_data->cbSize =
+ sizeof(*pdev_iface_detail_data);
+ } else {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device interfaces");
+ goto free_dev_info;
+ }
+ }
+
+ dev_file = CreateFile(pdev_iface_detail_data->DevicePath, 0,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, 0,
+ NULL);
+ g_free(pdev_iface_detail_data);
+
+ if (!DeviceIoControl(dev_file, IOCTL_STORAGE_GET_DEVICE_NUMBER,
+ NULL, 0, &sdn, sizeof(sdn), &size, NULL)) {
+ CloseHandle(dev_file);
error_setg_win32(errp, GetLastError(),
- "failed to get device name");
+ "failed to get device slot number");
goto free_dev_info;
}
- }
- if (g_strcmp0(buffer, dev_name)) {
- continue;
+ CloseHandle(dev_file);
+ if (sdn.DeviceNumber != number) {
+ continue;
+ }
+ } else {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device interfaces");
+ goto free_dev_info;
}
- g_debug("found device %s", dev_name);
- /* There is no need to allocate buffer in the next functions. The size
- * is known and ULONG according to
- * https://support.microsoft.com/en-us/kb/253232
- * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
- */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_BUSNUMBER, &data, (PBYTE)&bus, size, NULL)) {
- debug_error("failed to get bus");
- bus = -1;
- partial_pci = true;
+ g_debug("found device slot %d. Getting storage controller", number);
+ {
+ CONFIGRET cr;
+ DEVINST dev_inst, parent_dev_inst;
+ ULONG dev_id_size = 0;
+
+ size = 0;
+ while (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data,
+ parent_dev_id, size, &size)) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ parent_dev_id = g_malloc(size);
+ } else {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device instance ID");
+ goto out;
+ }
+ }
+
+ /*
+ * CM API used here as opposed to
+ * SetupDiGetDeviceProperty(..., DEVPKEY_Device_Parent, ...)
+ * which exports are only available in mingw-w64 6+
+ */
+ cr = CM_Locate_DevInst(&dev_inst, parent_dev_id, 0);
+ if (cr != CR_SUCCESS) {
+ g_error("CM_Locate_DevInst failed with code %lx", cr);
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device instance");
+ goto out;
+ }
+ cr = CM_Get_Parent(&parent_dev_inst, dev_inst, 0);
+ if (cr != CR_SUCCESS) {
+ g_error("CM_Get_Parent failed with code %lx", cr);
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device instance");
+ goto out;
+ }
+
+ cr = CM_Get_Device_ID_Size(&dev_id_size, parent_dev_inst, 0);
+ if (cr != CR_SUCCESS) {
+ g_error("CM_Get_Device_ID_Size failed with code %lx", cr);
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device ID length");
+ goto out;
+ }
+
+ ++dev_id_size;
+ if (dev_id_size > size) {
+ g_free(parent_dev_id);
+ parent_dev_id = g_malloc(dev_id_size);
+ }
+
+ cr = CM_Get_Device_ID(parent_dev_inst, parent_dev_id, dev_id_size,
+ 0);
+ if (cr != CR_SUCCESS) {
+ g_error("CM_Get_Device_ID failed with code %lx", cr);
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device ID");
+ goto out;
+ }
}
- /* The function retrieves the device's address. This value will be
- * transformed into device function and number */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) {
- debug_error("failed to get address");
- addr = -1;
- partial_pci = true;
+ g_debug("querying storage controller %s for PCI information",
+ parent_dev_id);
+ parent_dev_info =
+ SetupDiGetClassDevs(&GUID_DEVINTERFACE_STORAGEPORT, parent_dev_id,
+ NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ g_free(parent_dev_id);
+
+ if (parent_dev_info == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device");
+ goto out;
}
- /* This call returns UINumber of DEVICE_CAPABILITIES structure.
- * This number is typically a user-perceived slot number. */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) {
- debug_error("failed to get slot");
- slot = -1;
- partial_pci = true;
+ parent_dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!SetupDiEnumDeviceInfo(parent_dev_info, 0, &parent_dev_info_data)) {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get parent device data");
+ goto out;
}
- /* SetupApi gives us the same information as driver with
- * IoGetDeviceProperty. According to Microsoft
- * https://support.microsoft.com/en-us/kb/253232
- * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF);
- * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF);
- * SPDRP_ADDRESS is propertyAddress, so we do the same.*/
-
- if (partial_pci) {
- pci->domain = -1;
- pci->slot = -1;
- pci->function = -1;
- pci->bus = -1;
- } else {
- func = ((int) addr == -1) ? -1 : addr & 0x0000FFFF;
- dev = ((int) addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
- pci->domain = dev;
- pci->slot = (int) slot;
- pci->function = func;
- pci->bus = (int) bus;
+ for (j = 0;
+ SetupDiEnumDeviceInfo(parent_dev_info, j, &parent_dev_info_data);
+ j++) {
+ DWORD addr, bus, ui_slot, type;
+ int func, slot;
+
+ /*
+ * There is no need to allocate buffer in the next functions. The
+ * size is known and ULONG according to
+ * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ parent_dev_info, &parent_dev_info_data, SPDRP_BUSNUMBER,
+ &type, (PBYTE)&bus, size, NULL)) {
+ debug_error("failed to get PCI bus");
+ bus = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * The function retrieves the device's address. This value will be
+ * transformed into device function and number
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ parent_dev_info, &parent_dev_info_data, SPDRP_ADDRESS,
+ &type, (PBYTE)&addr, size, NULL)) {
+ debug_error("failed to get PCI address");
+ addr = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * This call returns UINumber of DEVICE_CAPABILITIES structure.
+ * This number is typically a user-perceived slot number.
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ parent_dev_info, &parent_dev_info_data, SPDRP_UI_NUMBER,
+ &type, (PBYTE)&ui_slot, size, NULL)) {
+ debug_error("failed to get PCI slot");
+ ui_slot = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * SetupApi gives us the same information as driver with
+ * IoGetDeviceProperty. According to Microsoft:
+ *
+ * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF)
+ * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF)
+ * SPDRP_ADDRESS is propertyAddress, so we do the same.
+ *
+ * https://docs.microsoft.com/en-us/windows/desktop/api/setupapi/nf-setupapi-setupdigetdeviceregistrypropertya
+ */
+ if (partial_pci) {
+ pci->domain = -1;
+ pci->slot = -1;
+ pci->function = -1;
+ pci->bus = -1;
+ continue;
+ } else {
+ func = ((int)addr == -1) ? -1 : addr & 0x0000FFFF;
+ slot = ((int)addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
+ if ((int)ui_slot != slot) {
+ g_debug("mismatch with reported slot values: %d vs %d",
+ (int)ui_slot, slot);
+ }
+ pci->domain = 0;
+ pci->slot = (int)ui_slot;
+ pci->function = func;
+ pci->bus = (int)bus;
+ break;
+ }
}
+ SetupDiDestroyDeviceInfoList(parent_dev_info);
break;
}
free_dev_info:
SetupDiDestroyDeviceInfoList(dev_info);
out:
- g_free(buffer);
- g_free(name);
return pci;
}
@@ -685,7 +789,8 @@ out_free:
return;
}
-static void get_single_disk_info(GuestDiskAddress *disk, Error **errp)
+static void get_single_disk_info(int disk_number,
+ GuestDiskAddress *disk, Error **errp)
{
SCSI_ADDRESS addr, *scsi_ad;
DWORD len;
@@ -714,7 +819,7 @@ static void get_single_disk_info(GuestDiskAddress *disk, Error **errp)
* if that doesn't hold since that suggests some other unexpected
* breakage
*/
- disk->pci_controller = get_pci_info(disk->dev, &local_err);
+ disk->pci_controller = get_pci_info(disk_number, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto err_close;
@@ -728,7 +833,7 @@ static void get_single_disk_info(GuestDiskAddress *disk, Error **errp)
/* We are able to use the same ioctls for different bus types
* according to Microsoft docs
* https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */
- g_debug("getting pci-controller info");
+ g_debug("getting SCSI info");
if (DeviceIoControl(disk_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
sizeof(SCSI_ADDRESS), &len, NULL)) {
disk->unit = addr.Lun;
@@ -776,12 +881,10 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
size = sizeof(VOLUME_DISK_EXTENTS);
extents = g_malloc0(size);
if (!DeviceIoControl(vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
- 0, extents, size, NULL, NULL)) {
+ 0, extents, size, &size, NULL)) {
DWORD last_err = GetLastError();
if (last_err == ERROR_MORE_DATA) {
/* Try once more with big enough buffer */
- size = sizeof(VOLUME_DISK_EXTENTS)
- + extents->NumberOfDiskExtents*sizeof(DISK_EXTENT);
g_free(extents);
extents = g_malloc0(size);
if (!DeviceIoControl(
@@ -797,7 +900,7 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
disk = g_malloc0(sizeof(GuestDiskAddress));
disk->has_dev = true;
disk->dev = g_strdup(name);
- get_single_disk_info(disk, &local_err);
+ get_single_disk_info(0xffffffff, disk, &local_err);
if (local_err) {
g_debug("failed to get disk info, ignoring error: %s",
error_get_pretty(local_err));
@@ -831,9 +934,9 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
*/
disk->has_dev = true;
disk->dev = g_strdup_printf("\\\\.\\PhysicalDrive%lu",
- extents->Extents[i].DiskNumber);
+ extents->Extents[i].DiskNumber);
- get_single_disk_info(disk, &local_err);
+ get_single_disk_info(extents->Extents[i].DiskNumber, disk, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
@@ -1941,12 +2044,24 @@ static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = {
{ 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"},
{ 6, 2, "Microsoft Windows Server 2012", "2012"},
{ 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
- {10, 0, "Microsoft Windows Server 2016", "2016"},
+ { 0, 0, 0},
{ 0, 0, 0},
{ 0, 0, 0}
}
};
+typedef struct _ga_win_10_0_server_t {
+ int final_build;
+ char const *version;
+ char const *version_id;
+} ga_win_10_0_server_t;
+
+static ga_win_10_0_server_t const WIN_10_0_SERVER_VERSION_MATRIX[3] = {
+ {14393, "Microsoft Windows Server 2016", "2016"},
+ {17763, "Microsoft Windows Server 2019", "2019"},
+ {0, 0}
+};
+
static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
{
typedef NTSTATUS(WINAPI * rtl_get_version_t)(
@@ -1971,10 +2086,23 @@ static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id)
{
DWORD major = os_version->dwMajorVersion;
DWORD minor = os_version->dwMinorVersion;
+ DWORD build = os_version->dwBuildNumber;
int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
+ ga_win_10_0_server_t const *win_10_0_table = WIN_10_0_SERVER_VERSION_MATRIX;
while (table->version != NULL) {
- if (major == table->major && minor == table->minor) {
+ if (major == 10 && minor == 0 && tbl_idx) {
+ while (win_10_0_table->version != NULL) {
+ if (build <= win_10_0_table->final_build) {
+ if (id) {
+ return g_strdup(win_10_0_table->version_id);
+ } else {
+ return g_strdup(win_10_0_table->version);
+ }
+ }
+ win_10_0_table++;
+ }
+ } else if (major == table->major && minor == table->minor) {
if (id) {
return g_strdup(table->version_id);
} else {
diff --git a/qga/main.c b/qga/main.c
index 87a0711..c0d77c7 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -523,15 +523,15 @@ fail:
#endif
}
-static int send_response(GAState *s, QDict *payload)
+static int send_response(GAState *s, const QDict *rsp)
{
const char *buf;
QString *payload_qstr, *response_qstr;
GIOStatus status;
- g_assert(payload && s->channel);
+ g_assert(rsp && s->channel);
- payload_qstr = qobject_to_json(QOBJECT(payload));
+ payload_qstr = qobject_to_json(QOBJECT(rsp));
if (!payload_qstr) {
return -EINVAL;
}
@@ -557,53 +557,24 @@ static int send_response(GAState *s, QDict *payload)
return 0;
}
-static void process_command(GAState *s, QDict *req)
-{
- QDict *rsp;
- int ret;
-
- g_assert(req);
- g_debug("processing command");
- rsp = qmp_dispatch(&ga_commands, QOBJECT(req), false);
- if (rsp) {
- ret = send_response(s, rsp);
- if (ret < 0) {
- g_warning("error sending response: %s", strerror(-ret));
- }
- qobject_unref(rsp);
- }
-}
-
/* handle requests/control events coming in over the channel */
static void process_event(void *opaque, QObject *obj, Error *err)
{
GAState *s = opaque;
- QDict *req, *rsp;
+ QDict *rsp;
int ret;
g_debug("process_event: called");
assert(!obj != !err);
if (err) {
- goto err;
- }
- req = qobject_to(QDict, obj);
- if (!req) {
- error_setg(&err, "Input must be a JSON object");
- goto err;
- }
- if (!qdict_haskey(req, "execute")) {
- g_warning("unrecognized payload format");
- error_setg(&err, QERR_UNSUPPORTED);
- goto err;
+ rsp = qmp_error_response(err);
+ goto end;
}
- process_command(s, req);
- qobject_unref(obj);
- return;
+ g_debug("processing command");
+ rsp = qmp_dispatch(&ga_commands, obj, false);
-err:
- g_warning("failed to parse event: %s", error_get_pretty(err));
- rsp = qmp_error_response(err);
+end:
ret = send_response(s, rsp);
if (ret < 0) {
g_warning("error sending error response: %s", strerror(-ret));
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a4ff5b8..fb4605c 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -532,12 +532,12 @@
#
# Suspend guest to disk.
#
-# This command tries to execute the scripts provided by the pm-utils package.
-# If it's not available, the suspend operation will be performed by manually
-# writing to a sysfs file.
+# This command attempts to suspend the guest using three strategies, in this
+# order:
#
-# For the best results it's strongly recommended to have the pm-utils
-# package installed in the guest.
+# - systemd hibernate
+# - pm-utils (via pm-hibernate)
+# - manual write into sysfs
#
# This command does NOT return a response on success. There is a high chance
# the command succeeded if the VM exits with a zero exit status or, when
@@ -560,12 +560,12 @@
#
# Suspend guest to ram.
#
-# This command tries to execute the scripts provided by the pm-utils package.
-# If it's not available, the suspend operation will be performed by manually
-# writing to a sysfs file.
+# This command attempts to suspend the guest using three strategies, in this
+# order:
#
-# For the best results it's strongly recommended to have the pm-utils
-# package installed in the guest.
+# - systemd suspend
+# - pm-utils (via pm-suspend)
+# - manual write into sysfs
#
# IMPORTANT: guest-suspend-ram requires working wakeup support in
# QEMU. You should check QMP command query-current-machine returns
@@ -594,7 +594,10 @@
#
# Save guest state to disk and suspend to ram.
#
-# This command requires the pm-utils package to be installed in the guest.
+# This command attempts to suspend the guest by executing, in this order:
+#
+# - systemd hybrid-sleep
+# - pm-utils (via pm-suspend-hybrid)
#
# IMPORTANT: guest-suspend-hybrid requires working wakeup support in
# QEMU. You should check QMP command query-current-machine returns
diff --git a/qga/vss-win32/Makefile.objs b/qga/vss-win32/Makefile.objs
index 23d08da..fd3ba18 100644
--- a/qga/vss-win32/Makefile.objs
+++ b/qga/vss-win32/Makefile.objs
@@ -3,9 +3,9 @@
qga-vss-dll-obj-y += requester.o provider.o install.o
obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y))
-$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -fstack-protector-all -fstack-protector-strong, $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor
+$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS := $(filter-out -fstack-protector-all -fstack-protector-strong, $(QEMU_CXXFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor
-$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static
+$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lglib-2.0 -lole32 -loleaut32 -lshlwapi -luuid -lintl -lws2_32 -static
$(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def
$(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS),"LINK","$(TARGET_DIR)$@")
diff --git a/tests/test-qga.c b/tests/test-qga.c
index 3d63774..891aa3d 100644
--- a/tests/test-qga.c
+++ b/tests/test-qga.c
@@ -225,18 +225,15 @@ static void test_qga_ping(gconstpointer fix)
qobject_unref(ret);
}
-static void test_qga_invalid_id(gconstpointer fix)
+static void test_qga_id(gconstpointer fix)
{
const TestFixture *fixture = fix;
- QDict *ret, *error;
- const char *class;
+ QDict *ret;
ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}");
g_assert_nonnull(ret);
-
- error = qdict_get_qdict(ret, "error");
- class = qdict_get_try_str(error, "class");
- g_assert_cmpstr(class, ==, "GenericError");
+ qmp_assert_no_error(ret);
+ g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1);
qobject_unref(ret);
}
@@ -992,7 +989,7 @@ int main(int argc, char **argv)
g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
- g_test_add_data_func("/qga/invalid-id", &fix, test_qga_invalid_id);
+ g_test_add_data_func("/qga/id", &fix, test_qga_id);
g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob);
g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);