From f342cc93ec918d684e8a6f6e646551a9c7fbc019 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Thu, 23 Mar 2017 18:26:50 +0200 Subject: [PATCH 1/8] qemu-ga: Make QGA VSS provider service run only when needed Currently the service runs in background on boot even though it is not needed and once it is running it never stops. The service needs to be running only during freeze operation and it should be stopped after executing thaw. Signed-off-by: Sameeh Jubran Signed-off-by: Michael Roth --- qga/vss-win32/install.cpp | 28 ++++++++++++++++++++++++++-- qga/vss-win32/install.h | 20 ++++++++++++++++++++ qga/vss-win32/requester.cpp | 2 ++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 qga/vss-win32/install.h diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index f4160a3a86..f41fcdfdda 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -14,7 +14,7 @@ #include "vss-common.h" #include -#include +#include "install.h" #include #include #include @@ -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 pUnknown; + COMPointer 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; +} diff --git a/qga/vss-win32/install.h b/qga/vss-win32/install.h new file mode 100644 index 0000000000..35364afdea --- /dev/null +++ b/qga/vss-win32/install.h @@ -0,0 +1,20 @@ +/* + * QEMU Guest Agent VSS requester declarations + * + * Copyright Hitachi Data Systems Corp. 2013 + * + * Authors: + * Tomoki Sekiyama + * + * 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 + +STDAPI StopService(void); + +#endif diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 0cd2f0ee7f..301762d8b1 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "vss-common.h" #include "requester.h" +#include "install.h" #include #include @@ -501,4 +502,5 @@ void requester_thaw(int *num_vols, ErrorSet *errset) requester_cleanup(); CoUninitialize(); + StopService(); } From 54858553def1879a3b0781529fb12a028ba36713 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Tue, 11 Apr 2017 15:52:05 +0300 Subject: [PATCH 2/8] qga-win: Enable 'can-offline' field in 'guest-get-vcpus' reply The QGA schema states: @can-offline: Whether offlining the VCPU is possible. This member is always filled in by the guest agent when the structure is returned, and always ignored on input (hence it can be omitted then). Currently 'can-offline' is missing entirely from the reply. This causes errors in libvirt which is expecting the reply to be compliant with the schema docs. BZ#1438735: https://bugzilla.redhat.com/show_bug.cgi?id=1438735 Signed-off-by: Sameeh Jubran Reviewed-by: Eric Blake Cc: qemu-stable@nongnu.org Signed-off-by: Michael Roth --- qga/commands-win32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 04026eedbf..9fec1fb638 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1344,7 +1344,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; From 94d81ae896810beafa60c85b88e991daa986e1fc Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Tue, 11 Apr 2017 10:50:36 +0300 Subject: [PATCH 3/8] qga-win: Fix a bug where qemu-ga service is stuck during stop operation After triggering a freeze command without any following thaw command, qemu-ga will not respond to stop operation. This behaviour is wanted on Linux as there is no time limit for a freeze command and we want to prevent quitting in the middle of freeze, on the other hand on Windows the time limit for freeze is 10 seconds, so we should wait for the timeout, thaw the file system and quit. Signed-off-by: Sameeh Jubran Signed-off-by: Michael Roth --- qga/main.c | 23 +++++++++++++++++++++++ qga/vss-win32.h | 1 + qga/vss-win32/vss-common.h | 11 +---------- qga/vss-win32/vss-handles.h | 14 ++++++++++++++ 4 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 qga/vss-win32/vss-handles.h diff --git a/qga/main.c b/qga/main.c index 07c295376f..ad6f68f187 100644 --- a/qga/main.c +++ b/qga/main.c @@ -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); diff --git a/qga/vss-win32.h b/qga/vss-win32.h index 51d303a8f6..4f8e39aa5c 100644 --- a/qga/vss-win32.h +++ b/qga/vss-win32.h @@ -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); diff --git a/qga/vss-win32/vss-common.h b/qga/vss-win32/vss-common.h index c81a8564b2..61c170b52e 100644 --- a/qga/vss-win32/vss-common.h +++ b/qga/vss-win32/vss-common.h @@ -51,21 +51,12 @@ * http://www.microsoft.com/en-us/download/details.aspx?id=23490 */ #include +#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, diff --git a/qga/vss-win32/vss-handles.h b/qga/vss-win32/vss-handles.h new file mode 100644 index 0000000000..ff399dd73a --- /dev/null +++ b/qga/vss-win32/vss-handles.h @@ -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 From 15296053378462e996032d9f50d62515f2230681 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Wed, 5 Apr 2017 15:01:06 +0300 Subject: [PATCH 4/8] qga-win: Fix Event Viewer errors caused by qemu-ga When the command "guest-fsfreeze-freeze" is executed it causes the VSS service to log the error below in the Event Viewer. This error is caused by an issue in the function "CommitSnapshots" in provider.cpp: * When VSS_TIMEOUT_MSEC expires the funtion returns E_ABORT. This causes the error #12293. |event id| error | * 12293 : Volume Shadow Copy Service error: Error calling a routine on a Shadow Copy Provider {00000000-0000-0000-0000-000000000000}. Routine details CommitSnapshots [hr = 0x80004004, Operation aborted. Signed-off-by: Sameeh Jubran Signed-off-by: Michael Roth --- qga/vss-win32/provider.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index ef9466909a..72d8b0e19d 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -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); From 0a3d197a71b0508f5ca066488fbbbe45a61c44fe Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Tue, 4 Apr 2017 08:46:31 +0200 Subject: [PATCH 5/8] qga: Add 'guest-get-host-name' command Retrieving the guest host name is a very useful feature for virtual management systems. This information can help to have more user friendly VM access details, instead of an IP there would be the host name. Also the host name reported can be used to have automated checks for valid SSL certificates. virsh # qemu-agent-command F25 '{ "execute": "guest-get-host-name" }' {"return":{"host-name":"F25.lab.evilissimo.net"}} Signed-off-by: Vinzenz Feenstra * minor whitespace fix-ups Signed-off-by: Michael Roth --- qga/commands.c | 11 +++++++++++ qga/qapi-schema.json | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/qga/commands.c b/qga/commands.c index 4d92946820..57a31bb5ef 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -499,3 +499,14 @@ 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; +} diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index a02dbf2d18..6307ae20fe 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1042,3 +1042,29 @@ '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' } From 1dbfbc17fe783e34644daf4abbb8f4e17344abcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 3 Apr 2017 11:54:38 +0200 Subject: [PATCH 6/8] qga: improve fsfreeze documentations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some users find the fsfreeze behaviour confusing. Add some notes about invalid mount points and Windows usage. Related to: https://bugzilla.redhat.com/show_bug.cgi?id=1436976 Signed-off-by: Marc-André Lureau Reviewed-by: Vinzenz Feenstra Signed-off-by: Michael Roth --- qga/qapi-schema.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 6307ae20fe..0f4cba5dc3 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -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. From 161a56a9065feb6fa2f69cec6237a5c4e714b9d3 Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Wed, 19 Apr 2017 11:26:15 +0200 Subject: [PATCH 7/8] qga: Add 'guest-get-users' command 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 * make g_hash_table_contains compat func inline to avoid unused warnings Signed-off-by: Michael Roth --- configure | 2 +- include/glib-compat.h | 6 +++ qga/commands-posix.c | 60 ++++++++++++++++++++++++ qga/commands-win32.c | 103 ++++++++++++++++++++++++++++++++++++++++++ qga/qapi-schema.json | 25 ++++++++++ 5 files changed, 195 insertions(+), 1 deletion(-) diff --git a/configure b/configure index c35acf1192..80d17f38c4 100755 --- a/configure +++ b/configure @@ -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="" diff --git a/include/glib-compat.h b/include/glib-compat.h index 863c8cf73d..fcffcd3f07 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -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 diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 915df9ed90..ba06be4c86 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -15,6 +15,7 @@ #include #include #include +#include #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 9fec1fb638..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 #include @@ -25,6 +28,7 @@ #include #endif #include +#include #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 0f4cba5dc3..f25467addf 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1076,3 +1076,28 @@ ## { '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'] } From 53c58e64d0a27c59d763778faa2b5a522c544719 Mon Sep 17 00:00:00 2001 From: Vinzenz Feenstra Date: Wed, 19 Apr 2017 12:52:58 +0200 Subject: [PATCH 8/8] qga: Add `guest-get-timezone` command Adds a new command `guest-get-timezone` reporting the currently configured timezone on the system. The information on what timezone is currently is configured is useful in case of Windows VMs where the offset of the hardware clock is required to have the same offset. This can be used for management systems like `oVirt` to detect the timezone difference and warn administrators of the misconfiguration. Signed-off-by: Vinzenz Feenstra Reviewed-by: Sameeh Jubran Tested-by: Sameeh Jubran * moved stub implementation to end of function for consistency * document that timezone names are for informational use only. Signed-off-by: Michael Roth --- qga/commands.c | 38 ++++++++++++++++++++++++++++++++++++++ qga/qapi-schema.json | 25 +++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/qga/commands.c b/qga/commands.c index 57a31bb5ef..3333ed50b2 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -510,3 +510,41 @@ GuestHostName *qmp_guest_get_host_name(Error **err) } 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 +} diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index f25467addf..03743ab905 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1101,3 +1101,28 @@ ## { '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' }