Commit 7ad691ec authored by Stefan Hajnoczi's avatar Stefan Hajnoczi
Browse files

Merge remote-tracking branch 'mdroth/tags/qga-pull-2017-04-25-v2-tag' into staging



qemu-ga patch queue

* new commands: guest-get-timezone, guest-get-users, guest-get-host-name
* fix hang on w32 when stopping qemu-ga service while fs frozen
* fix missing setting of can-offline in guest-get-vcpus
* make qemu-ga VSS w32 service on-demand rather than on-startup
* fix unecessary errors to EventLog on w32
* improvements to fsfreeze documentation

v2:
 * document 'zone' field of guest-get-timezone as informational-only
   (Daniel, Eric)
 * fix build error for glib < 2.32 (Peter)

# gpg: Signature made Thu 27 Apr 2017 06:43:42 AM BST
# gpg:                using RSA key 0x3353C9CEF108B584
# gpg: Good signature from "Michael Roth <flukshun@gmail.com>"
# gpg:                 aka "Michael Roth <mdroth@utexas.edu>"
# gpg:                 aka "Michael Roth <mdroth@linux.vnet.ibm.com>"
# Primary key fingerprint: CEAC C9E1 5534 EBAB B82D  3FA0 3353 C9CE F108 B584

* mdroth/tags/qga-pull-2017-04-25-v2-tag:
  qga: Add `guest-get-timezone` command
  qga: Add 'guest-get-users' command
  qga: improve fsfreeze documentations
  qga: Add 'guest-get-host-name' command
  qga-win: Fix Event Viewer errors caused by qemu-ga
  qga-win: Fix a bug where qemu-ga service is stuck during stop operation
  qga-win: Enable 'can-offline' field in 'guest-get-vcpus' reply
  qemu-ga: Make QGA VSS provider service run only when needed

Signed-off-by: default avatarStefan Hajnoczi <stefanha@redhat.com>
parents 81b2d5ce 53c58e64
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -743,7 +743,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=""
+6 −0
Original line number Diff line number Diff line
@@ -217,6 +217,12 @@ static inline void g_hash_table_add(GHashTable *hash_table, gpointer key)
{
    g_hash_table_replace(hash_table, key, key);
}

static inline 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
+60 −0
Original line number Diff line number Diff line
@@ -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;
}
+104 −1
Original line number Diff line number Diff line
@@ -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
}
+49 −0
Original line number Diff line number Diff line
@@ -499,3 +499,52 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp)
    error_setg(errp, "invalid whence code %"PRId64, whence->u.value);
    return -1;
}

GuestHostName *qmp_guest_get_host_name(Error **err)
{
    GuestHostName *result = NULL;
    gchar const *hostname = g_get_host_name();
    if (hostname != NULL) {
        result = g_new0(GuestHostName, 1);
        result->host_name = g_strdup(hostname);
    }
    return result;
}

GuestTimezone *qmp_guest_get_timezone(Error **errp)
{
#if GLIB_CHECK_VERSION(2, 28, 0)
    GuestTimezone *info = NULL;
    GTimeZone *tz = NULL;
    gint64 now = 0;
    gint32 intv = 0;
    gchar const *name = NULL;

    info = g_new0(GuestTimezone, 1);
    tz = g_time_zone_new_local();
    if (tz == NULL) {
        error_setg(errp, QERR_QGA_COMMAND_FAILED,
                   "Couldn't retrieve local timezone");
        goto error;
    }

    now = g_get_real_time() / G_USEC_PER_SEC;
    intv = g_time_zone_find_interval(tz, G_TIME_TYPE_UNIVERSAL, now);
    info->offset = g_time_zone_get_offset(tz, intv);
    name = g_time_zone_get_abbreviation(tz, intv);
    if (name != NULL) {
        info->has_zone = true;
        info->zone = g_strdup(name);
    }
    g_time_zone_unref(tz);

    return info;

error:
    g_free(info);
    return NULL;
#else
    error_setg(errp, QERR_UNSUPPORTED);
    return NULL;
#endif
}
Loading