mirror of https://gitee.com/openkylin/qemu.git
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>
This commit is contained in:
commit
54281eefc2
|
@ -4934,7 +4934,7 @@ int main(void) {
|
||||||
EOF
|
EOF
|
||||||
if compile_prog "" "" ; then
|
if compile_prog "" "" ; then
|
||||||
guest_agent_ntddscsi=yes
|
guest_agent_ntddscsi=yes
|
||||||
libs_qga="-lsetupapi $libs_qga"
|
libs_qga="-lsetupapi -lcfgmgr32 $libs_qga"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
33
monitor.c
33
monitor.c
|
@ -250,8 +250,6 @@ QEMUBH *qmp_dispatcher_bh;
|
||||||
struct QMPRequest {
|
struct QMPRequest {
|
||||||
/* Owner of the request */
|
/* Owner of the request */
|
||||||
Monitor *mon;
|
Monitor *mon;
|
||||||
/* "id" field of the request */
|
|
||||||
QObject *id;
|
|
||||||
/*
|
/*
|
||||||
* Request object to be handled or Error to be reported
|
* Request object to be handled or Error to be reported
|
||||||
* (exactly one of them is non-null)
|
* (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)
|
static void qmp_request_free(QMPRequest *req)
|
||||||
{
|
{
|
||||||
qobject_unref(req->id);
|
|
||||||
qobject_unref(req->req);
|
qobject_unref(req->req);
|
||||||
error_free(req->err);
|
error_free(req->err);
|
||||||
g_free(req);
|
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.
|
* Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP.
|
||||||
* Nothing is emitted then.
|
* 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 (rsp) {
|
||||||
if (id) {
|
|
||||||
qdict_put_obj(rsp, "id", qobject_ref(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
qmp_send_response(mon, rsp);
|
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;
|
Monitor *old_mon;
|
||||||
QDict *rsp;
|
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);
|
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;
|
mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1;
|
||||||
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
|
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
|
||||||
if (req_obj->req) {
|
if (req_obj->req) {
|
||||||
trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: "");
|
QDict *qdict = qobject_to(QDict, req_obj->req);
|
||||||
monitor_qmp_dispatch(mon, req_obj->req, req_obj->id);
|
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 {
|
} else {
|
||||||
assert(req_obj->err);
|
assert(req_obj->err);
|
||||||
rsp = qmp_error_response(req_obj->err);
|
rsp = qmp_error_response(req_obj->err);
|
||||||
req_obj->err = NULL;
|
req_obj->err = NULL;
|
||||||
monitor_qmp_respond(mon, rsp, NULL);
|
monitor_qmp_respond(mon, rsp);
|
||||||
qobject_unref(rsp);
|
qobject_unref(rsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4239,8 +4234,7 @@ static void handle_qmp_command(void *opaque, QObject *req, Error *err)
|
||||||
|
|
||||||
qdict = qobject_to(QDict, req);
|
qdict = qobject_to(QDict, req);
|
||||||
if (qdict) {
|
if (qdict) {
|
||||||
id = qobject_ref(qdict_get(qdict, "id"));
|
id = qdict_get(qdict, "id");
|
||||||
qdict_del(qdict, "id");
|
|
||||||
} /* else will fail qmp_dispatch() */
|
} /* else will fail qmp_dispatch() */
|
||||||
|
|
||||||
if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
|
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)) {
|
if (qdict && qmp_is_oob(qdict)) {
|
||||||
/* OOB commands are executed immediately */
|
/* OOB commands are executed immediately */
|
||||||
trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id)
|
trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: "");
|
||||||
?: "");
|
monitor_qmp_dispatch(mon, req);
|
||||||
monitor_qmp_dispatch(mon, req, id);
|
|
||||||
qobject_unref(req);
|
qobject_unref(req);
|
||||||
qobject_unref(id);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
req_obj = g_new0(QMPRequest, 1);
|
req_obj = g_new0(QMPRequest, 1);
|
||||||
req_obj->mon = mon;
|
req_obj->mon = mon;
|
||||||
req_obj->id = id;
|
|
||||||
req_obj->req = req;
|
req_obj->req = req;
|
||||||
req_obj->err = err;
|
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
|
* 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.
|
* etc. will be delivered to the handler side.
|
||||||
*/
|
*/
|
||||||
assert(mon->qmp.qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
|
assert(mon->qmp.qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX);
|
||||||
|
|
|
@ -58,6 +58,8 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
|
||||||
"QMP input member 'arguments' must be an object");
|
"QMP input member 'arguments' must be an object");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
} else if (!strcmp(arg_name, "id")) {
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "QMP input member '%s' is unexpected",
|
error_setg(errp, "QMP input member '%s' is unexpected",
|
||||||
arg_name);
|
arg_name);
|
||||||
|
@ -165,11 +167,11 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||||
bool allow_oob)
|
bool allow_oob)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
QObject *ret;
|
QDict *dict = qobject_to(QDict, request);
|
||||||
|
QObject *ret, *id = dict ? qdict_get(dict, "id") : NULL;
|
||||||
QDict *rsp;
|
QDict *rsp;
|
||||||
|
|
||||||
ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
|
ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
rsp = qmp_error_response(err);
|
rsp = qmp_error_response(err);
|
||||||
} else if (ret) {
|
} else if (ret) {
|
||||||
|
@ -180,5 +182,9 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||||
rsp = NULL;
|
rsp = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rsp && id) {
|
||||||
|
qdict_put_obj(rsp, "id", qobject_ref(id));
|
||||||
|
}
|
||||||
|
|
||||||
return rsp;
|
return rsp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <winioctl.h>
|
#include <winioctl.h>
|
||||||
#include <ntddscsi.h>
|
#include <ntddscsi.h>
|
||||||
#include <setupapi.h>
|
#include <setupapi.h>
|
||||||
|
#include <cfgmgr32.h>
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
#endif
|
#endif
|
||||||
#include <lm.h>
|
#include <lm.h>
|
||||||
|
@ -485,56 +486,29 @@ static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
|
||||||
return win2qemu[(int)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,
|
DEFINE_GUID(GUID_DEVINTERFACE_DISK,
|
||||||
0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
|
0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
|
||||||
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
|
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(int number, Error **errp)
|
||||||
static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
|
|
||||||
{
|
{
|
||||||
HDEVINFO dev_info;
|
HDEVINFO dev_info;
|
||||||
SP_DEVINFO_DATA dev_info_data;
|
SP_DEVINFO_DATA dev_info_data;
|
||||||
DWORD size = 0;
|
SP_DEVICE_INTERFACE_DATA dev_iface_data;
|
||||||
|
HANDLE dev_file;
|
||||||
int i;
|
int i;
|
||||||
char dev_name[MAX_PATH];
|
|
||||||
char *buffer = NULL;
|
|
||||||
GuestPCIAddress *pci = NULL;
|
GuestPCIAddress *pci = NULL;
|
||||||
char *name = NULL;
|
|
||||||
bool partial_pci = false;
|
bool partial_pci = false;
|
||||||
|
|
||||||
pci = g_malloc0(sizeof(*pci));
|
pci = g_malloc0(sizeof(*pci));
|
||||||
pci->domain = -1;
|
pci->domain = -1;
|
||||||
pci->slot = -1;
|
pci->slot = -1;
|
||||||
pci->function = -1;
|
pci->function = -1;
|
||||||
pci->bus = -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,
|
dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0,
|
||||||
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||||
if (dev_info == INVALID_HANDLE_VALUE) {
|
if (dev_info == INVALID_HANDLE_VALUE) {
|
||||||
|
@ -544,90 +518,220 @@ static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
|
||||||
|
|
||||||
g_debug("enumerating devices");
|
g_debug("enumerating devices");
|
||||||
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
|
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++) {
|
for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
|
||||||
DWORD addr, bus, slot, data, size2;
|
PSP_DEVICE_INTERFACE_DETAIL_DATA pdev_iface_detail_data = NULL;
|
||||||
int func, dev;
|
STORAGE_DEVICE_NUMBER sdn;
|
||||||
while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
|
char *parent_dev_id = NULL;
|
||||||
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
|
HDEVINFO parent_dev_info;
|
||||||
&data, (PBYTE)buffer, size,
|
SP_DEVINFO_DATA parent_dev_info_data;
|
||||||
&size2)) {
|
DWORD j;
|
||||||
size = MAX(size, size2);
|
DWORD size = 0;
|
||||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
|
||||||
g_free(buffer);
|
g_debug("getting device path");
|
||||||
/* Double the size to avoid problems on
|
if (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data,
|
||||||
* W2k MBCS systems per KB 888609.
|
&GUID_DEVINTERFACE_DISK, 0,
|
||||||
* https://support.microsoft.com/en-us/kb/259695 */
|
&dev_iface_data)) {
|
||||||
buffer = g_malloc(size * 2);
|
while (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
|
||||||
} else {
|
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(),
|
error_setg_win32(errp, GetLastError(),
|
||||||
"failed to get device name");
|
"failed to get device slot number");
|
||||||
goto free_dev_info;
|
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)) {
|
g_debug("querying storage controller %s for PCI information",
|
||||||
continue;
|
parent_dev_id);
|
||||||
}
|
parent_dev_info =
|
||||||
g_debug("found device %s", dev_name);
|
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
|
if (parent_dev_info == INVALID_HANDLE_VALUE) {
|
||||||
* is known and ULONG according to
|
error_setg_win32(errp, GetLastError(),
|
||||||
* https://support.microsoft.com/en-us/kb/253232
|
"failed to get parent device");
|
||||||
* https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
|
goto out;
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The function retrieves the device's address. This value will be
|
parent_dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||||
* transformed into device function and number */
|
if (!SetupDiEnumDeviceInfo(parent_dev_info, 0, &parent_dev_info_data)) {
|
||||||
if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
|
error_setg_win32(errp, GetLastError(),
|
||||||
SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) {
|
"failed to get parent device data");
|
||||||
debug_error("failed to get address");
|
goto out;
|
||||||
addr = -1;
|
|
||||||
partial_pci = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This call returns UINumber of DEVICE_CAPABILITIES structure.
|
for (j = 0;
|
||||||
* This number is typically a user-perceived slot number. */
|
SetupDiEnumDeviceInfo(parent_dev_info, j, &parent_dev_info_data);
|
||||||
if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
|
j++) {
|
||||||
SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) {
|
DWORD addr, bus, ui_slot, type;
|
||||||
debug_error("failed to get slot");
|
int func, slot;
|
||||||
slot = -1;
|
|
||||||
partial_pci = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SetupApi gives us the same information as driver with
|
/*
|
||||||
* IoGetDeviceProperty. According to Microsoft
|
* There is no need to allocate buffer in the next functions. The
|
||||||
* https://support.microsoft.com/en-us/kb/253232
|
* size is known and ULONG according to
|
||||||
* FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF);
|
* https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
|
||||||
* DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF);
|
*/
|
||||||
* SPDRP_ADDRESS is propertyAddress, so we do the same.*/
|
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;
|
* The function retrieves the device's address. This value will be
|
||||||
pci->slot = -1;
|
* transformed into device function and number
|
||||||
pci->function = -1;
|
*/
|
||||||
pci->bus = -1;
|
if (!SetupDiGetDeviceRegistryProperty(
|
||||||
} else {
|
parent_dev_info, &parent_dev_info_data, SPDRP_ADDRESS,
|
||||||
func = ((int) addr == -1) ? -1 : addr & 0x0000FFFF;
|
&type, (PBYTE)&addr, size, NULL)) {
|
||||||
dev = ((int) addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
|
debug_error("failed to get PCI address");
|
||||||
pci->domain = dev;
|
addr = -1;
|
||||||
pci->slot = (int) slot;
|
partial_pci = true;
|
||||||
pci->function = func;
|
}
|
||||||
pci->bus = (int) bus;
|
|
||||||
|
/*
|
||||||
|
* 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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_dev_info:
|
free_dev_info:
|
||||||
SetupDiDestroyDeviceInfoList(dev_info);
|
SetupDiDestroyDeviceInfoList(dev_info);
|
||||||
out:
|
out:
|
||||||
g_free(buffer);
|
|
||||||
g_free(name);
|
|
||||||
return pci;
|
return pci;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,7 +789,8 @@ out_free:
|
||||||
return;
|
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;
|
SCSI_ADDRESS addr, *scsi_ad;
|
||||||
DWORD len;
|
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
|
* if that doesn't hold since that suggests some other unexpected
|
||||||
* breakage
|
* breakage
|
||||||
*/
|
*/
|
||||||
disk->pci_controller = get_pci_info(disk->dev, &local_err);
|
disk->pci_controller = get_pci_info(disk_number, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto err_close;
|
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
|
/* We are able to use the same ioctls for different bus types
|
||||||
* according to Microsoft docs
|
* according to Microsoft docs
|
||||||
* https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */
|
* 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,
|
if (DeviceIoControl(disk_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
|
||||||
sizeof(SCSI_ADDRESS), &len, NULL)) {
|
sizeof(SCSI_ADDRESS), &len, NULL)) {
|
||||||
disk->unit = addr.Lun;
|
disk->unit = addr.Lun;
|
||||||
|
@ -776,12 +881,10 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
|
||||||
size = sizeof(VOLUME_DISK_EXTENTS);
|
size = sizeof(VOLUME_DISK_EXTENTS);
|
||||||
extents = g_malloc0(size);
|
extents = g_malloc0(size);
|
||||||
if (!DeviceIoControl(vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
|
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();
|
DWORD last_err = GetLastError();
|
||||||
if (last_err == ERROR_MORE_DATA) {
|
if (last_err == ERROR_MORE_DATA) {
|
||||||
/* Try once more with big enough buffer */
|
/* Try once more with big enough buffer */
|
||||||
size = sizeof(VOLUME_DISK_EXTENTS)
|
|
||||||
+ extents->NumberOfDiskExtents*sizeof(DISK_EXTENT);
|
|
||||||
g_free(extents);
|
g_free(extents);
|
||||||
extents = g_malloc0(size);
|
extents = g_malloc0(size);
|
||||||
if (!DeviceIoControl(
|
if (!DeviceIoControl(
|
||||||
|
@ -797,7 +900,7 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
|
||||||
disk = g_malloc0(sizeof(GuestDiskAddress));
|
disk = g_malloc0(sizeof(GuestDiskAddress));
|
||||||
disk->has_dev = true;
|
disk->has_dev = true;
|
||||||
disk->dev = g_strdup(name);
|
disk->dev = g_strdup(name);
|
||||||
get_single_disk_info(disk, &local_err);
|
get_single_disk_info(0xffffffff, disk, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
g_debug("failed to get disk info, ignoring error: %s",
|
g_debug("failed to get disk info, ignoring error: %s",
|
||||||
error_get_pretty(local_err));
|
error_get_pretty(local_err));
|
||||||
|
@ -831,9 +934,9 @@ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
|
||||||
*/
|
*/
|
||||||
disk->has_dev = true;
|
disk->has_dev = true;
|
||||||
disk->dev = g_strdup_printf("\\\\.\\PhysicalDrive%lu",
|
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) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
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, 1, "Microsoft Windows Server 2008 R2", "2008r2"},
|
||||||
{ 6, 2, "Microsoft Windows Server 2012", "2012"},
|
{ 6, 2, "Microsoft Windows Server 2012", "2012"},
|
||||||
{ 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
|
{ 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
|
||||||
{10, 0, "Microsoft Windows Server 2016", "2016"},
|
{ 0, 0, 0},
|
||||||
{ 0, 0, 0},
|
{ 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)
|
static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
|
||||||
{
|
{
|
||||||
typedef NTSTATUS(WINAPI * rtl_get_version_t)(
|
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 major = os_version->dwMajorVersion;
|
||||||
DWORD minor = os_version->dwMinorVersion;
|
DWORD minor = os_version->dwMinorVersion;
|
||||||
|
DWORD build = os_version->dwBuildNumber;
|
||||||
int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
|
int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
|
||||||
ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
|
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) {
|
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) {
|
if (id) {
|
||||||
return g_strdup(table->version_id);
|
return g_strdup(table->version_id);
|
||||||
} else {
|
} else {
|
||||||
|
|
47
qga/main.c
47
qga/main.c
|
@ -523,15 +523,15 @@ fail:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_response(GAState *s, QDict *payload)
|
static int send_response(GAState *s, const QDict *rsp)
|
||||||
{
|
{
|
||||||
const char *buf;
|
const char *buf;
|
||||||
QString *payload_qstr, *response_qstr;
|
QString *payload_qstr, *response_qstr;
|
||||||
GIOStatus status;
|
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) {
|
if (!payload_qstr) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -557,53 +557,24 @@ static int send_response(GAState *s, QDict *payload)
|
||||||
return 0;
|
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 */
|
/* handle requests/control events coming in over the channel */
|
||||||
static void process_event(void *opaque, QObject *obj, Error *err)
|
static void process_event(void *opaque, QObject *obj, Error *err)
|
||||||
{
|
{
|
||||||
GAState *s = opaque;
|
GAState *s = opaque;
|
||||||
QDict *req, *rsp;
|
QDict *rsp;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
g_debug("process_event: called");
|
g_debug("process_event: called");
|
||||||
assert(!obj != !err);
|
assert(!obj != !err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto err;
|
rsp = qmp_error_response(err);
|
||||||
}
|
goto end;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process_command(s, req);
|
g_debug("processing command");
|
||||||
qobject_unref(obj);
|
rsp = qmp_dispatch(&ga_commands, obj, false);
|
||||||
return;
|
|
||||||
|
|
||||||
err:
|
end:
|
||||||
g_warning("failed to parse event: %s", error_get_pretty(err));
|
|
||||||
rsp = qmp_error_response(err);
|
|
||||||
ret = send_response(s, rsp);
|
ret = send_response(s, rsp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_warning("error sending error response: %s", strerror(-ret));
|
g_warning("error sending error response: %s", strerror(-ret));
|
||||||
|
|
|
@ -532,12 +532,12 @@
|
||||||
#
|
#
|
||||||
# Suspend guest to disk.
|
# Suspend guest to disk.
|
||||||
#
|
#
|
||||||
# This command tries to execute the scripts provided by the pm-utils package.
|
# This command attempts to suspend the guest using three strategies, in this
|
||||||
# If it's not available, the suspend operation will be performed by manually
|
# order:
|
||||||
# writing to a sysfs file.
|
|
||||||
#
|
#
|
||||||
# For the best results it's strongly recommended to have the pm-utils
|
# - systemd hibernate
|
||||||
# package installed in the guest.
|
# - pm-utils (via pm-hibernate)
|
||||||
|
# - manual write into sysfs
|
||||||
#
|
#
|
||||||
# This command does NOT return a response on success. There is a high chance
|
# 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
|
# the command succeeded if the VM exits with a zero exit status or, when
|
||||||
|
@ -560,12 +560,12 @@
|
||||||
#
|
#
|
||||||
# Suspend guest to ram.
|
# Suspend guest to ram.
|
||||||
#
|
#
|
||||||
# This command tries to execute the scripts provided by the pm-utils package.
|
# This command attempts to suspend the guest using three strategies, in this
|
||||||
# If it's not available, the suspend operation will be performed by manually
|
# order:
|
||||||
# writing to a sysfs file.
|
|
||||||
#
|
#
|
||||||
# For the best results it's strongly recommended to have the pm-utils
|
# - systemd suspend
|
||||||
# package installed in the guest.
|
# - pm-utils (via pm-suspend)
|
||||||
|
# - manual write into sysfs
|
||||||
#
|
#
|
||||||
# IMPORTANT: guest-suspend-ram requires working wakeup support in
|
# IMPORTANT: guest-suspend-ram requires working wakeup support in
|
||||||
# QEMU. You should check QMP command query-current-machine returns
|
# QEMU. You should check QMP command query-current-machine returns
|
||||||
|
@ -594,7 +594,10 @@
|
||||||
#
|
#
|
||||||
# Save guest state to disk and suspend to ram.
|
# 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
|
# IMPORTANT: guest-suspend-hybrid requires working wakeup support in
|
||||||
# QEMU. You should check QMP command query-current-machine returns
|
# QEMU. You should check QMP command query-current-machine returns
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
qga-vss-dll-obj-y += requester.o provider.o install.o
|
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 = $(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
|
$(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)$@")
|
$(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS),"LINK","$(TARGET_DIR)$@")
|
||||||
|
|
||||||
|
|
|
@ -225,18 +225,15 @@ static void test_qga_ping(gconstpointer fix)
|
||||||
qobject_unref(ret);
|
qobject_unref(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_qga_invalid_id(gconstpointer fix)
|
static void test_qga_id(gconstpointer fix)
|
||||||
{
|
{
|
||||||
const TestFixture *fixture = fix;
|
const TestFixture *fixture = fix;
|
||||||
QDict *ret, *error;
|
QDict *ret;
|
||||||
const char *class;
|
|
||||||
|
|
||||||
ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}");
|
ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}");
|
||||||
g_assert_nonnull(ret);
|
g_assert_nonnull(ret);
|
||||||
|
qmp_assert_no_error(ret);
|
||||||
error = qdict_get_qdict(ret, "error");
|
g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1);
|
||||||
class = qdict_get_try_str(error, "class");
|
|
||||||
g_assert_cmpstr(class, ==, "GenericError");
|
|
||||||
|
|
||||||
qobject_unref(ret);
|
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-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/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/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-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-cmd", &fix, test_qga_invalid_cmd);
|
||||||
g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);
|
g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);
|
||||||
|
|
Loading…
Reference in New Issue