All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 3/3] qga support process list, netstat and file stat/delete
@ 2015-03-25 12:25 itamar.tal4
  2015-03-26 19:24 ` Eric Blake
  0 siblings, 1 reply; 2+ messages in thread
From: itamar.tal4 @ 2015-03-25 12:25 UTC (permalink / raw)
  To: qemu-devel; +Cc: ori, ariel, mdroth, pavel, Itamar Tal

From: Itamar Tal <itamar@guardicore.com>

this patch add support for some more functionality in the qemu-guest-agent,
both for windows and linux. Main added features are:
- interface listing in Windows
- Process list in Windows
- network connections enumeration in Windows
- file delete for both Windows and Posix
- file stat() for both Windows and Posix
- system uptime for both Windows and Posix

Itamar,
Guardicore
itamar@guardicore.com

---
 qga/commands-posix.c    |  49 +++++
 qga/commands-win32.c    | 552 +++++++++++++++++++++++++++++++++++++++++++++++-
 qga/commands.c          |  25 +++
 qga/guest-agent-core.h  |   2 +
 qga/main.c              |   9 +
 qga/qapi-schema.json    | 172 +++++++++++++++
 qga/win32-definitions.h | 115 ++++++++++
 qga/win32-iptypes.h     | 411 +++++++++++++++++++++++++++++++++++
 8 files changed, 1332 insertions(+), 3 deletions(-)
 create mode 100644 qga/win32-definitions.h
 create mode 100644 qga/win32-iptypes.h

diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index ba8de62..15e39fc 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -23,6 +23,7 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <inttypes.h>
+#include <sys/sysinfo.h>
 #include "qga/guest-agent-core.h"
 #include "qga-qmp-commands.h"
 #include "qapi/qmp/qerror.h"
@@ -2456,3 +2457,51 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
 #endif
     ga_command_state_add(cs, guest_file_init, NULL);
 }
+
+uint64_t qmp_guest_uptime(Error **errp)
+{
+	struct sysinfo sys_info;
+	if (sysinfo(&sys_info))
+	{
+		error_setg(errp, "Failed reading system info");
+	}
+
+	return sys_info.uptime;
+}
+
+GuestFileStat *qmp_guest_file_stat(const char *path, Error **errp)
+{
+	GuestFileStat * file_stat = g_malloc0(sizeof(GuestFileStat));
+	struct stat file_os_stat;
+
+	if (stat(path, &file_os_stat))
+	{
+		error_setg(errp, "Failed to get file stat() for file '%s'", path);
+		return NULL;
+	}
+
+	file_stat->mode = file_os_stat.st_mode;
+	file_stat->inode = file_os_stat.st_ino;
+	file_stat->dev = file_os_stat.st_dev;
+	file_stat->nlink = file_os_stat.st_nlink;
+	file_stat->uid = file_os_stat.st_uid;
+	file_stat->gid = file_os_stat.st_gid;
+	file_stat->size = file_os_stat.st_size;
+	file_stat->atime = file_os_stat.st_atime;
+	file_stat->mtime = file_os_stat.st_mtime;
+	file_stat->ctime = file_os_stat.st_ctime;
+
+	return file_stat;
+}
+
+GuestProcessInfoList *qmp_guest_get_process_list(Error **errp)
+{
+	error_set(errp, QERR_UNSUPPORTED);
+	return NULL;
+}
+
+GuestActiveConnectionList *qmp_guest_get_active_connections(Error **errp)
+{
+	error_set(errp, QERR_UNSUPPORTED);
+	return NULL;
+}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 3ef0549..55d8f2e 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -19,6 +19,7 @@
 #include "qga/guest-agent-core.h"
 #include "qga/vss-win32.h"
 #include "qga-qmp-commands.h"
+#include "qga/win32-definitions.h"
 #include "qapi/qmp/qerror.h"
 #include "qemu/queue.h"
 
@@ -26,6 +27,41 @@
 #define SHTDN_REASON_FLAG_PLANNED 0x80000000
 #endif
 
+ULONG (__stdcall * GetAdaptersAddresses)(	uint32_t Family,
+											uint32_t Flags,
+											void * Reserved,
+											IP_ADAPTER_ADDRESSES * AdapterAddresses,
+											uint32_t * SizePointer) = NULL;
+
+DWORD (__stdcall * GetExtendedTcpTable)(void * pTcpTable,
+										DWORD * pdwSize,
+										BOOL bOrder,
+										ULONG ulAf,
+										TCP_TABLE_CLASS TableClass,
+										ULONG Reserved) = NULL;
+
+DWORD (__stdcall * GetExtendedUdpTable)(	void * pUdpTable,
+										DWORD * pdwSize,
+										BOOL bOrder,
+										ULONG ulAf,
+										UDP_TABLE_CLASS TableClass,
+										ULONG Reserved) = NULL;
+
+NTSTATUS (__stdcall * RtlUnicodeStringToAnsiString)(ANSI_STRING * DestinationString,
+													UNICODE_STRING * SourceString,
+													BOOLEAN AllocateDestinationString) = NULL;
+
+NTSTATUS (__stdcall * NtQueryInformationProcess)(	HANDLE ProcessHandle,
+													PROCESSINFOCLASS ProcessInformationClass,
+													void * ProcessInformation,
+													uint32_t ProcessInformationLength,
+													uint32_t * ReturnLength) = NULL;
+
+NTSTATUS (__stdcall * NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass,
+												void * SystemInformation,
+												uint32_t SystemInformationLength,
+												uint32_t * ReturnLength) = NULL;
+
 /* multiple of 100 nanoseconds elapsed between windows baseline
  *    (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */
 #define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \
@@ -591,8 +627,132 @@ void qmp_guest_suspend_hybrid(Error **errp)
 
 GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
 {
-    error_set(errp, QERR_UNSUPPORTED);
-    return NULL;
+	GuestNetworkInterfaceList * iface_list = NULL;
+	IP_ADAPTER_ADDRESSES * ip_adapter_addresses = NULL;
+	IP_ADAPTER_ADDRESSES * current_adapter = NULL;
+	uint32_t adapter_buff_size = 15 * 1024;
+	ULONG ret = 0;
+
+	for (;;)
+	{
+		ip_adapter_addresses = g_malloc0(adapter_buff_size);
+		if (NULL == ip_adapter_addresses)
+		{
+			error_setg(errp, "Failed allocating adapters info buffer");
+			return NULL;
+		}
+
+		ret = GetAdaptersAddresses(	AF_UNSPEC,
+									GAA_FLAG_SKIP_ANYCAST |
+									GAA_FLAG_SKIP_MULTICAST |
+									GAA_FLAG_SKIP_DNS_SERVER |
+									GAA_FLAG_INCLUDE_PREFIX,
+									NULL,
+									ip_adapter_addresses,
+									&adapter_buff_size);
+		if (NO_ERROR != ret)
+		{
+			(void)g_free(ip_adapter_addresses);
+			ip_adapter_addresses = NULL;
+
+			if (ERROR_BUFFER_OVERFLOW == ret)
+			{
+				adapter_buff_size <<= 1;
+				continue;
+			}
+
+			error_setg(errp, "Error quering network adapters info");
+			return NULL;
+		}
+
+		break;
+	}
+
+	/* add all adapters to the list */
+	for (	current_adapter = ip_adapter_addresses;
+			NULL != current_adapter;
+			current_adapter = current_adapter->Next)
+	{
+		IP_ADAPTER_UNICAST_ADDRESS * ip_address = NULL;
+		UNICODE_STRING adapter_name_us;
+		ANSI_STRING adapter_name;
+		GuestNetworkInterfaceList * new_item = g_malloc0(sizeof(GuestNetworkInterfaceList));
+		new_item->value = g_malloc0(sizeof(GuestNetworkInterface));
+		new_item->next = iface_list;
+		iface_list = new_item;
+
+		adapter_name_us.Buffer = current_adapter->FriendlyName;
+		adapter_name_us.Length = wcslen(current_adapter->FriendlyName) * sizeof(wchar_t);
+		adapter_name_us.MaximumLength = adapter_name_us.Length + sizeof(wchar_t);
+		adapter_name.MaximumLength = adapter_name_us.MaximumLength;
+		adapter_name.Length = 0;
+		adapter_name.Buffer = g_malloc0(adapter_name_us.MaximumLength);
+
+		/* convert the adapter name to ansi string */
+		(void)RtlUnicodeStringToAnsiString(	&adapter_name,
+											&adapter_name_us,
+											FALSE);
+		new_item->value->name = adapter_name.Buffer;
+
+		/* copy physical address */
+		if (current_adapter->PhysicalAddressLength != 0)
+		{
+			int i = 0;
+
+			new_item->value->hardware_address = g_malloc0(current_adapter->PhysicalAddressLength * 3 + 1);
+
+			for (i = 0; i < current_adapter->PhysicalAddressLength; ++i)
+			{
+				sprintf(&new_item->value->hardware_address[i * 3],
+						"%02x:", current_adapter->PhysicalAddress[i]);
+			}
+
+			new_item->value->hardware_address[i * 3 - 1] = '\0';
+			new_item->value->has_hardware_address = true;
+		}
+
+		/* build IP list */
+		for (	ip_address = current_adapter->FirstUnicastAddress;
+				NULL != ip_address;
+				ip_address = ip_address->Next)
+		{
+			GuestIpAddressList * new_ip = NULL;
+
+			/* skip unknown addresses */
+			if ((AF_INET != ip_address->Address.lpSockaddr->sa_family) &&
+				(AF_INET6 != ip_address->Address.lpSockaddr->sa_family))
+			{
+				continue;
+			}
+
+			/* allocate new item in the list */
+			new_ip = g_malloc0(sizeof(GuestIpAddressList));
+			new_ip->value = g_malloc0(sizeof(GuestIpAddress));
+			new_item->value->has_ip_addresses = true;
+
+			/* push the new IP at the head of the list */
+			new_ip->next = new_item->value->ip_addresses;
+			new_item->value->ip_addresses = new_ip;
+
+
+			/* convert IP info by family */
+			if (AF_INET == ip_address->Address.lpSockaddr->sa_family)
+			{
+				SOCKADDR_IN * in_addr = (SOCKADDR_IN *)ip_address->Address.lpSockaddr;
+				new_ip->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
+				new_ip->value->ip_address = g_strdup(inet_ntoa(in_addr->sin_addr));
+			}
+			else if (AF_INET6 == ip_address->Address.lpSockaddr->sa_family)
+			{
+				new_ip->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
+				new_ip->value->ip_address = g_strdup("IPv6 not supported");
+			}
+		}
+	}
+
+	(void)g_free(ip_adapter_addresses);
+
+    return iface_list;
 }
 
 int64_t qmp_guest_get_time(Error **errp)
@@ -703,11 +863,73 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
     return NULL;
 }
 
+int ga_win_commands_init(void)
+{
+	HMODULE ntdll_module = GetModuleHandle("ntdll.dll");
+	HMODULE iphlpapi_module = LoadLibrary("iphlpapi.dll");
+	WSADATA wsa_data = {0};
+
+	if (WSAStartup(MAKEWORD(2,2), &wsa_data))
+	{
+		g_critical("failed to initialize WSA engine");
+		goto error;
+	}
+
+	if ((NULL == ntdll_module) ||
+		(NULL == iphlpapi_module))
+	{
+		goto error;
+	}
+
+	NtQuerySystemInformation = (void *)GetProcAddress(ntdll_module, "NtQuerySystemInformation");
+	if (NULL == NtQuerySystemInformation)
+	{
+		goto error;
+	}
+
+	RtlUnicodeStringToAnsiString = (void *)GetProcAddress(ntdll_module, "RtlUnicodeStringToAnsiString");
+	if (NULL == RtlUnicodeStringToAnsiString)
+	{
+		goto error;
+	}
+
+	NtQueryInformationProcess = (void *)GetProcAddress(ntdll_module, "NtQueryInformationProcess");
+	if (NULL == NtQueryInformationProcess)
+	{
+		goto error;
+	}
+
+	GetAdaptersAddresses = (void *)GetProcAddress(iphlpapi_module, "GetAdaptersAddresses");
+	if (NULL == GetAdaptersAddresses)
+	{
+		goto error;
+	}
+
+	GetExtendedTcpTable = (void *)GetProcAddress(iphlpapi_module, "GetExtendedTcpTable");
+	if (NULL == GetExtendedTcpTable)
+	{
+		goto error;
+	}
+
+	GetExtendedUdpTable = (void *)GetProcAddress(iphlpapi_module, "GetExtendedUdpTable");
+	if (NULL == GetExtendedUdpTable)
+	{
+		goto error;
+	}
+
+	return 1;
+
+error:
+	(void)FreeLibrary(iphlpapi_module);
+
+	return 0;
+}
+
 /* add unsupported commands to the blacklist */
 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",
@@ -742,3 +964,327 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
     }
     ga_command_state_add(cs, guest_file_init, NULL);
 }
+
+uint64_t qmp_guest_uptime(Error **errp)
+{
+	uint64_t uptime_milli = GetTickCount();
+	return uptime_milli / 1000;
+}
+
+GuestFileStat *qmp_guest_file_stat(const char *path, Error **errp)
+{
+	GuestFileStat * file_stat = g_malloc0(sizeof(GuestFileStat));
+	struct _stat file_os_stat = {0};
+
+	if (_stat(path, &file_os_stat))
+	{
+		error_setg(errp, "Failed to get file stat() for file '%s': %d", path, (int)GetLastError());
+		return NULL;
+	}
+
+	file_stat->mode = file_os_stat.st_mode;
+	file_stat->inode = file_os_stat.st_ino;
+	file_stat->dev = file_os_stat.st_dev;
+	file_stat->nlink = file_os_stat.st_nlink;
+	file_stat->uid = file_os_stat.st_uid;
+	file_stat->gid = file_os_stat.st_gid;
+	file_stat->size = file_os_stat.st_size;
+	file_stat->atime = file_os_stat.st_atime;
+	file_stat->mtime = file_os_stat.st_mtime;
+	file_stat->ctime = file_os_stat.st_ctime;
+
+	return file_stat;
+}
+
+GuestProcessInfoList *qmp_guest_get_process_list(Error **errp)
+{
+	uint32_t bytes_needed = sizeof(SYSTEM_PROCESS_INFORMATION) * 20;
+	SYSTEM_PROCESS_INFORMATION * process_list = NULL;
+	SYSTEM_PROCESS_INFORMATION * process_list_iter = NULL;
+	GuestProcessInfoList * process_list_head = NULL;
+	GuestProcessInfoList * process_list_last = NULL;
+	NTSTATUS status = STATUS_SUCCESS;
+
+	/* find the minimal buffer for the process list */
+	for (;;)
+	{
+		process_list = g_malloc0(bytes_needed);
+		if (NULL == process_list)
+		{
+			error_setg(errp, "Failed allocating space for process list (%d bytes)", bytes_needed);
+			return NULL;
+		}
+
+		/* query the process list (if enough bytes are given) */
+		status = NtQuerySystemInformation(	SystemProcessInformation,
+											process_list,
+											bytes_needed,
+											&bytes_needed);
+		if (STATUS_SUCCESS != status)
+		{
+			(void)g_free(process_list);
+
+			if (STATUS_INFO_LENGTH_MISMATCH == status)
+			{
+				bytes_needed <<= 1;
+				continue;
+			}
+
+			error_setg(errp, "Failed quering process list (status %08X)", (int)status);
+			return NULL;
+		}
+
+		break;
+	}
+
+	/* iterate the process list and build the JSON reply */
+	for (process_list_iter = process_list;;)
+	{
+		GuestProcessInfoList * list_item = g_malloc0(sizeof(GuestProcessInfoList));
+		ANSI_STRING process_name;
+		HANDLE process_handle = NULL;
+		UNICODE_STRING * process_image_path = NULL;
+		PROCESS_BASIC_INFORMATION process_basic_info = {0};
+
+		if (NULL == process_list_head)
+		{
+			process_list_head = list_item;
+		}
+		if (NULL != process_list_last)
+		{
+			process_list_last->next = list_item;
+		}
+		process_list_last = list_item;
+
+		list_item->next = NULL;
+		list_item->value = g_malloc0(sizeof(GuestProcessInfo));
+		list_item->value->process_id = (int)process_list_iter->UniqueProcessId;
+		list_item->value->session_id = (int)process_list_iter->SessionId;
+
+		process_name.MaximumLength = process_list_iter->ImageName.MaximumLength;
+		process_name.Length = 0;
+		process_name.Buffer = g_malloc0(process_name.MaximumLength);
+
+		/* convert the image name to ansi string */
+		(void)RtlUnicodeStringToAnsiString(	&process_name,
+											&process_list_iter->ImageName,
+											FALSE);
+		list_item->value->process_name = process_name.Buffer;
+
+		if (!process_list_iter->NextEntryOffset)
+		{
+			break;
+		}
+
+		process_handle = OpenProcess(	PROCESS_QUERY_INFORMATION,
+										FALSE,
+										list_item->value->process_id);
+		if (NULL != process_handle)
+		{
+			status = NtQueryInformationProcess(	process_handle,
+												ProcessBasicInformation,
+												&process_basic_info,
+												sizeof(PROCESS_BASIC_INFORMATION),
+												NULL);
+			if (STATUS_SUCCESS == status)
+			{
+				list_item->value->parent_id = (int)process_basic_info.InheritedFromUniqueProcessId;
+			}
+
+			process_image_path = g_malloc0(sizeof(UNICODE_STRING) + 256 * sizeof(wchar_t));
+
+			status = NtQueryInformationProcess(	process_handle,
+												ProcessImageFileName,
+												process_image_path,
+												sizeof(UNICODE_STRING) + 256 * sizeof(wchar_t),
+												NULL);
+			if (STATUS_SUCCESS == status)
+			{
+				process_name.MaximumLength = process_image_path->MaximumLength;
+				process_name.Length = 0;
+				process_name.Buffer = g_malloc0(process_name.MaximumLength);
+
+				/* convert the image name to ansi string */
+				(void)RtlUnicodeStringToAnsiString(	&process_name,
+													process_image_path,
+													FALSE);
+				list_item->value->image_path = process_name.Buffer;
+			}
+
+			(void)g_free(process_image_path);
+
+			(void)CloseHandle(process_handle);
+		}
+
+		process_list_iter = (void *)((uint32_t)process_list_iter + process_list_iter->NextEntryOffset);
+	}
+
+	(void)g_free(process_list);
+
+	return process_list_head;
+}
+
+GuestActiveConnectionList *qmp_guest_get_active_connections(Error **errp)
+{
+	MIB_UDPTABLE_OWNER_MODULE * udp_table = NULL;
+	MIB_TCPTABLE_OWNER_MODULE * tcp_table = NULL;
+	DWORD bytes_needed = sizeof(MIB_UDPTABLE_OWNER_MODULE) * 30;
+	GuestActiveConnectionList * connections = NULL;
+	int entry_index = 0;
+	DWORD ret;
+
+	/* get the UDP table */
+	for (;;)
+	{
+		udp_table = g_malloc0(bytes_needed);
+		if (NULL == udp_table)
+		{
+			error_setg(errp, "Failed allocating active UDP connections table");
+			return NULL;
+		}
+
+		ret = GetExtendedUdpTable(	udp_table,
+									&bytes_needed,
+									TRUE,
+									AF_INET,
+									UDP_TABLE_OWNER_MODULE,
+									0);
+		if (NO_ERROR != ret)
+		{
+			(void)g_free(tcp_table);
+			udp_table = NULL;
+
+			if (ERROR_INSUFFICIENT_BUFFER == ret)
+			{
+				bytes_needed <<= 1;
+				continue;
+			}
+		}
+
+		break;
+	}
+
+	/* add the UDP connections to the list backward */
+	for (	entry_index = udp_table->dwNumEntries - 1;
+			entry_index >= 0;
+			--entry_index)
+	{
+		/* allocate new active connection item */
+		GuestActiveConnectionList * new_item = g_malloc0(sizeof(GuestActiveConnectionList));
+		new_item->value = g_malloc0(sizeof(GuestActiveConnection));
+
+		/* push the connection to the head of the list */
+		new_item->next = connections;
+		connections = new_item;
+
+		new_item->value->source_addr = g_strdup(inet_ntoa(*(struct in_addr *)&udp_table->table[entry_index].dwLocalAddr));
+		new_item->value->source_port = htons(udp_table->table[entry_index].dwLocalPort);
+		new_item->value->owner_process_id = udp_table->table[entry_index].dwOwningPid;
+		new_item->value->if_family = GUEST_IP_ADDRESS_TYPE_IPV4;
+		new_item->value->protocol = GUEST_IP_PROTOCOL_UDP;
+		new_item->value->start_time = udp_table->table[entry_index].liCreateTimestamp.QuadPart;
+	}
+
+	(void)g_free(udp_table);
+	udp_table = NULL;
+
+	bytes_needed = sizeof(MIB_TCPROW_OWNER_PID) * 30;
+
+	/* get the TCP table */
+	for (;;)
+	{
+		tcp_table = g_malloc0(bytes_needed);
+		if (NULL == tcp_table)
+		{
+			error_setg(errp, "Failed allocating active connections table");
+			return NULL;
+		}
+
+		ret = GetExtendedTcpTable(	tcp_table,
+									&bytes_needed,
+									TRUE,
+									AF_INET,
+									TCP_TABLE_OWNER_MODULE_ALL,
+									0);
+		if (NO_ERROR != ret)
+		{
+			(void)g_free(tcp_table);
+			tcp_table = NULL;
+
+			if (ERROR_INSUFFICIENT_BUFFER == ret)
+			{
+				bytes_needed <<= 1;
+				continue;
+			}
+		}
+
+		break;
+	}
+
+	/* add the TCP connections to the list backward */
+	for (	entry_index = tcp_table->dwNumEntries - 1;
+			entry_index >= 0;
+			--entry_index)
+	{
+		/* allocate new active connection item */
+		GuestActiveConnectionList * new_item = g_malloc0(sizeof(GuestActiveConnectionList));
+		new_item->value = g_malloc0(sizeof(GuestActiveConnection));
+
+		/* push the connection to the head of the list */
+		new_item->next = connections;
+		connections = new_item;
+
+		new_item->value->source_addr = g_strdup(inet_ntoa(*(struct in_addr *)&tcp_table->table[entry_index].dwLocalAddr));
+		new_item->value->source_port = htons(tcp_table->table[entry_index].dwLocalPort);
+		new_item->value->dest_addr = g_strdup(inet_ntoa(*(struct in_addr *)&tcp_table->table[entry_index].dwRemoteAddr));
+		new_item->value->dest_port = htons(tcp_table->table[entry_index].dwRemotePort);
+		new_item->value->owner_process_id = tcp_table->table[entry_index].dwOwningPid;
+		new_item->value->if_family = GUEST_IP_ADDRESS_TYPE_IPV4;
+		new_item->value->protocol = GUEST_IP_PROTOCOL_TCP;
+		new_item->value->start_time = tcp_table->table[entry_index].liCreateTimestamp.QuadPart;
+
+		switch (tcp_table->table[entry_index].dwState)
+		{
+		case MIB_TCP_STATE_CLOSED:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_CLOSED;
+			break;
+		case MIB_TCP_STATE_LISTEN:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_LISTEN;
+			break;
+		case MIB_TCP_STATE_SYN_SENT:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_SYN_SENT;
+			break;
+		case MIB_TCP_STATE_SYN_RCVD:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_SYN_RCVD;
+			break;
+		case MIB_TCP_STATE_ESTAB:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_ESTABLISHED;
+			break;
+		case MIB_TCP_STATE_FIN_WAIT1:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_FIN_WAIT1;
+			break;
+		case MIB_TCP_STATE_FIN_WAIT2:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_FIN_WAIT2;
+			break;
+		case MIB_TCP_STATE_CLOSE_WAIT:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_CLOSE_WAIT;
+			break;
+		case MIB_TCP_STATE_CLOSING:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_CLOSING;
+			break;
+		case MIB_TCP_STATE_LAST_ACK:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_LAST_ACK;
+			break;
+		case MIB_TCP_STATE_TIME_WAIT:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_TIME_WAIT;
+			break;
+		case MIB_TCP_STATE_DELETE_TCB:
+			new_item->value->state = GUEST_TCP_PROTOCOL_STATE_DELETE_TCB;
+			break;
+		}
+	}
+
+	(void)g_free(tcp_table);
+
+	return connections;
+}
diff --git a/qga/commands.c b/qga/commands.c
index 7834967..2fb364c 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -70,3 +70,28 @@ struct GuestAgentInfo *qmp_guest_info(Error **errp)
     qmp_for_each_command(qmp_command_info, info);
     return info;
 }
+
+void qmp_guest_file_delete(const char *path, Error **errp)
+{
+	if (unlink(path))
+	{
+		error_setg(errp, "Error deleting file '%s'", path);
+	}
+}
+
+char *qmp_guest_get_hostname(Error **errp)
+{
+	char hostname[64];
+
+	if (gethostname(hostname, 64))
+	{
+#ifdef _WIN32
+		error_setg(errp, "Error getting hostname (%d)", WSAGetLastError());
+#else
+		error_setg(errp, "Error getting hostname");
+#endif
+		return NULL;
+	}
+
+	return g_strdup(hostname);
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index e92c6ab..89d4165 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -40,4 +40,6 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp);
 
 #ifndef _WIN32
 void reopen_fd_to_null(int fd);
+#else
+int ga_win_commands_init(void);
 #endif
diff --git a/qga/main.c b/qga/main.c
index 9939a2b..3992aac 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1163,6 +1163,15 @@ int main(int argc, char **argv)
         g_critical("failed to register signal handlers");
         goto out_bad;
     }
+#else
+    {
+
+    	if (!ga_win_commands_init())
+    	{
+    		g_critical("failed initializing commands module");
+    		goto out_bad;
+    	}
+    }
 #endif
 
     s->main_loop = g_main_loop_new(NULL, false);
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 95f49e3..1e38f98 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -891,3 +891,175 @@
 ##
 { 'command': 'guest-get-memory-block-info',
   'returns': 'GuestMemoryBlockInfo' }
+
+##
+# @GuestFileStat
+#
+# @st_mode: file access permissions mode
+# @st_ino: file inode id
+# @st_dev: file device node
+# @st_nlink: number of links pointing to the file
+# @st_uid: file user id
+# @st_gid: file group id
+# @st_atime: file access time
+# @st_mtime: file modification time
+# @st_ctime: file creation time
+# @st_size: file size in bytes
+#
+# Since: 2.3
+##
+{ 'type': 'GuestFileStat',
+  'data': {'mode': 'int', 'inode': 'int', 'dev': 'int',
+  			'nlink': 'int', 'uid': 'int', 'gid': 'int',
+  			'size': 'uint64', 'atime': 'int', 'mtime': 'int',
+  			'ctime': 'int'
+           }}
+
+##
+# @guest-file-stat:
+#
+# Get the stat() for a file in the guest's operating system
+#
+# Returns: hostname string.
+#
+# Since 2.3
+##
+{ 'command': 'guest-file-stat',
+  'data': { 'path': 'str' },
+  'returns': 'GuestFileStat' }
+  
+##
+# @guest-file-delete:
+#
+# Delete a file in the guest's operating system
+#
+# Returns: 
+#
+# Since 2.3
+##
+{ 'command': 'guest-file-delete',
+  'data': { 'path': 'str' }}
+
+##
+# @guest-get-hostname:
+#
+# Get the hostname of the guest's operating system
+#
+# Returns: hostname string.
+#
+# Since 2.3
+##
+{ 'command': 'guest-get-hostname',
+  'returns': 'str' }
+ 
+##
+# @guest-uptime:
+#
+# Get the time in seconds since the guest machine operating system was started
+#
+# Returns: uptime in seconds
+#
+# Since 2.3
+##
+{ 'command': 'guest-uptime',
+  'returns': 'uint64' }
+
+##
+# @GuestProcessInfo
+#
+# @process-id: the process unique id
+# @parent-id: the process parent unique id
+# @process-name: the name of the process
+# @image-path: full path of the process image
+# @session-id: the session id of the process
+#
+# Since: 2.3
+##
+{ 'type': 'GuestProcessInfo',
+  'data': {'process-id': 'int', 'parent-id': 'int', 'process-name': 'str',
+  			'image-path': 'str', 'session-id': 'int'}}
+  
+##
+# @guest-get-process-list:
+#
+# Get the list of active processes on the guest operating system
+#
+# Returns: array of active processes
+#
+# Since 2.3
+##
+{ 'command': 'guest-get-process-list',
+  'returns': ['GuestProcessInfo'] }
+
+##
+# @GuestIpProtocol:
+#
+# An enumeration of supported IP protocols
+#
+# @tcp: TCP
+#
+# @udp: UDP
+#
+# Since: 2.3
+##
+{ 'enum': 'GuestIpProtocol',
+  'data': [ 'tcp', 'udp' ] }
+  
+##
+# @GuestTcpProtocolState:
+#
+# An enumeration of TCP connection state
+#
+# @closed: CLOSED
+# @listen: LISTEN
+# @syn-sent: SYN_SENT
+# @syn-rcvd: SYN_RCVD
+# @established: ESTABLISHED
+# @fin-wait1: FIN_WAIT1
+# @fin-wait2: FIN_WAIT2
+# @close-wait: CLOSE_WAIT
+# @closing: CLOSING
+# @last-ack: LAST_ACK
+# @time-wait: TIME_WAIT
+# @delete-tcb: DELETE_TCB
+#
+# Since: 2.3
+##
+{ 'enum': 'GuestTcpProtocolState',
+  'data': [ 'closed', 'listen', 'syn-sent', 'syn-rcvd', 'established',
+  			'fin-wait1', 'fin-wait2', 'close-wait', 'closing',
+  			'last-ack', 'time-wait', 'delete-tcb' ] }
+
+##
+# @GuestActiveConnection
+#
+# @if-family: 4 / 6
+# @protocol: TCP / UDP
+# @source-addr: the source IP address of the connection
+# @source-port: the source port of the connection
+# @dest-addr: the destination IP address of the connection
+# @dest-port: the destination port of the connection
+# @owner-process_id: the process unique id for the connection owner
+# @state: connection protocol state
+# @start-time: time where bind() was called for the connection
+#
+# Since: 2.3
+##
+{ 'type': 'GuestActiveConnection',
+  'data': {	'source-addr': 'str', 'source-port': 'int', 'dest-addr': 'str',
+  			'dest-port': 'int', 'owner-process_id': 'int', 'state': 'GuestTcpProtocolState',
+  			'if-family': 'GuestIpAddressType', 'protocol': 'GuestIpProtocol',
+  			'start-time': 'uint64'}}
+ 
+ ##
+# @guest-get-active-connections:
+#
+# Get the list of active connections on the guest operating system
+#
+# Returns: array of active connections
+#
+# Since 2.3
+##
+{ 'command': 'guest-get-active-connections',
+  'returns': ['GuestActiveConnection'] }
+ 
diff --git a/qga/win32-definitions.h b/qga/win32-definitions.h
new file mode 100644
index 0000000..6bb610e
--- /dev/null
+++ b/qga/win32-definitions.h
@@ -0,0 +1,115 @@
+
+#ifndef WIN32_DEFINITIONS_H_
+#define WIN32_DEFINITIONS_H_	1
+
+#include "win32-iptypes.h"
+
+#define STATUS_INFO_LENGTH_MISMATCH		(0xC0000004)
+
+typedef
+enum _PROCESSINFOCLASS
+{
+	ProcessBasicInformation=0x0000,
+	ProcessDebugPort=0x0007,
+	ProcessWow64Information=0x001a,
+	ProcessImageFileName=0x001b,
+	ProcessBreakOnTermination=0x001d,
+} PROCESSINFOCLASS;
+
+typedef struct
+{
+    ULONG          AllocationSize;
+    ULONG          ActualSize;
+    ULONG          Flags;
+    ULONG          Unknown1;
+    UNICODE_STRING Unknown2;
+    HANDLE         InputHandle;
+    HANDLE         OutputHandle;
+    HANDLE         ErrorHandle;
+    UNICODE_STRING CurrentDirectory;
+    HANDLE         CurrentDirectoryHandle;
+    UNICODE_STRING SearchPaths;
+    UNICODE_STRING ApplicationName;
+    UNICODE_STRING CommandLine;
+    PVOID          EnvironmentBlock;
+    ULONG          Unknown[9];
+    UNICODE_STRING Unknown3;
+    UNICODE_STRING Unknown4;
+    UNICODE_STRING Unknown5;
+    UNICODE_STRING Unknown6;
+} PROCESS_PARAMETERS, *PPROCESS_PARAMETERS;
+
+typedef struct
+{
+    ULONG               AllocationSize;
+    ULONG               Unknown1;
+    HINSTANCE           ProcessHinstance;
+    PVOID               ListDlls;
+    PPROCESS_PARAMETERS ProcessParameters;
+    ULONG               Unknown2;
+    HANDLE              Heap;
+} PEB, *PPEB;
+
+typedef struct
+{
+    DWORD ExitStatus;
+    PPEB  PebBaseAddress;
+    DWORD AffinityMask;
+    DWORD BasePriority;
+    ULONG UniqueProcessId;
+    ULONG InheritedFromUniqueProcessId;
+}   PROCESS_BASIC_INFORMATION;
+
+typedef
+enum _SYSTEM_INFORMATION_CLASS
+{
+    SystemBasicInformation=0x0000,
+    SystemProcessorInformation=0x0001,
+    SystemPerformanceInformation=0x0002,
+    SystemTimeOfDayInformation=0x0003,
+    SystemPathInformation=0x0004,
+    SystemProcessInformation=0x0005,
+    SystemDeviceInformation=0x0007,
+    SystemModuleInformation=0x000B,
+} SYSTEM_INFORMATION_CLASS;
+
+typedef
+struct _SYSTEM_PROCESS_INFORMATION // Size=184
+{
+    ULONG NextEntryOffset; // Size=4 Offset=0
+    ULONG NumberOfThreads; // Size=4 Offset=4
+    LARGE_INTEGER WorkingSetPrivateSize; // Size=8 Offset=8
+    ULONG HardFaultCount; // Size=4 Offset=16
+    ULONG NumberOfThreadsHighWatermark; // Size=4 Offset=20
+    ULONGLONG CycleTime; // Size=8 Offset=24
+    LARGE_INTEGER CreateTime; // Size=8 Offset=32
+    LARGE_INTEGER UserTime; // Size=8 Offset=40
+    LARGE_INTEGER KernelTime; // Size=8 Offset=48
+    UNICODE_STRING ImageName; // Size=8 Offset=56
+    LONG BasePriority; // Size=4 Offset=64
+    PVOID UniqueProcessId; // Size=4 Offset=68
+    PVOID InheritedFromUniqueProcessId; // Size=4 Offset=72
+    ULONG HandleCount; // Size=4 Offset=76
+    ULONG SessionId; // Size=4 Offset=80
+    ULONG UniqueProcessKey; // Size=4 Offset=84
+    ULONG PeakVirtualSize; // Size=4 Offset=88
+    ULONG VirtualSize; // Size=4 Offset=92
+    ULONG PageFaultCount; // Size=4 Offset=96
+    ULONG PeakWorkingSetSize; // Size=4 Offset=100
+    ULONG WorkingSetSize; // Size=4 Offset=104
+    ULONG QuotaPeakPagedPoolUsage; // Size=4 Offset=108
+    ULONG QuotaPagedPoolUsage; // Size=4 Offset=112
+    ULONG QuotaPeakNonPagedPoolUsage; // Size=4 Offset=116
+    ULONG QuotaNonPagedPoolUsage; // Size=4 Offset=120
+    ULONG PagefileUsage; // Size=4 Offset=124
+    ULONG PeakPagefileUsage; // Size=4 Offset=128
+    ULONG PrivatePageCount; // Size=4 Offset=132
+    LARGE_INTEGER ReadOperationCount; // Size=8 Offset=136
+    LARGE_INTEGER WriteOperationCount; // Size=8 Offset=144
+    LARGE_INTEGER OtherOperationCount; // Size=8 Offset=152
+    LARGE_INTEGER ReadTransferCount; // Size=8 Offset=160
+    LARGE_INTEGER WriteTransferCount; // Size=8 Offset=168
+    LARGE_INTEGER OtherTransferCount; // Size=8 Offset=176
+} SYSTEM_PROCESS_INFORMATION;
+
+#endif /* WIN32_DEFINITIONS_H_ */
diff --git a/qga/win32-iptypes.h b/qga/win32-iptypes.h
new file mode 100644
index 0000000..baae4ea
--- /dev/null
+++ b/qga/win32-iptypes.h
@@ -0,0 +1,411 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    iptypes.h
+
+--*/
+
+#ifndef IP_TYPES_INCLUDED
+#define IP_TYPES_INCLUDED
+
+#include <time.h>
+
+#define INET_ADDRSTRLEN         (16)
+#define INET6_ADDRSTRLEN        (48)
+
+// Definitions and structures used by getnetworkparams and getadaptersinfo apis
+
+#define MAX_ADAPTER_DESCRIPTION_LENGTH  128 // arb.
+#define MAX_ADAPTER_NAME_LENGTH         256 // arb.
+#define MAX_ADAPTER_ADDRESS_LENGTH      8   // arb.
+#define DEFAULT_MINIMUM_ENTITIES        32  // arb.
+#define MAX_HOSTNAME_LEN                128 // arb.
+#define MAX_DOMAIN_NAME_LEN             128 // arb.
+#define MAX_SCOPE_ID_LEN                256 // arb.
+
+//
+// types
+//
+
+// Node Type
+
+#define BROADCAST_NODETYPE              1
+#define PEER_TO_PEER_NODETYPE           2
+#define MIXED_NODETYPE                  4
+#define HYBRID_NODETYPE                 8
+
+//
+// IP_ADDRESS_STRING - store an IP address as a dotted decimal string
+//
+
+typedef struct {
+    char String[4 * 4];
+} IP_ADDRESS_STRING, *PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
+
+//
+// IP_ADDR_STRING - store an IP address with its corresponding subnet mask,
+// both as dotted decimal strings
+//
+
+typedef struct _IP_ADDR_STRING {
+    struct _IP_ADDR_STRING* Next;
+    IP_ADDRESS_STRING IpAddress;
+    IP_MASK_STRING IpMask;
+    DWORD Context;
+} IP_ADDR_STRING, *PIP_ADDR_STRING;
+
+//
+// ADAPTER_INFO - per-adapter information. All IP addresses are stored as
+// strings
+//
+
+typedef struct _IP_ADAPTER_INFO {
+    struct _IP_ADAPTER_INFO* Next;
+    DWORD ComboIndex;
+    char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
+    char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
+    UINT AddressLength;
+    BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];
+    DWORD Index;
+    UINT Type;
+    UINT DhcpEnabled;
+    PIP_ADDR_STRING CurrentIpAddress;
+    IP_ADDR_STRING IpAddressList;
+    IP_ADDR_STRING GatewayList;
+    IP_ADDR_STRING DhcpServer;
+    BOOL HaveWins;
+    IP_ADDR_STRING PrimaryWinsServer;
+    IP_ADDR_STRING SecondaryWinsServer;
+    time_t LeaseObtained;
+    time_t LeaseExpires;
+} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;
+
+//
+// The following types require Winsock2.
+//
+
+typedef enum {
+    IpPrefixOriginOther = 0,
+    IpPrefixOriginManual,
+    IpPrefixOriginWellKnown,
+    IpPrefixOriginDhcp,
+    IpPrefixOriginRouterAdvertisement,
+} IP_PREFIX_ORIGIN;
+
+typedef enum {
+    IpSuffixOriginOther = 0,
+    IpSuffixOriginManual,
+    IpSuffixOriginWellKnown,
+    IpSuffixOriginDhcp,
+    IpSuffixOriginLinkLayerAddress,
+    IpSuffixOriginRandom,
+} IP_SUFFIX_ORIGIN;
+
+typedef enum {
+    IpDadStateInvalid    = 0,
+    IpDadStateTentative,
+    IpDadStateDuplicate,
+    IpDadStateDeprecated,
+    IpDadStatePreferred,
+} IP_DAD_STATE;
+
+typedef struct _IP_ADAPTER_UNICAST_ADDRESS {
+    union {
+        ULONGLONG Alignment;
+        struct { 
+            ULONG Length;
+            DWORD Flags;
+        };
+    };
+    struct _IP_ADAPTER_UNICAST_ADDRESS *Next;
+    SOCKET_ADDRESS Address;
+
+    IP_PREFIX_ORIGIN PrefixOrigin;
+    IP_SUFFIX_ORIGIN SuffixOrigin;
+    IP_DAD_STATE DadState;
+
+    ULONG ValidLifetime;
+    ULONG PreferredLifetime;
+    ULONG LeaseLifetime;
+} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS;
+
+typedef struct _IP_ADAPTER_ANYCAST_ADDRESS {
+    union {
+        ULONGLONG Alignment;
+        struct { 
+            ULONG Length;
+            DWORD Flags;
+        };
+    };
+    struct _IP_ADAPTER_ANYCAST_ADDRESS *Next;
+    SOCKET_ADDRESS Address;
+} IP_ADAPTER_ANYCAST_ADDRESS, *PIP_ADAPTER_ANYCAST_ADDRESS;
+
+typedef struct _IP_ADAPTER_MULTICAST_ADDRESS {
+    union {
+        ULONGLONG Alignment;
+        struct {
+            ULONG Length;
+            DWORD Flags;
+        };
+    };
+    struct _IP_ADAPTER_MULTICAST_ADDRESS *Next;
+    SOCKET_ADDRESS Address;
+} IP_ADAPTER_MULTICAST_ADDRESS, *PIP_ADAPTER_MULTICAST_ADDRESS;
+
+//
+// Per-address Flags
+//
+#define IP_ADAPTER_ADDRESS_DNS_ELIGIBLE 0x01
+#define IP_ADAPTER_ADDRESS_TRANSIENT    0x02
+
+typedef struct _IP_ADAPTER_DNS_SERVER_ADDRESS {
+    union {
+        ULONGLONG Alignment;
+        struct {
+            ULONG Length;
+            DWORD Reserved;
+        };
+    };
+    struct _IP_ADAPTER_DNS_SERVER_ADDRESS *Next;
+    SOCKET_ADDRESS Address;
+} IP_ADAPTER_DNS_SERVER_ADDRESS, *PIP_ADAPTER_DNS_SERVER_ADDRESS;
+
+typedef struct _IP_ADAPTER_PREFIX {
+    union {
+        ULONGLONG Alignment;
+        struct {
+            ULONG Length;
+            DWORD Flags;
+        };
+    };
+    struct _IP_ADAPTER_PREFIX *Next;
+    SOCKET_ADDRESS Address;
+    ULONG PrefixLength;
+} IP_ADAPTER_PREFIX, *PIP_ADAPTER_PREFIX;
+
+//
+// Per-adapter Flags
+//
+#define IP_ADAPTER_DDNS_ENABLED               0x01
+#define IP_ADAPTER_REGISTER_ADAPTER_SUFFIX    0x02
+#define IP_ADAPTER_DHCP_ENABLED               0x04
+#define IP_ADAPTER_RECEIVE_ONLY               0x08
+#define IP_ADAPTER_NO_MULTICAST               0x10
+#define IP_ADAPTER_IPV6_OTHER_STATEFUL_CONFIG 0x20
+
+//
+// OperStatus values from RFC 2863
+//
+typedef enum {
+    IfOperStatusUp = 1,
+    IfOperStatusDown,
+    IfOperStatusTesting,
+    IfOperStatusUnknown,
+    IfOperStatusDormant,
+    IfOperStatusNotPresent,
+    IfOperStatusLowerLayerDown
+} IF_OPER_STATUS;
+
+//
+// Scope levels from RFC 2373 used with ZoneIndices array.
+//
+typedef enum {
+    ScopeLevelInterface    = 1,
+    ScopeLevelLink         = 2,
+    ScopeLevelSubnet       = 3,
+    ScopeLevelAdmin        = 4,
+    ScopeLevelSite         = 5,
+    ScopeLevelOrganization = 8,
+    ScopeLevelGlobal       = 14
+} SCOPE_LEVEL;
+
+typedef struct _IP_ADAPTER_ADDRESSES {
+    union {
+        ULONGLONG Alignment;
+        struct {
+            ULONG Length;
+            DWORD IfIndex;
+        };
+    };
+    struct _IP_ADAPTER_ADDRESSES *Next;
+    PCHAR AdapterName;
+    PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress;
+    PIP_ADAPTER_ANYCAST_ADDRESS FirstAnycastAddress;
+    PIP_ADAPTER_MULTICAST_ADDRESS FirstMulticastAddress;
+    PIP_ADAPTER_DNS_SERVER_ADDRESS FirstDnsServerAddress;
+    PWCHAR DnsSuffix;
+    PWCHAR Description;
+    PWCHAR FriendlyName;
+    BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH];
+    DWORD PhysicalAddressLength;
+    DWORD Flags;
+    DWORD Mtu;
+    DWORD IfType;
+    IF_OPER_STATUS OperStatus;
+    DWORD Ipv6IfIndex;
+    DWORD ZoneIndices[16];
+    PIP_ADAPTER_PREFIX FirstPrefix;
+} IP_ADAPTER_ADDRESSES, *PIP_ADAPTER_ADDRESSES;
+
+//
+// Flags used as argument to GetAdaptersAddresses().
+// "SKIP" flags are added when the default is to include the information.
+// "INCLUDE" flags are added when the default is to skip the information.
+//
+#define GAA_FLAG_SKIP_UNICAST       0x0001
+#define GAA_FLAG_SKIP_ANYCAST       0x0002
+#define GAA_FLAG_SKIP_MULTICAST     0x0004
+#define GAA_FLAG_SKIP_DNS_SERVER    0x0008
+#define GAA_FLAG_INCLUDE_PREFIX     0x0010
+#define GAA_FLAG_SKIP_FRIENDLY_NAME 0x0020
+
+//
+// IP_PER_ADAPTER_INFO - per-adapter IP information such as DNS server list.
+//
+
+typedef struct _IP_PER_ADAPTER_INFO {
+    UINT AutoconfigEnabled;
+    UINT AutoconfigActive;
+    PIP_ADDR_STRING CurrentDnsServer;
+    IP_ADDR_STRING DnsServerList;
+} IP_PER_ADAPTER_INFO, *PIP_PER_ADAPTER_INFO;
+
+//
+// FIXED_INFO - the set of IP-related information which does not depend on DHCP
+//
+
+typedef struct {
+    char HostName[MAX_HOSTNAME_LEN + 4] ;
+    char DomainName[MAX_DOMAIN_NAME_LEN + 4];
+    PIP_ADDR_STRING CurrentDnsServer;
+    IP_ADDR_STRING DnsServerList;
+    UINT NodeType;
+    char ScopeId[MAX_SCOPE_ID_LEN + 4];
+    UINT EnableRouting;
+    UINT EnableProxy;
+    UINT EnableDns;
+} FIXED_INFO, *PFIXED_INFO;
+
+typedef struct ip_interface_name_info {
+    ULONG           Index;      // Interface Index
+    ULONG           MediaType;  // Interface Types - see ipifcons.h
+    UCHAR           ConnectionType;
+    UCHAR           AccessType;
+    GUID            DeviceGuid; // Device GUID is the guid of the device
+                                // that IP exposes
+    GUID            InterfaceGuid; // Interface GUID, if not GUID_NULL is the
+                                // GUID for the interface mapped to the device.
+} IP_INTERFACE_NAME_INFO, *PIP_INTERFACE_NAME_INFO;
+
+typedef enum  {
+  TCP_TABLE_BASIC_LISTENER,
+  TCP_TABLE_BASIC_CONNECTIONS,
+  TCP_TABLE_BASIC_ALL,
+  TCP_TABLE_OWNER_PID_LISTENER,
+  TCP_TABLE_OWNER_PID_CONNECTIONS,
+  TCP_TABLE_OWNER_PID_ALL,
+  TCP_TABLE_OWNER_MODULE_LISTENER,
+  TCP_TABLE_OWNER_MODULE_CONNECTIONS,
+  TCP_TABLE_OWNER_MODULE_ALL
+} TCP_TABLE_CLASS, *PTCP_TABLE_CLASS;
+
+typedef enum  {
+  UDP_TABLE_BASIC,
+  UDP_TABLE_OWNER_PID,
+  UDP_TABLE_OWNER_MODULE
+} UDP_TABLE_CLASS, *PUDP_TABLE_CLASS;
+
+#define TCPIP_OWNING_MODULE_SIZE 	(16)
+#define ANY_SIZE					(1)
+
+typedef enum
+{
+  MIB_TCP_STATE_CLOSED = 1,
+  MIB_TCP_STATE_LISTEN = 2,
+  MIB_TCP_STATE_SYN_SENT = 3,
+  MIB_TCP_STATE_SYN_RCVD = 4,
+  MIB_TCP_STATE_ESTAB = 5,
+  MIB_TCP_STATE_FIN_WAIT1 = 6,
+  MIB_TCP_STATE_FIN_WAIT2 = 7,
+  MIB_TCP_STATE_CLOSE_WAIT = 8,
+  MIB_TCP_STATE_CLOSING = 9,
+  MIB_TCP_STATE_LAST_ACK = 10,
+  MIB_TCP_STATE_TIME_WAIT = 11,
+  MIB_TCP_STATE_DELETE_TCB = 12
+} MIB_TCP_STATE;
+
+typedef struct _MIB_UDPROW {
+  DWORD dwLocalAddr;
+  DWORD dwLocalPort;
+} MIB_UDPROW, *PMIB_UDPROW;
+
+typedef struct _MIB_UDPTABLE {
+  DWORD      dwNumEntries;
+  MIB_UDPROW table[ANY_SIZE];
+} MIB_UDPTABLE, *PMIB_UDPTABLE;
+
+typedef struct _MIB_UDPROW_OWNER_PID {
+  DWORD dwLocalAddr;
+  DWORD dwLocalPort;
+  DWORD dwOwningPid;
+} MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID;
+
+typedef struct _MIB_UDPTABLE_OWNER_PID {
+  DWORD                dwNumEntries;
+  MIB_UDPROW_OWNER_PID table[ANY_SIZE];
+} MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID;
+
+typedef struct _MIB_UDPROW_OWNER_MODULE {
+  DWORD         dwLocalAddr;
+  DWORD         dwLocalPort;
+  DWORD         dwOwningPid;
+  LARGE_INTEGER liCreateTimestamp;
+  union {
+    struct {
+      int SpecificPortBind  :1;
+    };
+    int    dwFlags;
+  };
+  ULONGLONG     OwningModuleInfo[TCPIP_OWNING_MODULE_SIZE];
+} MIB_UDPROW_OWNER_MODULE, *PMIB_UDPROW_OWNER_MODULE;
+
+typedef struct _MIB_UDPTABLE_OWNER_MODULE {
+  DWORD                   dwNumEntries;
+  MIB_UDPROW_OWNER_MODULE table[ANY_SIZE];
+} MIB_UDPTABLE_OWNER_MODULE, *PMIB_UDPTABLE_OWNER_MODULE;
+
+typedef struct _MIB_TCPROW_OWNER_MODULE {
+  DWORD 		dwState;
+  DWORD         dwLocalAddr;
+  DWORD         dwLocalPort;
+  DWORD         dwRemoteAddr;
+  DWORD         dwRemotePort;
+  DWORD         dwOwningPid;
+  LARGE_INTEGER liCreateTimestamp;
+  ULONGLONG     OwningModuleInfo[TCPIP_OWNING_MODULE_SIZE];
+} MIB_TCPROW_OWNER_MODULE, *PMIB_TCPROW_OWNER_MODULE;
+
+typedef struct {
+  DWORD                   dwNumEntries;
+  MIB_TCPROW_OWNER_MODULE table[ANY_SIZE];
+} MIB_TCPTABLE_OWNER_MODULE, *PMIB_TCPTABLE_OWNER_MODULE;
+
+typedef struct _MIB_TCPROW_OWNER_PID {
+  DWORD dwState;
+  DWORD dwLocalAddr;
+  DWORD dwLocalPort;
+  DWORD dwRemoteAddr;
+  DWORD dwRemotePort;
+  DWORD dwOwningPid;
+} MIB_TCPROW_OWNER_PID, *PMIB_TCPROW_OWNER_PID;
+
+typedef struct {
+  DWORD                dwNumEntries;
+  MIB_TCPROW_OWNER_PID table[ANY_SIZE];
+} MIB_TCPTABLE_OWNER_PID, *PMIB_TCPTABLE_OWNER_PID;
+
+#endif /* IP_TYPES_INCLUDED */ 
-- 
2.3.4

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [Qemu-devel] [PATCH 3/3] qga support process list, netstat and file stat/delete
  2015-03-25 12:25 [Qemu-devel] [PATCH 3/3] qga support process list, netstat and file stat/delete itamar.tal4
@ 2015-03-26 19:24 ` Eric Blake
  0 siblings, 0 replies; 2+ messages in thread
