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)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJZAYUOAAoJEDNTyc7xCLWEILEH/iyom0A3PQ/OnAX51ep+J3EK
 2k8hU207bynbASv3rVo+RZORQAJ3LncU+ZnS27iRhAZI3MUjMM0YzLROfBqO5lSl
 aMWjX5mDcEsJgiHWkmN6t01tUyxayShn228xP8VsTKNEVHq+xwC0QSHv8fDMCPmc
 qD51GVC53DO2HSpDFMBFOI8uVFxMIuaB/yBCpOQKtTuW+2HDmJm8797ypvIqhrmN
 0PXldJaRcsPbApwv6K/9qa3cESb1IKRLAoerUjhtXa3uaHXth/n8h3VNaFuTtO1X
 aQygV/6SlowTZwdiJi3Jpv7q3OL+OpK9BrUbMfg3ag49BmsZfkrq5T/sn5w5JR4=
 =hhMj
 -----END PGP SIGNATURE-----

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: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2017-04-28 16:12:11 +01:00
commit 7ad691ec98
14 changed files with 393 additions and 17 deletions

2
configure vendored
View File

@ -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=""

View File

@ -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

View File

@ -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;
}

View File

@ -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
}

View File

@ -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
}

View File

@ -131,9 +131,32 @@ static void quit_handler(int sig)
* unless all log/pid files are on unfreezable filesystems. there's
* also a very likely chance killing the agent before unfreezing
* the filesystems is a mistake (or will be viewed as one later).
* On Windows the freeze interval is limited to 10 seconds, so
* we should quit, but first we should wait for the timeout, thaw
* the filesystem and quit.
*/
if (ga_is_frozen(ga_state)) {
#ifdef _WIN32
int i = 0;
Error *err = NULL;
HANDLE hEventTimeout;
g_debug("Thawing filesystems before exiting");
hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
if (hEventTimeout) {
WaitForSingleObject(hEventTimeout, 0);
CloseHandle(hEventTimeout);
}
qga_vss_fsfreeze(&i, false, &err);
if (err) {
g_debug("Error unfreezing filesystems prior to exiting: %s",
error_get_pretty(err));
error_free(err);
}
#else
return;
#endif
}
g_debug("received signal num %d, quitting", sig);

View File

@ -426,7 +426,13 @@
##
# @guest-fsfreeze-freeze:
#
# Sync and freeze all freezable, local guest filesystems
# Sync and freeze all freezable, local guest filesystems. If this
# command succeeded, you may call @guest-fsfreeze-thaw later to
# unfreeze.
#
# Note: On Windows, the command is implemented with the help of a
# Volume Shadow-copy Service DLL helper. The frozen state is limited
# for up to 10 seconds by VSS.
#
# Returns: Number of file systems currently frozen. On error, all filesystems
# will be thawed.
@ -439,10 +445,12 @@
##
# @guest-fsfreeze-freeze-list:
#
# Sync and freeze specified guest filesystems
# Sync and freeze specified guest filesystems.
# See also @guest-fsfreeze-freeze.
#
# @mountpoints: an array of mountpoints of filesystems to be frozen.
# If omitted, every mounted filesystem is frozen.
# Invalid mount points are ignored.
#
# Returns: Number of file systems currently frozen. On error, all filesystems
# will be thawed.
@ -1042,3 +1050,79 @@
'data': { 'path': 'str', '*arg': ['str'], '*env': ['str'],
'*input-data': 'str', '*capture-output': 'bool' },
'returns': 'GuestExec' }
##
# @GuestHostName:
# @host-name: Fully qualified domain name of the guest OS
#
# Since: 2.10
##
{ 'struct': 'GuestHostName',
'data': { 'host-name': 'str' } }
##
# @guest-get-host-name:
#
# Return a name for the machine.
#
# The returned name is not necessarily a fully-qualified domain name, or even
# present in DNS or some other name service at all. It need not even be unique
# on your local network or site, but usually it is.
#
# Returns: the host name of the machine on success
#
# Since: 2.10
##
{ 'command': 'guest-get-host-name',
'returns': 'GuestHostName' }
##
# @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'] }
##
# @GuestTimezone:
#
# @zone: Timezone name. These values may differ depending on guest/OS and
# should only be used for informational purposes.
# @offset: Offset to UTC in seconds, negative numbers for time zones west of
# GMT, positive numbers for east
#
# Since: 2.10
##
{ 'struct': 'GuestTimezone',
'data': { '*zone': 'str', 'offset': 'int' } }
##
# @guest-get-timezone:
#
# Retrieves the timezone information from the guest.
#
# Returns: A GuestTimezone dictionary.
#
# Since: 2.10
##
{ 'command': 'guest-get-timezone',
'returns': 'GuestTimezone' }

