[Qemu-devel] [PATCH v6 0/1] qga: Add `guest-get-timezone` command

2017-04-19 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Changes since v5:
- Dropped unnecessary g_time_zone_unref in error handling
- Made implementation check for glib 2.28 or higher
- Move variable declaration to function top

Vinzenz Feenstra (1):
  qga: Add `guest-get-timezone` command

 qga/commands.c   | 38 ++
 qga/qapi-schema.json | 26 ++
 2 files changed, 64 insertions(+)

-- 
2.9.3




[Qemu-devel] [PATCH v6 1/1] qga: Add `guest-get-timezone` command

2017-04-19 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 qga/commands.c   | 38 ++
 qga/qapi-schema.json | 26 ++
 2 files changed, 64 insertions(+)

diff --git a/qga/commands.c b/qga/commands.c
index 4d92946..999538d 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -499,3 +499,41 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp)
 error_setg(errp, "invalid whence code %"PRId64, whence->u.value);
 return -1;
 }
+
+GuestTimezone *qmp_guest_get_timezone(Error **errp)
+{
+#if !GLIB_CHECK_VERSION(2, 28, 0)
+error_setg(errp, QERR_UNSUPPORTED);
+return NULL;
+#else
+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;
+#endif
+}
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a02dbf2..5183ea2 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' }
+
+
+##
+# @GuestTimezone:
+#
+# @zone:Timezone name
+# @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' }
-- 
2.9.3




[Qemu-devel] [PATCH v5 1/1] qga: Add 'guest-get-users' command

2017-04-19 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 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 
 #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 100
+
+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 
 #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];
+

[Qemu-devel] [PATCH v5 0/1] qga: Add 'guest-get-users' command

2017-04-19 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Changes since v4:
- Removed unnecessary ifdefs
- Win32 implementation now uses W32_FT_OFFSET instead of custom constant
- Moved variable declarations to the top of the function
- Also ensure that only the earliest login time on Windows is reported

Changes since v3:
- Removed ifdef/endif around QGA_MICRO_SECOND_TO_SECOND

Changes since v2:
- Updated the documentation of login-time to be more precise what the value
  contains and what time is actually reported and in which format.

Changes since v1:
- fixed spelling issues in the commit message and schema
- added login-time field which specifies the time the users logged on
- applied changes suggested for glib hash table usage
- added new link time library dependency for wtsapi32
- setting now _WIN32_WINNT to 0x0600 if not defined globally, which is Windows
  Vista and/or Windows 2008 Server and higher

Vinzenz Feenstra (1):
  qga: Add 'guest-get-users' command

 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(-)

-- 
2.9.3




[Qemu-devel] [PATCH v4 0/1] qga: Add 'guest-get-users' command

2017-04-13 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Changes since v3:
- Removed ifdef/endif around QGA_MICRO_SECOND_TO_SECOND

Changes since v2:
- Updated the documentation of login-time to be more precise what the value
  contains and what time is actually reported and in which format.

Changes since v1:
- fixed spelling issues in the commit message and schema
- added login-time field which specifies the time the users logged on
- applied changes suggested for glib hash table usage
- added new link time library dependency for wtsapi32
- setting now _WIN32_WINNT to 0x0600 if not defined globally, which is Windows
  Vista and/or Windows 2008 Server and higher

Vinzenz Feenstra (1):
  qga: Add 'guest-get-users' command

 configure |  2 +-
 include/glib-compat.h |  5 +++
 qga/commands-posix.c  | 52 ++
 qga/commands-win32.c  | 89 +++
 qga/qapi-schema.json  | 24 ++
 5 files changed, 171 insertions(+), 1 deletion(-)

-- 
2.9.3




[Qemu-devel] [PATCH v4 1/1] qga: Add 'guest-get-users' command

2017-04-13 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 configure |  2 +-
 include/glib-compat.h |  5 +++
 qga/commands-posix.c  | 52 ++
 qga/commands-win32.c  | 89 +++
 qga/qapi-schema.json  | 24 ++
 5 files changed, 171 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..4a59988 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,54 @@ 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 100
+
+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 = g_hash_table_new(g_str_hash, g_str_equal);
+GuestUserList *head = NULL, *cur_item = NULL;
+setutxent();
+for (;;) {
+struct utmpx *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)) {
+gpointer value = g_hash_table_lookup(cache, user_info->ut_user);
+GuestUser *user = (GuestUser *)value;
+double 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;
+}
+
+GuestUserList *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..8b84a90 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,88 @@ 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 

[Qemu-devel] [PATCH v2 0/1] qga: Add 'guest-get-host-name' command

2017-04-04 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Since v1:
- Renamed from FQDN to hostname and expanded documentation

Vinzenz Feenstra (1):
  qga: Add 'guest-get-host-name' command

 qga/commands.c   | 11 +++
 qga/qapi-schema.json | 29 +
 2 files changed, 40 insertions(+)

-- 
2.9.3




[Qemu-devel] [PATCH v2 1/1] qga: Add 'guest-get-host-name' command

2017-04-04 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 qga/commands.c   | 11 +++
 qga/qapi-schema.json | 29 +
 2 files changed, 40 insertions(+)

diff --git a/qga/commands.c b/qga/commands.c
index 4d92946..57a31bb 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 a02dbf2..b9f99ae 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1042,3 +1042,32 @@
   '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' }
+
-- 
2.9.3




[Qemu-devel] [PATCH v3 1/1] qga: Add 'guest-get-users' command

2017-04-03 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 configure |  2 +-
 include/glib-compat.h |  5 +++
 qga/commands-posix.c  | 54 +++
 qga/commands-win32.c  | 89 +++
 qga/qapi-schema.json  | 24 ++
 5 files changed, 173 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index d1ce33b..779ebfd 100755
