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
 -----BEGIN PGP SIGNATURE-----
 
 iQFOBAABCgA4FiEEzqzJ4VU066u4LT+gM1PJzvEItYQFAlyPxVAaHG1kcm90aEBs
 aW51eC52bmV0LmlibS5jb20ACgkQM1PJzvEItYTUegf+NTCVX15dKrOTgXfbh9A8
 CTnSY+1qj3oOW/z9obvAto6Dgq/BUefC18fKSxWMgtcaMQcH8rTQXQRYuw7b9TKP
 rLoBa/AmSsJqjl/X6b9IVnFwpoqZzmyjsvjbcSh5RUMy0dMg3DridQQGmu0vduFk
 TAoL5eeFup/dbdKOIqUBCv3b0X3eCYMYRrwMkIX1GLaWaC/Gb3gasXDdKSi39E00
 sgJSsnXbsepOpht9BA74CpgVaawUsFfq/RiRQbUgLSNt+4AqkTVsVN2WkmcxN/MA
 Zgmwvl0jsuf1l0jO3aUFAK9uUDTB8oYqF0oCiun3nK2eqF4CLb+PG9DTjk8vycLA
 vA==
 =IICQ
 -----END PGP SIGNATURE-----

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>
master
Peter Maydell 2019-03-19 09:53:29 +00:00
commit 54281eefc2
8 changed files with 288 additions and 192 deletions

2
configure vendored
View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}
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 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;
}
}
if (g_strcmp0(buffer, dev_name)) {
continue;
}
g_debug("found device %s", dev_name);
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);
/* 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;
if (parent_dev_info == INVALID_HANDLE_VALUE) {
error_setg_win32(errp, GetLastError(),
"failed to get parent device");
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;
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;
}
/* 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;
}
for (j = 0;
SetupDiEnumDeviceInfo(parent_dev_info, j, &parent_dev_info_data);
j++) {
DWORD addr, bus, ui_slot, type;
int func, slot;
/* 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.*/
/*
* 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;
}
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;
/*
* 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 {

View File

@ -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));

View File

@ -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

View File

@ -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)$@")

View File

@ -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);