View File

@ -13,6 +13,7 @@
#ifndef VSS_WIN32_H
#define VSS_WIN32_H
#include "qga/vss-win32/vss-handles.h"
bool vss_init(bool init_requester);
void vss_deinit(bool deinit_requester);

View File

@ -14,7 +14,7 @@
#include "vss-common.h"
#include <inc/win2003/vscoordint.h>
#include <comadmin.h>
#include "install.h"
#include <wbemidl.h>
#include <comdef.h>
#include <comutil.h>
@ -276,7 +276,7 @@ STDAPI COMRegister(void)
chk(pCatalog->CreateServiceForApplication(
_bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME),
_bstr_t(L"SERVICE_AUTO_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"),
_bstr_t(L"SERVICE_DEMAND_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"),
_bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE));
chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME),
_bstr_t(dllPath), _bstr_t(tlbPath),
@ -461,3 +461,27 @@ namespace _com_util
return bstr;
}
}
/* Stop QGA VSS provider service from COM+ Application Admin Catalog */
STDAPI StopService(void)
{
HRESULT hr;
COMInitializer initializer;
COMPointer<IUnknown> pUnknown;
COMPointer<ICOMAdminCatalog2> pCatalog;
int count = 0;
chk(QGAProviderFind(QGAProviderCount, (void *)&count));
if (count) {
chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
IID_IUnknown, (void **)pUnknown.replace()));
chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
(void **)pCatalog.replace()));
chk(pCatalog->ShutdownApplication(_bstr_t(QGA_PROVIDER_LNAME)));
}
out:
return hr;
}

20
qga/vss-win32/install.h Normal file
View File

@ -0,0 +1,20 @@
/*
* QEMU Guest Agent VSS requester declarations
*
* Copyright Hitachi Data Systems Corp. 2013
*
* Authors:
* Tomoki Sekiyama <tomoki.sekiyama@hds.com>
*
* 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 INSTALL_H
#define INSTALL_H
#include <comadmin.h>
STDAPI StopService(void);
#endif

View File

@ -377,7 +377,6 @@ STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId)
if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) {
/* Send event to qemu-ga to notify the provider is timed out */
SetEvent(hEventTimeout);
hr = E_ABORT;
}
CloseHandle(hEventThaw);

View File

@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include "vss-common.h"
#include "requester.h"
#include "install.h"
#include <inc/win2003/vswriter.h>
#include <inc/win2003/vsbackup.h>
@ -501,4 +502,5 @@ void requester_thaw(int *num_vols, ErrorSet *errset)
requester_cleanup();
CoUninitialize();
StopService();
}

View File

@ -51,21 +51,12 @@
* http://www.microsoft.com/en-us/download/details.aspx?id=23490
*/
#include <inc/win2003/vss.h>
#include "vss-handles.h"
/* Macros to convert char definitions to wchar */
#define _L(a) L##a
#define L(a) _L(a)
/* Constants for QGA VSS Provider */
#define QGA_PROVIDER_NAME "QEMU Guest Agent VSS Provider"
#define QGA_PROVIDER_LNAME L(QGA_PROVIDER_NAME)
#define QGA_PROVIDER_VERSION L(QEMU_VERSION)
#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen"
#define EVENT_NAME_THAW "Global\\QGAVSSEvent-thaw"
#define EVENT_NAME_TIMEOUT "Global\\QGAVSSEvent-timeout"
const GUID g_gProviderId = { 0x3629d4ed, 0xee09, 0x4e0e,
{0x9a, 0x5c, 0x6d, 0x8b, 0xa2, 0x87, 0x2a, 0xef} };
const GUID g_gProviderVersion = { 0x11ef8b15, 0xcac6, 0x40d6,

View File

@ -0,0 +1,14 @@
#ifndef VSS_HANDLES
#define VSS_HANDLES
/* Constants for QGA VSS Provider */
#define QGA_PROVIDER_NAME "QEMU Guest Agent VSS Provider"
#define QGA_PROVIDER_LNAME L(QGA_PROVIDER_NAME)
#define QGA_PROVIDER_VERSION L(QEMU_VERSION)
#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen"
#define EVENT_NAME_THAW "Global\\QGAVSSEvent-thaw"
#define EVENT_NAME_TIMEOUT "Global\\QGAVSSEvent-timeout"
#endif