--- a/configure
+++ b/configure
@@ -737,7 +737,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 73d93eb..3081ee7 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"
@@ -2515,3 +2516,56 @@ void ga_command_state_init(GAState *s, GACommandState 
*cs)
 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
 #endif
 }
+
+#ifndef QGA_MICRO_SECOND_TO_SECOND
+#   define QGA_MICRO_SECOND_TO_SECOND 100
+#endif
+
+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 = g_hash_table_new(g_str_hash, g_str_equal);
+GuestUserList *head = NULL, *cur_item = NULL;
+setutxent();
+for (;;) {
+struct utmpx *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)) {
+gpointer value = g_hash_table_lookup(cache, user_info->ut_user);
+GuestUser *user = (GuestUser *)value;
+double 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;
+}
+
+GuestUserList *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..8b84a90 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,88 @@ 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;
+

[Qemu-devel] [PATCH v3 0/1] qga: Add 'guest-get-users' command

2017-04-03 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Changes since v2:
- Updated the documentation of login-time to be more precise what the value
  contains and what time is actually reported and in which format.

Changes since v1:
- fixed spelling issues in the commit message and schema
- added login-time field which specifies the time the users logged on
- applied changes suggested for glib hash table usage
- added new link time library dependency for wtsapi32
- setting now _WIN32_WINNT to 0x0600 if not defined globally, which is Windows
  Vista and/or Windows 2008 Server and higher

Vinzenz Feenstra (1):
  qga: Add 'guest-get-users' command

 configure |  2 +-
 include/glib-compat.h |  5 +++
 qga/commands-posix.c  | 54 +++
 qga/commands-win32.c  | 89 +++
 qga/qapi-schema.json  | 24 ++
 5 files changed, 173 insertions(+), 1 deletion(-)

-- 
2.9.3




[Qemu-devel] [PATCH v2 1/1] qga: Add 'guest-get-users' command

2017-04-03 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 configure |  2 +-
 include/glib-compat.h |  5 +++
 qga/commands-posix.c  | 54 +++
 qga/commands-win32.c  | 89 +++
 qga/qapi-schema.json  | 22 +
 5 files changed, 171 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index d1ce33b..779ebfd 100755
--- a/configure
+++ b/configure
@@ -737,7 +737,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 73d93eb..3081ee7 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"
@@ -2515,3 +2516,56 @@ void ga_command_state_init(GAState *s, GACommandState 
*cs)
 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
 #endif
 }
+
+#ifndef QGA_MICRO_SECOND_TO_SECOND
+#   define QGA_MICRO_SECOND_TO_SECOND 100
+#endif
+
+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 = g_hash_table_new(g_str_hash, g_str_equal);
+GuestUserList *head = NULL, *cur_item = NULL;
+setutxent();
+for (;;) {
+struct utmpx *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)) {
+gpointer value = g_hash_table_lookup(cache, user_info->ut_user);
+GuestUser *user = (GuestUser *)value;
+double 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;
+}
+
+GuestUserList *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..8b84a90 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,88 @@ 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;
+

[Qemu-devel] [PATCH v2 0/1] qga: Add 'guest-get-users' command

2017-04-03 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Changes since v1:
- fixed spelling issues in the commit message and schema
- added login-time field which specifies the time the users logged on
- applied changes suggested for glib hash table usage
- added new link time library dependency for wtsapi32
- setting now _WIN32_WINNT to 0x0600 if not defined globally, which is Windows
  Vista and/or Windows 2008 Server and higher

Vinzenz Feenstra (1):
  qga: Add 'guest-get-users' command

 configure |  2 +-
 include/glib-compat.h |  5 +++
 qga/commands-posix.c  | 54 +++
 qga/commands-win32.c  | 89 +++
 qga/qapi-schema.json  | 22 +
 5 files changed, 171 insertions(+), 1 deletion(-)

-- 
2.9.3




[Qemu-devel] [PATCH v3] qemu-ga: add guest-get-osrelease command

2017-03-31 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Add a new 'guest-get-osrelease' command to report OS information in the
os-release format. As documented here:
https://www.freedesktop.org/software/systemd/man/os-release.html

The win32 implementation generates the information.
On POSIX systems the /etc/os-release or /usr/lib/os-release files
content is returned when available and gets extended with the fields:
- QGA_UNAME_RELEASE which is the content of `uname -r`
- QGA_UNAME_VERSION which is the content of `uname -v`
- QGA_UNAME_MACHINE which is the content of `uname -m`

Here an example for a Fedora 25 VM:

virsh # qemu-agent-command F25 '{ "execute": "guest-get-osrelease" }'
{"return":{"content":"NAME=Fedora\nVERSION=\"25 (Server Edition)\"\n
ID=fedora\nVERSION_ID=25\nPRETTY_NAME=\"Fedora 25 (Server Edition)\"\n
ANSI_COLOR=\"0;34\"\nCPE_NAME=\"cpe:/o:fedoraproject:fedora:25\"\n
HOME_URL=\"https://fedoraproject.org/\"\n
BUG_REPORT_URL=\"https://bugzilla.redhat.com/\"\n
REDHAT_BUGZILLA_PRODUCT=\"Fedora\"\n
REDHAT_BUGZILLA_PRODUCT_VERSION=25\nREDHAT_SUPPORT_PRODUCT=\"Fedora\"\n
REDHAT_SUPPORT_PRODUCT_VERSION=25\n
PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy\n
VARIANT=\"Server Edition\"\nVARIANT_ID=server\n\n
QGA_UNAME_RELEASE=\"4.8.6-300.fc25.x86_64\"\n
QGA_UNAME_VERSION=\"#1 SMP Tue Nov 1 12:36:38 UTC 2016\"\n
QGA_UNAME_MACHINE=\"x86_64\"\n"}}

