#ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0600
#endif
+
#include "qemu/osdep.h"
#include <wtypes.h>
#include <powrprof.h>
#endif
#include <lm.h>
#include <wtsapi32.h>
+#include <wininet.h>
-#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"
}
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;
} else {
error_setg_win32(errp, GetLastError(),
"failed to get device name");
- goto out;
+ goto free_dev_info;
}
}
pci->bus = bus;
break;
}
+
+free_dev_info:
+ SetupDiDestroyDeviceInfoList(dev_info);
out:
g_free(buffer);
g_free(name);
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);
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);
}
#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;
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;
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:
* 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;
}
return NULL;
#endif
}
+
+typedef struct _ga_matrix_lookup_t {
+ int major;
+ int minor;
+ char const *version;
+ char const *version_id;
+} ga_matrix_lookup_t;
+
+static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = {
+ {
+ /* Desktop editions */
+ { 5, 0, "Microsoft Windows 2000", "2000"},
+ { 5, 1, "Microsoft Windows XP", "xp"},
+ { 6, 0, "Microsoft Windows Vista", "vista"},
+ { 6, 1, "Microsoft Windows 7" "7"},
+ { 6, 2, "Microsoft Windows 8", "8"},
+ { 6, 3, "Microsoft Windows 8.1", "8.1"},
+ {10, 0, "Microsoft Windows 10", "10"},
+ { 0, 0, 0}
+ },{
+ /* Server editions */
+ { 5, 2, "Microsoft Windows Server 2003", "2003"},
+ { 6, 0, "Microsoft Windows Server 2008", "2008"},
+ { 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"},
+ { 6, 2, "Microsoft Windows Server 2012", "2012"},
+ { 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
+ {10, 0, "Microsoft Windows Server 2016", "2016"},
+ { 0, 0, 0},
+ { 0, 0, 0}
+ }
+};
+
+static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
+{
+ typedef NTSTATUS(WINAPI * rtl_get_version_t)(
+ RTL_OSVERSIONINFOEXW *os_version_info_ex);
+
+ info->dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+
+ HMODULE module = GetModuleHandle("ntdll");
+ PVOID fun = GetProcAddress(module, "RtlGetVersion");
+ if (fun == NULL) {
+ error_setg(errp, QERR_QGA_COMMAND_FAILED,
+ "Failed to get address of RtlGetVersion");
+ return;
+ }
+
+ rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun;
+ rtl_get_version(info);
+ return;
+}
+
+static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id)
+{
+ DWORD major = os_version->dwMajorVersion;
+ DWORD minor = os_version->dwMinorVersion;
+ int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
+ ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
+ while (table->version != NULL) {
+ if (major == table->major && minor == table->minor) {
+ if (id) {
+ return g_strdup(table->version_id);
+ } else {
+ return g_strdup(table->version);
+ }
+ }
+ ++table;
+ }
+ slog("failed to lookup Windows version: major=%lu, minor=%lu",
+ major, minor);
+ return g_strdup("N/A");
+}
+
+static char *ga_get_win_product_name(Error **errp)
+{
+ HKEY key = NULL;
+ DWORD size = 128;
+ char *result = g_malloc0(size);
+ LONG err = ERROR_SUCCESS;
+
+ err = RegOpenKeyA(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ &key);
+ if (err != ERROR_SUCCESS) {
+ error_setg_win32(errp, err, "failed to open registry key");
+ goto fail;
+ }
+
+ err = RegQueryValueExA(key, "ProductName", NULL, NULL,
+ (LPBYTE)result, &size);
+ if (err == ERROR_MORE_DATA) {
+ slog("ProductName longer than expected (%lu bytes), retrying",
+ size);
+ g_free(result);
+ result = NULL;
+ if (size > 0) {
+ result = g_malloc0(size);
+ err = RegQueryValueExA(key, "ProductName", NULL, NULL,
+ (LPBYTE)result, &size);
+ }
+ }
+ if (err != ERROR_SUCCESS) {
+ error_setg_win32(errp, err, "failed to retrive ProductName");
+ goto fail;
+ }
+
+ return result;
+
+fail:
+ g_free(result);
+ return NULL;
+}
+
+static char *ga_get_current_arch(void)
+{
+ SYSTEM_INFO info;
+ GetNativeSystemInfo(&info);
+ char *result = NULL;
+ switch (info.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ result = g_strdup("x86_64");
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM:
+ result = g_strdup("arm");
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ result = g_strdup("ia64");
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ result = g_strdup("x86");
+ break;
+ case PROCESSOR_ARCHITECTURE_UNKNOWN:
+ default:
+ slog("unknown processor architecture 0x%0x",
+ info.wProcessorArchitecture);
+ result = g_strdup("unknown");
+ break;
+ }
+ return result;
+}
+
+GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
+{
+ Error *local_err = NULL;
+ OSVERSIONINFOEXW os_version = {0};
+ bool server;
+ char *product_name;
+ GuestOSInfo *info;
+
+ ga_get_win_version(&os_version, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ server = os_version.wProductType != VER_NT_WORKSTATION;
+ product_name = ga_get_win_product_name(&local_err);
+ if (product_name == NULL) {
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ info = g_new0(GuestOSInfo, 1);
+
+ info->has_kernel_version = true;
+ info->kernel_version = g_strdup_printf("%lu.%lu",
+ os_version.dwMajorVersion,
+ os_version.dwMinorVersion);
+ info->has_kernel_release = true;
+ info->kernel_release = g_strdup_printf("%lu",
+ os_version.dwBuildNumber);
+ info->has_machine = true;
+ info->machine = ga_get_current_arch();
+
+ info->has_id = true;
+ info->id = g_strdup("mswindows");
+ info->has_name = true;
+ info->name = g_strdup("Microsoft Windows");
+ info->has_pretty_name = true;
+ info->pretty_name = product_name;
+ info->has_version = true;
+ info->version = ga_get_win_name(&os_version, false);
+ info->has_version_id = true;
+ info->version_id = ga_get_win_name(&os_version, true);
+ info->has_variant = true;
+ info->variant = g_strdup(server ? "server" : "client");
+ info->has_variant_id = true;
+ info->variant_id = g_strdup(server ? "server" : "client");
+
+ return info;
+}