From 009f38d9858d4338ccaaef787a5d54fd1c4c9198 Mon Sep 17 00:00:00 2001 From: Daniel Rempel Date: Wed, 5 Jul 2017 12:01:13 +0300 Subject: [PATCH 1/8] qga-win: fix installation on localized windows Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1357789 Replace hardcoded user and group names ("Administrators", "SYSTEM") with the ones acquired from system. Windows uses localized strings for these names and it may cause the installation to fail. Windows has Well-known SIDs for "Administrators" group and "SYSTEM" user so they were used to identify required users and groups. Well-known SIDs: https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems Signed-off-by: Daniel Rempel Signed-off-by: Sameeh Jubran Reviewed-by: Sameeh Jubran Signed-off-by: Michael Roth --- qga/vss-win32/install.cpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index f41fcdfdda..ba7c94eb25 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -18,6 +18,9 @@ #include #include #include +#include + +#define BUFFER_SIZE 1024 extern HINSTANCE g_hinstDll; @@ -135,6 +138,27 @@ out: return hr; } +/* Acquire group or user name by SID */ +static HRESULT getNameByStringSID( + const wchar_t *sid, LPWSTR buffer, LPDWORD bufferLen) +{ + HRESULT hr = S_OK; + PSID psid = NULL; + SID_NAME_USE groupType; + DWORD domainNameLen = BUFFER_SIZE; + wchar_t domainName[BUFFER_SIZE]; + + chk(ConvertStringSidToSidW(sid, &psid)); + LookupAccountSidW(NULL, psid, buffer, bufferLen, + domainName, &domainNameLen, &groupType); + hr = HRESULT_FROM_WIN32(GetLastError()); + + LocalFree(psid); + +out: + return hr; +} + /* Find and iterate QGA VSS provider in COM+ Application Catalog */ static HRESULT QGAProviderFind( HRESULT (*found)(ICatalogCollection *, int, void *), void *arg) @@ -216,6 +240,10 @@ STDAPI COMRegister(void) CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH]; bool unregisterOnFailure = false; int count = 0; + DWORD bufferLen = BUFFER_SIZE; + wchar_t buffer[BUFFER_SIZE]; + const wchar_t *administratorsGroupSID = L"S-1-5-32-544"; + const wchar_t *systemUserSID = L"S-1-5-18"; if (!g_hinstDll) { errmsg(E_FAIL, "Failed to initialize DLL"); @@ -284,11 +312,12 @@ STDAPI COMRegister(void) /* Setup roles of the applicaion */ + chk(getNameByStringSID(administratorsGroupSID, buffer, &bufferLen)); chk(pApps->GetCollection(_bstr_t(L"Roles"), key, (IDispatch **)pRoles.replace())); chk(pRoles->Populate()); chk(pRoles->Add((IDispatch **)pObj.replace())); - chk(put_Value(pObj, L"Name", L"Administrators")); + chk(put_Value(pObj, L"Name", buffer)); chk(put_Value(pObj, L"Description", L"Administrators group")); chk(pRoles->SaveChanges(&n)); chk(pObj->get_Key(&key)); @@ -303,8 +332,10 @@ STDAPI COMRegister(void) chk(GetAdminName(&name)); chk(put_Value(pObj, L"User", _bstr_t(".\\") + name)); + bufferLen = BUFFER_SIZE; + chk(getNameByStringSID(systemUserSID, buffer, &bufferLen)); chk(pUsersInRole->Add((IDispatch **)pObj.replace())); - chk(put_Value(pObj, L"User", L"SYSTEM")); + chk(put_Value(pObj, L"User", buffer)); chk(pUsersInRole->SaveChanges(&n)); out: From f2dce827f55a9b76ca7b2fc60b976b4760fc9eb1 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Thu, 1 Jun 2017 14:47:46 +0200 Subject: [PATCH 2/8] qemu-ga: add missing libpcre to MSI build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glib depends on libpcre which was not shipped with the MSI, thus starting of the qemu-ga.exe failed with the respective error message. Tell WIXL to ship this library with the MSI to avoid this problem. Signed-off-by: Thomas Lamprecht CC: Stefan Weil CC: Michael Roth Reviewed-by: Marc-André Lureau Signed-off-by: Michael Roth --- qga/installer/qemu-ga.wxs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index fa2260cafa..5af11627f8 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -125,6 +125,9 @@ + + + @@ -173,6 +176,7 @@ + From e674605f9821a275e3ed87ce9accc835d565b753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Golembiovsk=C3=BD?= Date: Mon, 17 Jul 2017 15:58:33 +0200 Subject: [PATCH 3/8] qemu-ga: check if utmpx.h is available on the system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 161a56a9065 added command guest-get-users and requires the utmpx.h (defined by POSIX) to work. It is however not always available (e.g. on OpenBSD) therefor a check for its existence is necessary. Signed-off-by: Tomáš Golembiovský Signed-off-by: Michael Roth --- configure | 19 +++++++++++++++++++ qga/commands-posix.c | 17 ++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/configure b/configure index a3f0522e8f..e8798cec79 100755 --- a/configure +++ b/configure @@ -4914,6 +4914,21 @@ if compile_prog "" "" ; then have_static_assert=yes fi +########################################## +# check for utmpx.h, it is missing e.g. on OpenBSD + +have_utmpx=no +cat > $TMPC << EOF +#include +struct utmpx user_info; +int main(void) { + return 0; +} +EOF +if compile_prog "" "" ; then + have_utmpx=yes +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -5959,6 +5974,10 @@ if test "$have_static_assert" = "yes" ; then echo "CONFIG_STATIC_ASSERT=y" >> $config_host_mak fi +if test "$have_utmpx" = "yes" ; then + echo "HAVE_UTMPX=y" >> $config_host_mak +fi + # Hold two types of flag: # CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on # a thread we have a handle to diff --git a/qga/commands-posix.c b/qga/commands-posix.c index d8e412275e..e7a047e4c2 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -15,7 +15,6 @@ #include #include #include -#include #include "qga/guest-agent-core.h" #include "qga-qmp-commands.h" #include "qapi/qmp/qerror.h" @@ -25,6 +24,10 @@ #include "qemu/base64.h" #include "qemu/cutils.h" +#ifdef HAVE_UTMPX +#include +#endif + #ifndef CONFIG_HAS_ENVIRON #ifdef __APPLE__ #include @@ -2519,6 +2522,8 @@ void ga_command_state_init(GAState *s, GACommandState *cs) #endif } +#ifdef HAVE_UTMPX + #define QGA_MICRO_SECOND_TO_SECOND 1000000 static double ga_get_login_time(struct utmpx *user_info) @@ -2577,3 +2582,13 @@ GuestUserList *qmp_guest_get_users(Error **err) g_hash_table_destroy(cache); return head; } + +#else + +GuestUserList *qmp_guest_get_users(Error **errp) +{ + error_setg(errp, QERR_UNSUPPORTED); + return NULL; +} + +#endif From f8837b3765f507409a2dd1946db60144fe7fb71e Mon Sep 17 00:00:00 2001 From: Peng Hao Date: Sun, 16 Jul 2017 06:44:01 +0800 Subject: [PATCH 4/8] qga-win32: remove a redundancy code In the first line of run_agent,it has set ga_state = s,don't need set ga_state = s again behind. Signed-off-by: Peng Hao Signed-off-by: Michael Roth --- qga/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/main.c b/qga/main.c index 405c1290f8..dcd6104bb0 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1314,7 +1314,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation) ga_command_state_init(s, s->command_state); ga_command_state_init_all(s->command_state); json_message_parser_init(&s->parser, process_event); - ga_state = s; + #ifndef _WIN32 if (!register_signal_handlers()) { g_critical("failed to register signal handlers"); From cbcd9ba1b76425915c58c67153432ff1d743aefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 1 Jun 2017 16:38:47 +0400 Subject: [PATCH 5/8] qga: report error on keyfile dump error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Cc:qemu-trivial@nongnu.org Signed-off-by: Michael Roth --- qga/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qga/main.c b/qga/main.c index dcd6104bb0..1b381d0bf3 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1074,7 +1074,12 @@ static void config_dump(GAConfig *config) g_free(tmp); tmp = g_key_file_to_data(keyfile, NULL, &error); - printf("%s", tmp); + if (error) { + g_critical("Failed to dump keyfile: %s", error->message); + g_clear_error(&error); + } else { + printf("%s", tmp); + } g_free(tmp); g_key_file_free(keyfile); From 9848f79740dc3bd2e0515470d24b8bec53904473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Golembiovsk=C3=BD?= Date: Fri, 14 Jul 2017 16:28:56 +0200 Subject: [PATCH 6/8] qemu-ga: add guest-get-osinfo command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Tomáš Golembiovský * moved declarations to beginning of functions * dropped unecessary initialization of struct utsname Signed-off-by: Michael Roth --- qga/commands-posix.c | 135 ++++++++++++++++++++++++++++++ qga/commands-win32.c | 191 +++++++++++++++++++++++++++++++++++++++++++ qga/qapi-schema.json | 65 +++++++++++++++ 3 files changed, 391 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index e7a047e4c2..7b3d89a92c 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" @@ -2592,3 +2593,137 @@ GuestUserList *qmp_guest_get_users(Error **errp) } #endif + +/* Replace escaped special characters with theire real values. The replacement + * is done in place -- returned value is in the original string. + */ +static void ga_osrelease_replace_special(gchar *value) +{ + gchar *p, *p2, quote; + + /* Trim the string at first space or semicolon if it is not enclosed in + * single or double quotes. */ + if ((value[0] != '"') || (value[0] == '\'')) { + p = strchr(value, ' '); + if (p != NULL) { + *p = 0; + } + p = strchr(value, ';'); + if (p != NULL) { + *p = 0; + } + return; + } + + quote = value[0]; + p2 = value; + p = value + 1; + while (*p != 0) { + if (*p == '\\') { + p++; + switch (*p) { + case '$': + case '\'': + case '"': + case '\\': + case '`': + break; + default: + /* Keep literal backslash followed by whatever is there */ + p--; + break; + } + } else if (*p == quote) { + *p2 = 0; + break; + } + *(p2++) = *(p++); + } +} + +static GKeyFile *ga_parse_osrelease(const char *fname) +{ + gchar *content = NULL; + gchar *content2 = NULL; + GError *err = NULL; + GKeyFile *keys = g_key_file_new(); + const char *group = "[os-release]\n"; + + if (!g_file_get_contents(fname, &content, NULL, &err)) { + slog("failed to read '%s', error: %s", fname, err->message); + goto fail; + } + + if (!g_utf8_validate(content, -1, NULL)) { + slog("file is not utf-8 encoded: %s", fname); + goto fail; + } + content2 = g_strdup_printf("%s%s", group, content); + + if (!g_key_file_load_from_data(keys, content2, -1, G_KEY_FILE_NONE, + &err)) { + slog("failed to parse file '%s', error: %s", fname, err->message); + goto fail; + } + + g_free(content); + g_free(content2); + return keys; + +fail: + g_error_free(err); + g_free(content); + g_free(content2); + g_key_file_free(keys); + return NULL; +} + +GuestOSInfo *qmp_guest_get_osinfo(Error **errp) +{ + GuestOSInfo *info = NULL; + struct utsname kinfo; + GKeyFile *osrelease; + + info = g_new0(GuestOSInfo, 1); + + if (uname(&kinfo) != 0) { + error_setg_errno(errp, errno, "uname failed"); + } else { + info->has_kernel_version = true; + info->kernel_version = g_strdup(kinfo.version); + info->has_kernel_release = true; + info->kernel_release = g_strdup(kinfo.release); + info->has_machine = true; + info->machine = g_strdup(kinfo.machine); + } + + osrelease = ga_parse_osrelease("/etc/os-release"); + if (osrelease == NULL) { + osrelease = ga_parse_osrelease("/usr/lib/os-release"); + } + + if (osrelease != NULL) { + char *value; + +#define GET_FIELD(field, osfield) do { \ + value = g_key_file_get_value(osrelease, "os-release", osfield, NULL); \ + if (value != NULL) { \ + ga_osrelease_replace_special(value); \ + info->has_ ## field = true; \ + info->field = value; \ + } \ +} while (0) + GET_FIELD(id, "ID"); + GET_FIELD(name, "NAME"); + GET_FIELD(pretty_name, "PRETTY_NAME"); + GET_FIELD(version, "VERSION"); + GET_FIELD(version_id, "VERSION_ID"); + GET_FIELD(variant, "VARIANT"); + GET_FIELD(variant_id, "VARIANT_ID"); +#undef GET_FIELD + + g_key_file_free(osrelease); + } + + return info; +} diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 6f1645747b..619dbd2bc2 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1642,3 +1642,194 @@ 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, Error **errp) +{ + typedef NTSTATUS(WINAPI * rtl_get_version_t)( + RTL_OSVERSIONINFOEXW *os_version_info_ex); + + info->dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + + HMODULE module = GetModuleHandle("ntdll"); + PVOID fun = GetProcAddress(module, "RtlGetVersion"); + if (fun == NULL) { + error_setg(errp, QERR_QGA_COMMAND_FAILED, + "Failed to get address of RtlGetVersion"); + return; + } + + rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun; + rtl_get_version(info); + return; +} + +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(Error **errp) +{ + HKEY key = NULL; + DWORD size = 128; + char *result = g_malloc0(size); + LONG err = ERROR_SUCCESS; + + err = RegOpenKeyA(HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + &key); + if (err != ERROR_SUCCESS) { + error_setg_win32(errp, err, "failed to open registry key"); + goto fail; + } + + 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) { + error_setg_win32(errp, err, "failed to retrive ProductName"); + goto fail; + } + + return result; + +fail: + g_free(result); + return NULL; +} + +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) +{ + Error *local_err = NULL; + OSVERSIONINFOEXW os_version = {0}; + bool server; + char *product_name; + GuestOSInfo *info; + + ga_get_win_version(&os_version, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; + } + + server = os_version.wProductType != VER_NT_WORKSTATION; + product_name = ga_get_win_product_name(&local_err); + if (product_name == NULL) { + error_propagate(errp, local_err); + return NULL; + } + + info = g_new0(GuestOSInfo, 1); + + info->has_kernel_version = true; + info->kernel_version = g_strdup_printf("%lu.%lu", + os_version.dwMajorVersion, + os_version.dwMinorVersion); + info->has_kernel_release = true; + info->kernel_release = g_strdup_printf("%lu", + os_version.dwBuildNumber); + info->has_machine = true; + info->machine = 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..90a0c8602b 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1126,3 +1126,68 @@ ## { 'command': 'guest-get-timezone', 'returns': 'GuestTimezone' } + +## +# @GuestOSInfo: +# +# @kernel-release: +# * POSIX: release field returned by uname(2) +# * Windows: version number of the OS +# @kernel-version: +# * POSIX: version field returned by uname(2) +# * Windows: build number of the OS +# @machine: +# * POSIX: machine field returned by uname(2) +# * Windows: one of x86, x86_64, arm, ia64 +# @id: +# * POSIX: as defined by os-release(5) +# * Windows: contains string "mswindows" +# @name: +# * POSIX: as defined by os-release(5) +# * Windows: contains string "Microsoft Windows" +# @pretty-name: +# * POSIX: as defined by os-release(5) +# * Windows: product name, e.g. "Microsoft Windows 10 Enterprise" +# @version: +# * POSIX: as defined by os-release(5) +# * Windows: long version string, e.g. "Microsoft Windows Server 2008" +# @version-id: +# * POSIX: as defined by os-release(5) +# * Windows: short version identifier, e.g. "7" or "20012r2" +# @variant: +# * POSIX: as defined by os-release(5) +# * Windows: contains string "server" or "client" +# @variant-id: +# * POSIX: as defined by os-release(5) +# * Windows: contains string "server" or "client" +# +# Notes: +# +# On POSIX systems 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. Their values +# are taken from the os-release file. If the file is not present in the system, +# or the values are not present in the file, the fields are not included. +# +# On Windows the values are filled from information gathered from the system. +# +# Since: 2.10 +## +{ 'struct': 'GuestOSInfo', + 'data': { + '*kernel-release': 'str', '*kernel-version': 'str', + '*machine': '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: @GuestOSInfo +# +# Since: 2.10 +## +{ 'command': 'guest-get-osinfo', + 'returns': 'GuestOSInfo' } From c28afa76c0e2829d1ebe5ad33062d1697bf2710b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Golembiovsk=C3=BD?= Date: Fri, 14 Jul 2017 16:28:57 +0200 Subject: [PATCH 7/8] test-qga: pass environemnt to qemu-ga MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify fixture_setup() to pass environemnt variables to spawned qemu-ga instance. Signed-off-by: Tomáš Golembiovský Reviewed-by: Marc-André Lureau Signed-off-by: Michael Roth --- tests/test-qga.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test-qga.c b/tests/test-qga.c index c77f241036..631b98639a 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -46,7 +46,7 @@ static void qga_watch(GPid pid, gint status, gpointer user_data) } static void -fixture_setup(TestFixture *fixture, gconstpointer data) +fixture_setup(TestFixture *fixture, gconstpointer data, gchar **envp) { const gchar *extra_arg = data; GError *error = NULL; @@ -67,7 +67,7 @@ fixture_setup(TestFixture *fixture, gconstpointer data) g_shell_parse_argv(cmd, NULL, &argv, &error); g_assert_no_error(error); - g_spawn_async(fixture->test_dir, argv, NULL, + g_spawn_async(fixture->test_dir, argv, envp, G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &fixture->pid, &error); g_assert_no_error(error); @@ -707,7 +707,7 @@ static void test_qga_blacklist(gconstpointer data) QDict *ret, *error; const gchar *class, *desc; - fixture_setup(&fix, "-b guest-ping,guest-get-time"); + fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL); /* check blacklist */ ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}"); @@ -943,7 +943,7 @@ int main(int argc, char **argv) setlocale (LC_ALL, ""); g_test_init(&argc, &argv, NULL); - fixture_setup(&fix, NULL); + fixture_setup(&fix, NULL, NULL); g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited); g_test_add_data_func("/qga/sync", &fix, test_qga_sync); From 339ca68bef9f30dd18e84b7d92398327e3f819a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Golembiovsk=C3=BD?= Date: Fri, 14 Jul 2017 16:28:58 +0200 Subject: [PATCH 8/8] test-qga: add test for guest-get-osinfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test for guest-get-osinfo command. Qemu-ga was modified to accept QGA_OS_RELEASE environment variable. If the variable is defined it is interpreted as path to the os-release file and it is parsed instead of the default paths. Signed-off-by: Tomáš Golembiovský Reviewed-by: Marc-André Lureau * move declarations to beginning of functions Signed-off-by: Michael Roth --- qga/commands-posix.c | 13 +++++--- tests/data/test-qga-os-release | 7 +++++ tests/test-qga.c | 56 ++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 tests/data/test-qga-os-release diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 7b3d89a92c..ab0c63d931 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2682,7 +2682,8 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) { GuestOSInfo *info = NULL; struct utsname kinfo; - GKeyFile *osrelease; + GKeyFile *osrelease = NULL; + const char *qga_os_release = g_getenv("QGA_OS_RELEASE"); info = g_new0(GuestOSInfo, 1); @@ -2697,9 +2698,13 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) info->machine = g_strdup(kinfo.machine); } - osrelease = ga_parse_osrelease("/etc/os-release"); - if (osrelease == NULL) { - osrelease = ga_parse_osrelease("/usr/lib/os-release"); + if (qga_os_release != NULL) { + osrelease = ga_parse_osrelease(qga_os_release); + } else { + osrelease = ga_parse_osrelease("/etc/os-release"); + if (osrelease == NULL) { + osrelease = ga_parse_osrelease("/usr/lib/os-release"); + } } if (osrelease != NULL) { diff --git a/tests/data/test-qga-os-release b/tests/data/test-qga-os-release new file mode 100644 index 0000000000..70664eb6ec --- /dev/null +++ b/tests/data/test-qga-os-release @@ -0,0 +1,7 @@ +ID=qemu-ga-test +NAME=QEMU-GA +PRETTY_NAME="QEMU Guest Agent test" +VERSION="Test 1" +VERSION_ID=1 +VARIANT="Unit test \"\'\$\`\\ and \\\\ etc." +VARIANT_ID=unit-test diff --git a/tests/test-qga.c b/tests/test-qga.c index 631b98639a..06783e7585 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -936,6 +936,60 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) QDECREF(ret); } +static void test_qga_guest_get_osinfo(gconstpointer data) +{ + TestFixture fixture; + const gchar *str; + gchar *cwd, *env[2]; + QDict *ret, *val; + + cwd = g_get_current_dir(); + env[0] = g_strdup_printf( + "QGA_OS_RELEASE=%s%ctests%cdata%ctest-qga-os-release", + cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR); + env[1] = NULL; + g_free(cwd); + fixture_setup(&fixture, NULL, env); + + ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + + str = qdict_get_try_str(val, "id"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "qemu-ga-test"); + + str = qdict_get_try_str(val, "name"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "QEMU-GA"); + + str = qdict_get_try_str(val, "pretty-name"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "QEMU Guest Agent test"); + + str = qdict_get_try_str(val, "version"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "Test 1"); + + str = qdict_get_try_str(val, "version-id"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "1"); + + str = qdict_get_try_str(val, "variant"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc."); + + str = qdict_get_try_str(val, "variant-id"); + g_assert_nonnull(str); + g_assert_cmpstr(str, ==, "unit-test"); + + QDECREF(ret); + g_free(env[0]); + fixture_tear_down(&fixture, NULL); +} + int main(int argc, char **argv) { TestFixture fix; @@ -972,6 +1026,8 @@ int main(int argc, char **argv) g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec); g_test_add_data_func("/qga/guest-exec-invalid", &fix, test_qga_guest_exec_invalid); + g_test_add_data_func("/qga/guest-get-osinfo", &fix, + test_qga_guest_get_osinfo); if (g_getenv("QGA_TEST_SIDE_EFFECTING")) { g_test_add_data_func("/qga/fsfreeze-and-thaw", &fix,