And an example for a Windows 2012 R2 VM:

virsh # qemu-agent-command Win2k12r2 '{ "execute": "guest-get-osrelease" }'
{"return":{"content":"NAME=\"Microsoft Windows\"\nID=mswindows\n
VERSION=\"Microsoft Windows Server 2012 R2 (6.3)\"\nVERSION_ID=6.3\n
PRETTY_NAME=\"Windows Server 2012 R2 Datacenter\"\nVARIANT=\"server\"\n
VARIANT_ID=server\n"}}

Signed-off-by: Vinzenz Feenstra 
---
 qga/commands-posix.c |  41 +
 qga/commands-win32.c | 127 +++
 qga/qapi-schema.json |  29 
 3 files changed, 197 insertions(+)

diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 73d93eb..0a1fa28 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -13,6 +13,7 @@
 
 #include "qemu/osdep.h"
 #include 
+#include 
 #include 
 #include 
 #include "qga/guest-agent-core.h"
@@ -2418,6 +2419,12 @@ GuestMemoryBlockInfo 
*qmp_guest_get_memory_block_info(Error **errp)
 return NULL;
 }
 
+GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
+{
+error_setg(errp, QERR_UNSUPPORTED);
+return NULL;
+}
+
 #endif
 
 #if !defined(CONFIG_FSFREEZE)
@@ -2515,3 +2522,37 @@ void ga_command_state_init(GAState *s, GACommandState 
*cs)
 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
 #endif
 }
+
+GuestOSRelease *qmp_guest_get_osrelease(Error **errp)
+{
+GuestOSRelease *info = g_new0(GuestOSRelease, 1);
+memset(info, 0, sizeof(GuestOSRelease));
+
+struct utsname kinfo;
+uname();
+
+if (!g_file_get_contents("/etc/os-release", >content, NULL,
+ NULL)){
+g_file_get_contents("/usr/lib/os-release", >content,
+NULL, NULL);
+}
+
+char *extension = g_strdup_printf(
+"\n"
+"QGA_UNAME_RELEASE=\"%s\"\n"
+"QGA_UNAME_VERSION=\"%s\"\n"
+"QGA_UNAME_MACHINE=\"%s\"\n",
+kinfo.release,
+kinfo.version,
+kinfo.machine
+);
+if (info->content != NULL) {
+char *previous = info->content;
+info->content = g_strconcat(previous, extension, NULL);
+g_free(previous);
+} else {
+info->content = extension;
+}
+return info;
+}
+
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 19d72b2..a1c217e 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1536,3 +1536,130 @@ void ga_command_state_init(GAState *s, GACommandState 
*cs)
 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
 }
 }
+
+typedef struct _ga_matrix_lookup_t {
+int major;
+int minor;
+char const *name;
+} ga_matrix_lookup_t;
+
+static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = {
+{
+{ 5, 0, "Microsoft Windows 2000"},
+{ 5, 1, "Microsoft Windows XP"},
+{ 6, 0, "Microsoft Windows Vista"},
+{ 6, 1, "Microsoft Windows 7"},
+
+{ 6, 2, "Microsoft Windows 8"},
+{ 6, 3, "Microsoft Windows 8.1"},
+{10, 0, "Microsoft Windows 10"},
+{ 0, 0, 0}
+},{
+{ 5, 2, "Microsoft Windows Server 2003"},
+{ 6, 0, "Microsoft Windows Server 2008"},
+{ 6, 1, "Microsoft Windows Server 2008 R2"},
+{ 6, 2, "Microsoft Windows Server 2012"},
+{ 6, 3, "Microsoft Windows Server 2012 R2"},
+{10, 0, "Microsoft Windows Server 2016"},
+{ 0, 0, 0},
+{ 0, 0, 0}
+}
+};
+
+static void ga_get_version(OSVERSIONINFOEXW *info)
+{
+typedef NTSTATUS(WINAPI * rtl_get_version_t)(
+OSVERSIONINFOEXW *os_version_info_ex);
+HMODULE module = GetModuleHandle("ntdll");
+   

[Qemu-devel] [PATCH v3] qemu-ga: add guest-get-osrelease command

2017-03-31 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

This is a concept change over v2 of the guest-get-osinfo command.
The command now adds all information in the os-release format.

Vinzenz Feenstra (1):
  qemu-ga: add guest-get-osrelease command

 qga/commands-posix.c |  41 +
 qga/commands-win32.c | 127 +++
 qga/qapi-schema.json |  29 
 3 files changed, 197 insertions(+)

-- 
2.9.3




[Qemu-devel] [PATCH v1] qga: Add 'guest-get-users' command

2017-03-28 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

A command that will list all currenctly logged in users having running
processes.

Examples:

virsh # qemu-agent-command F25 '{ "execute": "guest-get-users" }'
{"return":[{"user":"root"}]}

virsh # qemu-agent-command Win2k12r2 '{ "execute": "guest-get-users" }'
{"return":[{"domain":"LADIDA","user":"Administrator"}]}

Signed-off-by: Vinzenz Feenstra 
---
 qga/commands-posix.c | 35 +++
 qga/commands-win32.c | 80 
 qga/qapi-schema.json | 21 ++
 3 files changed, 136 insertions(+)

diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 73d93eb..8cb2094 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"
@@ -2515,3 +2516,37 @@ void ga_command_state_init(GAState *s, GACommandState 
*cs)
 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
 #endif
 }
