diff --git a/configure b/configure index 3063739a5e..c89b1eab9b 100755 --- a/configure +++ b/configure @@ -315,6 +315,7 @@ snappy="" bzip2="" guest_agent="" guest_agent_with_vss="no" +guest_agent_ntddscsi="no" guest_agent_msi="" vss_win32_sdk="" win_sdk="no" @@ -732,7 +733,7 @@ if test "$mingw32" = "yes" ; then sysconfdir="\${prefix}" local_statedir= confsuffix="" - libs_qga="-lws2_32 -lwinmm -lpowrprof $libs_qga" + libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi $libs_qga" fi werror="" @@ -3819,6 +3820,26 @@ if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$guest_agent_with_vss" fi fi +########################################## +# check if mingw environment provides a recent ntddscsi.h +if test "$mingw32" = "yes" -a "$guest_agent" != "no"; then + cat > $TMPC << EOF +#include +#include +int main(void) { +#if !defined(IOCTL_SCSI_GET_ADDRESS) +#error Missing required ioctl definitions +#endif + SCSI_ADDRESS addr = { .Lun = 0, .TargetId = 0, .PathId = 0 }; + return addr.Lun; +} +EOF + if compile_prog "" "" ; then + guest_agent_ntddscsi=yes + libs_qga="-lsetupapi $libs_qga" + fi +fi + ########################################## # Guest agent Window MSI package @@ -4489,6 +4510,7 @@ echo "libiscsi support $libiscsi" echo "libnfs support $libnfs" echo "build guest agent $guest_agent" echo "QGA VSS support $guest_agent_with_vss" +echo "QGA w32 disk info $guest_agent_ntddscsi" echo "seccomp support $seccomp" echo "coroutine backend $coroutine" echo "coroutine pool $coroutine_pool" @@ -4566,6 +4588,9 @@ if test "$mingw32" = "yes" ; then echo "CONFIG_QGA_VSS=y" >> $config_host_mak echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak fi + if test "$guest_agent_ntddscsi" = "yes" ; then + echo "CONFIG_QGA_NTDDDISK=y" >> $config_host_mak + fi if test "$guest_agent_msi" != "no"; then echo "QEMU_GA_MSI_ENABLED=yes" >> $config_host_mak echo "QEMU_GA_MSI_MINGW_DLL_PATH=${QEMU_GA_MSI_MINGW_DLL_PATH}" >> $config_host_mak diff --git a/qga/commands-posix.c b/qga/commands-posix.c index befd00b00d..675f4b4c66 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) { @@ -1325,18 +1332,18 @@ 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; int fd; Error *local_err = NULL; - struct fstrim_range r = { - .start = 0, - .len = -1, - .minlen = has_minimum ? minimum : 0, - }; + struct fstrim_range r; slog("guest-fstrim called"); @@ -1344,36 +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 */ @@ -2402,9 +2432,11 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) #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_setg(errp, QERR_UNSUPPORTED); + return NULL; } #endif diff --git a/qga/commands-win32.c b/qga/commands-win32.c index fbddc8b1b2..a7822d5ff7 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -16,11 +16,22 @@ #include #include #include +#include +#include +#include +#include +#ifdef CONFIG_QGA_NTDDSCSI +#include +#include +#include +#include +#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 @@ -382,10 +393,305 @@ static void guest_file_init(void) QTAILQ_INIT(&guest_file_state.filehandles); } +#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) +{ + 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) { - error_setg(errp, QERR_UNSUPPORTED); - return NULL; + 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; } /* @@ -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_setg(errp, QERR_UNSUPPORTED); + return NULL; } typedef enum { @@ -589,10 +897,218 @@ void qmp_guest_suspend_hybrid(Error **errp) error_setg(errp, QERR_UNSUPPORTED); } +static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp) +{ + 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) { - error_setg(errp, QERR_UNSUPPORTED); - return NULL; + 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) @@ -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; diff --git a/qga/main.c b/qga/main.c index 23cde0104a..791982ef01 100644 --- a/qga/main.c +++ b/qga/main.c @@ -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 diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index b446dc729d..8a9b818d18 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -424,6 +424,30 @@ { 'command': 'guest-fsfreeze-thaw', '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: # @@ -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 @@ -677,12 +703,24 @@ # @uml: UML disks # @sata: SATA disks # @sd: SD cards +# @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 ## { '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: