|
|
3f2699 |
From c456b78c44d3f132b06d5b77de0de5e6e81870e7 Mon Sep 17 00:00:00 2001
|
|
|
3f2699 |
From: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
3f2699 |
Date: Thu, 19 Apr 2018 12:33:43 -0300
|
|
|
3f2699 |
Subject: [PATCH 2/7] qga: Add 'guest-get-users' command
|
|
|
3f2699 |
MIME-Version: 1.0
|
|
|
3f2699 |
Content-Type: text/plain; charset=UTF-8
|
|
|
3f2699 |
Content-Transfer-Encoding: 8bit
|
|
|
3f2699 |
|
|
|
3f2699 |
RH-Author: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
3f2699 |
Message-id: <9c0e4bb70a07f0e6cc94bab75d0aedd193f21be8.1524139831.git.mrezanin@redhat.com>
|
|
|
3f2699 |
Patchwork-id: 79708
|
|
|
3f2699 |
O-Subject: [RHEL-7.5.z qemu-guest-agent PATCH 2/7] qga: Add 'guest-get-users' command
|
|
|
3f2699 |
Bugzilla: 1598210
|
|
|
3f2699 |
RH-Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
|
|
|
3f2699 |
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
|
3f2699 |
RH-Acked-by: Tomáš Golembiovský <tgolembi@redhat.com>
|
|
|
3f2699 |
|
|
|
3f2699 |
From: Vinzenz Feenstra <vfeenstr@redhat.com>
|
|
|
3f2699 |
|
|
|
3f2699 |
A command that will list all currently logged in users, and the time
|
|
|
3f2699 |
since when they are logged in.
|
|
|
3f2699 |
|
|
|
3f2699 |
Examples:
|
|
|
3f2699 |
|
|
|
3f2699 |
virsh # qemu-agent-command F25 '{ "execute": "guest-get-users" }'
|
|
|
3f2699 |
{"return":[{"login-time":1490622289.903835,"user":"root"}]}
|
|
|
3f2699 |
|
|
|
3f2699 |
virsh # qemu-agent-command Win2k12r2 '{ "execute": "guest-get-users" }'
|
|
|
3f2699 |
{"return":[{"login-time":1490351044.670552,"domain":"LADIDA",
|
|
|
3f2699 |
"user":"Administrator"}]}
|
|
|
3f2699 |
|
|
|
3f2699 |
Signed-off-by: Vinzenz Feenstra <vfeenstr@redhat.com>
|
|
|
3f2699 |
* make g_hash_table_contains compat func inline to avoid
|
|
|
3f2699 |
unused warnings
|
|
|
3f2699 |
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
|
|
|
3f2699 |
(cherry picked from commit 161a56a9065feb6fa2f69cec6237a5c4e714b9d3)
|
|
|
3f2699 |
Signed-off-by: Wainer dos Santos Moschetta <wainersm@redhat.com>
|
|
|
3f2699 |
---
|
|
|
3f2699 |
configure | 2 +-
|
|
|
3f2699 |
include/glib-compat.h | 6 +++
|
|
|
3f2699 |
qga/commands-posix.c | 60 +++++++++++++++++++++++++++++
|
|
|
3f2699 |
qga/commands-win32.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
3f2699 |
qga/qapi-schema.json | 25 ++++++++++++
|
|
|
3f2699 |
5 files changed, 195 insertions(+), 1 deletion(-)
|
|
|
3f2699 |
|
|
|
3f2699 |
diff --git a/configure b/configure
|
|
|
3f2699 |
index 3770d7c263..ccd2a0e230 100755
|
|
|
3f2699 |
--- a/configure
|
|
|
3f2699 |
+++ b/configure
|
|
|
3f2699 |
@@ -724,7 +724,7 @@ if test "$mingw32" = "yes" ; then
|
|
|
3f2699 |
sysconfdir="\${prefix}"
|
|
|
3f2699 |
local_statedir=
|
|
|
3f2699 |
confsuffix=""
|
|
|
3f2699 |
- libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi -lnetapi32 $libs_qga"
|
|
|
3f2699 |
+ libs_qga="-lws2_32 -lwinmm -lpowrprof -lwtsapi32 -liphlpapi -lnetapi32 $libs_qga"
|
|
|
3f2699 |
fi
|
|
|
3f2699 |
|
|
|
3f2699 |
werror=""
|
|
|
3f2699 |
diff --git a/include/glib-compat.h b/include/glib-compat.h
|
|
|
3f2699 |
index acf254d2a0..f84ed5d64a 100644
|
|
|
3f2699 |
--- a/include/glib-compat.h
|
|
|
3f2699 |
+++ b/include/glib-compat.h
|
|
|
3f2699 |
@@ -217,6 +217,12 @@ static inline void g_hash_table_add(GHashTable *hash_table, gpointer key)
|
|
|
3f2699 |
{
|
|
|
3f2699 |
g_hash_table_replace(hash_table, key, key);
|
|
|
3f2699 |
}
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+static inline gboolean g_hash_table_contains(GHashTable *hash_table,
|
|
|
3f2699 |
+ gpointer key)
|
|
|
3f2699 |
+{
|
|
|
3f2699 |
+ return g_hash_table_lookup_extended(hash_table, key, NULL, NULL);
|
|
|
3f2699 |
+}
|
|
|
3f2699 |
#endif
|
|
|
3f2699 |
|
|
|
3f2699 |
#ifndef g_assert_true
|
|
|
3f2699 |
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
|
|
|
3f2699 |
index ea37c097cf..55ac4fe9e7 100644
|
|
|
3f2699 |
--- a/qga/commands-posix.c
|
|
|
3f2699 |
+++ b/qga/commands-posix.c
|
|
|
3f2699 |
@@ -15,6 +15,7 @@
|
|
|
3f2699 |
#include <sys/ioctl.h>
|
|
|
3f2699 |
#include <sys/wait.h>
|
|
|
3f2699 |
#include <dirent.h>
|
|
|
3f2699 |
+#include <utmpx.h>
|
|
|
3f2699 |
#include "qga/guest-agent-core.h"
|
|
|
3f2699 |
#include "qga-qmp-commands.h"
|
|
|
3f2699 |
#include "qapi/qmp/qerror.h"
|
|
|
3f2699 |
@@ -2512,3 +2513,62 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
|
|
|
3f2699 |
ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
|
|
|
3f2699 |
#endif
|
|
|
3f2699 |
}
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+#define QGA_MICRO_SECOND_TO_SECOND 1000000
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+static double ga_get_login_time(struct utmpx *user_info)
|
|
|
3f2699 |
+{
|
|
|
3f2699 |
+ double seconds = (double)user_info->ut_tv.tv_sec;
|
|
|
3f2699 |
+ double useconds = (double)user_info->ut_tv.tv_usec;
|
|
|
3f2699 |
+ useconds /= QGA_MICRO_SECOND_TO_SECOND;
|
|
|
3f2699 |
+ return seconds + useconds;
|
|
|
3f2699 |
+}
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+GuestUserList *qmp_guest_get_users(Error **err)
|
|
|
3f2699 |
+{
|
|
|
3f2699 |
+ GHashTable *cache = NULL;
|
|
|
3f2699 |
+ GuestUserList *head = NULL, *cur_item = NULL;
|
|
|
3f2699 |
+ struct utmpx *user_info = NULL;
|
|
|
3f2699 |
+ gpointer value = NULL;
|
|
|
3f2699 |
+ GuestUser *user = NULL;
|
|
|
3f2699 |
+ GuestUserList *item = NULL;
|
|
|
3f2699 |
+ double login_time = 0;
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ cache = g_hash_table_new(g_str_hash, g_str_equal);
|
|
|
3f2699 |
+ setutxent();
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ for (;;) {
|
|
|
3f2699 |
+ user_info = getutxent();
|
|
|
3f2699 |
+ if (user_info == NULL) {
|
|
|
3f2699 |
+ break;
|
|
|
3f2699 |
+ } else if (user_info->ut_type != USER_PROCESS) {
|
|
|
3f2699 |
+ continue;
|
|
|
3f2699 |
+ } else if (g_hash_table_contains(cache, user_info->ut_user)) {
|
|
|
3f2699 |
+ value = g_hash_table_lookup(cache, user_info->ut_user);
|
|
|
3f2699 |
+ user = (GuestUser *)value;
|
|
|
3f2699 |
+ login_time = ga_get_login_time(user_info);
|
|
|
3f2699 |
+ /* We're ensuring the earliest login time to be sent */
|
|
|
3f2699 |
+ if (login_time < user->login_time) {
|
|
|
3f2699 |
+ user->login_time = login_time;
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ continue;
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ item = g_new0(GuestUserList, 1);
|
|
|
3f2699 |
+ item->value = g_new0(GuestUser, 1);
|
|
|
3f2699 |
+ item->value->user = g_strdup(user_info->ut_user);
|
|
|
3f2699 |
+ item->value->login_time = ga_get_login_time(user_info);
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ g_hash_table_insert(cache, item->value->user, item->value);
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ if (!cur_item) {
|
|
|
3f2699 |
+ head = cur_item = item;
|
|
|
3f2699 |
+ } else {
|
|
|
3f2699 |
+ cur_item->next = item;
|
|
|
3f2699 |
+ cur_item = item;
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ endutxent();
|
|
|
3f2699 |
+ g_hash_table_destroy(cache);
|
|
|
3f2699 |
+ return head;
|
|
|
3f2699 |
+}
|
|
|
3f2699 |
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
|
|
|
3f2699 |
index 19d72b2411..fa99a8f846 100644
|
|
|
3f2699 |
--- a/qga/commands-win32.c
|
|
|
3f2699 |
+++ b/qga/commands-win32.c
|
|
|
3f2699 |
@@ -11,6 +11,9 @@
|
|
|
3f2699 |
* See the COPYING file in the top-level directory.
|
|
|
3f2699 |
*/
|
|
|
3f2699 |
|
|
|
3f2699 |
+#ifndef _WIN32_WINNT
|
|
|
3f2699 |
+# define _WIN32_WINNT 0x0600
|
|
|
3f2699 |
+#endif
|
|
|
3f2699 |
#include "qemu/osdep.h"
|
|
|
3f2699 |
#include <wtypes.h>
|
|
|
3f2699 |
#include <powrprof.h>
|
|
|
3f2699 |
@@ -25,6 +28,7 @@
|
|
|
3f2699 |
#include <initguid.h>
|
|
|
3f2699 |
#endif
|
|
|
3f2699 |
#include <lm.h>
|
|
|
3f2699 |
+#include <wtsapi32.h>
|
|
|
3f2699 |
|
|
|
3f2699 |
#include "qga/guest-agent-core.h"
|
|
|
3f2699 |
#include "qga/vss-win32.h"
|
|
|
3f2699 |
@@ -1536,3 +1540,102 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
|
|
|
3f2699 |
ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
|
|
|
3f2699 |
}
|
|
|
3f2699 |
}
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+/* MINGW is missing two fields: IncomingFrames & OutgoingFrames */
|
|
|
3f2699 |
+typedef struct _GA_WTSINFOA {
|
|
|
3f2699 |
+ WTS_CONNECTSTATE_CLASS State;
|
|
|
3f2699 |
+ DWORD SessionId;
|
|
|
3f2699 |
+ DWORD IncomingBytes;
|
|
|
3f2699 |
+ DWORD OutgoingBytes;
|
|
|
3f2699 |
+ DWORD IncomingFrames;
|
|
|
3f2699 |
+ DWORD OutgoingFrames;
|
|
|
3f2699 |
+ DWORD IncomingCompressedBytes;
|
|
|
3f2699 |
+ DWORD OutgoingCompressedBy;
|
|
|
3f2699 |
+ CHAR WinStationName[WINSTATIONNAME_LENGTH];
|
|
|
3f2699 |
+ CHAR Domain[DOMAIN_LENGTH];
|
|
|
3f2699 |
+ CHAR UserName[USERNAME_LENGTH + 1];
|
|
|
3f2699 |
+ LARGE_INTEGER ConnectTime;
|
|
|
3f2699 |
+ LARGE_INTEGER DisconnectTime;
|
|
|
3f2699 |
+ LARGE_INTEGER LastInputTime;
|
|
|
3f2699 |
+ LARGE_INTEGER LogonTime;
|
|
|
3f2699 |
+ LARGE_INTEGER CurrentTime;
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+} GA_WTSINFOA;
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+GuestUserList *qmp_guest_get_users(Error **err)
|
|
|
3f2699 |
+{
|
|
|
3f2699 |
+#if (_WIN32_WINNT >= 0x0600)
|
|
|
3f2699 |
+#define QGA_NANOSECONDS 10000000
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ GHashTable *cache = NULL;
|
|
|
3f2699 |
+ GuestUserList *head = NULL, *cur_item = NULL;
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ DWORD buffer_size = 0, count = 0, i = 0;
|
|
|
3f2699 |
+ GA_WTSINFOA *info = NULL;
|
|
|
3f2699 |
+ WTS_SESSION_INFOA *entries = NULL;
|
|
|
3f2699 |
+ GuestUserList *item = NULL;
|
|
|
3f2699 |
+ GuestUser *user = NULL;
|
|
|
3f2699 |
+ gpointer value = NULL;
|
|
|
3f2699 |
+ INT64 login = 0;
|
|
|
3f2699 |
+ double login_time = 0;
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ cache = g_hash_table_new(g_str_hash, g_str_equal);
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ if (WTSEnumerateSessionsA(NULL, 0, 1, &entries, &count)) {
|
|
|
3f2699 |
+ for (i = 0; i < count; ++i) {
|
|
|
3f2699 |
+ buffer_size = 0;
|
|
|
3f2699 |
+ info = NULL;
|
|
|
3f2699 |
+ if (WTSQuerySessionInformationA(
|
|
|
3f2699 |
+ NULL,
|
|
|
3f2699 |
+ entries[i].SessionId,
|
|
|
3f2699 |
+ WTSSessionInfo,
|
|
|
3f2699 |
+ (LPSTR *)&info,
|
|
|
3f2699 |
+ &buffer_size
|
|
|
3f2699 |
+ )) {
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ if (strlen(info->UserName) == 0) {
|
|
|
3f2699 |
+ WTSFreeMemory(info);
|
|
|
3f2699 |
+ continue;
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ login = info->LogonTime.QuadPart;
|
|
|
3f2699 |
+ login -= W32_FT_OFFSET;
|
|
|
3f2699 |
+ login_time = ((double)login) / QGA_NANOSECONDS;
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ if (g_hash_table_contains(cache, info->UserName)) {
|
|
|
3f2699 |
+ value = g_hash_table_lookup(cache, info->UserName);
|
|
|
3f2699 |
+ user = (GuestUser *)value;
|
|
|
3f2699 |
+ if (user->login_time > login_time) {
|
|
|
3f2699 |
+ user->login_time = login_time;
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ } else {
|
|
|
3f2699 |
+ item = g_new0(GuestUserList, 1);
|
|
|
3f2699 |
+ item->value = g_new0(GuestUser, 1);
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ item->value->user = g_strdup(info->UserName);
|
|
|
3f2699 |
+ item->value->domain = g_strdup(info->Domain);
|
|
|
3f2699 |
+ item->value->has_domain = true;
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ item->value->login_time = login_time;
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ g_hash_table_add(cache, item->value->user);
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+ if (!cur_item) {
|
|
|
3f2699 |
+ head = cur_item = item;
|
|
|
3f2699 |
+ } else {
|
|
|
3f2699 |
+ cur_item->next = item;
|
|
|
3f2699 |
+ cur_item = item;
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ WTSFreeMemory(info);
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ WTSFreeMemory(entries);
|
|
|
3f2699 |
+ }
|
|
|
3f2699 |
+ g_hash_table_destroy(cache);
|
|
|
3f2699 |
+ return head;
|
|
|
3f2699 |
+#else
|
|
|
3f2699 |
+ error_setg(err, QERR_UNSUPPORTED);
|
|
|
3f2699 |
+ return NULL;
|
|
|
3f2699 |
+#endif
|
|
|
3f2699 |
+}
|
|
|
3f2699 |
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
|
|
|
3f2699 |
index 8271c1eb84..1695afe3a9 100644
|
|
|
3f2699 |
--- a/qga/qapi-schema.json
|
|
|
3f2699 |
+++ b/qga/qapi-schema.json
|
|
|
3f2699 |
@@ -1054,3 +1054,28 @@
|
|
|
3f2699 |
##
|
|
|
3f2699 |
{ 'command': 'guest-get-host-name',
|
|
|
3f2699 |
'returns': 'GuestHostName' }
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+##
|
|
|
3f2699 |
+# @GuestUser:
|
|
|
3f2699 |
+# @user: Username
|
|
|
3f2699 |
+# @domain: Logon domain (windows only)
|
|
|
3f2699 |
+# @login-time: Time of login of this user on the computer. If multiple
|
|
|
3f2699 |
+# instances of the user are logged in, the earliest login time is
|
|
|
3f2699 |
+# reported. The value is in fractional seconds since epoch time.
|
|
|
3f2699 |
+#
|
|
|
3f2699 |
+# Since: 2.10
|
|
|
3f2699 |
+##
|
|
|
3f2699 |
+{ 'struct': 'GuestUser',
|
|
|
3f2699 |
+ 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' } }
|
|
|
3f2699 |
+
|
|
|
3f2699 |
+##
|
|
|
3f2699 |
+# @guest-get-users:
|
|
|
3f2699 |
+# Retrieves a list of currently active users on the VM.
|
|
|
3f2699 |
+#
|
|
|
3f2699 |
+# Returns: A unique list of users.
|
|
|
3f2699 |
+#
|
|
|
3f2699 |
+# Since: 2.10
|
|
|
3f2699 |
+##
|
|
|
3f2699 |
+{ 'command': 'guest-get-users',
|
|
|
3f2699 |
+ 'returns': ['GuestUser'] }
|
|
|
3f2699 |
--
|
|
|
3f2699 |
2.13.6
|
|
|
3f2699 |
|