+
+GuestUserList *qmp_guest_get_users(Error **err)
+{
+GHashTable *cache = g_hash_table_new_full(g_str_hash, g_str_equal,
+  NULL, NULL);
+GuestUserList *head = NULL, *cur_item = NULL;
+setutxent();
+for (;;) {
+struct utmpx *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)) {
+continue;
+}
+
+g_hash_table_insert(cache, user_info->ut_user, NULL);
+
+GuestUserList *item = g_new0(GuestUserList, 1);
+item->value = g_new0(GuestUser, 1);
+item->value->user = g_strdup(user_info->ut_user);
+
+if (!cur_item) {
+head = cur_item = item;
+} else {
+cur_item->next = item;
+cur_item = item;
+}
+}
+endutxent();
+g_hash_table_unref(cache);
+return head;
+}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 19d72b2..46c038b 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1536,3 +1536,83 @@ void ga_command_state_init(GAState *s, GACommandState 
*cs)
 ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
 }
 }
+
+GuestUserList *qmp_guest_get_users(Error **err)
+{
+GHashTable *cache = g_hash_table_new_full(g_str_hash, g_str_equal,
+  NULL, NULL);
+GuestUserList *head = NULL, *cur_item = NULL;
+
+LPWKSTA_USER_INFO_1 buffer = NULL, iter_buffer = 0;
+DWORD level = 1; /* Level 1 has more information */
+DWORD prefered_max_length = MAX_PREFERRED_LENGTH;
+DWORD entries_read = 0;
+DWORD total_entries = 0;
+DWORD resume_handle = 0;
+LPWSTR server_name = NULL;
+NET_API_STATUS result = ERROR_MORE_DATA;
+
+while (result == ERROR_MORE_DATA) {
+result = NetWkstaUserEnum(
+server_name,
+level,
+(LPBYTE *),
+prefered_max_length,
+_read,
+_entries,
+_handle);
+
+if (result != ERROR_MORE_DATA && result != ERROR_SUCCESS) {
+error_setg_win32(err, result, "Failed to enumerate active users");
+goto error;
+}
+
+iter_buffer = buffer;
+DWORD i = 0;
+for (i = 0; i < entries_read; ++i, ++iter_buffer) {
+gchar *name = g_utf16_to_utf8(
+iter_buffer->wkui1_username,
+-1, NULL, NULL, NULL
+);
+
+if (name == NULL) {
+continue;
+}
+
+if (g_hash_table_contains(cache, name)) {
+g_free(name);
+continue;
+}
+
+gchar *domain = g_utf16_to_utf8(
+iter_buffer->wkui1_logon_domain,
+-1, NULL, NULL, NULL
+);
+
+g_hash_table_insert(cache, name, NULL);
+GuestUserList *item = g_new0(GuestUserList, 1);
+item->value = g_new0(GuestUser, 1);
+item->value->user = name;
+item->value->domain = domain;
+item->value->has_domain = true;
+
+if (!cur_item) {
+head = cur_item = item;
+} else {
+cur_item->next = item;
+cur_item = item;
+}
+}
+}
+NetApiBufferFree(buffer);
+g_hash_table_unref(cache);
+return head;
+
+error:
+if (buffer != NULL) {
+NetApiBufferFree(buffer);
+}
+g_hash_table_unref(cache);
+qapi_free_GuestUserList(head);
+return NULL;
+}
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a02dbf2..190e8b4 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1042,3 +1042,24 @@
   'data':{ 'path': 'str', '*arg': ['str'], 

[Qemu-devel] [PATCH v1] qga: Add 'guest-get-fqdn' command

2017-03-27 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Retrieving the guest OS fully qualified domain name (FQDN) 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 FQDN. Also the FQDN reported can be used to have automated
checks for valid SSL certificates.

virsh # qemu-agent-command F25 '{ "execute": "guest-get-fqdn" }'
{"return":{"fqdn":"F25.lab.evilissimo.net"}}

Signed-off-by: Vinzenz Feenstra 
---
 qga/commands.c   | 11 +++
 qga/qapi-schema.json | 25 +
 2 files changed, 36 insertions(+)

diff --git a/qga/commands.c b/qga/commands.c
index 4d92946..61577af 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;
 }
+
+GuestFQDN *qmp_guest_get_fqdn(Error **err)
+{
+GuestFQDN *result = NULL;
+gchar const *hostname = g_get_host_name();
+if (hostname != NULL) {
+result = g_new0(GuestFQDN, 1);
+result->fqdn = g_strdup(hostname);
+}
+return result;
+}
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a02dbf2..0a2c0a4 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1042,3 +1042,28 @@
   'data':{ 'path': 'str', '*arg': ['str'], '*env': ['str'],
'*input-data': 'str', '*capture-output': 'bool' },
   'returns': 'GuestExec' }
+
+
+
+##
+# @GuestFQDN:
+# @fqdn: Fully qualified domain name of the guest OS
+#
+# Since: 2.10
+##
+{ 'struct': 'GuestFQDN',
+  'data':   { 'fqdn': 'str' } }
+
+
+##
+# @guest-get-fqdn:
+#
+# Request the FQDN (Fully Qualified Domain Name) of the guest operating system
+#
+# Returns: FQDN on success
+#
+# Since: 2.10
+##
+{ 'command': 'guest-get-fqdn',
+  'returns': 'GuestFQDN' }
+
-- 
2.9.3




[Qemu-devel] [PATCH v5] qga: Add `guest-get-timezone` command

2017-03-24 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Changes since v4:
- Corrected usage of the optional zone (Missing has_zone = true)
- Updated documentation string for offset in the schema
- Removed unnecessary checks
- Uses the current time to check for the interval, not 0
- Switched to UNIVERSAL time type, since that can't fail and is all that's
  needed

