summaryrefslogtreecommitdiff
path: root/qga
diff options
context:
space:
mode:
authorYonghee Han <onstudy@samsung.com>2016-07-27 16:40:17 +0900
committerYonghee Han <onstudy@samsung.com>2016-07-27 00:53:56 -0700
commit3158f4a51894e46ecb593bffbfd12824e1d6534a (patch)
tree2bef7f0238e687c5de65f48b5995ee124a95d157 /qga
parenta3b133b0ea0696e42fd876b9a803e28bc6ef5299 (diff)
downloadqemu-3158f4a51894e46ecb593bffbfd12824e1d6534a.tar.gz
qemu-3158f4a51894e46ecb593bffbfd12824e1d6534a.tar.bz2
qemu-3158f4a51894e46ecb593bffbfd12824e1d6534a.zip
Imported Upstream version 2.4.1upstream/2.4.1
Change-Id: I0b584f569cb0e0f4eac13cdb79e110c2dbc34bfc
Diffstat (limited to 'qga')
-rw-r--r--qga/channel-win32.c2
-rw-r--r--qga/commands-posix.c101
-rw-r--r--qga/commands-win32.c593
-rw-r--r--qga/installer/qemu-ga.wxs145
-rw-r--r--qga/main.c16
-rw-r--r--qga/qapi-schema.json86
6 files changed, 842 insertions, 101 deletions
diff --git a/qga/channel-win32.c b/qga/channel-win32.c
index 0d5e5f511..04fa5e4d1 100644
--- a/qga/channel-win32.c
+++ b/qga/channel-win32.c
@@ -306,7 +306,7 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
if (c->handle == INVALID_HANDLE_VALUE) {
- g_critical("error opening path");
+ g_critical("error opening path %s", newpath);
return false;
}
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 4449628b1..675f4b4c6 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -154,6 +154,8 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
/* If user has passed a time, validate and set it. */
if (has_time) {
+ GDate date = { 0, };
+
/* year-2038 will overflow in case time_t is 32bit */
if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
error_setg(errp, "Time %" PRId64 " is too large", time_ns);
@@ -162,6 +164,11 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
tv.tv_sec = time_ns / 1000000000;
tv.tv_usec = (time_ns % 1000000000) / 1000;
+ g_date_set_time_t(&date, tv.tv_sec);
+ if (date.year < 1970 || date.year >= 2070) {
+ error_setg_errno(errp, errno, "Invalid time");
+ return;
+ }
ret = settimeofday(&tv, NULL);
if (ret < 0) {
@@ -382,15 +389,15 @@ static int guest_file_toggle_flags(int fd, int flags, bool set, Error **err)
old_flags = fcntl(fd, F_GETFL);
if (old_flags == -1) {
- error_set_errno(err, errno, QERR_QGA_COMMAND_FAILED,
- "failed to fetch filehandle flags");
+ error_setg_errno(err, errno, QERR_QGA_COMMAND_FAILED,
+ "failed to fetch filehandle flags");
return -1;
}
ret = fcntl(fd, F_SETFL, set ? (old_flags | flags) : (old_flags & ~flags));
if (ret == -1) {
- error_set_errno(err, errno, QERR_QGA_COMMAND_FAILED,
- "failed to set filehandle flags");
+ error_setg_errno(err, errno, QERR_QGA_COMMAND_FAILED,
+ "failed to set filehandle flags");
return -1;
}
@@ -1325,8 +1332,12 @@ static void guest_fsfreeze_cleanup(void)
/*
* Walk list of mounted file systems in the guest, and trim them.
*/
-void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
+GuestFilesystemTrimResponse *
+qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
{
+ GuestFilesystemTrimResponse *response;
+ GuestFilesystemTrimResultList *list;
+ GuestFilesystemTrimResult *result;
int ret = 0;
FsMountList mounts;
struct FsMount *mount;
@@ -1340,39 +1351,59 @@ void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
build_fs_mount_list(&mounts, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ return NULL;
}
+ response = g_malloc0(sizeof(*response));
+
QTAILQ_FOREACH(mount, &mounts, next) {
+ result = g_malloc0(sizeof(*result));
+ result->path = g_strdup(mount->dirname);
+
+ list = g_malloc0(sizeof(*list));
+ list->value = result;
+ list->next = response->paths;
+ response->paths = list;
+
fd = qemu_open(mount->dirname, O_RDONLY);
if (fd == -1) {
- error_setg_errno(errp, errno, "failed to open %s", mount->dirname);
- goto error;
+ result->error = g_strdup_printf("failed to open: %s",
+ strerror(errno));
+ result->has_error = true;
+ continue;
}
/* We try to cull filesytems we know won't work in advance, but other
* filesytems may not implement fstrim for less obvious reasons. These
- * will report EOPNOTSUPP; we simply ignore these errors. Any other
- * error means an unexpected error, so return it in those cases. In
- * some other cases ENOTTY will be reported (e.g. CD-ROMs).
+ * will report EOPNOTSUPP; while in some other cases ENOTTY will be
+ * reported (e.g. CD-ROMs).
+ * Any other error means an unexpected error.
*/
r.start = 0;
r.len = -1;
r.minlen = has_minimum ? minimum : 0;
ret = ioctl(fd, FITRIM, &r);
if (ret == -1) {
- if (errno != ENOTTY && errno != EOPNOTSUPP) {
- error_setg_errno(errp, errno, "failed to trim %s",
- mount->dirname);
- close(fd);
- goto error;
+ result->has_error = true;
+ if (errno == ENOTTY || errno == EOPNOTSUPP) {
+ result->error = g_strdup("trim not supported");
+ } else {
+ result->error = g_strdup_printf("failed to trim: %s",
+ strerror(errno));
}
+ close(fd);
+ continue;
}
+
+ result->has_minimum = true;
+ result->minimum = r.minlen;
+ result->has_trimmed = true;
+ result->trimmed = r.len;
close(fd);
}
-error:
free_fs_mount_list(&mounts);
+ return response;
}
#endif /* CONFIG_FSTRIM */
@@ -2301,34 +2332,34 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
void qmp_guest_suspend_disk(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
}
void qmp_guest_suspend_ram(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
}
void qmp_guest_suspend_hybrid(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
}
GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return -1;
}
@@ -2337,25 +2368,25 @@ void qmp_guest_set_user_password(const char *username,
bool crypted,
Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
}
GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
GuestMemoryBlockResponseList *
qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
@@ -2365,20 +2396,20 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return 0;
}
int64_t qmp_guest_fsfreeze_freeze(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return 0;
}
@@ -2387,23 +2418,25 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
strList *mountpoints,
Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return 0;
}
int64_t qmp_guest_fsfreeze_thaw(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return 0;
}
#endif /* CONFIG_FSFREEZE */
#if !defined(CONFIG_FSTRIM)
-void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
+GuestFilesystemTrimResponse *
+qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
+ return NULL;
}
#endif
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 3ef0549c0..a7822d5ff 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -16,11 +16,22 @@
#include <powrprof.h>
#include <stdio.h>
#include <string.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <iptypes.h>
+#include <iphlpapi.h>
+#ifdef CONFIG_QGA_NTDDSCSI
+#include <winioctl.h>
+#include <ntddscsi.h>
+#include <setupapi.h>
+#include <initguid.h>
+#endif
#include "qga/guest-agent-core.h"
#include "qga/vss-win32.h"
#include "qga-qmp-commands.h"
#include "qapi/qmp/qerror.h"
#include "qemu/queue.h"
+#include "qemu/host-utils.h"
#ifndef SHTDN_REASON_FLAG_PLANNED
#define SHTDN_REASON_FLAG_PLANNED 0x80000000
@@ -182,8 +193,8 @@ static void acquire_privilege(const char *name, Error **errp)
TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
{
if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) {
- error_set(&local_err, QERR_QGA_COMMAND_FAILED,
- "no luid for requested privilege");
+ error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
+ "no luid for requested privilege");
goto out;
}
@@ -191,14 +202,14 @@ static void acquire_privilege(const char *name, Error **errp)
priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) {
- error_set(&local_err, QERR_QGA_COMMAND_FAILED,
- "unable to acquire requested privilege");
+ error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
+ "unable to acquire requested privilege");
goto out;
}
} else {
- error_set(&local_err, QERR_QGA_COMMAND_FAILED,
- "failed to open privilege token");
+ error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
+ "failed to open privilege token");
}
out:
@@ -217,8 +228,8 @@ static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque,
HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL);
if (!thread) {
- error_set(&local_err, QERR_QGA_COMMAND_FAILED,
- "failed to dispatch asynchronous command");
+ error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
+ "failed to dispatch asynchronous command");
error_propagate(errp, local_err);
}
}
@@ -237,8 +248,8 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
} else if (strcmp(mode, "reboot") == 0) {
shutdown_flag |= EWX_REBOOT;
} else {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, "mode",
- "halt|powerdown|reboot");
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "mode",
+ "halt|powerdown|reboot");
return;
}
@@ -252,7 +263,7 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
slog("guest-shutdown failed: %lu", GetLastError());
- error_set(errp, QERR_UNDEFINED_ERROR);
+ error_setg(errp, QERR_UNDEFINED_ERROR);
}
}
@@ -382,19 +393,314 @@ static void guest_file_init(void)
QTAILQ_INIT(&guest_file_state.filehandles);
}
-GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
+#ifdef CONFIG_QGA_NTDDSCSI
+
+static STORAGE_BUS_TYPE win2qemu[] = {
+ [BusTypeUnknown] = GUEST_DISK_BUS_TYPE_UNKNOWN,
+ [BusTypeScsi] = GUEST_DISK_BUS_TYPE_SCSI,
+ [BusTypeAtapi] = GUEST_DISK_BUS_TYPE_IDE,
+ [BusTypeAta] = GUEST_DISK_BUS_TYPE_IDE,
+ [BusType1394] = GUEST_DISK_BUS_TYPE_IEEE1394,
+ [BusTypeSsa] = GUEST_DISK_BUS_TYPE_SSA,
+ [BusTypeFibre] = GUEST_DISK_BUS_TYPE_SSA,
+ [BusTypeUsb] = GUEST_DISK_BUS_TYPE_USB,
+ [BusTypeRAID] = GUEST_DISK_BUS_TYPE_RAID,
+#if (_WIN32_WINNT >= 0x0600)
+ [BusTypeiScsi] = GUEST_DISK_BUS_TYPE_ISCSI,
+ [BusTypeSas] = GUEST_DISK_BUS_TYPE_SAS,
+ [BusTypeSata] = GUEST_DISK_BUS_TYPE_SATA,
+ [BusTypeSd] = GUEST_DISK_BUS_TYPE_SD,
+ [BusTypeMmc] = GUEST_DISK_BUS_TYPE_MMC,
+#endif
+#if (_WIN32_WINNT >= 0x0601)
+ [BusTypeVirtual] = GUEST_DISK_BUS_TYPE_VIRTUAL,
+ [BusTypeFileBackedVirtual] = GUEST_DISK_BUS_TYPE_FILE_BACKED_VIRTUAL,
+#endif
+};
+
+static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
+{
+ if (bus > ARRAY_SIZE(win2qemu) || (int)bus < 0) {
+ return GUEST_DISK_BUS_TYPE_UNKNOWN;
+ }
+ return win2qemu[(int)bus];
+}
+
+DEFINE_GUID(GUID_DEVINTERFACE_VOLUME,
+ 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2,
+ 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+
+static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
+{
+ HDEVINFO dev_info;
+ SP_DEVINFO_DATA dev_info_data;
+ DWORD size = 0;
+ int i;
+ char dev_name[MAX_PATH];
+ char *buffer = NULL;
+ GuestPCIAddress *pci = NULL;
+ char *name = g_strdup(&guid[4]);
+
+ 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_VOLUME, 0, 0,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if (dev_info == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(), "failed to get devices tree");
+ goto out;
+ }
+
+ dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+ for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
+ DWORD addr, bus, slot, func, dev, data, size2;
+ 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 {
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device name");
+ goto out;
+ }
+ }
+
+ if (g_strcmp0(buffer, dev_name)) {
+ continue;
+ }
+
+ /* 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)) {
+ break;
+ }
+
+ /* 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)) {
+ break;
+ }
+
+ /* 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)) {
+ break;
+ }
+
+ /* 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.*/
+
+ func = addr & 0x0000FFFF;
+ dev = (addr >> 16) & 0x0000FFFF;
+ pci = g_malloc0(sizeof(*pci));
+ pci->domain = dev;
+ pci->slot = slot;
+ pci->function = func;
+ pci->bus = bus;
+ break;
+ }
+out:
+ g_free(buffer);
+ g_free(name);
+ return pci;
+}
+
+static int get_disk_bus_type(HANDLE vol_h, Error **errp)
+{
+ STORAGE_PROPERTY_QUERY query;
+ STORAGE_DEVICE_DESCRIPTOR *dev_desc, buf;
+ DWORD received;
+
+ dev_desc = &buf;
+ dev_desc->Size = sizeof(buf);
+ query.PropertyId = StorageDeviceProperty;
+ query.QueryType = PropertyStandardQuery;
+
+ if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query,
+ sizeof(STORAGE_PROPERTY_QUERY), dev_desc,
+ dev_desc->Size, &received, NULL)) {
+ error_setg_win32(errp, GetLastError(), "failed to get bus type");
+ return -1;
+ }
+
+ return dev_desc->BusType;
+}
+
+/* VSS provider works with volumes, thus there is no difference if
+ * the volume consist of spanned disks. Info about the first disk in the
+ * volume is returned for the spanned disk group (LVM) */
+static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
+{
+ GuestDiskAddressList *list = NULL;
+ GuestDiskAddress *disk;
+ SCSI_ADDRESS addr, *scsi_ad;
+ DWORD len;
+ int bus;
+ HANDLE vol_h;
+
+ scsi_ad = &addr;
+ char *name = g_strndup(guid, strlen(guid)-1);
+
+ vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ 0, NULL);
+ if (vol_h == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(), "failed to open volume");
+ goto out_free;
+ }
+
+ bus = get_disk_bus_type(vol_h, errp);
+ if (bus < 0) {
+ goto out_close;
+ }
+
+ disk = g_malloc0(sizeof(*disk));
+ disk->bus_type = find_bus_type(bus);
+ if (bus == BusTypeScsi || bus == BusTypeAta || bus == BusTypeRAID
+#if (_WIN32_WINNT >= 0x0600)
+ /* This bus type is not supported before Windows Server 2003 SP1 */
+ || bus == BusTypeSas
+#endif
+ ) {
+ /* 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 */
+ if (DeviceIoControl(vol_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
+ sizeof(SCSI_ADDRESS), &len, NULL)) {
+ disk->unit = addr.Lun;
+ disk->target = addr.TargetId;
+ disk->bus = addr.PathId;
+ disk->pci_controller = get_pci_info(name, errp);
+ }
+ /* We do not set error in this case, because we still have enough
+ * information about volume. */
+ } else {
+ disk->pci_controller = NULL;
+ }
+
+ list = g_malloc0(sizeof(*list));
+ list->value = disk;
+ list->next = NULL;
+out_close:
+ CloseHandle(vol_h);
+out_free:
+ g_free(name);
+ return list;
+}
+
+#else
+
+static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
return NULL;
}
+#endif /* CONFIG_QGA_NTDDSCSI */
+
+static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
+{
+ DWORD info_size;
+ char mnt, *mnt_point;
+ char fs_name[32];
+ char vol_info[MAX_PATH+1];
+ size_t len;
+ GuestFilesystemInfo *fs = NULL;
+
+ GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size);
+ if (GetLastError() != ERROR_MORE_DATA) {
+ error_setg_win32(errp, GetLastError(), "failed to get volume name");
+ return NULL;
+ }
+
+ mnt_point = g_malloc(info_size + 1);
+ if (!GetVolumePathNamesForVolumeName(guid, mnt_point, info_size,
+ &info_size)) {
+ error_setg_win32(errp, GetLastError(), "failed to get volume name");
+ goto free;
+ }
+
+ len = strlen(mnt_point);
+ mnt_point[len] = '\\';
+ mnt_point[len+1] = 0;
+ if (!GetVolumeInformation(mnt_point, vol_info, sizeof(vol_info), NULL, NULL,
+ NULL, (LPSTR)&fs_name, sizeof(fs_name))) {
+ if (GetLastError() != ERROR_NOT_READY) {
+ error_setg_win32(errp, GetLastError(), "failed to get volume info");
+ }
+ goto free;
+ }
+
+ fs_name[sizeof(fs_name) - 1] = 0;
+ fs = g_malloc(sizeof(*fs));
+ fs->name = g_strdup(guid);
+ if (len == 0) {
+ fs->mountpoint = g_strdup("System Reserved");
+ } else {
+ fs->mountpoint = g_strndup(mnt_point, len);
+ }
+ fs->type = g_strdup(fs_name);
+ fs->disk = build_guest_disk_info(guid, errp);;
+free:
+ g_free(mnt_point);
+ return fs;
+}
+
+GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
+{
+ HANDLE vol_h;
+ GuestFilesystemInfoList *new, *ret = NULL;
+ char guid[256];
+
+ vol_h = FindFirstVolume(guid, sizeof(guid));
+ if (vol_h == INVALID_HANDLE_VALUE) {
+ error_setg_win32(errp, GetLastError(), "failed to find any volume");
+ return NULL;
+ }
+
+ do {
+ GuestFilesystemInfo *info = build_guest_fsinfo(guid, errp);
+ if (info == NULL) {
+ continue;
+ }
+ new = g_malloc(sizeof(*ret));
+ new->value = info;
+ new->next = ret;
+ ret = new;
+ } while (FindNextVolume(vol_h, guid, sizeof(guid)));
+
+ if (GetLastError() != ERROR_NO_MORE_FILES) {
+ error_setg_win32(errp, GetLastError(), "failed to find next volume");
+ }
+
+ FindVolumeClose(vol_h);
+ return ret;
+}
+
/*
* Return status of freeze/thaw
*/
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
{
if (!vss_initialized()) {
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return 0;
}
@@ -415,7 +721,7 @@ int64_t qmp_guest_fsfreeze_freeze(Error **errp)
Error *local_err = NULL;
if (!vss_initialized()) {
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return 0;
}
@@ -446,7 +752,7 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
strList *mountpoints,
Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return 0;
}
@@ -459,7 +765,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
int i;
if (!vss_initialized()) {
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return 0;
}
@@ -493,9 +799,11 @@ static void guest_fsfreeze_cleanup(void)
* Walk list of mounted file systems in the guest, and discard unused
* areas.
*/
-void qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
+GuestFilesystemTrimResponse *
+qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
+ return NULL;
}
typedef enum {
@@ -510,27 +818,27 @@ static void check_suspend_mode(GuestSuspendMode mode, Error **errp)
ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps));
if (!GetPwrCapabilities(&sys_pwr_caps)) {
- error_set(&local_err, QERR_QGA_COMMAND_FAILED,
- "failed to determine guest suspend capabilities");
+ error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
+ "failed to determine guest suspend capabilities");
goto out;
}
switch (mode) {
case GUEST_SUSPEND_MODE_DISK:
if (!sys_pwr_caps.SystemS4) {
- error_set(&local_err, QERR_QGA_COMMAND_FAILED,
- "suspend-to-disk not supported by OS");
+ error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
+ "suspend-to-disk not supported by OS");
}
break;
case GUEST_SUSPEND_MODE_RAM:
if (!sys_pwr_caps.SystemS3) {
- error_set(&local_err, QERR_QGA_COMMAND_FAILED,
- "suspend-to-ram not supported by OS");
+ error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
+ "suspend-to-ram not supported by OS");
}
break;
default:
- error_set(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
- "GuestSuspendMode");
+ error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
+ "GuestSuspendMode");
}
out:
@@ -586,15 +894,223 @@ void qmp_guest_suspend_ram(Error **errp)
void qmp_guest_suspend_hybrid(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
}
-GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
+static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ IP_ADAPTER_ADDRESSES *adptr_addrs = NULL;
+ ULONG adptr_addrs_len = 0;
+ DWORD ret;
+
+ /* Call the first time to get the adptr_addrs_len. */
+ GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
+ NULL, adptr_addrs, &adptr_addrs_len);
+
+ adptr_addrs = g_malloc(adptr_addrs_len);
+ ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
+ NULL, adptr_addrs, &adptr_addrs_len);
+ if (ret != ERROR_SUCCESS) {
+ error_setg_win32(errp, ret, "failed to get adapters addresses");
+ g_free(adptr_addrs);
+ adptr_addrs = NULL;
+ }
+ return adptr_addrs;
+}
+
+static char *guest_wctomb_dup(WCHAR *wstr)
+{
+ char *str;
+ size_t i;
+
+ i = wcslen(wstr) + 1;
+ str = g_malloc(i);
+ WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK,
+ wstr, -1, str, i, NULL, NULL);
+ return str;
+}
+
+static char *guest_addr_to_str(IP_ADAPTER_UNICAST_ADDRESS *ip_addr,
+ Error **errp)
+{
+ char addr_str[INET6_ADDRSTRLEN + INET_ADDRSTRLEN];
+ DWORD len;
+ int ret;
+
+ if (ip_addr->Address.lpSockaddr->sa_family == AF_INET ||
+ ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
+ len = sizeof(addr_str);
+ ret = WSAAddressToString(ip_addr->Address.lpSockaddr,
+ ip_addr->Address.iSockaddrLength,
+ NULL,
+ addr_str,
+ &len);
+ if (ret != 0) {
+ error_setg_win32(errp, WSAGetLastError(),
+ "failed address presentation form conversion");
+ return NULL;
+ }
+ return g_strdup(addr_str);
+ }
return NULL;
}
+#if (_WIN32_WINNT >= 0x0600)
+static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
+{
+ /* For Windows Vista/2008 and newer, use the OnLinkPrefixLength
+ * field to obtain the prefix.
+ */
+ return ip_addr->OnLinkPrefixLength;
+}
+#else
+/* When using the Windows XP and 2003 build environment, do the best we can to
+ * figure out the prefix.
+ */
+static IP_ADAPTER_INFO *guest_get_adapters_info(void)
+{
+ IP_ADAPTER_INFO *adptr_info = NULL;
+ ULONG adptr_info_len = 0;
+ DWORD ret;
+
+ /* Call the first time to get the adptr_info_len. */
+ GetAdaptersInfo(adptr_info, &adptr_info_len);
+
+ adptr_info = g_malloc(adptr_info_len);
+ ret = GetAdaptersInfo(adptr_info, &adptr_info_len);
+ if (ret != ERROR_SUCCESS) {
+ g_free(adptr_info);
+ adptr_info = NULL;
+ }
+ return adptr_info;
+}
+
+static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
+{
+ int64_t prefix = -1; /* Use for AF_INET6 and unknown/undetermined values. */
+ IP_ADAPTER_INFO *adptr_info, *info;
+ IP_ADDR_STRING *ip;
+ struct in_addr *p;
+
+ if (ip_addr->Address.lpSockaddr->sa_family != AF_INET) {
+ return prefix;
+ }
+ adptr_info = guest_get_adapters_info();
+ if (adptr_info == NULL) {
+ return prefix;
+ }
+
+ /* Match up the passed in ip_addr with one found in adaptr_info.
+ * The matching one in adptr_info will have the netmask.
+ */
+ p = &((struct sockaddr_in *)ip_addr->Address.lpSockaddr)->sin_addr;
+ for (info = adptr_info; info; info = info->Next) {
+ for (ip = &info->IpAddressList; ip; ip = ip->Next) {
+ if (p->S_un.S_addr == inet_addr(ip->IpAddress.String)) {
+ prefix = ctpop32(inet_addr(ip->IpMask.String));
+ goto out;
+ }
+ }
+ }
+out:
+ g_free(adptr_info);
+ return prefix;
+}
+#endif
+
+GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
+{
+ IP_ADAPTER_ADDRESSES *adptr_addrs, *addr;
+ IP_ADAPTER_UNICAST_ADDRESS *ip_addr = NULL;
+ GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
+ GuestIpAddressList *head_addr, *cur_addr;
+ GuestNetworkInterfaceList *info;
+ GuestIpAddressList *address_item = NULL;
+ unsigned char *mac_addr;
+ char *addr_str;
+ WORD wsa_version;
+ WSADATA wsa_data;
+ int ret;
+
+ adptr_addrs = guest_get_adapters_addresses(errp);
+ if (adptr_addrs == NULL) {
+ return NULL;
+ }
+
+ /* Make WSA APIs available. */
+ wsa_version = MAKEWORD(2, 2);
+ ret = WSAStartup(wsa_version, &wsa_data);
+ if (ret != 0) {
+ error_setg_win32(errp, ret, "failed socket startup");
+ goto out;
+ }
+
+ for (addr = adptr_addrs; addr; addr = addr->Next) {
+ info = g_malloc0(sizeof(*info));
+
+ if (cur_item == NULL) {
+ head = cur_item = info;
+ } else {
+ cur_item->next = info;
+ cur_item = info;
+ }
+
+ info->value = g_malloc0(sizeof(*info->value));
+ info->value->name = guest_wctomb_dup(addr->FriendlyName);
+
+ if (addr->PhysicalAddressLength != 0) {
+ mac_addr = addr->PhysicalAddress;
+
+ info->value->hardware_address =
+ g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
+ (int) mac_addr[0], (int) mac_addr[1],
+ (int) mac_addr[2], (int) mac_addr[3],
+ (int) mac_addr[4], (int) mac_addr[5]);
+
+ info->value->has_hardware_address = true;
+ }
+
+ head_addr = NULL;
+ cur_addr = NULL;
+ for (ip_addr = addr->FirstUnicastAddress;
+ ip_addr;
+ ip_addr = ip_addr->Next) {
+ addr_str = guest_addr_to_str(ip_addr, errp);
+ if (addr_str == NULL) {
+ continue;
+ }
+
+ address_item = g_malloc0(sizeof(*address_item));
+
+ if (!cur_addr) {
+ head_addr = cur_addr = address_item;
+ } else {
+ cur_addr->next = address_item;
+ cur_addr = address_item;
+ }
+
+ address_item->value = g_malloc0(sizeof(*address_item->value));
+ address_item->value->ip_address = addr_str;
+ address_item->value->prefix = guest_ip_prefix(ip_addr);
+ if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) {
+ address_item->value->ip_address_type =
+ GUEST_IP_ADDRESS_TYPE_IPV4;
+ } else if (ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
+ address_item->value->ip_address_type =
+ GUEST_IP_ADDRESS_TYPE_IPV6;
+ }
+ }
+ if (head_addr) {
+ info->value->has_ip_addresses = true;
+ info->value->ip_addresses = head_addr;
+ }
+ }
+ WSACleanup();
+out:
+ g_free(adptr_addrs);
+ return head;
+}
+
int64_t qmp_guest_get_time(Error **errp)
{
SYSTEMTIME ts = {0};
@@ -666,13 +1182,13 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return -1;
}
@@ -681,25 +1197,25 @@ void qmp_guest_set_user_password(const char *username,
bool crypted,
Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
}
GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
GuestMemoryBlockResponseList *
qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
{
- error_set(errp, QERR_UNSUPPORTED);
+ error_setg(errp, QERR_UNSUPPORTED);
return NULL;
}
@@ -707,12 +1223,12 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
GList *ga_command_blacklist_init(GList *blacklist)
{
const char *list_unsupported[] = {
- "guest-suspend-hybrid", "guest-network-get-interfaces",
+ "guest-suspend-hybrid",
"guest-get-vcpus", "guest-set-vcpus",
"guest-set-user-password",
"guest-get-memory-blocks", "guest-set-memory-blocks",
"guest-get-memory-block-size",
- "guest-fsfreeze-freeze-list", "guest-get-fsinfo",
+ "guest-fsfreeze-freeze-list",
"guest-fstrim", NULL};
char **p = (char **)list_unsupported;
@@ -721,6 +1237,7 @@ GList *ga_command_blacklist_init(GList *blacklist)
}
if (!vss_init(true)) {
+ g_debug("vss_init failed, vss commands are going to be disabled");
const char *list[] = {
"guest-get-fsinfo", "guest-fsfreeze-status",
"guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs
new file mode 100644
index 000000000..2c43f1b5a
--- /dev/null
+++ b/qga/installer/qemu-ga.wxs
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+ <?ifndef env.QEMU_GA_VERSION ?>
+ <?error Environment variable QEMU_GA_VERSION undefined?>
+ <?endif?>
+
+ <?ifndef env.QEMU_GA_DISTRO ?>
+ <?error Environment variable QEMU_GA_DISTRO undefined?>
+ <?endif?>
+
+ <?ifndef env.QEMU_GA_MANUFACTURER ?>
+ <?error Environment variable QEMU_GA_MANUFACTURER undefined?>
+ <?endif?>
+
+ <?ifndef var.Arch?>
+ <?error Define Arch to 32 or 64?>
+ <?endif?>
+
+ <?ifndef var.Mingw_bin?>
+ <?if $(var.Arch) = "64"?>
+ <?define Mingw_bin=/usr/x86_64-w64-mingw32/sys-root/mingw/bin ?>
+ <?endif?>
+ <?if $(var.Arch) = "32"?>
+ <?define Mingw_bin=/usr/i686-w64-mingw32/sys-root/mingw/bin ?>
+ <?endif?>
+ <?endif?>
+
+ <?if $(var.Arch) = "64"?>
+ <?define ArchLib=libgcc_s_seh-1.dll?>
+ <?define GaProgramFilesFolder="ProgramFiles64Folder" ?>
+ <?endif?>
+
+ <?if $(var.Arch) = "32"?>
+ <?define ArchLib=libgcc_s_sjlj-1.dll?>
+ <?define GaProgramFilesFolder="ProgramFilesFolder" ?>
+ <?endif?>
+
+ <?ifndef var.ArchLib ?>
+ <?error Unexpected Arch value $(var.Arch)?>
+ <?endif?>
+
+ <Product
+ Name="QEMU guest agent"
+ Id="*"
+ UpgradeCode="{EB6B8302-C06E-4bec-ADAC-932C68A3A98D}"
+ Manufacturer="$(env.QEMU_GA_MANUFACTURER)"
+ Version="$(env.QEMU_GA_VERSION)"
+ Language="1033">
+ <?if $(var.Arch) = 32 ?>
+ <Condition Message="Error: 32-bit version of Qemu GA can not be installed on 64-bit Windows.">NOT VersionNT64</Condition>
+ <?endif?>
+ <Package
+ Manufacturer="$(env.QEMU_GA_MANUFACTURER)"
+ InstallerVersion="200"
+ Languages="1033"
+ Compressed="yes"
+ InstallScope="perMachine"
+ />
+ <Media Id="1" Cabinet="qemu_ga.$(env.QEMU_GA_VERSION).cab" EmbedCab="yes" />
+ <Property Id="WHSLogo">1</Property>
+ <Property Id="PREVIOUSVERSIONSINSTALLED" />
+ <Upgrade Id="{EB6B8302-C06E-4bec-ADAC-932C68A3A98D}">
+ <UpgradeVersion
+ Minimum="1.0.0.0" Maximum="$(env.QEMU_GA_VERSION)"
+ Property="PREVIOUSVERSIONSINSTALLED"
+ IncludeMinimum="yes" IncludeMaximum="no" />
+ </Upgrade>
+
+ <Directory Id="TARGETDIR" Name="SourceDir">
+ <Directory Id="$(var.GaProgramFilesFolder)" Name="QEMU Guest Agent">
+ <Directory Id="qemu_ga_directory" Name="Qemu-ga">
+ <Component Id="qemu_ga" Guid="{908B7199-DE2A-4dc6-A8D0-27A5AE444FEA}">
+ <File Id="qemu_ga.exe" Name="qemu-ga.exe" Source="../../qemu-ga.exe" KeyPath="yes" DiskId="1"/>
+ <?ifdef var.InstallVss ?>
+ <File Id="qga_vss.dll" Name="qga-vss.dll" Source="../vss-win32/qga-vss.dll" KeyPath="no" DiskId="1"/>
+ <File Id="qga_vss.tlb" Name="qga-vss.tlb" Source="../vss-win32/qga-vss.tlb" KeyPath="no" DiskId="1"/>
+ <?endif?>
+ <File Id="iconv.dll" Name="iconv.dll" Source="$(var.Mingw_bin)/iconv.dll" KeyPath="no" DiskId="1"/>
+ <File Id="libgcc_arch_lib" Name="$(var.ArchLib)" Source="$(var.Mingw_bin)/$(var.ArchLib)" KeyPath="no" DiskId="1"/>
+ <File Id="libglib_2.0_0.dll" Name="libglib-2.0-0.dll" Source="$(var.Mingw_bin)/libglib-2.0-0.dll" KeyPath="no" DiskId="1"/>
+ <File Id="libintl_8.dll" Name="libintl-8.dll" Source="$(var.Mingw_bin)/libintl-8.dll" KeyPath="no" DiskId="1"/>
+ <File Id="libssp_0.dll" Name="libssp-0.dll" Source="$(var.Mingw_bin)/libssp-0.dll" KeyPath="no" DiskId="1"/>
+ <File Id="libwinpthread_1.dll" Name="libwinpthread-1.dll" Source="$(var.Mingw_bin)/libwinpthread-1.dll" KeyPath="no" DiskId="1"/>
+ <ServiceInstall
+ Id="ServiceInstaller"
+ Type="ownProcess"
+ Vital="yes"
+ Name="QEMU-GA"
+ DisplayName="QEMU Guest Agent"
+ Description="QEMU Guest Agent"
+ Start="auto"
+ Account="LocalSystem"
+ ErrorControl="ignore"
+ Interactive="no"
+ Arguments="-d"
+ >
+ </ServiceInstall>
+ <ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="QEMU-GA" Wait="no" />
+ </Component>
+
+ <Component Id="registry_entries" Guid="d075d109-51ca-11e3-9f8b-000c29858960">
+ <RegistryKey Root="HKLM"
+ Key="Software\$(env.QEMU_GA_MANUFACTURER)\$(env.QEMU_GA_DISTRO)\Tools\QemuGA">
+ <RegistryValue Type="string" Name="ProductID" Value="fb0a0d66-c7fb-4e2e-a16b-c4a3bfe8d13b" />
+ <RegistryValue Type="string" Name="Version" Value="$(env.QEMU_GA_VERSION)" />
+ </RegistryKey>
+ </Component>
+ </Directory>
+ </Directory>
+ </Directory>
+
+ <Property Id="cmd" Value="cmd.exe"/>
+
+ <?ifdef var.InstallVss ?>
+ <CustomAction Id="RegisterCom"
+ ExeCommand='/c "[qemu_ga_directory]qemu-ga.exe" -s vss-install'
+ Execute="deferred"
+ Property="cmd"
+ Impersonate="no"
+ Return="check"
+ >
+ </CustomAction>
+ <CustomAction Id="UnRegisterCom"
+ ExeCommand='/c "[qemu_ga_directory]qemu-ga.exe" -s vss-uninstall'
+ Execute="deferred"
+ Property="cmd"
+ Impersonate="no"
+ Return="check"
+ ></CustomAction>
+ <?endif?>
+
+ <Feature Id="QEMUFeature" Title="QEMU Guest Agent" Level="1">
+ <ComponentRef Id="qemu_ga" />
+ <ComponentRef Id="registry_entries" />
+ </Feature>
+
+ <InstallExecuteSequence>
+ <RemoveExistingProducts Before="InstallInitialize" />
+ <?ifdef var.InstallVss ?>
+ <Custom Action="RegisterCom" After="InstallServices">NOT Installed</Custom>
+ <Custom Action="UnRegisterCom" After="StopServices">Installed</Custom>
+ <?endif?>
+ </InstallExecuteSequence>
+ </Product>
+</Wix>
diff --git a/qga/main.c b/qga/main.c
index 9939a2b62..791982ef0 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -211,7 +211,7 @@ static void usage(const char *cmd)
" -V, --version print version information and exit\n"
" -d, --daemonize become a daemon\n"
#ifdef _WIN32
-" -s, --service service commands: install, uninstall\n"
+" -s, --service service commands: install, uninstall, vss-install, vss-uninstall\n"
#endif
" -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"\n"
" to list available RPCs)\n"
@@ -274,7 +274,7 @@ static void ga_log(const gchar *domain, GLogLevelFlags level,
level &= G_LOG_LEVEL_MASK;
#ifndef _WIN32
- if (domain && strcmp(domain, "syslog") == 0) {
+ if (g_strcmp0(domain, "syslog") == 0) {
syslog(LOG_INFO, "%s: %s", level_str, msg);
} else if (level & s->log_level) {
#else
@@ -578,7 +578,7 @@ static void process_event(JSONMessageParser *parser, QList *tokens)
qdict = qdict_new();
if (!err) {
g_warning("failed to parse event: unknown error");
- error_set(&err, QERR_JSON_PARSING);
+ error_setg(&err, QERR_JSON_PARSING);
} else {
g_warning("failed to parse event: %s", error_get_pretty(err));
}
@@ -598,7 +598,7 @@ static void process_event(JSONMessageParser *parser, QList *tokens)
QDECREF(qdict);
qdict = qdict_new();
g_warning("unrecognized payload format");
- error_set(&err, QERR_UNSUPPORTED);
+ error_setg(&err, QERR_UNSUPPORTED);
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
error_free(err);
}
@@ -1036,6 +1036,14 @@ int main(int argc, char **argv)
} else if (strcmp(service, "uninstall") == 0) {
ga_uninstall_vss_provider();
return ga_uninstall_service();
+ } else if (strcmp(service, "vss-install") == 0) {
+ if (ga_install_vss_provider()) {
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+ } else if (strcmp(service, "vss-uninstall") == 0) {
+ ga_uninstall_vss_provider();
+ return EXIT_SUCCESS;
} else {
printf("Unknown service command.\n");
return EXIT_FAILURE;
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 95f49e369..18e3cc37d 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -150,7 +150,7 @@
#
# Since 1.1.0
##
-{ 'type': 'GuestAgentCommandInfo',
+{ 'struct': 'GuestAgentCommandInfo',
'data': { 'name': 'str', 'enabled': 'bool', 'success-response': 'bool' } }
##
@@ -164,7 +164,7 @@
#
# Since 0.15.0
##
-{ 'type': 'GuestAgentInfo',
+{ 'struct': 'GuestAgentInfo',
'data': { 'version': 'str',
'supported_commands': ['GuestAgentCommandInfo'] } }
##
@@ -195,7 +195,7 @@
# Since: 0.15.0
##
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' },
- 'success-response': 'no' }
+ 'success-response': false }
##
# @guest-file-open:
@@ -242,7 +242,7 @@
#
# Since: 0.15.0
##
-{ 'type': 'GuestFileRead',
+{ 'struct': 'GuestFileRead',
'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
##
@@ -274,7 +274,7 @@
#
# Since: 0.15.0
##
-{ 'type': 'GuestFileWrite',
+{ 'struct': 'GuestFileWrite',
'data': { 'count': 'int', 'eof': 'bool' } }
##
@@ -309,7 +309,7 @@
#
# Since: 0.15.0
##
-{ 'type': 'GuestFileSeek',
+{ 'struct': 'GuestFileSeek',
'data': { 'position': 'int', 'eof': 'bool' } }
##
@@ -425,6 +425,30 @@
'returns': 'int' }
##
+# @GuestFilesystemTrimResult
+#
+# @path: path that was trimmed
+# @error: an error message when trim failed
+# @trimmed: bytes trimmed for this path
+# @minimum: reported effective minimum for this path
+#
+# Since: 2.4
+##
+{ 'struct': 'GuestFilesystemTrimResult',
+ 'data': {'path': 'str',
+ '*trimmed': 'int', '*minimum': 'int', '*error': 'str'} }
+
+##
+# @GuestFilesystemTrimResponse
+#
+# @paths: list of @GuestFilesystemTrimResult per path that was trimmed
+#
+# Since: 2.4
+##
+{ 'struct': 'GuestFilesystemTrimResponse',
+ 'data': {'paths': ['GuestFilesystemTrimResult']} }
+
+##
# @guest-fstrim:
#
# Discard (or "trim") blocks which are not in use by the filesystem.
@@ -437,12 +461,14 @@
# fragmented free space, although not all blocks will be discarded.
# The default value is zero, meaning "discard every free block".
#
-# Returns: Nothing.
+# Returns: A @GuestFilesystemTrimResponse which contains the
+# status of all trimmed paths. (since 2.4)
#
# Since: 1.2
##
{ 'command': 'guest-fstrim',
- 'data': { '*minimum': 'int' } }
+ 'data': { '*minimum': 'int' },
+ 'returns': 'GuestFilesystemTrimResponse' }
##
# @guest-suspend-disk
@@ -470,7 +496,7 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-disk', 'success-response': 'no' }
+{ 'command': 'guest-suspend-disk', 'success-response': false }
##
# @guest-suspend-ram
@@ -502,7 +528,7 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-ram', 'success-response': 'no' }
+{ 'command': 'guest-suspend-ram', 'success-response': false }
##
# @guest-suspend-hybrid
@@ -529,7 +555,7 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-hybrid', 'success-response': 'no' }
+{ 'command': 'guest-suspend-hybrid', 'success-response': false }
##
# @GuestIpAddressType:
@@ -556,7 +582,7 @@
#
# Since: 1.1
##
-{ 'type': 'GuestIpAddress',
+{ 'struct': 'GuestIpAddress',
'data': {'ip-address': 'str',
'ip-address-type': 'GuestIpAddressType',
'prefix': 'int'} }
@@ -572,7 +598,7 @@
#
# Since: 1.1
##
-{ 'type': 'GuestNetworkInterface',
+{ 'struct': 'GuestNetworkInterface',
'data': {'name': 'str',
'*hardware-address': 'str',
'*ip-addresses': ['GuestIpAddress'] } }
@@ -604,7 +630,7 @@
#
# Since: 1.5
##
-{ 'type': 'GuestLogicalProcessor',
+{ 'struct': 'GuestLogicalProcessor',
'data': {'logical-id': 'int',
'online': 'bool',
'*can-offline': 'bool'} }
@@ -677,12 +703,24 @@
# @uml: UML disks
# @sata: SATA disks
# @sd: SD cards
-#
-# Since: 2.2
+# @unknown: Unknown bus type
+# @ieee1394: Win IEEE 1394 bus type
+# @ssa: Win SSA bus type
+# @fibre: Win fiber channel bus type
+# @raid: Win RAID bus type
+# @iscsi: Win iScsi bus type
+# @sas: Win serial-attaches SCSI bus type
+# @mmc: Win multimedia card (MMC) bus type
+# @virtual: Win virtual bus type
+# @file-backed virtual: Win file-backed bus type
+#
+# Since: 2.2; 'Unknown' and all entries below since 2.4
##
{ 'enum': 'GuestDiskBusType',
'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata',
- 'sd' ] }
+ 'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi',
+ 'sas', 'mmc', 'virtual', 'file-backed-virtual' ] }
+
##
# @GuestPCIAddress:
@@ -694,7 +732,7 @@
#
# Since: 2.2
##
-{ 'type': 'GuestPCIAddress',
+{ 'struct': 'GuestPCIAddress',
'data': {'domain': 'int', 'bus': 'int',
'slot': 'int', 'function': 'int'} }
@@ -709,7 +747,7 @@
#
# Since: 2.2
##
-{ 'type': 'GuestDiskAddress',
+{ 'struct': 'GuestDiskAddress',
'data': {'pci-controller': 'GuestPCIAddress',
'bus-type': 'GuestDiskBusType',
'bus': 'int', 'target': 'int', 'unit': 'int'} }
@@ -725,7 +763,7 @@
#
# Since: 2.2
##
-{ 'type': 'GuestFilesystemInfo',
+{ 'struct': 'GuestFilesystemInfo',
'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
'disk': ['GuestDiskAddress']} }
@@ -782,7 +820,7 @@
#
# Since: 2.3
##
-{ 'type': 'GuestMemoryBlock',
+{ 'struct': 'GuestMemoryBlock',
'data': {'phys-index': 'uint64',
'online': 'bool',
'*can-offline': 'bool'} }
@@ -808,7 +846,7 @@
#
# An enumeration of memory block operation result.
#
-# @sucess: the operation of online/offline memory block is successful.
+# @success: the operation of online/offline memory block is successful.
# @not-found: can't find the corresponding memoryXXX directory in sysfs.
# @operation-not-supported: for some old kernels, it does not support
# online or offline memory block.
@@ -835,7 +873,7 @@
#
# Since: 2.3
##
-{ 'type': 'GuestMemoryBlockResponse',
+{ 'struct': 'GuestMemoryBlockResponse',
'data': { 'phys-index': 'uint64',
'response': 'GuestMemoryBlockResponseType',
'*error-code': 'int' }}
@@ -876,7 +914,7 @@
#
# Since: 2.3
##
-{ 'type': 'GuestMemoryBlockInfo',
+{ 'struct': 'GuestMemoryBlockInfo',
'data': {'size': 'uint64'} }
##