From: Eric Blake @ 2015-03-26 19:24 UTC (permalink / raw)
  To: itamar.tal4, qemu-devel; +Cc: ori, ariel, mdroth, pavel, Itamar Tal

[-- Attachment #1: Type: text/plain, Size: 8500 bytes --]

On 03/25/2015 06:25 AM, itamar.tal4@gmail.com wrote:
> From: Itamar Tal <itamar@guardicore.com>

This patch says 3/3, but I see no 1/3 or 2/3 (let alone a 0/3 cover
letter) in the mail archives.

> 
> this patch add support for some more functionality in the qemu-guest-agent,
> both for windows and linux. Main added features are:
> - interface listing in Windows
> - Process list in Windows
> - network connections enumeration in Windows
> - file delete for both Windows and Posix
> - file stat() for both Windows and Posix
> - system uptime for both Windows and Posix
> 
> Itamar,
> Guardicore
> itamar@guardicore.com
> 
> ---
>  qga/commands-posix.c    |  49 +++++
>  qga/commands-win32.c    | 552 +++++++++++++++++++++++++++++++++++++++++++++++-
>  qga/commands.c          |  25 +++
>  qga/guest-agent-core.h  |   2 +
>  qga/main.c              |   9 +
>  qga/qapi-schema.json    | 172 +++++++++++++++
>  qga/win32-definitions.h | 115 ++++++++++
>  qga/win32-iptypes.h     | 411 +++++++++++++++++++++++++++++++++++

That's awfully big.  Please split it up into one or two closely-related
qga commands per patch, rather than lots of commands all at once.

> +++ b/qga/qapi-schema.json
> @@ -891,3 +891,175 @@
>  ##
>  { 'command': 'guest-get-memory-block-info',
>    'returns': 'GuestMemoryBlockInfo' }
> +
> +##
> +# @GuestFileStat
> +#
> +# @st_mode: file access permissions mode
> +# @st_ino: file inode id
> +# @st_dev: file device node

Is this portable to be exposing?  Windows in particular is notorious for
not having decent ino/dev in its basic stat() call, and anything we do
to fake it is likely to break someone's use case.  I'd rather play it
conservative and go with fewer fields that we know are portable than to
try and expose all of struct stat, if we can't prove that the extra
fields would be useful.

> +# @st_nlink: number of links pointing to the file
> +# @st_uid: file user id
> +# @st_gid: file group id
> +# @st_atime: file access time

Insufficient.  Please report timestamps as a struct containing seconds
since Epoch (1970) AND nanoseconds.  Truncating timestamps to the
nearest second by returning only an int is not friendly.

> +# @st_mtime: file modification time
> +# @st_ctime: file creation time

Absolutely not.  ctime is NOT creation time, but last change time.
Creation time is typically reported as Birthtime on decent Unix-y
systems.  Yes, many (but not all) filesystems track FOUR times per file,
and even though POSIX only requires three timestamps, all of BSD, Linux,
and Cygwin have their struct stat() report all four timestamps (where
birthtime is an obvious all-0s for a filesystem that doesn't track it).
(The fact that windows tracks BOTH birth time AND change time, but
reports ctime as birthtime in its stat() call, doesn't help matters).

> +# @st_size: file size in bytes

The names documented here...[1]

> +#
> +# Since: 2.3

You missed 2.3.  The earliest this can go in is 2.4.

> +##
> +{ 'type': 'GuestFileStat',
> +  'data': {'mode': 'int', 'inode': 'int', 'dev': 'int',
> +  			'nlink': 'int', 'uid': 'int', 'gid': 'int',

TAB damage.  Please make sure your patch passes ./scripts/checkpatch.pl
before submitting.

> +  			'size': 'uint64', 'atime': 'int', 'mtime': 'int',
> +  			'ctime': 'int'

[1]...don't match the names actually used here.

> +           }}
> +
> +##
> +# @guest-file-stat:
> +#
> +# Get the stat() for a file in the guest's operating system
> +#
> +# Returns: hostname string.

Wrong copy-and-paste.

> +#
> +# Since 2.3

2.4.  (Won't mention it again)

> +##
> +{ 'command': 'guest-file-stat',
> +  'data': { 'path': 'str' },
> +  'returns': 'GuestFileStat' }

Are you planning on an fstat counterpart of an open handle?  Are you
planning on handling the symlink aspect of stat vs. lstat?  Should you
be providing more of an fstatat() interface where you can specify a name
relative to an already-open directory handle?

> +  
> +##
> +# @guest-file-delete:
> +#
> +# Delete a file in the guest's operating system
> +#
> +# Returns: 
> +#
> +# Since 2.3
> +##
> +{ 'command': 'guest-file-delete',
> +  'data': { 'path': 'str' }}

Can this delete directories?  If so, should it be named remove?  Should
it provide removeat() functionality of specifying a name relative to an
already-open directory handle?

> +
> +##
> +# @guest-get-hostname:
> +#
> +# Get the hostname of the guest's operating system
> +#
> +# Returns: hostname string.
> +#
> +# Since 2.3
> +##
> +{ 'command': 'guest-get-hostname',
> +  'returns': 'str' }
> + 
> +##
> +# @guest-uptime:
> +#
> +# Get the time in seconds since the guest machine operating system was started
> +#
> +# Returns: uptime in seconds

Is this sufficient, or should you provide more resolution?

> +#
> +# Since 2.3
> +##
> +{ 'command': 'guest-uptime',
> +  'returns': 'uint64' }
> +
> +##
> +# @GuestProcessInfo
> +#
> +# @process-id: the process unique id
> +# @parent-id: the process parent unique id
> +# @process-name: the name of the process
> +# @image-path: full path of the process image
> +# @session-id: the session id of the process
> +#
> +# Since: 2.3
> +##
> +{ 'type': 'GuestProcessInfo',
> +  'data': {'process-id': 'int', 'parent-id': 'int', 'process-name': 'str',
> +  			'image-path': 'str', 'session-id': 'int'}}

Do you really need this, or is the existing guest-file-open on /proc/...
sufficient for getting at the same information without needing a new
command?

> +  
> +##
> +# @guest-get-process-list:
> +#
> +# Get the list of active processes on the guest operating system
> +#
> +# Returns: array of active processes
> +#
> +# Since 2.3
> +##
> +{ 'command': 'guest-get-process-list',
> +  'returns': ['GuestProcessInfo'] }

Almost guaranteed to be stale (processes will probably come and go
during the time that the guest agent is collecting data to return), and
rather expensive to collect.  Should it take an optional argument for
filtering the results to a specific pid?

> +
> +##
> +# @GuestIpProtocol:
> +#
> +# An enumeration of supported IP protocols
> +#
> +# @tcp: TCP
> +#
> +# @udp: UDP
> +#
> +# Since: 2.3
> +##
> +{ 'enum': 'GuestIpProtocol',
> +  'data': [ 'tcp', 'udp' ] }
> +  
> +##
> +# @GuestTcpProtocolState:
> +#
> +# An enumeration of TCP connection state
> +#
> +# @closed: CLOSED
> +# @listen: LISTEN
> +# @syn-sent: SYN_SENT
> +# @syn-rcvd: SYN_RCVD
> +# @established: ESTABLISHED
> +# @fin-wait1: FIN_WAIT1
> +# @fin-wait2: FIN_WAIT2
> +# @close-wait: CLOSE_WAIT
> +# @closing: CLOSING
> +# @last-ack: LAST_ACK
> +# @time-wait: TIME_WAIT
> +# @delete-tcb: DELETE_TCB
> +#
> +# Since: 2.3
> +##
> +{ 'enum': 'GuestTcpProtocolState',
> +  'data': [ 'closed', 'listen', 'syn-sent', 'syn-rcvd', 'established',
> +  			'fin-wait1', 'fin-wait2', 'close-wait', 'closing',
> +  			'last-ack', 'time-wait', 'delete-tcb' ] }
> +
> +##
> +# @GuestActiveConnection
> +#
> +# @if-family: 4 / 6
> +# @protocol: TCP / UDP
> +# @source-addr: the source IP address of the connection
> +# @source-port: the source port of the connection
> +# @dest-addr: the destination IP address of the connection
> +# @dest-port: the destination port of the connection
> +# @owner-process_id: the process unique id for the connection owner
> +# @state: connection protocol state
> +# @start-time: time where bind() was called for the connection
> +#
> +# Since: 2.3
> +##
> +{ 'type': 'GuestActiveConnection',
> +  'data': {	'source-addr': 'str', 'source-port': 'int', 'dest-addr': 'str',
> +  			'dest-port': 'int', 'owner-process_id': 'int', 'state': 'GuestTcpProtocolState',
> +  			'if-family': 'GuestIpAddressType', 'protocol': 'GuestIpProtocol',
> +  			'start-time': 'uint64'}}
> + 
> + ##
> +# @guest-get-active-connections:
> +#
> +# Get the list of active connections on the guest operating system
> +#
> +# Returns: array of active connections
> +#
> +# Since 2.3
> +##
> +{ 'command': 'guest-get-active-connections',
> +  'returns': ['GuestActiveConnection'] }
> + 

I'm not sure if all of these commands make sense to add, but I _am_ sure
that you are trying to add too much in one commit.  Please break this up
before resubmitting.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2015-03-26 19:24 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-25 12:25 [Qemu-devel] [PATCH 3/3] qga support process list, netstat and file stat/delete itamar.tal4
2015-03-26 19:24 ` Eric Blake

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.