Vinzenz Feenstra (1):
  qga: Add `guest-get-timezone` command

 qga/commands.c   | 29 +
 qga/qapi-schema.json | 26 ++
 2 files changed, 55 insertions(+)

-- 
2.9.3




[Qemu-devel] [PATCH v5] qga: Add `guest-get-timezone` command

2017-03-24 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 qga/commands.c   | 29 +
 qga/qapi-schema.json | 26 ++
 2 files changed, 55 insertions(+)

diff --git a/qga/commands.c b/qga/commands.c
index 4d92946..3b5789c 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -499,3 +499,32 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp)
 error_setg(errp, "invalid whence code %"PRId64, whence->u.value);
 return -1;
 }
+
+GuestTimezone *qmp_guest_get_timezone(Error **errp)
+{
+GuestTimezone *info = g_new0(GuestTimezone, 1);
+GTimeZone *tz = g_time_zone_new_local();
+if (tz == NULL) {
+error_setg(errp, QERR_QGA_COMMAND_FAILED,
+   "Couldn't retrieve local timezone");
+goto error;
+}
+
+gint64 now = g_get_real_time() / G_USEC_PER_SEC;
+gint32 intv = g_time_zone_find_interval(tz, G_TIME_TYPE_UNIVERSAL, now);
+info->offset = g_time_zone_get_offset(tz, intv);
+gchar const *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_time_zone_unref(tz);
+g_free(info);
+return NULL;
+}
+
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a02dbf2..5183ea2 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' }
+
+
+##
+# @GuestTimezone:
+#
+# @zone:Timezone name
+# @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' }
-- 
2.9.3




[Qemu-devel] [PATCH v4] qga: Add `guest-get-timezone` command

2017-03-23 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Changes since v3:

- Made zone optional, and won't fail if the name can't be looked up.
- Removed unncessary check for g_new0 allocation failure

Vinzenz Feenstra (1):
  qga: Add `guest-get-timezone` command

 qga/commands.c   | 34 ++
 qga/qapi-schema.json | 26 ++
 2 files changed, 60 insertions(+)

-- 
2.9.3




[Qemu-devel] [PATCH v4] qga: Add `guest-get-timezone` command

2017-03-23 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 qga/commands.c   | 34 ++
 qga/qapi-schema.json | 26 ++
 2 files changed, 60 insertions(+)

diff --git a/qga/commands.c b/qga/commands.c
index 4d92946..8a0ff0a 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -499,3 +499,37 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp)
 error_setg(errp, "invalid whence code %"PRId64, whence->u.value);
 return -1;
 }
+
+GuestTimezone *qmp_guest_get_timezone(Error **errp)
+{
+GuestTimezone *info = g_new0(GuestTimezone, 1);
+GTimeZone *tz = g_time_zone_new_local();
+if (tz == NULL) {
+error_setg(errp, QERR_QGA_COMMAND_FAILED,
+   "Couldn't retrieve local timezone");
+goto error;
+}
+
+gint32 interval = g_time_zone_find_interval(tz, G_TIME_TYPE_STANDARD, 0);
+if (interval != -1) {
+info->offset = g_time_zone_get_offset(tz, interval);
+gchar const *name = g_time_zone_get_abbreviation(tz, interval);
+if (name != NULL) {
+info->zone = g_strdup(name);
+} else {
+info->zone = NULL;
+}
+} else {
+error_setg(errp, QERR_QGA_COMMAND_FAILED,
+   "Failed to lookup timezone interval");
+goto error;
+}
+g_time_zone_unref(tz);
+return info;
+
+error:
+g_time_zone_unref(tz);
+g_free(info);
+return NULL;
+}
+
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a02dbf2..20d12a8 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' }
+
+
+##
+# @GuestTimezone:
+#
+# @zone:Timezone name
+# @offset:  Offset to UTC in seconds. For western timezones the offset has a
+#   negative value and for eastern the offset is positive value
+#
+# 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' }
-- 
2.9.3




[Qemu-devel] [PATCH v3] qga: Add `guest-get-timezone` command

2017-03-23 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 qga/commands.c   | 42 ++
 qga/qapi-schema.json | 26 ++
 2 files changed, 68 insertions(+)

diff --git a/qga/commands.c b/qga/commands.c
index 4d92946..1debc91 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -499,3 +499,45 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp)
 error_setg(errp, "invalid whence code %"PRId64, whence->u.value);
 return -1;
 }
+
+GuestTimezone *qmp_guest_get_timezone(Error **errp)
+{
+GuestTimezone *info = g_new0(GuestTimezone, 1);
+if (info == NULL) {
+error_setg(errp, QERR_QGA_COMMAND_FAILED,
+   "Couldn't allocate GuestTimezone dict");
+return NULL;
+}
+
+GTimeZone *tz = g_time_zone_new_local();
+if (tz == NULL) {
+error_setg(errp, QERR_QGA_COMMAND_FAILED,
+   "Couldn't retrieve local timezone");
+goto error;
+}
+
+gint32 interval = g_time_zone_find_interval(tz, G_TIME_TYPE_STANDARD, 0);
+if (interval != -1) {
+gchar const *name = g_time_zone_get_abbreviation(tz, interval);
+if (name != NULL) {
+info->offset = g_time_zone_get_offset(tz, interval);
+info->zone = g_strdup(name);
+} else {
+error_setg(errp, QERR_QGA_COMMAND_FAILED,
+   "Timezone lookup failed");
+goto error;
+}
+} else {
+error_setg(errp, QERR_QGA_COMMAND_FAILED,
+   "Failed to lookup timezone interval");
+goto error;
+}
+g_time_zone_unref(tz);
+return info;
+
+error:
+g_time_zone_unref(tz);
+g_free(info);
+return NULL;
+}
+
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a02dbf2..976fbbb 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' }
+
+
+##
+# @GuestTimezone:
+#
+# @zone:Timezone name
+# @offset:  Offset to UTC in seconds. For western timezones the offset has a
+#   negative value and for eastern the offset is positive value
+#
+# 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' }
-- 
2.9.3




