* [Qemu-devel] [PATCH 1/4] added qga hash file command (win/linux)
2015-03-31 8:34 [Qemu-devel] [PATCH 0/4] qga added functionality itamar.tal4
@ 2015-03-31 8:34 ` itamar.tal4
2015-03-31 8:34 ` [Qemu-devel] [PATCH 2/4] added missing file commands (attributes, deletion) itamar.tal4
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: itamar.tal4 @ 2015-03-31 8:34 UTC (permalink / raw)
To: qemu-devel; +Cc: ori, ariel, mdroth, pavel, Itamar Tal
From: Itamar Tal <itamar@guardicore.com>
Added support for retrieving file hash from the guest machine.
This is command is useful for ensuring that no changes were made for a given
file between consecutive read operations and reduce read/write operations.
---
qga/Makefile.objs | 2 +-
qga/commands.c | 43 +++++++++++++
qga/guest-agent-core.h | 6 ++
qga/qapi-schema.json | 13 ++++
qga/sha256.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++
qga/sha256.h | 40 ++++++++++++
6 files changed, 274 insertions(+), 1 deletion(-)
create mode 100644 qga/sha256.c
create mode 100644 qga/sha256.h
diff --git a/qga/Makefile.objs b/qga/Makefile.objs
index 1c5986c..a68d78d 100644
--- a/qga/Makefile.objs
+++ b/qga/Makefile.objs
@@ -1,4 +1,4 @@
-qga-obj-y = commands.o guest-agent-command-state.o main.o
+qga-obj-y = commands.o guest-agent-command-state.o main.o sha256.o
qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o
qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
qga-obj-$(CONFIG_WIN32) += vss-win32.o
diff --git a/qga/commands.c b/qga/commands.c
index 7834967..f9378f4 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -11,6 +11,7 @@
*/
#include <glib.h>
+#include "qga/sha256.h"
#include "qga/guest-agent-core.h"
#include "qga-qmp-commands.h"
#include "qapi/qmp/qerror.h"
@@ -70,3 +71,45 @@ struct GuestAgentInfo *qmp_guest_info(Error **errp)
qmp_for_each_command(qmp_command_info, info);
return info;
}
+
+char *qmp_guest_file_hash(const char *path, Error **errp)
+{
+ const char *hex = "0123456789abcdef";
+ BYTE sha256_block[SHA256_BLOCK_SIZE];
+ char sha256_hexdigest[SHA256_BLOCK_SIZE * 2 + 1];
+ BYTE buffer[1024];
+ size_t len = 0;
+ SHA256_CTX ctx;
+ FILE *file = NULL;
+ int i = 0;
+
+ sha256_init(&ctx);
+
+ file = fopen(path, "rb");
+ if (NULL == file) {
+ error_setg(errp, "Error opening file '%s'", path);
+ return NULL;
+ }
+
+ for (;;) {
+ len = fread(buffer, 1, 1024, file);
+ if (0 == len) {
+ break;
+ }
+
+ sha256_update(&ctx, buffer, len);
+ }
+
+ fclose(file);
+
+ sha256_final(&ctx, sha256_block);
+
+ /* transform the digest to hex string */
+ for (i = 0; i < SHA256_BLOCK_SIZE; ++i) {
+ sha256_hexdigest[i * 2] = hex[(sha256_block[i] >> 4) & 0xF];
+ sha256_hexdigest[i * 2 + 1] = hex[sha256_block[i] & 0xF];
+ }
+ sha256_hexdigest[SHA256_BLOCK_SIZE * 2] = '\0';
+
+ return g_strdup((char *)sha256_hexdigest);
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index e92c6ab..b496c47 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -10,6 +10,10 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
+
+#ifndef _GUEST_AGENT_CORE_H_
+#define _GUEST_AGENT_CORE_H_
+
#include "qapi/qmp/dispatch.h"
#include "qemu-common.h"
@@ -41,3 +45,5 @@ int64_t ga_get_fd_handle(GAState *s, Error **errp);
#ifndef _WIN32
void reopen_fd_to_null(int fd);
#endif
+
+#endif /* _GUEST_AGENT_CORE_H_ */
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 95f49e3..68b420d 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -891,3 +891,16 @@
##
{ 'command': 'guest-get-memory-block-info',
'returns': 'GuestMemoryBlockInfo' }
+
+##
+# @guest-file-hash:
+#
+# Get the a SHA256 hash for a file in the guest's operating system
+#
+# Returns: file SHA256 hash string
+#
+# Since 2.4
+##
+{ 'command': 'guest-file-hash',
+ 'data': { 'path': 'str' },
+ 'returns': 'str' }
diff --git a/qga/sha256.c b/qga/sha256.c
new file mode 100644
index 0000000..40abb42
--- /dev/null
+++ b/qga/sha256.c
@@ -0,0 +1,171 @@
+/*********************************************************************
+* Filename: sha256.c
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Implementation of the SHA-256 hashing algorithm.
+ SHA-256 is one of the three algorithms in the SHA2
+ specification. The others, SHA-384 and SHA-512, are not
+ offered in this implementation.
+ Algorithm specification can be found here:
+ * http://csrc.nist.gov/publications/fips/fips180-2/
+ fips180-2withchangenotice.pdf
+ This implementation uses little endian byte order.
+*********************************************************************/
+
+/*************************** HEADER FILES ***************************/
+
+#include <memory.h>
+#include "sha256.h"
+
+/****************************** MACROS ******************************/
+#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32-(b))))
+#define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32-(b))))
+
+#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
+#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22))
+#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25))
+#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3))
+#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10))
+
+/**************************** VARIABLES *****************************/
+static const uint32_t k[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
+ 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
+ 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/*********************** FUNCTION DEFINITIONS ***********************/
+
+void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
+{
+ uint32_t a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
+
+ for (i = 0, j = 0; i < 16; ++i, j += 4) {
+ m[i] = (data[j] << 24) |
+ (data[j + 1] << 16) |
+ (data[j + 2] << 8) |
+ (data[j + 3]);
+ }
+ for ( ; i < 64; ++i) {
+ m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
+ }
+
+ a = ctx->state[0];
+ b = ctx->state[1];
+ c = ctx->state[2];
+ d = ctx->state[3];
+ e = ctx->state[4];
+ f = ctx->state[5];
+ g = ctx->state[6];
+ h = ctx->state[7];
+
+ for (i = 0; i < 64; ++i) {
+ t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i];
+ t2 = EP0(a) + MAJ(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + t1;
+ d = c;
+ c = b;
+ b = a;
+ a = t1 + t2;
+ }
+
+ ctx->state[0] += a;
+ ctx->state[1] += b;
+ ctx->state[2] += c;
+ ctx->state[3] += d;
+ ctx->state[4] += e;
+ ctx->state[5] += f;
+ ctx->state[6] += g;
+ ctx->state[7] += h;
+}
+
+void sha256_init(SHA256_CTX *ctx)
+{
+ ctx->datalen = 0;
+ ctx->bitlen = 0;
+ ctx->state[0] = 0x6a09e667;
+ ctx->state[1] = 0xbb67ae85;
+ ctx->state[2] = 0x3c6ef372;
+ ctx->state[3] = 0xa54ff53a;
+ ctx->state[4] = 0x510e527f;
+ ctx->state[5] = 0x9b05688c;
+ ctx->state[6] = 0x1f83d9ab;
+ ctx->state[7] = 0x5be0cd19;
+}
+
+void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
+{
+ uint32_t i;
+
+ for (i = 0; i < len; ++i) {
+ ctx->data[ctx->datalen] = data[i];
+ ctx->datalen++;
+ if (ctx->datalen == 64) {
+ sha256_transform(ctx, ctx->data);
+ ctx->bitlen += 512;
+ ctx->datalen = 0;
+ }
+ }
+}
+
+void sha256_final(SHA256_CTX *ctx, BYTE hash[])
+{
+ uint32_t i;
+
+ i = ctx->datalen;
+
+ /* Pad whatever data is left in the buffer */
+ if (ctx->datalen < 56) {
+ ctx->data[i++] = 0x80;
+ while (i < 56) {
+ ctx->data[i++] = 0x00;
+ }
+ } else {
+ ctx->data[i++] = 0x80;
+ while (i < 64) {
+ ctx->data[i++] = 0x00;
+ }
+ sha256_transform(ctx, ctx->data);
+ memset(ctx->data, 0, 56);
+ }
+
+ /* Append to the padding the total message's length in bits and
+ transform */
+ ctx->bitlen += ctx->datalen * 8;
+ ctx->data[63] = ctx->bitlen;
+ ctx->data[62] = ctx->bitlen >> 8;
+ ctx->data[61] = ctx->bitlen >> 16;
+ ctx->data[60] = ctx->bitlen >> 24;
+ ctx->data[59] = ctx->bitlen >> 32;
+ ctx->data[58] = ctx->bitlen >> 40;
+ ctx->data[57] = ctx->bitlen >> 48;
+ ctx->data[56] = ctx->bitlen >> 56;
+ sha256_transform(ctx, ctx->data);
+
+ /* Since this implementation uses little endian byte ordering and SHA uses
+ big endian, reverse all the bytes when copying the final state to the
+ output hash. */
+ for (i = 0; i < 4; ++i) {
+ hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
+ hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
+ }
+}
diff --git a/qga/sha256.h b/qga/sha256.h
new file mode 100644
index 0000000..e62f99b
--- /dev/null
+++ b/qga/sha256.h
@@ -0,0 +1,40 @@
+/*********************************************************************
+* Filename: sha256.h
+* Author: Brad Conte (brad AT bradconte.com)
+* Copyright:
+* Disclaimer: This code is presented "as is" without any guarantees.
+* Details: Defines the API for the corresponding SHA1 implementation.
+*********************************************************************/
+
+#ifndef _SHA256_H_INCLUDE
+#define _SHA256_H_INCLUDE
+
+/*************************** HEADER FILES ***************************/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "qga/guest-agent-core.h"
+
+/****************************** MACROS ******************************/
+#define SHA256_BLOCK_SIZE 32 /* SHA256 outputs a 32 byte digest */
+
+/**************************** DATA TYPES ****************************/
+
+typedef unsigned char BYTE; /* 8-bit byte */
+
+typedef struct {
+ BYTE data[64];
+ unsigned int datalen;
+ unsigned long long bitlen;
+ unsigned int state[8];
+} SHA256_CTX;
+
+/*********************** FUNCTION DECLARATIONS **********************/
+
+void sha256_init(SHA256_CTX *ctx);
+void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
+void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
+void sha256_transform(SHA256_CTX *ctx, const BYTE data[]);
+
+#endif /* _SHA256_H_INCLUDE */
--
2.3.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [Qemu-devel] [PATCH 2/4] added missing file commands (attributes, deletion)
2015-03-31 8:34 [Qemu-devel] [PATCH 0/4] qga added functionality itamar.tal4
2015-03-31 8:34 ` [Qemu-devel] [PATCH 1/4] added qga hash file command (win/linux) itamar.tal4
@ 2015-03-31 8:34 ` itamar.tal4
2015-03-31 8:34 ` [Qemu-devel] [PATCH 3/4] added qga system uptime and hostname commands itamar.tal4
2015-03-31 8:34 ` [Qemu-devel] [PATCH 4/4] added qga processes and connections enumeration commands (win/linux) itamar.tal4
3 siblings, 0 replies; 5+ messages in thread
From: itamar.tal4 @ 2015-03-31 8:34 UTC (permalink / raw)
To: qemu-devel; +Cc: ori, ariel, mdroth, pavel, Itamar Tal
From: Itamar Tal <itamar@guardicore.com>
This patch add support for retrieving file attributes and file deletion by
file name, to get a more complete file functionality over the guest agent.
---
qga/commands-posix.c | 23 +++++++++
qga/commands-win32.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++
qga/commands.c | 8 +++
qga/qapi-schema.json | 42 ++++++++++++++++
4 files changed, 212 insertions(+)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index ba8de62..028d533 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -7,6 +7,9 @@
* Michael Roth <mdroth@linux.vnet.ibm.com>
* Michal Privoznik <mprivozn@redhat.com>
*
+ * Changes (itamar@guardicore.com):
+ * - file attributes, removal, hashes
+ *
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
@@ -2456,3 +2459,23 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
#endif
ga_command_state_add(cs, guest_file_init, NULL);
}
+
+GuestFileAttributes *qmp_guest_file_attributes(const char *path, Error **errp)
+{
+ GuestFileAttributes *file_stat = g_malloc0(sizeof(GuestFileAttributes));
+ struct stat file_os_stat;
+
+ if (stat(path, &file_os_stat)) {
+ error_setg(errp, "Failed to get file attributes for '%s' (error %d)",
+ path, errno);
+ return NULL;
+ }
+
+ file_stat->mode = file_os_stat.st_mode;
+ file_stat->size = file_os_stat.st_size;
+ file_stat->access_time = file_os_stat.st_atime;
+ file_stat->modification_time = file_os_stat.st_mtime;
+ file_stat->creation_time = 0; /* not all Linux FS store file BoD */
+
+ return file_stat;
+}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 3ef0549..aeb49c5 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -7,6 +7,9 @@
* Michael Roth <mdroth@linux.vnet.ibm.com>
* Gal Hammer <ghammer@redhat.com>
*
+ * Changes (itamar@guardicore.com):
+ * - file attributes, removal, hashes
+ *
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
@@ -742,3 +745,139 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
}
ga_command_state_add(cs, guest_file_init, NULL);
}
+
+/* The CRT of Windows has a number of flaws wrt. its stat() implementation:
+ - time stamps are restricted to second resolution
+ - file modification times suffer from forth-and-back conversions between
+ UTC and local time
+ Therefore, we implement our own stat, based on the Win32 API directly.
+*/
+
+/* Seconds between 1.1.1601 and 1.1.1970 */
+static __int64 secs_between_epochs = 11644473600;
+
+static void
+FILE_TIME_to_time_t_nsec(FILETIME *in_ptr, time_t *time_out, int* nsec_out)
+{
+ /* XXX endianness. Shouldn't matter, as all Windows implementations are
+ little-endian */
+ /* Cannot simply cast and dereference in_ptr,
+ since it might not be aligned properly */
+ __int64 in;
+ memcpy(&in, in_ptr, sizeof(in));
+ *nsec_out = (int)(in % 10000000) * 100; /* FILETIME is in units of 100
+ nsec. */
+ *time_out = (time_t)((in / 10000000) - secs_between_epochs);
+}
+
+static int
+attributes_to_mode(DWORD attr)
+{
+ int m = 0;
+ if (0 != (attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ m |= _S_IFDIR | 0111; /* IFEXEC for user,group,other */
+ } else {
+ m |= _S_IFREG;
+ }
+ if (0 != (attr & FILE_ATTRIBUTE_READONLY)) {
+ m |= 0444;
+ } else {
+ m |= 0666;
+ }
+ return m;
+}
+
+static int
+attribute_data_to_stat(WIN32_FILE_ATTRIBUTE_DATA *info,
+ GuestFileAttributes *result)
+{
+ int nsec = 0;
+
+ memset(result, 0, sizeof(*result));
+ result->mode = attributes_to_mode(info->dwFileAttributes);
+ result->size = (((__int64)info->nFileSizeHigh)<<32) + info->nFileSizeLow;
+ FILE_TIME_to_time_t_nsec(&info->ftCreationTime,
+ (time_t *)&result->creation_time, &nsec);
+ FILE_TIME_to_time_t_nsec(&info->ftLastWriteTime,
+ (time_t *)&result->modification_time, &nsec);
+ FILE_TIME_to_time_t_nsec(&info->ftLastAccessTime,
+ (time_t *)&result->access_time, &nsec);
+
+ return 0;
+}
+
+static BOOL
+attributes_from_dir(LPCSTR pszFile, LPWIN32_FILE_ATTRIBUTE_DATA pfad)
+{
+ HANDLE hFindFile;
+ WIN32_FIND_DATAA FileData;
+ hFindFile = FindFirstFileA(pszFile, &FileData);
+ if (hFindFile == INVALID_HANDLE_VALUE) {
+ return FALSE;
+ }
+ (void)FindClose(hFindFile);
+ pfad->dwFileAttributes = FileData.dwFileAttributes;
+ pfad->ftCreationTime = FileData.ftCreationTime;
+ pfad->ftLastAccessTime = FileData.ftLastAccessTime;
+ pfad->ftLastWriteTime = FileData.ftLastWriteTime;
+ pfad->nFileSizeHigh = FileData.nFileSizeHigh;
+ pfad->nFileSizeLow = FileData.nFileSizeLow;
+ return TRUE;
+}
+
+/* based upon Python implementation of windows file stat() */
+GuestFileAttributes *qmp_guest_file_attributes(const char *path, Error **errp)
+{
+ WIN32_FILE_ATTRIBUTE_DATA info;
+ int code;
+ char *dot = NULL;
+ GuestFileAttributes *result = NULL;
+
+ if (!GetFileAttributesExA(path, GetFileExInfoStandard, &info)) {
+ if (GetLastError() != ERROR_SHARING_VIOLATION) {
+ error_setg(errp, "Failed querying '%s' for attributes (error %d)",
+ path, (int)GetLastError());
+ /* Protocol violation: we explicitly clear errno, instead of
+ setting it to a POSIX error. Callers should use GetLastError. */
+ errno = 0;
+ return NULL;
+ } else {
+ /* Could not get attributes on open file. Fall back to
+ reading the directory. */
+ if (!attributes_from_dir(path, &info)) {
+ error_setg(errp, "Error querying '%s' attributes (error %d)",
+ path, (int)GetLastError());
+ /* Very strange. This should not fail now */
+ errno = 0;
+ return NULL;
+ }
+ }
+ }
+
+ result = g_malloc(sizeof(GuestFileAttributes));
+ if (NULL == result) {
+ error_setg(errp, "No memory for attributes result for '%s' (error %d)",
+ path, errno);
+ return NULL;
+ }
+
+ code = attribute_data_to_stat(&info, result);
+ if (code != 0) {
+ g_free(result);
+ error_setg(errp, "Error converting attributes result '%s' (code %d)",
+ path, code);
+ return NULL;
+ }
+ /* Set S_IFEXEC if it is an .exe, .bat, ... */
+ dot = strrchr(path, '.');
+ if (dot) {
+ if (stricmp(dot, ".bat") == 0 ||
+ stricmp(dot, ".cmd") == 0 ||
+ stricmp(dot, ".exe") == 0 ||
+ stricmp(dot, ".com") == 0) {
+ result->mode |= 0111;
+ }
+ }
+ return result;
+}
+
diff --git a/qga/commands.c b/qga/commands.c
index f9378f4..64404d6 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -113,3 +113,11 @@ char *qmp_guest_file_hash(const char *path, Error **errp)
return g_strdup((char *)sha256_hexdigest);
}
+
+void qmp_guest_file_remove(const char *path, Error **errp)
+{
+ if (unlink(path)) {
+ error_setg(errp, "Error deleting file '%s' (error %d)", path, errno);
+ errno = 0;
+ }
+}
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 68b420d..cc670b2 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -904,3 +904,45 @@
{ 'command': 'guest-file-hash',
'data': { 'path': 'str' },
'returns': 'str' }
+
+##
+# @GuestFileAttributes
+#
+# @mode: file access permissions mode
+# @size: file size in bytes
+# @access-time: file last access time
+# @modification-time: file last modification time
+# @creation-time: file creation time (when possible)
+#
+# Since: 2.4
+##
+{ 'type': 'GuestFileAttributes',
+ 'data': {'mode': 'int', 'size': 'uint64', 'access-time': 'int',
+ 'modification-time': 'int', 'creation-time': 'int'
+ }}
+
+##
+# @guest-file-attributes:
+#
+# Get file attributes for a file in the guest's operating system
+# for a given file path
+#
+# Returns: file attributes structure (GuestFileAttributes type)
+#
+# Since 2.4
+##
+{ 'command': 'guest-file-attributes',
+ 'data': { 'path': 'str' },
+ 'returns': 'GuestFileAttributes'}
+
+##
+# @guest-file-remove:
+#
+# Remove a file in the guest's operating system for a given file path
+#
+# Returns:
+#
+# Since 2.4
+##
+{ 'command': 'guest-file-remove',
+ 'data': { 'path': 'str' }}
--
2.3.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [Qemu-devel] [PATCH 4/4] added qga processes and connections enumeration commands (win/linux)
2015-03-31 8:34 [Qemu-devel] [PATCH 0/4] qga added functionality itamar.tal4
` (2 preceding siblings ...)
2015-03-31 8:34 ` [Qemu-devel] [PATCH 3/4] added qga system uptime and hostname commands itamar.tal4
@ 2015-03-31 8:34 ` itamar.tal4
3 siblings, 0 replies; 5+ messages in thread
From: itamar.tal4 @ 2015-03-31 8:34 UTC (permalink / raw)
To: qemu-devel; +Cc: ori, ariel, mdroth, pavel, Itamar Tal
From: Itamar Tal <itamar@guardicore.com>
This patch add more missing commands to the qga (both Windows and Linux):
- current active process list
- current active connections list
the abovementioned commands are added to align the guest agent functionality
with other common hypervisor agents.
---
qga/commands-posix.c | 608 ++++++++++++++++++++++++++++++++++++++++++++++++
qga/commands-win32.c | 404 +++++++++++++++++++++++++++++++-
qga/qapi-schema.json | 99 ++++++++
qga/win32-definitions.h | 110 +++++++++
qga/win32-iptypes.h | 412 ++++++++++++++++++++++++++++++++
5 files changed, 1631 insertions(+), 2 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 38b639c..c4b82eb 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -9,6 +9,7 @@
*
* Changes (itamar@guardicore.com):
* - file attributes, removal, hashes
+ * - added process and connection enumeration support
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -50,6 +51,53 @@ extern char **environ;
#include <sys/socket.h>
#include <net/if.h>
+#define PRG_HASH_SIZE (211)
+
+typedef
+struct prg_node {
+ struct prg_node *next;
+ int inode;
+ int pid;
+} prg_node;
+
+typedef
+struct prg_hash {
+ struct prg_node *hash[PRG_HASH_SIZE];
+} prg_hash;
+
+#define PRG_LOCAL_ADDRESS "local_address"
+#define PRG_INODE "inode"
+#define PRG_SOCKET_PFX "socket:["
+#define PRG_SOCKET_PFXl (strlen(PRG_SOCKET_PFX))
+#define PRG_SOCKET_PFX2 "[0000]:"
+#define PRG_SOCKET_PFX2l (strlen(PRG_SOCKET_PFX2))
+
+#define PRG_HASHIT(x) ((x) % PRG_HASH_SIZE)
+#ifndef LINE_MAX
+#define LINE_MAX 4096
+#endif
+#define PATH_PROC "/proc"
+#define PATH_FD_SUFF "fd"
+#define PATH_FD_SUFFl strlen(PATH_FD_SUFF)
+#define PATH_PROC_X_FD PATH_PROC "/%s/" PATH_FD_SUFF
+#define PATH_CMDLINE "cmdline"
+#define PATH_CMDLINEl strlen(PATH_CMDLINE)
+
+/* These enums are used by IPX too. :-( */
+enum {
+ TCP_ESTABLISHED = 1,
+ TCP_SYN_SENT,
+ TCP_SYN_RECV,
+ TCP_FIN_WAIT1,
+ TCP_FIN_WAIT2,
+ TCP_TIME_WAIT,
+ TCP_CLOSE,
+ TCP_CLOSE_WAIT,
+ TCP_LAST_ACK,
+ TCP_LISTEN,
+ TCP_CLOSING /* now a valid state */
+};
+
#ifdef FIFREEZE
#define CONFIG_FSFREEZE
#endif
@@ -2491,4 +2539,564 @@ uint32_t qmp_guest_uptime(Error **errp)
return sys_info.uptime;
}
+GuestProcessInfoList *qmp_guest_get_process_list(Error **errp)
+{
+ struct dirent *ent = NULL;
+ GuestProcessInfoList *process_list_head = NULL;
+ GuestProcessInfoList *process_list_last = NULL;
+
+ DIR *procfs = opendir("/proc");
+
+ if (NULL == procfs) {
+ error_setg(errp, "Failed opening procfs (error %d)", errno);
+ return NULL;
+ }
+
+ /* read entries until EOD */
+ errno = 0;
+ while (NULL != (ent = readdir(procfs))) {
+ int pid = strtoul(ent->d_name, NULL, 10);
+ GuestProcessInfoList *list_item = NULL;
+ char local_path[256];
+ FILE *info_file = NULL;
+ char info[256];
+ size_t len;
+
+ /* ensure directory name is a number */
+ if ((0 == pid) || (ULONG_MAX == pid)) {
+ continue;
+ }
+
+ list_item = g_malloc0(sizeof(GuestProcessInfoList));
+ 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 = pid;
+
+ /* read process name */
+ snprintf(local_path, 256, "/proc/%d/comm", pid);
+ info_file = fopen(local_path, "rb");
+ if (NULL != info_file) {
+ len = fread(info, 1, 256, info_file);
+ info[len] = '\0';
+ if ((0 < len) && ('\n' == info[len - 1])) {
+ info[len - 1] = '\0';
+ }
+ list_item->value->process_name = g_strdup(info);
+
+ fclose(info_file);
+ }
+
+ /* read process parent id */
+ snprintf(local_path, 256, "/proc/%d/status", pid);
+ info_file = fopen(local_path, "rb");
+ if (NULL != info_file) {
+ char *line = NULL;
+ size_t line_size = 0;
+
+ /* read lines, search for ppid */
+ for (;;) {
+ len = getline(&line, &line_size, info_file);
+ if (-1 == len) {
+ break;
+ }
+
+ if (line == strstr(line, "PPid:")) {
+ list_item->value->parent_id = strtoul(line + 5, NULL, 10);
+ }
+ }
+
+ fclose(info_file);
+ }
+
+ /* read process session id */
+ snprintf(local_path, 256, "/proc/%d/sessionid", pid);
+ info_file = fopen(local_path, "rb");
+ if (NULL != info_file) {
+ len = fread(info, 1, 256, info_file);
+ info[len] = '\0';
+ list_item->value->session_id = strtoul(info, NULL, 10);
+
+ fclose(info_file);
+ }
+
+ /* read process image path */
+ snprintf(local_path, 256, "/proc/%d/exe", pid);
+ len = readlink(local_path, info, sizeof(info) - 1);
+ if (-1 != len) {
+ info[len] = '\0';
+ list_item->value->image_path = g_strdup(info);
+ }
+
+ errno = 0;
+ }
+
+ (void)closedir(procfs);
+
+ if (0 != errno) {
+ error_setg(errp, "Error while reading procfs directory (error %d)",
+ errno);
+ return NULL;
+ }
+
+ return process_list_head;
+}
+
+/* this function is taken out of netstat (found in net-tools) and modified */
+static void prg_cache_add(prg_hash *hash, int inode, int pid)
+{
+ unsigned hi = PRG_HASHIT(inode);
+ struct prg_node **pnp = NULL, *pn = NULL;
+
+ /* look for the inode in the table or find next free entry for the inode */
+ for (pnp = &hash->hash[hi]; (pn = *pnp); pnp = &pn->next) {
+ if (pn->inode == inode) {
+ /* Some warning should be appropriate here
+ as we got multiple processes for one i-node */
+ return;
+ }
+ }
+
+ /* allocate new entry for this inode */
+ *pnp = g_malloc0(sizeof(struct prg_node));
+ if (NULL == *pnp) {
+ return;
+ }
+
+ pn = *pnp;
+ pn->next = NULL;
+ pn->inode = inode;
+ pn->pid = pid;
+}
+
+/* this function is taken out of netstat (found in net-tools) and modified */
+static int prg_cache_get(prg_hash *hash, int inode)
+{
+ unsigned hi = PRG_HASHIT(inode);
+ struct prg_node *pn = NULL;
+
+ for (pn = hash->hash[hi]; pn; pn = pn->next) {
+ if (pn->inode == inode) {
+ return pn->pid;
+ }
+ }
+
+ return -1;
+}
+
+/* this function is taken out of netstat (found in net-tools) and modified */
+static void prg_cache_clear(prg_hash *hash)
+{
+ struct prg_node **pnp = NULL, *pn = NULL;
+
+
+ for (pnp = hash->hash; pnp < hash->hash + PRG_HASH_SIZE; pnp++) {
+ pn = *pnp;
+ while (NULL != pn) {
+ *pnp = pn->next;
+ free(pn);
+ pn = *pnp;
+ }
+ }
+}
+
+/* this function is taken out of netstat (found in net-tools) and modified */
+static void extract_type_1_socket_inode(const char lname[], long *inode_p)
+{
+
+ /* If lname is of the form "socket:[12345]", extract the "12345"
+ as *inode_p. Otherwise, return -1 as *inode_p.
+ */
+
+ if (strlen(lname) < PRG_SOCKET_PFXl+3) {
+ *inode_p = -1;
+ } else if (memcmp(lname, PRG_SOCKET_PFX, PRG_SOCKET_PFXl)) {
+ *inode_p = -1;
+ } else if (lname[strlen(lname)-1] != ']') {
+ *inode_p = -1;
+ } else {
+ char inode_str[strlen(lname + 1)]; /* e.g. "12345" */
+ const int inode_str_len = strlen(lname) - PRG_SOCKET_PFXl - 1;
+ char *serr = NULL;
+
+ strncpy(inode_str, lname + PRG_SOCKET_PFXl, inode_str_len);
+ inode_str[inode_str_len] = '\0';
+ *inode_p = strtol(inode_str, &serr, 0);
+ if (!serr || *serr || *inode_p < 0 || *inode_p >= INT_MAX) {
+ *inode_p = -1;
+ }
+ }
+}
+
+/* this function is taken out of netstat (found in net-tools) and modified */
+static void extract_type_2_socket_inode(const char lname[], long *inode_p)
+{
+
+ /* If lname is of the form "[0000]:12345", extract the "12345"
+ as *inode_p. Otherwise, return -1 as *inode_p.
+ */
+
+ if (strlen(lname) < PRG_SOCKET_PFX2l+1) {
+ *inode_p = -1;
+ } else if (memcmp(lname, PRG_SOCKET_PFX2, PRG_SOCKET_PFX2l)) {
+ *inode_p = -1;
+ } else {
+ char *serr = NULL;
+
+ *inode_p = strtol(lname + PRG_SOCKET_PFX2l, &serr, 0);
+ if (!serr || *serr || *inode_p < 0 || *inode_p >= INT_MAX) {
+ *inode_p = -1;
+ }
+ }
+}
+
+/* this function is taken out of netstat (found in net-tools) and modified */
+static int prg_cache_load(prg_hash *hash)
+{
+ char line[LINE_MAX];
+ int procfdlen, lnamelen;
+ char lname[30];
+ long inode;
+ const char *cs = NULL;
+ DIR *dirproc = NULL, *dirfd = NULL;
+ struct dirent *direproc = NULL, *direfd = NULL;
+
+ /* reset the hash table */
+ memset(hash, 0, sizeof(prg_hash));
+
+ dirproc = opendir(PATH_PROC);
+ if (NULL == dirproc) {
+ return 0;
+ }
+
+ for (;;) {
+ int pid = 0;
+
+ errno = 0;
+ direproc = readdir(dirproc);
+ if (NULL == direproc) {
+ break;
+ }
+
+#ifdef DIRENT_HAVE_D_TYPE_WORKS
+ if (direproc->d_type != DT_DIR) {
+ continue;
+ }
+#endif
+ for (cs = direproc->d_name; *cs; cs++) {
+ if (!isdigit(*cs)) {
+ break;
+ }
+ }
+ if (*cs) {
+ continue;
+ }
+
+ procfdlen = snprintf(line, sizeof(line),
+ PATH_PROC_X_FD, direproc->d_name);
+ if ((procfdlen <= 0) || (procfdlen >= sizeof(line) - 5)) {
+ continue;
+ }
+ errno = 0;
+ dirfd = opendir(line);
+ if (NULL == dirfd) {
+ continue;
+ }
+ line[procfdlen] = '/';
+ for (;;) {
+ errno = 0;
+ direfd = readdir(dirfd);
+ if (NULL == direfd) {
+ break;
+ }
+
+#ifdef DIRENT_HAVE_D_TYPE_WORKS
+ if (direfd->d_type != DT_LNK) {
+ continue;
+ }
+#endif
+ if (procfdlen + 1 + strlen(direfd->d_name) + 1 > sizeof(line)) {
+ continue;
+ }
+ memcpy(line + procfdlen - PATH_FD_SUFFl, PATH_FD_SUFF "/",
+ PATH_FD_SUFFl + 1);
+ strcpy(line + procfdlen + 1, direfd->d_name);
+ lnamelen = readlink(line, lname, sizeof(lname)-1);
+ lname[lnamelen] = '\0'; /*make it a null-terminated string*/
+
+ extract_type_1_socket_inode(lname, &inode);
+
+ if (inode < 0) {
+ extract_type_2_socket_inode(lname, &inode);
+ }
+
+ if (inode < 0) {
+ continue;
+ }
+
+ pid = strtoul(direproc->d_name, NULL, 10);
+ prg_cache_add(hash, inode, pid);
+ }
+ closedir(dirfd);
+ dirfd = NULL;
+ }
+ if (dirproc) {
+ closedir(dirproc);
+ }
+ if (dirfd) {
+ closedir(dirfd);
+ }
+
+ return 1;
+}
+
+/* this function is taken out of netstat (found in net-tools) and modified */
+static GuestActiveConnection *udp_do_one(prg_hash *hash, const char *line)
+{
+ GuestActiveConnection *conn = NULL;
+ char local_addr[64], rem_addr[64];
+ char more[512];
+ int num, local_port, rem_port, d, state, timer_run, uid, timeout;
+ struct sockaddr_in localaddr, remaddr;
+ unsigned long rxq, txq, time_len, retr, inode;
+
+ more[0] = '\0';
+ num = sscanf(line,
+ "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X"
+ " %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+ &d, local_addr, &local_port,
+ rem_addr, &rem_port, &state,
+ &txq, &rxq, &timer_run, &time_len,
+ &retr, &uid, &timeout, &inode, more);
+
+ if (strlen(local_addr) > 8) {
+ /* ipv6 support is missing */
+ } else {
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+ sscanf(rem_addr, "%X",
+ &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+ ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ }
+
+ retr = 0L;
+
+ if (num < 10) {
+ return NULL;
+ }
+
+ conn = g_malloc0(sizeof(GuestActiveConnection));
+
+ if (AF_INET == localaddr.sin_family) {
+ conn->source_addr = g_strdup(inet_ntoa(localaddr.sin_addr));
+ conn->dest_addr = g_strdup(inet_ntoa(remaddr.sin_addr));
+ }
+
+ conn->source_port = local_port;
+ conn->dest_port = rem_port;
+ conn->owner_process_id = prg_cache_get(hash, inode);
+ conn->if_family = GUEST_IP_ADDRESS_TYPE_IPV4;
+ conn->protocol = GUEST_IP_PROTOCOL_UDP;
+
+ switch (state) {
+ case TCP_CLOSE:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_CLOSED;
+ break;
+ case TCP_ESTABLISHED:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_ESTABLISHED;
+ break;
+ default:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_LISTEN;
+ break;
+ }
+
+ return conn;
+}
+
+/* this function is taken out of netstat (found in net-tools) and modified */
+static GuestActiveConnection *tcp_do_one(prg_hash *hash, const char *line)
+{
+ unsigned long rxq, txq, time_len, retr, inode;
+ int num, local_port, rem_port, d, state, uid, timer_run, timeout;
+ char rem_addr[128], local_addr[128], more[512];
+ struct sockaddr_in localaddr, remaddr;
+ GuestActiveConnection *conn = NULL;
+
+ num = sscanf(line,
+ "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X"
+ " %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
+ &d, local_addr, &local_port, rem_addr, &rem_port, &state,
+ &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout,
+ &inode, more);
+
+ if (strlen(local_addr) > 8) {
+ /* ipv6 support is missing */
+ } else {
+ sscanf(local_addr, "%X",
+ &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
+ sscanf(rem_addr, "%X",
+ &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
+ ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
+ ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
+ }
+
+ if (num < 11) {
+ return NULL;
+ }
+
+ if (state == TCP_LISTEN) {
+ time_len = 0;
+ retr = 0L;
+ rxq = 0L;
+ txq = 0L;
+ }
+
+ conn = g_malloc0(sizeof(GuestActiveConnection));
+
+ if (AF_INET == localaddr.sin_family) {
+ conn->source_addr = g_strdup(inet_ntoa(localaddr.sin_addr));
+ conn->dest_addr = g_strdup(inet_ntoa(remaddr.sin_addr));
+ }
+
+ conn->source_port = local_port;
+ conn->dest_port = rem_port;
+ conn->owner_process_id = prg_cache_get(hash, inode);
+ conn->if_family = GUEST_IP_ADDRESS_TYPE_IPV4;
+ conn->protocol = GUEST_IP_PROTOCOL_TCP;
+
+ switch (state) {
+ case TCP_CLOSE:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_CLOSED;
+ break;
+ case TCP_LISTEN:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_LISTEN;
+ break;
+ case TCP_SYN_SENT:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_SYN_SENT;
+ break;
+ case TCP_SYN_RECV:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_SYN_RCVD;
+ break;
+ case TCP_ESTABLISHED:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_ESTABLISHED;
+ break;
+ case TCP_FIN_WAIT1:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_FIN_WAIT1;
+ break;
+ case TCP_FIN_WAIT2:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_FIN_WAIT2;
+ break;
+ case TCP_CLOSE_WAIT:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_CLOSE_WAIT;
+ break;
+ case TCP_CLOSING:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_CLOSING;
+ break;
+ case TCP_LAST_ACK:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_LAST_ACK;
+ break;
+ case TCP_TIME_WAIT:
+ conn->state = GUEST_TCP_PROTOCOL_STATE_TIME_WAIT;
+ break;
+ }
+
+ return conn;
+}
+
+GuestActiveConnectionList *qmp_guest_get_active_connections(Error **errp)
+{
+ GuestActiveConnectionList *conn_list_head = NULL;
+ GuestActiveConnectionList *conn_list_last = NULL;
+ prg_hash hash;
+ FILE *udp_info = NULL;
+ FILE *tcp_info = fopen("/proc/net/tcp", "r");
+ int i = 0;
+
+ if (NULL == tcp_info) {
+ error_setg(errp, "Error opening '/proc/net/tcp' (error %d)", errno);
+ return NULL;
+ }
+
+ prg_cache_load(&hash);
+
+ for (i = 0; !feof(tcp_info); ++i) {
+ char tcp_info_line[1024];
+ GuestActiveConnectionList *conn_item = NULL;
+ GuestActiveConnection *conn = NULL;
+
+ if (NULL == fgets(tcp_info_line, 1024, tcp_info)) {
+ continue;
+ }
+
+ if (i == 0) {
+ continue;
+ }
+
+ conn = tcp_do_one(&hash, tcp_info_line);
+ if (NULL == conn) {
+ continue;
+ }
+
+ /* add connection to the end of list */
+ conn_item = g_malloc0(sizeof(GuestActiveConnectionList));
+ if (NULL == conn_list_head) {
+ conn_list_head = conn_item;
+ }
+ if (NULL != conn_list_last) {
+ conn_list_last->next = conn_item;
+ }
+ conn_list_last = conn_item;
+ conn_item->value = conn;
+ }
+
+ fclose(tcp_info);
+
+ udp_info = fopen("/proc/net/udp", "r");
+ if (NULL == udp_info) {
+ error_setg(errp, "Error opening '/proc/net/udp' (error %d)", errno);
+ return NULL;
+ }
+
+ for (i = 0; !feof(udp_info); ++i) {
+ char udp_info_line[1024];
+ GuestActiveConnectionList *conn_item = NULL;
+ GuestActiveConnection *conn = NULL;
+
+ if (NULL == fgets(udp_info_line, 1024, udp_info)) {
+ continue;
+ }
+
+ if (i == 0) {
+ continue;
+ }
+
+ conn = udp_do_one(&hash, udp_info_line);
+ if (NULL == conn) {
+ continue;
+ }
+
+ /* add connection to the end of list */
+ conn_item = g_malloc0(sizeof(GuestActiveConnectionList));
+ if (NULL == conn_list_head) {
+ conn_list_head = conn_item;
+ }
+ if (NULL != conn_list_last) {
+ conn_list_last->next = conn_item;
+ }
+ conn_list_last = conn_item;
+ conn_item->value = conn;
+ }
+
+ prg_cache_clear(&hash);
+
+ return conn_list_head;
+}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 6987f4e..30dcd50 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -9,6 +9,7 @@
*
* Changes (itamar@guardicore.com):
* - file attributes, removal, hashes
+ * - added process and connection enumeration support
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -22,6 +23,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"
@@ -29,6 +31,10 @@
#define SHTDN_REASON_FLAG_PLANNED 0x80000000
#endif
+#ifndef MAX_PATH
+#define MAX_PATH (256)
+#endif
+
/* 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 * \
@@ -37,6 +43,38 @@
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+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,
+ BOOL 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;
+
typedef struct GuestFileHandle {
int64_t id;
HANDLE fh;
@@ -386,17 +424,59 @@ static void guest_file_init(void)
}
int ga_win_commands_init(void)
-{
+ {
+ HMODULE ntdll_module = GetModuleHandle("ntdll.dll");
+ HMODULE iphlpapi_module = LoadLibrary("iphlpapi.dll");
WSADATA wsa_data = {0};
+ if ((NULL == ntdll_module) ||
+ (NULL == iphlpapi_module)) {
+ goto error;
+ }
+
if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) {
g_critical("failed to initialize WSA engine");
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;
+ }
+
+ 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:
+ error:
+ (void)FreeLibrary(iphlpapi_module);
+
return 0;
}
@@ -902,4 +982,324 @@ uint32_t qmp_guest_uptime(Error **errp)
return uptime_milli / 1000;
}
+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;
+ DWORD current_process_id = GetCurrentProcessId();
+ 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, "No memory 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;
+
+ if (0 == list_item->value->process_id) {
+ list_item->value->process_name = g_strdup("System Idle Process");
+ process_list_iter = (void *)((uint32_t)process_list_iter +
+ process_list_iter->NextEntryOffset);
+ continue;
+ } else if (4 == list_item->value->process_id) {
+ list_item->value->process_name = g_strdup("System");
+ process_list_iter = (void *)((uint32_t)process_list_iter +
+ process_list_iter->NextEntryOffset);
+ continue;
+ }
+
+ 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;
+ }
+
+ if (current_process_id != list_item->value->process_id) {
+ process_handle = OpenProcess(PROCESS_QUERY_INFORMATION,
+ FALSE,
+ list_item->value->process_id);
+ } else {
+ char curr_image_path[MAX_PATH];
+
+ process_handle = GetCurrentProcess();
+
+ if (GetModuleFileName(NULL,
+ curr_image_path,
+ MAX_PATH)) {
+ list_item->value->image_path = g_strdup(&curr_image_path[0]);
+ }
+ }
+
+ if (NULL != process_handle) {
+ /* get process parent ID */
+ 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;
+ }
+
+ /* get process image path */
+ if (NULL == list_item->value->image_path) {
+ process_image_path = g_malloc0(sizeof(UNICODE_STRING) +
+ MAX_PATH * sizeof(wchar_t));
+
+ status = NtQueryInformationProcess(process_handle,
+ ProcessImageFileName,
+ process_image_path,
+ sizeof(UNICODE_STRING) +
+ MAX_PATH * 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) {
+ MIB_UDPROW_OWNER_MODULE *udp_row = &udp_table->table[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_row->dwLocalAddr));
+ new_item->value->source_port = htons(udp_row->dwLocalPort);
+ new_item->value->owner_process_id = udp_row->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_row->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) {
+ MIB_TCPROW_OWNER_MODULE *tcp_row = &tcp_table->table[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_row->dwLocalAddr));
+ new_item->value->source_port = htons(tcp_row->dwLocalPort);
+ new_item->value->dest_addr =
+ g_strdup(inet_ntoa(*(struct in_addr *)&tcp_row->dwRemoteAddr));
+ new_item->value->dest_port = htons(tcp_row->dwRemotePort);
+ new_item->value->owner_process_id = tcp_row->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_row->liCreateTimestamp.QuadPart;
+
+ switch (tcp_row->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/qapi-schema.json b/qga/qapi-schema.json
index 61f8a5a..9df985d 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -971,3 +971,102 @@
{ 'command': 'guest-uptime',
'returns': 'uint32' }
+##
+# @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.4
+##
+{ '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.4
+##
+{ 'command': 'guest-get-process-list',
+ 'returns': ['GuestProcessInfo'] }
+
+##
+# @GuestIpProtocol:
+#
+# An enumeration of supported IP protocols
+#
+# @tcp: TCP
+#
+# @udp: UDP
+#
+# Since: 2.4
+##
+{ '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.4
+##
+{ '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: ipv4 / ipv6
+# @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.4
+##
+{ '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.4
+##
+{ '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..c4f0a2b
--- /dev/null
+++ b/qga/win32-definitions.h
@@ -0,0 +1,110 @@
+
+#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..ec55300
--- /dev/null
+++ b/qga/win32-iptypes.h
@@ -0,0 +1,412 @@
+/*++
+
+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
+#define MAX_ADAPTER_NAME_LENGTH 256
+#define MAX_ADAPTER_ADDRESS_LENGTH 8
+#define DEFAULT_MINIMUM_ENTITIES 32
+#define MAX_HOSTNAME_LEN 128
+#define MAX_DOMAIN_NAME_LEN 128
+#define MAX_SCOPE_ID_LEN 256
+
+/*
+ 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_UNKNOWN = 0,
+ 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] 5+ messages in thread