* [Qemu-devel] [PATCH v5 0/1] qemu-ga: add guest-get-osinfo command
@ 2017-06-07 12:02 Tomáš Golembiovský
2017-06-07 12:02 ` [Qemu-devel] [PATCH v5 1/1] " Tomáš Golembiovský
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Tomáš Golembiovský @ 2017-06-07 12:02 UTC (permalink / raw)
To: Marc-André Lureau, Eric Blake, Michael Roth,
Vinzenz 'evilissimo' Feenstra
Cc: qemu-devel, Tomáš Golembiovský
v5:
- fixed build failure with older glib
- fixed coding style issues
- fixed one log string
This is a continuation of the work started by Vinzenz Feenstra in the
threads:
https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg04154.html
https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg04302.html
https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg06262.html
The idea is to report some basic information from uname and from
os-release file, if it is present. On MS Windows, where neither uname
nor os-release exist we fill the values based on the information we can
get from the OS.
The example output on Fedora is:
{
"return": {
"kernel-version": "#1 SMP Mon May 8 18:46:06 UTC 2017",
"kernel-release": "4.10.15-200.fc25.x86_64",
"machine-hardware": "x86_64",
"id": "fedora",
"name": "Fedora",
"pretty-name": "Fedora 25 (Server Edition)",
"version": "25 (Server Edition)",
"variant": "Server Edition",
"version-id": "25",
"variant-id": "server"
}
}
The example output on MS Windows 10 is:
{
"return": {
"kernel-version": "10.0",
"kernel-release": "10240",
"machine-hardware": "x86_64",
"id": "mswindows",
"name": "Microsoft Windows",
"pretty-name": "Windows 10 Enterprise",
"version": "Microsoft Windows 10",
"version-id": "10",
"variant": "client",
"variant-id": "client"
}
}
One issue I see with the current implementation is that one is not able
to distinguish between various (non-linux) POSIX systems from the
returned values. That's because without os-release file (which I assume
is not common on non-linux platforms) only kernel-version,
kernel-release and machine-hardware are returned and telling what OS is
running there is a guessing game. Is this a problem?
Also the qapi documentiaton probably need some polishing. Unfortunately,
so far I was unable to get qapi parser satisfied and still include all
the important information.
Tomas Golembiovsky
Tomáš Golembiovský (1):
qemu-ga: add guest-get-osinfo command
configure | 2 +-
qga/commands-posix.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++
qga/commands-win32.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++
qga/qapi-schema.json | 57 +++++++++++++++++
4 files changed, 388 insertions(+), 1 deletion(-)
--
2.13.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [Qemu-devel] [PATCH v5 1/1] qemu-ga: add guest-get-osinfo command
2017-06-07 12:02 [Qemu-devel] [PATCH v5 0/1] qemu-ga: add guest-get-osinfo command Tomáš Golembiovský
@ 2017-06-07 12:02 ` Tomáš Golembiovský
2017-06-26 12:27 ` Marc-André Lureau
2017-06-13 6:33 ` [Qemu-devel] [PATCH v5 0/1] " Tomáš Golembiovský
2017-06-26 12:27 ` Marc-André Lureau
2 siblings, 1 reply; 6+ messages in thread
From: Tomáš Golembiovský @ 2017-06-07 12:02 UTC (permalink / raw)
To: Marc-André Lureau, Eric Blake, Michael Roth,
Vinzenz 'evilissimo' Feenstra
Cc: qemu-devel, Tomáš Golembiovský
Add a new 'guest-get-osinfo' command for reporting basic information of
the guest operating system. This includes machine architecture,
version and release of the kernel and several fields from os-release
file if it is present (as defined in [1]).
[1] https://www.freedesktop.org/software/systemd/man/os-release.html
Signed-off-by: Vinzenz Feenstra <vfeenstr@redhat.com>
Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
---
configure | 2 +-
qga/commands-posix.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++
qga/commands-win32.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++
qga/qapi-schema.json | 57 +++++++++++++++++
4 files changed, 388 insertions(+), 1 deletion(-)
diff --git a/configure b/configure
index 21944eaa05..94e294e350 100755
--- a/configure
+++ b/configure
@@ -3131,7 +3131,7 @@ if test "$mingw32" = yes; then
else
glib_req_ver=2.22
fi
-glib_modules=gthread-2.0
+glib_modules="gthread-2.0 gio-2.0"
if test "$modules" = yes; then
glib_modules="$glib_modules gmodule-2.0"
fi
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 284ecc6d7e..48b812e013 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -13,9 +13,11 @@
#include "qemu/osdep.h"
#include <sys/ioctl.h>
+#include <sys/utsname.h>
#include <sys/wait.h>
#include <dirent.h>
#include <utmpx.h>
+#include <gio/gio.h>
#include "qga/guest-agent-core.h"
#include "qga-qmp-commands.h"
#include "qapi/qmp/qerror.h"
@@ -2579,3 +2581,161 @@ GuestUserList *qmp_guest_get_users(Error **err)
g_hash_table_destroy(cache);
return head;
}
+
+static GHashTable *ga_parse_osrelease(const char *fname)
+{
+ GFile *f;
+ GFileInputStream *fis = NULL;
+ GDataInputStream *dis = NULL;
+ GError *err = NULL;
+ gsize length;
+ char *line;
+ char *value;
+ GHashTable *tbl = NULL;
+
+ /* Open the file */
+ f = g_file_new_for_path(fname);
+ fis = g_file_read(f, NULL, &err);
+ if (err != NULL) {
+ slog("failed to open file '%s', error: %s", fname, err->message);
+ g_error_free(err);
+ g_object_unref(f);
+ return NULL;
+ }
+ dis = g_data_input_stream_new(G_INPUT_STREAM(fis));
+
+ err = NULL;
+ tbl = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ /* Go through all lines */
+ line = NULL;
+ while (err == NULL) {
+ g_free(line);
+ line = g_data_input_stream_read_line(dis, &length, NULL, &err);
+ if (err != NULL) {
+ slog("failed to read line from file '%s', error: %s",
+ fname, err->message);
+ g_error_free(err);
+ break;
+ } else if (line == NULL) {
+ /* EOF */
+ break;
+ }
+ if (g_utf8_validate(line, length, NULL) == FALSE) {
+ slog("file '%s' contains non-UTF8 content", fname);
+ break;
+ } else if (length == 0 || line[0] == '#') {
+ continue;
+ }
+
+ value = strchr(line, '=');
+ if (value == NULL) {
+ continue;
+ }
+ value[0] = 0;
+ value++;
+
+ if ((value[0] == '"') || (value[0] == '\'')) {
+ char *p;
+ char end = value[0];
+ value++;
+ p = value;
+ while (*p != 0) {
+ if (*p == '\\') {
+ switch (*(p + 1)) {
+ case '\'':
+ case '"':
+ case '$':
+ case '`':
+ memmove(p, p + 1, strlen(p + 1));
+ break;
+ default:
+ /* Keep literal backslash */
+ p++;
+ break;
+ }
+ if (*p != 0) {
+ p++;
+ }
+ continue;
+ }
+ if (*p == end) {
+ *p = 0;
+ break;
+ }
+ p++;
+ }
+ }
+ g_hash_table_insert(tbl, g_strdup(line), g_strdup(value));
+ }
+ g_free(line);
+
+ g_object_unref(fis);
+ g_object_unref(dis);
+ g_object_unref(f);
+
+ return tbl;
+}
+
+GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
+{
+ GuestOSInfo *info = g_new0(GuestOSInfo, 1);
+ memset(info, 0, sizeof(GuestOSInfo));
+
+ struct utsname kinfo;
+ uname(&kinfo);
+
+ info->kernel_version = g_strdup(kinfo.version);
+ info->kernel_release = g_strdup(kinfo.release);
+ info->machine_hardware = g_strdup(kinfo.machine);
+
+ GHashTable *osrelease = ga_parse_osrelease("/etc/os-release");
+ if (osrelease == NULL) {
+ slog("could not read /etc/os-release");
+ osrelease = ga_parse_osrelease("/usr/lib/os-release");
+ if (osrelease == NULL) {
+ slog("could not read /usr/lib/os-release");
+ }
+ }
+
+ if (osrelease != NULL) {
+ char *value;
+ value = g_hash_table_lookup(osrelease, "ID");
+ if (value != NULL) {
+ info->has_id = true;
+ info->id = g_strdup(value);
+ }
+ value = g_hash_table_lookup(osrelease, "PRETTY_NAME");
+ if (value != NULL) {
+ info->has_pretty_name = true;
+ info->pretty_name = g_strdup(value);
+ }
+ value = g_hash_table_lookup(osrelease, "NAME");
+ if (value != NULL) {
+ info->has_name = true;
+ info->name = g_strdup(value);
+ }
+ value = g_hash_table_lookup(osrelease, "VERSION");
+ if (value != NULL) {
+ info->has_version = true;
+ info->version = g_strdup(value);
+ }
+ value = g_hash_table_lookup(osrelease, "VERSION_ID");
+ if (value != NULL) {
+ info->has_version_id = true;
+ info->version_id = g_strdup(value);
+ }
+ value = g_hash_table_lookup(osrelease, "VARIANT");
+ if (value != NULL) {
+ info->has_variant = true;
+ info->variant = g_strdup(value);
+ }
+ value = g_hash_table_lookup(osrelease, "VARIANT_ID");
+ if (value != NULL) {
+ info->has_variant_id = true;
+ info->variant_id = g_strdup(value);
+ }
+ g_hash_table_destroy(osrelease);
+ }
+
+ return info;
+}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 439d229225..f9867baf1f 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1639,3 +1639,173 @@ GuestUserList *qmp_guest_get_users(Error **err)
return NULL;
#endif
}
+
+typedef struct _ga_matrix_lookup_t {
+ int major;
+ int minor;
+ char const *version;
+ char const *version_id;
+} ga_matrix_lookup_t;
+
+static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = {
+ {
+ /* Desktop editions */
+ { 5, 0, "Microsoft Windows 2000", "2000"},
+ { 5, 1, "Microsoft Windows XP", "xp"},
+ { 6, 0, "Microsoft Windows Vista", "vista"},
+ { 6, 1, "Microsoft Windows 7" "7"},
+ { 6, 2, "Microsoft Windows 8", "8"},
+ { 6, 3, "Microsoft Windows 8.1", "8.1"},
+ {10, 0, "Microsoft Windows 10", "10"},
+ { 0, 0, 0}
+ },{
+ /* Server editions */
+ { 5, 2, "Microsoft Windows Server 2003", "2003"},
+ { 6, 0, "Microsoft Windows Server 2008", "2008"},
+ { 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"},
+ { 6, 2, "Microsoft Windows Server 2012", "2012"},
+ { 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
+ {10, 0, "Microsoft Windows Server 2016", "2016"},
+ { 0, 0, 0},
+ { 0, 0, 0}
+ }
+};
+
+static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info)
+{
+ info->dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+
+ typedef NTSTATUS(WINAPI * rtl_get_version_t)(
+ RTL_OSVERSIONINFOEXW *os_version_info_ex);
+ HMODULE module = GetModuleHandle("ntdll");
+ PVOID fun = GetProcAddress(module, "RtlGetVersion");
+ rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun;
+ rtl_get_version(info);
+}
+
+static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id)
+{
+ DWORD major = os_version->dwMajorVersion;
+ DWORD minor = os_version->dwMinorVersion;
+ int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
+ ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
+ while (table->version != NULL) {
+ if (major == table->major && minor == table->minor) {
+ if (id) {
+ return g_strdup(table->version_id);
+ } else {
+ return g_strdup(table->version);
+ }
+ }
+ ++table;
+ }
+ slog("failed to lookup Windows version: major=%lu, minor=%lu",
+ major, minor);
+ return g_strdup("N/A");
+}
+
+static char *ga_get_win_product_name(void)
+{
+ HKEY key = NULL;
+ DWORD size = 128;
+ char *result = g_malloc0(size);
+ memset(result, 0, size);
+ LONG err = ERROR_SUCCESS;
+
+ err = RegOpenKeyA(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ &key);
+ if (err == ERROR_SUCCESS) {
+ err = RegQueryValueExA(key, "ProductName", NULL, NULL,
+ (LPBYTE)result, &size);
+ if (err == ERROR_MORE_DATA) {
+ slog("ProductName longer than expected (%lu bytes), retrying",
+ size);
+ g_free(result);
+ result = NULL;
+ if (size > 0) {
+ result = g_malloc0(size);
+ err = RegQueryValueExA(key, "ProductName", NULL, NULL,
+ (LPBYTE)result, &size);
+ if (err != ERROR_SUCCESS) {
+ g_free(result);
+ result = NULL;
+ }
+ }
+ }
+ if (err != ERROR_SUCCESS) {
+ slog("failed to retrive ProductName");
+ }
+ }
+ return result;
+}
+
+static char *ga_get_current_arch(void)
+{
+ SYSTEM_INFO info;
+ GetNativeSystemInfo(&info);
+ char *result = NULL;
+ switch (info.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ result = g_strdup("x86_64");
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM:
+ result = g_strdup("arm");
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ result = g_strdup("ia64");
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ result = g_strdup("x86");
+ break;
+ case PROCESSOR_ARCHITECTURE_UNKNOWN:
+ default:
+ slog("unknown processor architecture 0x%0x",
+ info.wProcessorArchitecture);
+ result = g_strdup("unknown");
+ break;
+ }
+ return result;
+}
+
+GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
+{
+ OSVERSIONINFOEXW os_version;
+ ga_get_win_version(&os_version);
+
+ bool server = os_version.wProductType != VER_NT_WORKSTATION;
+ char *product_name = ga_get_win_product_name();
+ if (product_name == NULL) {
+ Error *local_err = NULL;
+ error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
+ "failed to retrieve ProductName from registry");
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ GuestOSInfo *info = g_new0(GuestOSInfo, 1);
+
+ info->kernel_version = g_strdup_printf("%lu.%lu",
+ os_version.dwMajorVersion,
+ os_version.dwMinorVersion);
+ info->kernel_release = g_strdup_printf("%lu",
+ os_version.dwBuildNumber);
+ info->machine_hardware = ga_get_current_arch();
+
+ info->has_id = true;
+ info->id = g_strdup("mswindows");
+ info->has_name = true;
+ info->name = g_strdup("Microsoft Windows");
+ info->has_pretty_name = true;
+ info->pretty_name = product_name;
+ info->has_version = true;
+ info->version = ga_get_win_name(&os_version, false);
+ info->has_version_id = true;
+ info->version_id = ga_get_win_name(&os_version, true);
+ info->has_variant = true;
+ info->variant = g_strdup(server ? "server" : "client");
+ info->has_variant_id = true;
+ info->variant_id = g_strdup(server ? "server" : "client");
+
+ return info;
+}
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 03743ab905..eb565da345 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1126,3 +1126,60 @@
##
{ 'command': 'guest-get-timezone',
'returns': 'GuestTimezone' }
+
+##
+# @GuestOSInfo:
+#
+# Notes:
+#
+# The fields @id, @name, @pretty-name, @version, @version-id, @variant and
+# @variant-id follow the definition specified in os-release(5). Refer to the
+# manual page for exact description of the fields.
+#
+# On POSIX systems:
+#
+# kernel-release: release field returned by uname(2)
+# kernel-version': version field returned by uname(2)
+# machine-hardware': machine field returned by uname(2)
+# id: as defined by os-release(5)
+# name: as defined by os-release(5)
+# pretty-name: as defined by os-release(5)
+# version: as defined by os-release(5)
+# version-id: as defined by os-release(5)
+# variant: as defined by os-release(5)
+# variant-id: as defined by os-release(5)
+#
+#
+# On Windows:
+#
+# kernel-release: version number of the OS
+# kernel-version': build number of the OS
+# machine-hardware': architecture of the hardware
+# id: contains string "mswindows"
+# name: contains string "Microsoft Windows"
+# pretty-name: product name, e.g. "Microsoft Windows 10 Enterprise"
+# version: long version string, e.g. "Microsoft Windows Server 2008"
+# version-id: short version identifier, e.g. "7" or "20012r2"
+# variant: contains string "server" or "client"
+# variant-id: contains string "server" or "client"
+#
+# Since: 2.10
+##
+{ 'struct': 'GuestOSInfo',
+ 'data': {
+ 'kernel-release': 'str', 'kernel-version': 'str',
+ 'machine-hardware': 'str', '*id': 'str', '*name': 'str',
+ '*pretty-name': 'str', '*version': 'str', '*version-id': 'str',
+ '*variant': 'str', '*variant-id': 'str' } }
+
+##
+# @guest-get-osinfo:
+#
+# Retrieve guest operating system information
+#
+# Returns: operating system information on success
+#
+# Since 2.10
+##
+{ 'command': 'guest-get-osinfo',
+ 'returns': 'GuestOSInfo' }
--
2.13.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v5 0/1] qemu-ga: add guest-get-osinfo command
2017-06-07 12:02 [Qemu-devel] [PATCH v5 0/1] qemu-ga: add guest-get-osinfo command Tomáš Golembiovský
2017-06-07 12:02 ` [Qemu-devel] [PATCH v5 1/1] " Tomáš Golembiovský
@ 2017-06-13 6:33 ` Tomáš Golembiovský
2017-06-26 12:27 ` Marc-André Lureau
2 siblings, 0 replies; 6+ messages in thread
From: Tomáš Golembiovský @ 2017-06-13 6:33 UTC (permalink / raw)
To: Marc-André Lureau, Eric Blake, Michael Roth,
Vinzenz 'evilissimo' Feenstra
Cc: qemu-devel
Hi,
anyone got a chance to look at it already?
Thanks,
Tomas
On Wed, 7 Jun 2017 14:02:05 +0200
Tomáš Golembiovský <tgolembi@redhat.com> wrote:
> v5:
> - fixed build failure with older glib
> - fixed coding style issues
> - fixed one log string
>
> This is a continuation of the work started by Vinzenz Feenstra in the
> threads:
>
> https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg04154.html
> https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg04302.html
> https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg06262.html
>
> The idea is to report some basic information from uname and from
> os-release file, if it is present. On MS Windows, where neither uname
> nor os-release exist we fill the values based on the information we can
> get from the OS.
>
> The example output on Fedora is:
>
> {
> "return": {
> "kernel-version": "#1 SMP Mon May 8 18:46:06 UTC 2017",
> "kernel-release": "4.10.15-200.fc25.x86_64",
> "machine-hardware": "x86_64",
> "id": "fedora",
> "name": "Fedora",
> "pretty-name": "Fedora 25 (Server Edition)",
> "version": "25 (Server Edition)",
> "variant": "Server Edition",
> "version-id": "25",
> "variant-id": "server"
> }
> }
>
> The example output on MS Windows 10 is:
>
> {
> "return": {
> "kernel-version": "10.0",
> "kernel-release": "10240",
> "machine-hardware": "x86_64",
> "id": "mswindows",
> "name": "Microsoft Windows",
> "pretty-name": "Windows 10 Enterprise",
> "version": "Microsoft Windows 10",
> "version-id": "10",
> "variant": "client",
> "variant-id": "client"
> }
> }
>
> One issue I see with the current implementation is that one is not able
> to distinguish between various (non-linux) POSIX systems from the
> returned values. That's because without os-release file (which I assume
> is not common on non-linux platforms) only kernel-version,
> kernel-release and machine-hardware are returned and telling what OS is
> running there is a guessing game. Is this a problem?
>
> Also the qapi documentiaton probably need some polishing. Unfortunately,
> so far I was unable to get qapi parser satisfied and still include all
> the important information.
>
> Tomas Golembiovsky
>
> Tomáš Golembiovský (1):
> qemu-ga: add guest-get-osinfo command
>
> configure | 2 +-
> qga/commands-posix.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++
> qga/commands-win32.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++
> qga/qapi-schema.json | 57 +++++++++++++++++
> 4 files changed, 388 insertions(+), 1 deletion(-)
>
> --
> 2.13.0
>
--
Tomáš Golembiovský <tgolembi@redhat.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v5 1/1] qemu-ga: add guest-get-osinfo command
2017-06-07 12:02 ` [Qemu-devel] [PATCH v5 1/1] " Tomáš Golembiovský
@ 2017-06-26 12:27 ` Marc-André Lureau
0 siblings, 0 replies; 6+ messages in thread
From: Marc-André Lureau @ 2017-06-26 12:27 UTC (permalink / raw)
To: Tomáš Golembiovský,
Eric Blake, Michael Roth, Vinzenz 'evilissimo' Feenstra
Cc: qemu-devel
Hi
Looks good overall,
On Wed, Jun 7, 2017 at 2:02 PM Tomáš Golembiovský <tgolembi@redhat.com>
wrote:
> Add a new 'guest-get-osinfo' command for reporting basic information of
> the guest operating system. This includes machine architecture,
> version and release of the kernel and several fields from os-release
> file if it is present (as defined in [1]).
>
> [1] https://www.freedesktop.org/software/systemd/man/os-release.html
>
> Signed-off-by: Vinzenz Feenstra <vfeenstr@redhat.com>
> Signed-off-by: Tomáš Golembiovský <tgolembi@redhat.com>
> ---
> configure | 2 +-
> qga/commands-posix.c | 160
> ++++++++++++++++++++++++++++++++++++++++++++++++
> qga/commands-win32.c | 170
> +++++++++++++++++++++++++++++++++++++++++++++++++++
> qga/qapi-schema.json | 57 +++++++++++++++++
> 4 files changed, 388 insertions(+), 1 deletion(-)
>
> diff --git a/configure b/configure
> index 21944eaa05..94e294e350 100755
> --- a/configure
> +++ b/configure
> @@ -3131,7 +3131,7 @@ if test "$mingw32" = yes; then
> else
> glib_req_ver=2.22
> fi
> -glib_modules=gthread-2.0
> +glib_modules="gthread-2.0 gio-2.0"
> if test "$modules" = yes; then
> glib_modules="$glib_modules gmodule-2.0"
> fi
>
gio will be added to glib_cflags/libs and LIBS, QEMU_CFLAGS etc.
I think you should only target libs_qga/cflags instead if you really need
gio.
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 284ecc6d7e..48b812e013 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -13,9 +13,11 @@
>
> #include "qemu/osdep.h"
> #include <sys/ioctl.h>
> +#include <sys/utsname.h>
> #include <sys/wait.h>
> #include <dirent.h>
> #include <utmpx.h>
> +#include <gio/gio.h>
> #include "qga/guest-agent-core.h"
> #include "qga-qmp-commands.h"
> #include "qapi/qmp/qerror.h"
> @@ -2579,3 +2581,161 @@ GuestUserList *qmp_guest_get_users(Error **err)
> g_hash_table_destroy(cache);
> return head;
> }
> +
> +static GHashTable *ga_parse_osrelease(const char *fname)
>
Too bad the osrealase file is not a .ini-like config file.. I wonder if we
could make it one by pre-pending "[osrelease]" on the content and using
g_key_file_load_from_data (), that would avoid a lot of parsing code..
+{
> + GFile *f;
> + GFileInputStream *fis = NULL;
> + GDataInputStream *dis = NULL;
> + GError *err = NULL;
> + gsize length;
> + char *line;
> + char *value;
> + GHashTable *tbl = NULL;
> +
> + /* Open the file */
> + f = g_file_new_for_path(fname);
> + fis = g_file_read(f, NULL, &err);
> + if (err != NULL) {
> + slog("failed to open file '%s', error: %s", fname, err->message);
> + g_error_free(err);
> + g_object_unref(f);
> + return NULL;
> + }
+ dis = g_data_input_stream_new(G_INPUT_STREAM(fis));
> +
>
+ err = NULL;
> + tbl = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
> + /* Go through all lines */
>
Or you could use g_file_get_contents () and g_strsplit(), that would remove
gio depdency I guess
> + line = NULL;
> + while (err == NULL) {
> + g_free(line);
>
More readable if it's placed after it's actually really needed, but ok
+ line = g_data_input_stream_read_line(dis, &length, NULL, &err);
> + if (err != NULL) {
> + slog("failed to read line from file '%s', error: %s",
> + fname, err->message);
> + g_error_free(err);
> + break;
> + } else if (line == NULL) {
> + /* EOF */
> + break;
> + }
> + if (g_utf8_validate(line, length, NULL) == FALSE) {
> + slog("file '%s' contains non-UTF8 content", fname);
> + break;
> + } else if (length == 0 || line[0] == '#') {
> + continue;
> + }
> +
> + value = strchr(line, '=');
> + if (value == NULL) {
> + continue;
> + }
> + value[0] = 0;
> + value++;
> +
> + if ((value[0] == '"') || (value[0] == '\'')) {
> + char *p;
> + char end = value[0];
> + value++;
> + p = value;
> + while (*p != 0) {
> + if (*p == '\\') {
> + switch (*(p + 1)) {
> + case '\'':
> + case '"':
> + case '$':
> + case '`':
> + memmove(p, p + 1, strlen(p + 1));
> + break;
> + default:
> + /* Keep literal backslash */
> + p++;
> + break;
> + }
> + if (*p != 0) {
> + p++;
> + }
> + continue;
> + }
> + if (*p == end) {
> + *p = 0;
> + break;
> + }
> + p++;
> + }
> + }
> + g_hash_table_insert(tbl, g_strdup(line), g_strdup(value));
> + }
> + g_free(line);
> +
> + g_object_unref(fis);
> + g_object_unref(dis);
> + g_object_unref(f);
> +
>
Parsing looks quite fine, but it would be very worth to have a few good/bad
test cases added in tests..
+ return tbl;
> +}
> +
> +GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
> +{
> + GuestOSInfo *info = g_new0(GuestOSInfo, 1);
> + memset(info, 0, sizeof(GuestOSInfo));
>
g_new0 + memset(0), never too sure :)
> +
> + struct utsname kinfo;
>
However here, I would rather initialize = { 0, } or memset it.
+ uname(&kinfo);
>
Please check return value / errno
> +
> + info->kernel_version = g_strdup(kinfo.version);
> + info->kernel_release = g_strdup(kinfo.release);
> + info->machine_hardware = g_strdup(kinfo.machine);
> +
> + GHashTable *osrelease = ga_parse_osrelease("/etc/os-release");
> + if (osrelease == NULL) {
> + slog("could not read /etc/os-release");
> + osrelease = ga_parse_osrelease("/usr/lib/os-release");
> + if (osrelease == NULL) {
> + slog("could not read /usr/lib/os-release");
> + }
> + }
> +
> + if (osrelease != NULL) {
> + char *value;
> + value = g_hash_table_lookup(osrelease, "ID");
> + if (value != NULL) {
> + info->has_id = true;
> + info->id = g_strdup(value);
> + }
> + value = g_hash_table_lookup(osrelease, "PRETTY_NAME");
> + if (value != NULL) {
> + info->has_pretty_name = true;
> + info->pretty_name = g_strdup(value);
> + }
> + value = g_hash_table_lookup(osrelease, "NAME");
> + if (value != NULL) {
> + info->has_name = true;
> + info->name = g_strdup(value);
> + }
> + value = g_hash_table_lookup(osrelease, "VERSION");
> + if (value != NULL) {
> + info->has_version = true;
> + info->version = g_strdup(value);
> + }
> + value = g_hash_table_lookup(osrelease, "VERSION_ID");
> + if (value != NULL) {
> + info->has_version_id = true;
> + info->version_id = g_strdup(value);
> + }
> + value = g_hash_table_lookup(osrelease, "VARIANT");
> + if (value != NULL) {
> + info->has_variant = true;
> + info->variant = g_strdup(value);
> + }
> + value = g_hash_table_lookup(osrelease, "VARIANT_ID");
> + if (value != NULL) {
> + info->has_variant_id = true;
> + info->variant_id = g_strdup(value);
> + }
> + g_hash_table_destroy(osrelease);
> + }
> +
> + return info;
> +}
> diff --git a/qga/commands-win32.c b/qga/commands-win32.c
> index 439d229225..f9867baf1f 100644
> --- a/qga/commands-win32.c
> +++ b/qga/commands-win32.c
> @@ -1639,3 +1639,173 @@ GuestUserList *qmp_guest_get_users(Error **err)
> return NULL;
> #endif
> }
> +
> +typedef struct _ga_matrix_lookup_t {
> + int major;
> + int minor;
> + char const *version;
> + char const *version_id;
> +} ga_matrix_lookup_t;
> +
> +static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = {
> + {
> + /* Desktop editions */
> + { 5, 0, "Microsoft Windows 2000", "2000"},
> + { 5, 1, "Microsoft Windows XP", "xp"},
> + { 6, 0, "Microsoft Windows Vista", "vista"},
> + { 6, 1, "Microsoft Windows 7" "7"},
> + { 6, 2, "Microsoft Windows 8", "8"},
> + { 6, 3, "Microsoft Windows 8.1", "8.1"},
> + {10, 0, "Microsoft Windows 10", "10"},
> + { 0, 0, 0}
> + },{
> + /* Server editions */
> + { 5, 2, "Microsoft Windows Server 2003", "2003"},
> + { 6, 0, "Microsoft Windows Server 2008", "2008"},
> + { 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"},
> + { 6, 2, "Microsoft Windows Server 2012", "2012"},
> + { 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
> + {10, 0, "Microsoft Windows Server 2016", "2016"},
> + { 0, 0, 0},
> + { 0, 0, 0}
> + }
> +};
> +
> +static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info)
> +{
> + info->dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
> +
> + typedef NTSTATUS(WINAPI * rtl_get_version_t)(
> + RTL_OSVERSIONINFOEXW *os_version_info_ex);
> + HMODULE module = GetModuleHandle("ntdll");
> + PVOID fun = GetProcAddress(module, "RtlGetVersion");
> + rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun;
>
g_assert(!=null) or g_return_if_fail() ?
> + rtl_get_version(info);
> +}
> +
> +static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id)
> +{
> + DWORD major = os_version->dwMajorVersion;
> + DWORD minor = os_version->dwMinorVersion;
> + int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
> + ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
> + while (table->version != NULL) {
> + if (major == table->major && minor == table->minor) {
> + if (id) {
> + return g_strdup(table->version_id);
> + } else {
> + return g_strdup(table->version);
> + }
> + }
> + ++table;
> + }
> + slog("failed to lookup Windows version: major=%lu, minor=%lu",
> + major, minor);
> + return g_strdup("N/A");
> +}
> +
> +static char *ga_get_win_product_name(void)
> +{
> + HKEY key = NULL;
> + DWORD size = 128;
> + char *result = g_malloc0(size);
> + memset(result, 0, size);
> + LONG err = ERROR_SUCCESS;
> +
> + err = RegOpenKeyA(HKEY_LOCAL_MACHINE,
> + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
> + &key);
> + if (err == ERROR_SUCCESS) {
> + err = RegQueryValueExA(key, "ProductName", NULL, NULL,
> + (LPBYTE)result, &size);
> + if (err == ERROR_MORE_DATA) {
> + slog("ProductName longer than expected (%lu bytes), retrying",
> + size);
> + g_free(result);
> + result = NULL;
> + if (size > 0) {
> + result = g_malloc0(size);
> + err = RegQueryValueExA(key, "ProductName", NULL, NULL,
> + (LPBYTE)result, &size);
> + if (err != ERROR_SUCCESS) {
> + g_free(result);
> + result = NULL;
> + }
> + }
> + }
> + if (err != ERROR_SUCCESS) {
> + slog("failed to retrive ProductName");
> + }
> + }
> + return result;
> +}
> +
> +static char *ga_get_current_arch(void)
> +{
> + SYSTEM_INFO info;
> + GetNativeSystemInfo(&info);
> + char *result = NULL;
> + switch (info.wProcessorArchitecture) {
> + case PROCESSOR_ARCHITECTURE_AMD64:
> + result = g_strdup("x86_64");
> + break;
> + case PROCESSOR_ARCHITECTURE_ARM:
> + result = g_strdup("arm");
> + break;
> + case PROCESSOR_ARCHITECTURE_IA64:
> + result = g_strdup("ia64");
> + break;
> + case PROCESSOR_ARCHITECTURE_INTEL:
> + result = g_strdup("x86");
> + break;
> + case PROCESSOR_ARCHITECTURE_UNKNOWN:
> + default:
> + slog("unknown processor architecture 0x%0x",
> + info.wProcessorArchitecture);
> + result = g_strdup("unknown");
> + break;
> + }
> + return result;
> +}
> +
> +GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
> +{
> + OSVERSIONINFOEXW os_version;
> + ga_get_win_version(&os_version);
> +
> + bool server = os_version.wProductType != VER_NT_WORKSTATION;
> + char *product_name = ga_get_win_product_name();
> + if (product_name == NULL) {
> + Error *local_err = NULL;
> + error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
> + "failed to retrieve ProductName from registry");
> + error_propagate(errp, local_err);
> + return NULL;
> + }
> +
> + GuestOSInfo *info = g_new0(GuestOSInfo, 1);
> +
> + info->kernel_version = g_strdup_printf("%lu.%lu",
> + os_version.dwMajorVersion,
> + os_version.dwMinorVersion);
> + info->kernel_release = g_strdup_printf("%lu",
> + os_version.dwBuildNumber);
> + info->machine_hardware = ga_get_current_arch();
> +
> + info->has_id = true;
> + info->id = g_strdup("mswindows");
> + info->has_name = true;
> + info->name = g_strdup("Microsoft Windows");
> + info->has_pretty_name = true;
> + info->pretty_name = product_name;
> + info->has_version = true;
> + info->version = ga_get_win_name(&os_version, false);
> + info->has_version_id = true;
> + info->version_id = ga_get_win_name(&os_version, true);
> + info->has_variant = true;
> + info->variant = g_strdup(server ? "server" : "client");
> + info->has_variant_id = true;
> + info->variant_id = g_strdup(server ? "server" : "client");
> +
> + return info;
> +}
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index 03743ab905..eb565da345 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -1126,3 +1126,60 @@
> ##
> { 'command': 'guest-get-timezone',
> 'returns': 'GuestTimezone' }
> +
> +##
> +# @GuestOSInfo:
> +#
> +# Notes:
> +#
> +# The fields @id, @name, @pretty-name, @version, @version-id, @variant and
> +# @variant-id follow the definition specified in os-release(5). Refer to
> the
> +# manual page for exact description of the fields.
> +#
> +# On POSIX systems:
> +#
> +# kernel-release: release field returned by uname(2)
> +# kernel-version': version field returned by uname(2)
> +# machine-hardware': machine field returned by uname(2)
> +# id: as defined by os-release(5)
> +# name: as defined by os-release(5)
> +# pretty-name: as defined by os-release(5)
> +# version: as defined by os-release(5)
> +# version-id: as defined by os-release(5)
> +# variant: as defined by os-release(5)
> +# variant-id: as defined by os-release(5)
> +#
> +#
> +# On Windows:
> +#
> +# kernel-release: version number of the OS
> +# kernel-version': build number of the OS
> +# machine-hardware': architecture of the hardware
> +# id: contains string "mswindows"
> +# name: contains string "Microsoft Windows"
> +# pretty-name: product name, e.g. "Microsoft Windows 10 Enterprise"
> +# version: long version string, e.g. "Microsoft Windows Server 2008"
> +# version-id: short version identifier, e.g. "7" or "20012r2"
> +# variant: contains string "server" or "client"
> +# variant-id: contains string "server" or "client"
> +#
> +# Since: 2.10
> +##
> +{ 'struct': 'GuestOSInfo',
> + 'data': {
> + 'kernel-release': 'str', 'kernel-version': 'str',
> + 'machine-hardware': 'str', '*id': 'str', '*name': 'str',
> + '*pretty-name': 'str', '*version': 'str', '*version-id': 'str',
> + '*variant': 'str', '*variant-id': 'str' } }
> +
> +##
> +# @guest-get-osinfo:
> +#
> +# Retrieve guest operating system information
> +#
> +# Returns: operating system information on success
> +#
> +# Since 2.10
> +##
> +{ 'command': 'guest-get-osinfo',
> + 'returns': 'GuestOSInfo' }
>
Please try to add a test for this command in tests/test-qga.c. You could
use the same trick we used for QGA_CONF, and have an environemt variable
for the os-release file, like QGA_OS_RELEASE.
--
Marc-André Lureau
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v5 0/1] qemu-ga: add guest-get-osinfo command
2017-06-07 12:02 [Qemu-devel] [PATCH v5 0/1] qemu-ga: add guest-get-osinfo command Tomáš Golembiovský
2017-06-07 12:02 ` [Qemu-devel] [PATCH v5 1/1] " Tomáš Golembiovský
2017-06-13 6:33 ` [Qemu-devel] [PATCH v5 0/1] " Tomáš Golembiovský
@ 2017-06-26 12:27 ` Marc-André Lureau
2017-06-26 23:41 ` Tomáš Golembiovský
2 siblings, 1 reply; 6+ messages in thread
From: Marc-André Lureau @ 2017-06-26 12:27 UTC (permalink / raw)
To: Tomáš Golembiovský,
Eric Blake, Michael Roth, Vinzenz 'evilissimo' Feenstra
Cc: qemu-devel
Hi
On Wed, Jun 7, 2017 at 2:02 PM Tomáš Golembiovský <tgolembi@redhat.com>
wrote:
> v5:
> - fixed build failure with older glib
> - fixed coding style issues
> - fixed one log string
>
> This is a continuation of the work started by Vinzenz Feenstra in the
> threads:
>
> https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg04154.html
> https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg04302.html
> https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg06262.html
>
> The idea is to report some basic information from uname and from
> os-release file, if it is present. On MS Windows, where neither uname
> nor os-release exist we fill the values based on the information we can
> get from the OS.
>
> The example output on Fedora is:
>
> {
> "return": {
> "kernel-version": "#1 SMP Mon May 8 18:46:06 UTC 2017",
> "kernel-release": "4.10.15-200.fc25.x86_64",
> "machine-hardware": "x86_64",
> "id": "fedora",
> "name": "Fedora",
> "pretty-name": "Fedora 25 (Server Edition)",
> "version": "25 (Server Edition)",
> "variant": "Server Edition",
> "version-id": "25",
> "variant-id": "server"
> }
> }
>
> The example output on MS Windows 10 is:
>
> {
> "return": {
> "kernel-version": "10.0",
> "kernel-release": "10240",
> "machine-hardware": "x86_64",
> "id": "mswindows",
> "name": "Microsoft Windows",
> "pretty-name": "Windows 10 Enterprise",
> "version": "Microsoft Windows 10",
> "version-id": "10",
> "variant": "client",
> "variant-id": "client"
> }
> }
>
> One issue I see with the current implementation is that one is not able
> to distinguish between various (non-linux) POSIX systems from the
> returned values. That's because without os-release file (which I assume
> is not common on non-linux platforms) only kernel-version,
> kernel-release and machine-hardware are returned and telling what OS is
> running there is a guessing game. Is this a problem?
>
>
I don't know, but I would rather solve this problem at OS & os-release
level. Raise your concerns on the XDG/osrelease mailing list?
> Also the qapi documentiaton probably need some polishing. Unfortunately,
> so far I was unable to get qapi parser satisfied and still include all
> the important information.
>
You mean the doc parser? What's the issue?
>
> Tomas Golembiovsky
>
> Tomáš Golembiovský (1):
> qemu-ga: add guest-get-osinfo command
>
> configure | 2 +-
> qga/commands-posix.c | 160
> ++++++++++++++++++++++++++++++++++++++++++++++++
> qga/commands-win32.c | 170
> +++++++++++++++++++++++++++++++++++++++++++++++++++
> qga/qapi-schema.json | 57 +++++++++++++++++
> 4 files changed, 388 insertions(+), 1 deletion(-)
>
> --
> 2.13.0
>
> --
Marc-André Lureau
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] [PATCH v5 0/1] qemu-ga: add guest-get-osinfo command
2017-06-26 12:27 ` Marc-André Lureau
@ 2017-06-26 23:41 ` Tomáš Golembiovský
0 siblings, 0 replies; 6+ messages in thread
From: Tomáš Golembiovský @ 2017-06-26 23:41 UTC (permalink / raw)
To: Marc-André Lureau
Cc: Eric Blake, Michael Roth, Vinzenz 'evilissimo' Feenstra,
qemu-devel
Hi,
On Mon, 26 Jun 2017 12:27:57 +0000
Marc-André Lureau <marcandre.lureau@gmail.com> wrote:
> Hi
>
> On Wed, Jun 7, 2017 at 2:02 PM Tomáš Golembiovský <tgolembi@redhat.com>
> wrote:
>
> > v5:
> > - fixed build failure with older glib
> > - fixed coding style issues
> > - fixed one log string
> >
> > This is a continuation of the work started by Vinzenz Feenstra in the
> > threads:
> >
> > https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg04154.html
> > https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg04302.html
> > https://lists.nongnu.org/archive/html/qemu-devel/2017-03/msg06262.html
> >
> > The idea is to report some basic information from uname and from
> > os-release file, if it is present. On MS Windows, where neither uname
> > nor os-release exist we fill the values based on the information we can
> > get from the OS.
> >
> > The example output on Fedora is:
> >
> > {
> > "return": {
> > "kernel-version": "#1 SMP Mon May 8 18:46:06 UTC 2017",
> > "kernel-release": "4.10.15-200.fc25.x86_64",
> > "machine-hardware": "x86_64",
> > "id": "fedora",
> > "name": "Fedora",
> > "pretty-name": "Fedora 25 (Server Edition)",
> > "version": "25 (Server Edition)",
> > "variant": "Server Edition",
> > "version-id": "25",
> > "variant-id": "server"
> > }
> > }
> >
> > The example output on MS Windows 10 is:
> >
> > {
> > "return": {
> > "kernel-version": "10.0",
> > "kernel-release": "10240",
> > "machine-hardware": "x86_64",
> > "id": "mswindows",
> > "name": "Microsoft Windows",
> > "pretty-name": "Windows 10 Enterprise",
> > "version": "Microsoft Windows 10",
> > "version-id": "10",
> > "variant": "client",
> > "variant-id": "client"
> > }
> > }
> >
> > One issue I see with the current implementation is that one is not able
> > to distinguish between various (non-linux) POSIX systems from the
> > returned values. That's because without os-release file (which I assume
> > is not common on non-linux platforms) only kernel-version,
> > kernel-release and machine-hardware are returned and telling what OS is
> > running there is a guessing game. Is this a problem?
> >
> >
> I don't know, but I would rather solve this problem at OS & os-release
> level. Raise your concerns on the XDG/osrelease mailing list?
>
>
> > Also the qapi documentiaton probably need some polishing. Unfortunately,
> > so far I was unable to get qapi parser satisfied and still include all
> > the important information.
> >
>
> You mean the doc parser? What's the issue?
>
In the end no issue at all. It turned out my problems were caused by
stray quotes.
Tomas
> >
> > Tomas Golembiovsky
> >
> > Tomáš Golembiovský (1):
> > qemu-ga: add guest-get-osinfo command
> >
> > configure | 2 +-
> > qga/commands-posix.c | 160
> > ++++++++++++++++++++++++++++++++++++++++++++++++
> > qga/commands-win32.c | 170
> > +++++++++++++++++++++++++++++++++++++++++++++++++++
> > qga/qapi-schema.json | 57 +++++++++++++++++
> > 4 files changed, 388 insertions(+), 1 deletion(-)
> >
> > --
> > 2.13.0
> >
> > --
> Marc-André Lureau
--
Tomáš Golembiovský <tgolembi@redhat.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2017-06-26 23:41 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-07 12:02 [Qemu-devel] [PATCH v5 0/1] qemu-ga: add guest-get-osinfo command Tomáš Golembiovský
2017-06-07 12:02 ` [Qemu-devel] [PATCH v5 1/1] " Tomáš Golembiovský
2017-06-26 12:27 ` Marc-André Lureau
2017-06-13 6:33 ` [Qemu-devel] [PATCH v5 0/1] " Tomáš Golembiovský
2017-06-26 12:27 ` Marc-André Lureau
2017-06-26 23:41 ` Tomáš Golembiovský
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.