[Qemu-devel] [PATCH v3] qga: Add `guest-get-timezone` command

2017-03-23 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Changes since v2:

- Changed offset from reporting minutes to reporting seconds like it is
  returned from the used glib API
- Applied more checks and better error handling in the implementation

Vinzenz Feenstra (1):
  qga: Add `guest-get-timezone` command

 qga/commands.c   | 42 ++
 qga/qapi-schema.json | 26 ++
 2 files changed, 68 insertions(+)

-- 
2.9.3




[Qemu-devel] [PATCH v2] qga: Add `guest-get-timezone` command

2017-03-23 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 qga/commands.c   | 19 +++
 qga/qapi-schema.json | 26 ++
 2 files changed, 45 insertions(+)

diff --git a/qga/commands.c b/qga/commands.c
index 4d92946..83d7f99 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -499,3 +499,22 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp)
 error_setg(errp, "invalid whence code %"PRId64, whence->u.value);
 return -1;
 }
+
+GuestTimezone *qmp_guest_get_timezone(Error **errp)
+{
+GuestTimezone *info = g_new0(GuestTimezone, 1);
+GTimeZone *tz = g_time_zone_new_local();
+gint32 interval = g_time_zone_find_interval(tz, G_TIME_TYPE_STANDARD, 0);
+gchar const *name = g_time_zone_get_abbreviation(tz, interval);
+if (name != NULL) {
+info->offset = g_time_zone_get_offset(tz, interval) / 60;
+info->zone = g_strdup(name);
+} else {
+error_setg(errp, "Timezone lookup failed");
+g_free(info);
+info = NULL;
+}
+g_time_zone_unref(tz);
+return info;
+}
+
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a02dbf2..62d6909 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' }
+
+
+##
+# @GuestTimezone:
+#
+# @zone:Timezone name
+# @offset:  Offset to UTC in minutes. For western timezones the offset has a
+#   negative value and for eastern the offset is positive value
+#
+# 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' }
-- 
2.9.3




[Qemu-devel] Changes since v2 -qga: Add `guest-get-timezone` command

2017-03-23 Thread Vinzenz 'evilissimo' Feenstra
Updated the documentation according to comments of eblake




[Qemu-devel] [PATCH] qga: Add `guest-get-timezone` command

2017-03-22 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

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 
---
 qga/commands.c   | 19 +++
 qga/qapi-schema.json | 25 +
 2 files changed, 44 insertions(+)

diff --git a/qga/commands.c b/qga/commands.c
index 4d92946..83d7f99 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -499,3 +499,22 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp)
 error_setg(errp, "invalid whence code %"PRId64, whence->u.value);
 return -1;
 }
+
+GuestTimezone *qmp_guest_get_timezone(Error **errp)
+{
+GuestTimezone *info = g_new0(GuestTimezone, 1);
+GTimeZone *tz = g_time_zone_new_local();
+gint32 interval = g_time_zone_find_interval(tz, G_TIME_TYPE_STANDARD, 0);
+gchar const *name = g_time_zone_get_abbreviation(tz, interval);
+if (name != NULL) {
+info->offset = g_time_zone_get_offset(tz, interval) / 60;
+info->zone = g_strdup(name);
+} else {
+error_setg(errp, "Timezone lookup failed");
+g_free(info);
+info = NULL;
+}
+g_time_zone_unref(tz);
+return info;
+}
+
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index a02dbf2..6683aae 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1042,3 +1042,28 @@
   'data':{ 'path': 'str', '*arg': ['str'], '*env': ['str'],
'*input-data': 'str', '*capture-output': 'bool' },
   'returns': 'GuestExec' }
+
+
+##
+# @GuestTimezone:
+#
+# @zone:Timezone name
+# @offset:  Offset to UTC in minutes
+#
+# Since: 2.10
+##
+{ 'struct': 'GuestTimezone',
+  'data':   { 'zone': 'str', 'offset': 'int' } }
+
+
+##
+# @guest-get-timezone:
+#
+# Retrieves the timezone information from the guest.
+#
+# Returns: The guest timezone GuestTimezone information on success.
+#
+# Since: 2.10
+##
+{ 'command': 'guest-get-timezone',
+  'returns': 'GuestTimezone' }
-- 
2.9.3




[Qemu-devel] [PATCH v2] qemu-ga: add guest-get-osinfo command

2017-03-22 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Add a new 'guest-get-osinfo' command for reporting basic information of
the guest operating system (hereafter just 'OS'). This information
includes the type of the OS, the version, and the architecture.
Additionally reported would be a name, distribution type and kernel
version where applicable.

Here an example for a Fedora 25 VM:

$ virsh -c qemu:system qemu-agent-command F25 \
'{ "execute": "guest-get-osinfo" }'
  {"return":{"arch":"x86_64","codename":"Server Edition","version":"25",
   "kernel":"4.8.6-300.fc25.x86_64","type":"linux","distribution":"Fedora"}}

And an example for a Windows 2012 R2 VM:

$ virsh -c qemu:system qemu-agent-command Win2k12R2 \
'{ "execute": "guest-get-osinfo" }'
  {"return":{"arch":"x86_64","codename":"Win 2012 R2",
   "version":"6.3","kernel":"","type":"windows","distribution":""}}

