summary refs log tree commit diff stats
path: root/qga/commands-win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'qga/commands-win32.c')
-rw-r--r--qga/commands-win32.c105
1 files changed, 104 insertions, 1 deletions
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 04026eedbf..439d229225 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -11,6 +11,9 @@
  * See the COPYING file in the top-level directory.
  */
 
+#ifndef _WIN32_WINNT
+#   define _WIN32_WINNT 0x0600
+#endif
 #include "qemu/osdep.h"
 #include <wtypes.h>
 #include <powrprof.h>
@@ -25,6 +28,7 @@
 #include <initguid.h>
 #endif
 #include <lm.h>
+#include <wtsapi32.h>
 
 #include "qga/guest-agent-core.h"
 #include "qga/vss-win32.h"
@@ -1344,7 +1348,7 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
                     vcpu = g_malloc0(sizeof *vcpu);
                     vcpu->logical_id = current++;
                     vcpu->online = true;
-                    vcpu->has_can_offline = false;
+                    vcpu->has_can_offline = true;
 
                     entry = g_malloc0(sizeof *entry);
                     entry->value = vcpu;
@@ -1536,3 +1540,102 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
         ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
     }
 }
+
+/* MINGW is missing two fields: IncomingFrames & OutgoingFrames */
+typedef struct _GA_WTSINFOA {
+    WTS_CONNECTSTATE_CLASS State;
+    DWORD SessionId;
+    DWORD IncomingBytes;
+    DWORD OutgoingBytes;
+    DWORD IncomingFrames;
+    DWORD OutgoingFrames;
+    DWORD IncomingCompressedBytes;
+    DWORD OutgoingCompressedBy;
+    CHAR WinStationName[WINSTATIONNAME_LENGTH];
+    CHAR Domain[DOMAIN_LENGTH];
+    CHAR UserName[USERNAME_LENGTH + 1];
+    LARGE_INTEGER ConnectTime;
+    LARGE_INTEGER DisconnectTime;
+    LARGE_INTEGER LastInputTime;
+    LARGE_INTEGER LogonTime;
+    LARGE_INTEGER CurrentTime;
+
+} GA_WTSINFOA;
+
+GuestUserList *qmp_guest_get_users(Error **err)
+{
+#if (_WIN32_WINNT >= 0x0600)
+#define QGA_NANOSECONDS 10000000
+
+    GHashTable *cache = NULL;
+    GuestUserList *head = NULL, *cur_item = NULL;
+
+    DWORD buffer_size = 0, count = 0, i = 0;
+    GA_WTSINFOA *info = NULL;
+    WTS_SESSION_INFOA *entries = NULL;
+    GuestUserList *item = NULL;
+    GuestUser *user = NULL;
+    gpointer value = NULL;
+    INT64 login = 0;
+    double login_time = 0;
+
+    cache = g_hash_table_new(g_str_hash, g_str_equal);
+
+    if (WTSEnumerateSessionsA(NULL, 0, 1, &entries, &count)) {
+        for (i = 0; i < count; ++i) {
+            buffer_size = 0;
+            info = NULL;
+            if (WTSQuerySessionInformationA(
+                NULL,
+                entries[i].SessionId,
+                WTSSessionInfo,
+                (LPSTR *)&info,
+                &buffer_size
+            )) {
+
+                if (strlen(info->UserName) == 0) {
+                    WTSFreeMemory(info);
+                    continue;
+                }
+
+                login = info->LogonTime.QuadPart;
+                login -= W32_FT_OFFSET;
+                login_time = ((double)login) / QGA_NANOSECONDS;
+
+                if (g_hash_table_contains(cache, info->UserName)) {
+                    value = g_hash_table_lookup(cache, info->UserName);
+                    user = (GuestUser *)value;
+                    if (user->login_time > login_time) {
+                        user->login_time = login_time;
+                    }
+                } else {
+                    item = g_new0(GuestUserList, 1);
+                    item->value = g_new0(GuestUser, 1);
+
+                    item->value->user = g_strdup(info->UserName);
+                    item->value->domain = g_strdup(info->Domain);
+                    item->value->has_domain = true;
+
+                    item->value->login_time = login_time;
+
+                    g_hash_table_add(cache, item->value->user);
+
+                    if (!cur_item) {
+                        head = cur_item = item;
+                    } else {
+                        cur_item->next = item;
+                        cur_item = item;
+                    }
+                }
+            }
+            WTSFreeMemory(info);
+        }
+        WTSFreeMemory(entries);
+    }
+    g_hash_table_destroy(cache);
+    return head;
+#else
+    error_setg(err, QERR_UNSUPPORTED);
+    return NULL;
+#endif
+}