All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michael Roth <mdroth@linux.vnet.ibm.com>
To: Vinzenz 'evilissimo' Feenstra <vfeenstr@redhat.com>,
	qemu-devel@nongnu.org
Cc: marcandre.lureau@gmail.com, eblake@redhat.com
Subject: Re: [Qemu-devel] [PATCH v5 1/1] qga: Add 'guest-get-users' command
Date: Tue, 25 Apr 2017 15:52:47 -0500	[thread overview]
Message-ID: <149315356773.26622.14312359714025216394@loki> (raw)
In-Reply-To: <20170419092615.11410-2-vfeenstr@redhat.com>

Quoting Vinzenz 'evilissimo' Feenstra (2017-04-19 04:26:15)
> From: Vinzenz Feenstra <vfeenstr@redhat.com>
> 
> A command that will list all currently logged in users, and the time
> since when they are logged in.
> 
> Examples:
> 
> virsh # qemu-agent-command F25 '{ "execute": "guest-get-users" }'
> {"return":[{"login-time":1490622289.903835,"user":"root"}]}
> 
> virsh # qemu-agent-command Win2k12r2 '{ "execute": "guest-get-users" }'
> {"return":[{"login-time":1490351044.670552,"domain":"LADIDA",
> "user":"Administrator"}]}
> 
> Signed-off-by: Vinzenz Feenstra <vfeenstr@redhat.com>

Thanks, applied to qga tree:
  https://github.com/mdroth/qemu/commits/qga

> ---
>  configure             |   2 +-
>  include/glib-compat.h |   5 +++
>  qga/commands-posix.c  |  60 +++++++++++++++++++++++++++++
>  qga/commands-win32.c  | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  qga/qapi-schema.json  |  24 ++++++++++++
>  5 files changed, 193 insertions(+), 1 deletion(-)
> 
> diff --git a/configure b/configure
> index be4d326..55e6654 100755
> --- a/configure
> +++ b/configure
> @@ -742,7 +742,7 @@ if test "$mingw32" = "yes" ; then
>    sysconfdir="\${prefix}"
>    local_statedir=
>    confsuffix=""
> -  libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi -lnetapi32 $libs_qga"
> +  libs_qga="-lws2_32 -lwinmm -lpowrprof -lwtsapi32 -liphlpapi -lnetapi32 $libs_qga"
>  fi
> 
>  werror=""
> diff --git a/include/glib-compat.h b/include/glib-compat.h
> index 863c8cf..f8ee9dc 100644
> --- a/include/glib-compat.h
> +++ b/include/glib-compat.h
> @@ -217,6 +217,11 @@ static inline void g_hash_table_add(GHashTable *hash_table, gpointer key)
>  {
>      g_hash_table_replace(hash_table, key, key);
>  }
> +
> +static gboolean g_hash_table_contains(GHashTable *hash_table, gpointer key)
> +{
> +    return g_hash_table_lookup_extended(hash_table, key, NULL, NULL);
> +}
>  #endif
> 
>  #ifndef g_assert_true
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 915df9e..ba06be4 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -15,6 +15,7 @@
>  #include <sys/ioctl.h>
>  #include <sys/wait.h>
>  #include <dirent.h>
> +#include <utmpx.h>
>  #include "qga/guest-agent-core.h"
>  #include "qga-qmp-commands.h"
>  #include "qapi/qmp/qerror.h"
> @@ -2517,3 +2518,62 @@ void ga_command_state_init(GAState *s, GACommandState *cs)
>      ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
>  #endif
>  }
> +
> +#define QGA_MICRO_SECOND_TO_SECOND 1000000
> +
> +static double ga_get_login_time(struct utmpx *user_info)
> +{
> +    double seconds = (double)user_info->ut_tv.tv_sec;
> +    double useconds = (double)user_info->ut_tv.tv_usec;
> +    useconds /= QGA_MICRO_SECOND_TO_SECOND;
> +    return seconds + useconds;
> +}
> +
> +GuestUserList *qmp_guest_get_users(Error **err)
> +{
> +    GHashTable *cache = NULL;
> +    GuestUserList *head = NULL, *cur_item = NULL;
> +    struct utmpx *user_info = NULL;
> +    gpointer value = NULL;
> +    GuestUser *user = NULL;
> +    GuestUserList *item = NULL;
> +    double login_time = 0;
> +
> +    cache = g_hash_table_new(g_str_hash, g_str_equal);
> +    setutxent();
> +
> +    for (;;) {
> +        user_info = getutxent();
> +        if (user_info == NULL) {
> +            break;
> +        } else if (user_info->ut_type != USER_PROCESS) {
> +            continue;
> +        } else if (g_hash_table_contains(cache, user_info->ut_user)) {
> +            value = g_hash_table_lookup(cache, user_info->ut_user);
> +            user = (GuestUser *)value;
> +            login_time = ga_get_login_time(user_info);
> +            /* We're ensuring the earliest login time to be sent */
> +            if (login_time < user->login_time) {
> +                user->login_time = login_time;
> +            }
> +            continue;
> +        }
> +
> +        item = g_new0(GuestUserList, 1);
> +        item->value = g_new0(GuestUser, 1);
> +        item->value->user = g_strdup(user_info->ut_user);
> +        item->value->login_time = ga_get_login_time(user_info);
> +
> +        g_hash_table_insert(cache, item->value->user, item->value);
> +
> +        if (!cur_item) {
> +            head = cur_item = item;
> +        } else {
> +            cur_item->next = item;
> +            cur_item = item;
> +        }
> +    }
> +    endutxent();
> +    g_hash_table_destroy(cache);
> +    return head;
> +}
> diff --git a/qga/commands-win32.c b/qga/commands-win32.c
> index 19d72b2..fa99a8f 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"
> @@ -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
> +}
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index a02dbf2..27d10be 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -1042,3 +1042,27 @@
>    'data':    { 'path': 'str', '*arg': ['str'], '*env': ['str'],
>                 '*input-data': 'str', '*capture-output': 'bool' },
>    'returns': 'GuestExec' }
> +
> +##
> +# @GuestUser:
> +# @user:       Username
> +# @domain:     Logon domain (windows only)
> +# @login-time: Time of login of this user on the computer. If multiple
> +#              instances of the user are logged in, the earliest login time is
> +#              reported. The value is in fractional seconds since epoch time.
> +#
> +# Since: 2.10
> +##
> +{ 'struct': 'GuestUser',
> +  'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' } }
> +
> +##
> +# @guest-get-users:
> +# Retrieves a list of currently active users on the VM.
> +#
> +# Returns: A unique list of users.
> +#
> +# Since: 2.10
> +##
> +{ 'command': 'guest-get-users',
> +  'returns': ['GuestUser'] }
> -- 
> 2.9.3
> 

      reply	other threads:[~2017-04-25 21:17 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-19  9:26 [Qemu-devel] [PATCH v5 0/1] qga: Add 'guest-get-users' command Vinzenz 'evilissimo' Feenstra
2017-04-19  9:26 ` [Qemu-devel] [PATCH v5 1/1] " Vinzenz 'evilissimo' Feenstra
2017-04-25 20:52   ` Michael Roth [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=149315356773.26622.14312359714025216394@loki \
    --to=mdroth@linux.vnet.ibm.com \
    --cc=eblake@redhat.com \
    --cc=marcandre.lureau@gmail.com \
    --cc=qemu-devel@nongnu.org \
    --cc=vfeenstr@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.