Signed-off-by: Vinzenz Feenstra 
---
 qga/commands-posix.c | 189 +++
 qga/commands-win32.c | 104 
 qga/qapi-schema.json |  40 +++
 3 files changed, 333 insertions(+)

diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 73d93eb..381c01a 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -13,6 +13,7 @@
 
 #include "qemu/osdep.h"
 #include 
+#include 
 #include 
 #include 
 #include "qga/guest-agent-core.h"
@@ -2356,6 +2357,188 @@ GuestMemoryBlockInfo 
*qmp_guest_get_memory_block_info(Error **errp)
 return info;
 }
 
+static void ga_strip_end(char *value)
+{
+size_t value_length = strlen(value);
+while (value_length > 0) {
+switch (value[value_length - 1]) {
+default:
+value_length = 0;
+break;
+case ' ': case '\n': case '\t': case '\'': case '"':
+value[value_length - 1] = 0;
+--value_length;
+break;
+}
+}
+}
+
+static void ga_parse_version_id(char const *value, GuestOSInfo *info)
+{
+if (strlen(value) < 128) {
+char codename[128];
+char version[128];
+
+if (*value == '"') {
+++value;
+}
+
+if (sscanf(value, "%[^(] (%[^)])", version, codename) == 2) {
+/* eg. VERSION="16.04.1 LTS (Xenial Xerus)" */
+info->codename = g_strdup(codename);
+info->version = g_strdup(version);
+} else if (sscanf(value, "%[^,] %[^\"]\"", version, codename) == 2) {
+/* eg. VERSION="12.04.5 LTS, Precise Pangolin" */
+info->codename = g_strdup(codename);
+info->version = g_strdup(version);
+} else {
+/* Just use the rest */
+info->version = g_strdup(version);
+}
+}
+}
+
+static void ga_parse_debian_version(FILE *fp, GuestOSInfo *info)
+{
+char *line = NULL;
+size_t n = 0;
+
+if (getline(, , fp) != -1) {
+ga_strip_end(line);
+info->version = g_strdup(line);
+info->distribution = g_strdup("Debian GNU/Linux");
+}
+free(line);
+}
+
+static void ga_parse_redhat_release(FILE *fp, GuestOSInfo *info)
+{
+char *line = NULL;
+size_t n = 0;
+
+if (getline(, , fp) != -1) {
+char *value = strstr(line, " release ");
+if (value != NULL) {
+*value = 0;
+info->distribution = g_strdup(line);
+value += 9;
+ga_strip_end(value);
+ga_parse_version_id(value, info);
+}
+}
+free(line);
+}
+
+static void ga_parse_os_release(FILE *fp, GuestOSInfo *info)
+{
+char *line = NULL;
+size_t n = 0;
+
+while (getline(, , fp) != -1) {
+char *value = strstr(line, "=");
+if (value != NULL) {
+*value = 0;
+++value;
+ga_strip_end(value);
+
+size_t len = strlen(line);
+if (len == 9 && strcmp(line, "VERSION_ID") == 0) {
+info->version = g_strdup(value);
+} else if (len == 7 && strcmp(line, "VERSION") == 0) {
+ga_parse_version_id(value, info);
+} else if (len == 4 && strcmp(line, "NAME") == 0) {
+info->distribution = g_strdup(value);
+}
+}
+}
+free(line);
+}
+
+static char *ga_stripped_strdup(char const *value)
+{
+char *result = NULL;
+while (value && *value == '"') {
+++value;
+}
+result = g_strdup(value);
+ga_strip_end(result);
+return result;
+}
+
+static void ga_parse_lsb_release(FILE *fp, GuestOSInfo *info)
+{
+char *line = NULL;
+size_t n = 0;
+
+while (getline(, , fp) != -1) {
+char *value = strstr(line, "=");
+if (value != NULL) {
+*value = 0;
+++value;
+ga_strip_end(value);
+
+size_t len = strlen(line);
+if (len == 15 && strcmp(line, "DISTRIB_RELEASE") == 0) {
+info->version = ga_stripped_strdup(value);
+} else 

[Qemu-devel] (no subject)

2017-03-22 Thread Vinzenz 'evilissimo' Feenstra
In this version:

- Changed the use of strdup to g_strdup and the use of sprintf with a local
  buffer to use g_strdup_printf instead.
- Made the majority of fields in the GuestOSInfo optional to allow 0 values
- Used the right target version in the schema (2.10 vs 2.8 before)
- Refactored the code for deciding which release/version file to use to use a
  configuration struct and a while loop to iterate over the options.

I was looking into the usage of uname, as suggested by eric, however after
looking into this I realized that there's no additional information to be
gained from this. Therefore I decided that this is still a feasible approach.
In most cases the code will break out of the loop after accessing the second
file. For older systems there are some supported fallbacks available, but
/etc/os-release and /usr/lib/os-release are already quite established.




[Qemu-devel] [PATCH] checkpatch: Supress warning in function pointer typedefs

2017-03-21 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

When importing dynamically functions via `GetProcAddress` in windows
related code, it is quite common to make a typedef for the resulting
function pointer. When the function to be imported, has a stdcall
calling convention, usually the `WINAPI` macro is used. This patch adds an
exception in the checkpatch.pl script to allow the calling convention
specification in function pointer typedefs, to be `WINAPI`.

Signed-off-by: Vinzenz Feenstra 
---
 scripts/checkpatch.pl | 9 -
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index f084542..33bf585 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1774,7 +1774,14 @@ sub process {
# likely a typedef for a function.
} elsif ($ctx =~ /$Type$/) {
 
-   } else {
+# If this is a typedef we need to allow WINAPI as a calling
+# convention. Even though there should be only one space around the
+# star, we allow none or any, to suppress the following warning.
+# The check for the number of spaces around the star is checked
+# elsewhere.
+   } elsif($ctx =~ 
/^\s*typedef\s+$Type\(WINAPI\s*\*\s*$Ident\)/) {
+
+} else {
ERROR("space prohibited between function name 
and open parenthesis '('\n" . $herecurr);
}
}
-- 
2.9.3




[Qemu-devel] [PATCH] qemu-ga: add guest-get-osinfo command

2017-03-21 Thread Vinzenz 'evilissimo' Feenstra
From: Vinzenz Feenstra 

Add a new 'guest-get-osinfo' command for reporting basic information of
the guest operating system (hereafter just 'OS'). This information
includes the type of the OS, the version, and the architecture.
Additionally reported would be a name, distribution type and kernel
version where applicable.

Here an example for a Fedora 25 VM:

$ virsh -c qemu:system qemu-agent-command F25 \
'{ "execute": "guest-get-osinfo" }'
  {"return":{"arch":"x86_64","codename":"Server Edition","version":"25",
   "kernel":"4.8.6-300.fc25.x86_64","type":"linux","distribution":"Fedora"}}

And an example for a Windows 2012 R2 VM:

$ virsh -c qemu:system qemu-agent-command Win2k12R2 \
'{ "execute": "guest-get-osinfo" }'
  {"return":{"arch":"x86_64","codename":"Win 2012 R2",
   "version":"6.3","kernel":"","type":"windows","distribution":""}}

Signed-off-by: Vinzenz Feenstra 
---
 qga/commands-posix.c | 206 +++
 qga/commands-win32.c | 105 ++
 qga/qapi-schema.json |  40 ++
 3 files changed, 351 insertions(+)

diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 73d93eb..70ec3fb 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -13,6 +13,7 @@
 
 #include "qemu/osdep.h"
 #include 
+#include 
 #include 
 #include 
 #include "qga/guest-agent-core.h"
@@ -2356,6 +2357,205 @@ GuestMemoryBlockInfo 
*qmp_guest_get_memory_block_info(Error **errp)
 return info;
 }
 
+static void ga_strip_end(char *value)
+{
+size_t value_length = strlen(value);
+while (value_length > 0) {
+switch (value[value_length - 1]) {
+default:
+value_length = 0;
+break;
+case ' ': case '\n': case '\t': case '\'': case '"':
+value[value_length - 1] = 0;
+--value_length;
+break;
+}
+}
+}
+
+static void ga_parse_version_id(char const *value, GuestOSInfo *info)
+{
+if (strlen(value) < 128) {
+char codename[128];
+char version[128];
+
+if (*value == '"') {
+++value;
+}
+
+if (sscanf(value, "%[^(] (%[^)])", version, codename) == 2) {
+/* eg. VERSION="16.04.1 LTS (Xenial Xerus)" */
+info->codename = strdup(codename);
+info->version = strdup(version);
+} else if (sscanf(value, "%[^,] %[^\"]\"", version, codename) == 2) {
+/* eg. VERSION="12.04.5 LTS, Precise Pangolin" */
+info->codename = strdup(codename);
+info->version = strdup(version);
+} else {
+/* Just use the rest */
+info->version = strdup(version);
+info->codename = strdup("");
+}
+}
+}
+
+static void ga_parse_debian_version(FILE *fp, GuestOSInfo *info)
+{
+char *line = NULL;
+size_t n = 0;
+
+if (getline(, , fp) != -1) {
+ga_strip_end(line);
+info->version = strdup(line);
+info->codename = strdup("");
+info->distribution = strdup("Debian GNU/Linux");
+}
+free(line);
+}
+
+static void ga_parse_redhat_release(FILE *fp, GuestOSInfo *info)
+{
+char *line = NULL;
+size_t n = 0;
+
+if (getline(, , fp) != -1) {
+char *value = strstr(line, " release ");
+if (value != NULL) {
+*value = 0;
+info->distribution = strdup(line);
+value += 9;
+ga_strip_end(value);
+ga_parse_version_id(value, info);
+}
+}
+free(line);
+}
+
+static void ga_parse_os_release(FILE *fp, GuestOSInfo *info)
+{
+char *line = NULL;
+size_t n = 0;
+while (getline(, , fp) != -1) {
+char *value = strstr(line, "=");
+if (value != NULL) {
+*value = 0;
+++value;
+ga_strip_end(value);
+
+size_t len = strlen(line);
+if (len == 9 && strcmp(line, "VERSION_ID") == 0) {
+info->version = strdup(value);
+} else if (len == 7 && strcmp(line, "VERSION") == 0) {
+ga_parse_version_id(value, info);
+} else if (len == 4 && strcmp(line, "NAME") == 0) {
+info->distribution = strdup(value);
+}
+}
+}
+free(line);
+}
+
+static char *ga_stripped_strdup(char const *value)
+{
+char *result = NULL;
+while (value && *value == '"') {
+++value;
+}
+result = strdup(value);
+ga_strip_end(result);
+return result;
+}
+
+static void ga_parse_lsb_release(FILE *fp, GuestOSInfo *info)
+{
+char *line = NULL;
+size_t n = 0;
+
+while (getline(, , fp) != -1) {
+char *value = strstr(line, "=");
+if (value != NULL) {
+*value = 0;
+++value;
+ga_strip_end(value);
+
+size_t len = strlen(line);
+if (len == 15 && strcmp(line, "DISTRIB_RELEASE") == 0) {
+