X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/f9dada2baabb639feb988b3a564df7a06d214e18..60eed0869d68b91eff71cc0a0facb01983726a5d:/qga/commands-win32.c diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 619dbd2bc2..98d9735389 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -14,6 +14,7 @@ #ifndef _WIN32_WINNT # define _WIN32_WINNT 0x0600 #endif + #include "qemu/osdep.h" #include #include @@ -29,10 +30,12 @@ #endif #include #include +#include -#include "qga/guest-agent-core.h" -#include "qga/vss-win32.h" -#include "qga-qmp-commands.h" +#include "guest-agent-core.h" +#include "vss-win32.h" +#include "qga-qapi-commands.h" +#include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/queue.h" #include "qemu/host-utils.h" @@ -315,7 +318,7 @@ GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, } if (!has_count) { count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0) { + } else if (count < 0 || count >= UINT32_MAX) { error_setg(errp, "value '%" PRId64 "' is invalid for argument count", count); return NULL; @@ -667,6 +670,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) char fs_name[32]; char vol_info[MAX_PATH+1]; size_t len; + uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes; GuestFilesystemInfo *fs = NULL; GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size); @@ -696,10 +700,21 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) fs_name[sizeof(fs_name) - 1] = 0; fs = g_malloc(sizeof(*fs)); fs->name = g_strdup(guid); + fs->has_total_bytes = false; + fs->has_used_bytes = false; if (len == 0) { fs->mountpoint = g_strdup("System Reserved"); } else { fs->mountpoint = g_strndup(mnt_point, len); + if (GetDiskFreeSpaceEx(fs->mountpoint, + (PULARGE_INTEGER) & i64FreeBytesToCaller, + (PULARGE_INTEGER) & i64TotalBytes, + (PULARGE_INTEGER) & i64FreeBytes)) { + fs->used_bytes = i64TotalBytes - i64FreeBytes; + fs->total_bytes = i64TotalBytes; + fs->has_total_bytes = true; + fs->has_used_bytes = true; + } } fs->type = g_strdup(fs_name); fs->disk = build_guest_disk_info(guid, errp); @@ -850,6 +865,19 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) GuestFilesystemTrimResponse *resp; HANDLE handle; WCHAR guid[MAX_PATH] = L""; + OSVERSIONINFO osvi; + BOOL win8_or_later; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + win8_or_later = (osvi.dwMajorVersion > 6 || + ((osvi.dwMajorVersion == 6) && + (osvi.dwMinorVersion >= 2))); + if (!win8_or_later) { + error_setg(errp, "fstrim is only supported for Win8+"); + return NULL; + } handle = FindFirstVolumeW(guid, ARRAYSIZE(guid)); if (handle == INVALID_HANDLE_VALUE) { @@ -1152,6 +1180,66 @@ out: } #endif +#define INTERFACE_PATH_BUF_SZ 512 + +static DWORD get_interface_index(const char *guid) +{ + ULONG index; + DWORD status; + wchar_t wbuf[INTERFACE_PATH_BUF_SZ]; + snwprintf(wbuf, INTERFACE_PATH_BUF_SZ, L"\\device\\tcpip_%s", guid); + wbuf[INTERFACE_PATH_BUF_SZ - 1] = 0; + status = GetAdapterIndex (wbuf, &index); + if (status != NO_ERROR) { + return (DWORD)~0; + } else { + return index; + } +} + +typedef NETIOAPI_API (WINAPI *GetIfEntry2Func)(PMIB_IF_ROW2 Row); + +static int guest_get_network_stats(const char *name, + GuestNetworkInterfaceStat *stats) +{ + OSVERSIONINFO os_ver; + + os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&os_ver); + if (os_ver.dwMajorVersion >= 6) { + MIB_IF_ROW2 a_mid_ifrow; + GetIfEntry2Func getifentry2_ex; + DWORD if_index = 0; + HMODULE module = GetModuleHandle("iphlpapi"); + PVOID func = GetProcAddress(module, "GetIfEntry2"); + + if (func == NULL) { + return -1; + } + + getifentry2_ex = (GetIfEntry2Func)func; + if_index = get_interface_index(name); + if (if_index == (DWORD)~0) { + return -1; + } + + memset(&a_mid_ifrow, 0, sizeof(a_mid_ifrow)); + a_mid_ifrow.InterfaceIndex = if_index; + if (NO_ERROR == getifentry2_ex(&a_mid_ifrow)) { + stats->rx_bytes = a_mid_ifrow.InOctets; + stats->rx_packets = a_mid_ifrow.InUcastPkts; + stats->rx_errs = a_mid_ifrow.InErrors; + stats->rx_dropped = a_mid_ifrow.InDiscards; + stats->tx_bytes = a_mid_ifrow.OutOctets; + stats->tx_packets = a_mid_ifrow.OutUcastPkts; + stats->tx_errs = a_mid_ifrow.OutErrors; + stats->tx_dropped = a_mid_ifrow.OutDiscards; + return 0; + } + } + return -1; +} + GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) { IP_ADAPTER_ADDRESSES *adptr_addrs, *addr; @@ -1159,6 +1247,7 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) GuestNetworkInterfaceList *head = NULL, *cur_item = NULL; GuestIpAddressList *head_addr, *cur_addr; GuestNetworkInterfaceList *info; + GuestNetworkInterfaceStat *interface_stat = NULL; GuestIpAddressList *address_item = NULL; unsigned char *mac_addr; char *addr_str; @@ -1238,6 +1327,17 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) info->value->has_ip_addresses = true; info->value->ip_addresses = head_addr; } + if (!info->value->has_statistics) { + interface_stat = g_malloc0(sizeof(*interface_stat)); + if (guest_get_network_stats(addr->AdapterName, + interface_stat) == -1) { + info->value->has_statistics = false; + g_free(interface_stat); + } else { + info->value->statistics = interface_stat; + info->value->has_statistics = true; + } + } } WSACleanup(); out: @@ -1277,8 +1377,41 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) * RTC yet: * * https://msdn.microsoft.com/en-us/library/aa908981.aspx + * + * Instead, a workaround is to use the Windows win32tm command to + * resync the time using the Windows Time service. */ - error_setg(errp, "Time argument is required on this platform"); + LPVOID msg_buffer; + DWORD ret_flags; + + HRESULT hr = system("w32tm /resync /nowait"); + + if (GetLastError() != 0) { + strerror_s((LPTSTR) & msg_buffer, 0, errno); + error_setg(errp, "system(...) failed: %s", (LPCTSTR)msg_buffer); + } else if (hr != 0) { + if (hr == HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE)) { + error_setg(errp, "Windows Time service not running on the " + "guest"); + } else { + if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + (DWORD)hr, MAKELANGID(LANG_NEUTRAL, + SUBLANG_DEFAULT), (LPTSTR) & msg_buffer, 0, + NULL)) { + error_setg(errp, "w32tm failed with error (0x%lx), couldn'" + "t retrieve error message", hr); + } else { + error_setg(errp, "w32tm failed with error (0x%lx): %s", hr, + (LPCTSTR)msg_buffer); + LocalFree(msg_buffer); + } + } + } else if (!InternetGetConnectedState(&ret_flags, 0)) { + error_setg(errp, "No internet connection on guest, sync not " + "accurate"); + } return; }