qemu-ga patch queue
* new command: qemu-get-osinfo * build fix for OpenBSD * better error-reporting for failure on keyfile dump * remove redundant initialization of qa_state global * include libpcre in w32 package * w32 localization fixes for service installation/registration v2: * fix build issue with older GCCs introduced with guest_get_osinfo * relocated some declarations in guest_get_osinfo -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJZbeh9AAoJEDNTyc7xCLWE3hgH/2n5tn4kqkZ4fNLWiXRQgzmC Sp6lXMS1iqRwAQTWcI0+C74k6rP9ByytzxYv6UmmdpTUGmk+v26xMaJlV4LysDWx 8ilOd75s4Fmu+pWOaJTXrA8bWKEeoq7RPhl5samtY1u27v2QbCWdqViwhjzbxzNJ QmomyOkUAKdEpC70XBtEbkSyKqB4aM6nPzlTli8m3/0CNXmtwBmq7g9VPV1alLxp U567tJu0IQKuu8WsqMTPUQTHQ8ATvl0NLaF5Mpux1/B/FBui1v/9swNXPnbD9YXg +ZWxjtCep9PWq3WVylU9O2tikttxMW2vXdgB2lYM1agjQZEX+AXOj8WzhF9d4bw= =UFcD -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2017-07-17-v2-tag' into staging qemu-ga patch queue * new command: qemu-get-osinfo * build fix for OpenBSD * better error-reporting for failure on keyfile dump * remove redundant initialization of qa_state global * include libpcre in w32 package * w32 localization fixes for service installation/registration v2: * fix build issue with older GCCs introduced with guest_get_osinfo * relocated some declarations in guest_get_osinfo # gpg: Signature made Tue 18 Jul 2017 11:52:45 BST # gpg: using RSA key 0x3353C9CEF108B584 # gpg: Good signature from "Michael Roth <flukshun@gmail.com>" # gpg: aka "Michael Roth <mdroth@utexas.edu>" # gpg: aka "Michael Roth <mdroth@linux.vnet.ibm.com>" # Primary key fingerprint: CEAC C9E1 5534 EBAB B82D 3FA0 3353 C9CE F108 B584 * remotes/mdroth/tags/qga-pull-2017-07-17-v2-tag: test-qga: add test for guest-get-osinfo test-qga: pass environemnt to qemu-ga qemu-ga: add guest-get-osinfo command qga: report error on keyfile dump error qga-win32: remove a redundancy code qemu-ga: check if utmpx.h is available on the system qemu-ga: add missing libpcre to MSI build qga-win: fix installation on localized windows Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
e9277a19a1
19
configure
vendored
19
configure
vendored
|
@ -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 <utmpx.h>
|
||||
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
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
#include <dirent.h>
|
||||
#include <utmpx.h>
|
||||
#include "qga/guest-agent-core.h"
|
||||
#include "qga-qmp-commands.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
@ -25,6 +25,10 @@
|
|||
#include "qemu/base64.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
#ifdef HAVE_UTMPX
|
||||
#include <utmpx.h>
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_HAS_ENVIRON
|
||||
#ifdef __APPLE__
|
||||
#include <crt_externs.h>
|
||||
|
@ -2519,6 +2523,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 +2583,152 @@ 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
|
||||
|
||||
/* 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 = NULL;
|
||||
const char *qga_os_release = g_getenv("QGA_OS_RELEASE");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -125,6 +125,9 @@
|
|||
<Component Id="libwinpthread" Guid="{6C117C78-0F47-4B07-8F34-6BEE11643829}">
|
||||
<File Id="libwinpthread_1.dll" Name="libwinpthread-1.dll" Source="$(var.Mingw_bin)/libwinpthread-1.dll" KeyPath="yes" DiskId="1"/>
|
||||
</Component>
|
||||
<Component Id="libpcre" Guid="{7A86B45E-A009-489A-A849-CE3BACF03CD0}">
|
||||
<File Id="libpcre_1.dll" Name="libpcre-1.dll" Source="$(var.Mingw_bin)/libpcre-1.dll" KeyPath="yes" DiskId="1"/>
|
||||
</Component>
|
||||
<Component Id="registry_entries" Guid="{D075D109-51CA-11E3-9F8B-000C29858960}">
|
||||
<RegistryKey Root="HKLM"
|
||||
Key="Software\$(env.QEMU_GA_MANUFACTURER)\$(env.QEMU_GA_DISTRO)\Tools\QemuGA">
|
||||
|
@ -173,6 +176,7 @@
|
|||
<ComponentRef Id="libssp" />
|
||||
<ComponentRef Id="libwinpthread" />
|
||||
<ComponentRef Id="registry_entries" />
|
||||
<ComponentRef Id="libpcre" />
|
||||
</Feature>
|
||||
|
||||
<InstallExecuteSequence>
|
||||
|
|
|
@ -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);
|
||||
|
@ -1314,7 +1319,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");
|
||||
|
|
|
@ -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' }
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
#include <wbemidl.h>
|
||||
#include <comdef.h>
|
||||
#include <comutil.h>
|
||||
#include <sddl.h>
|
||||
|
||||
#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:
|
||||
|
|
7
tests/data/test-qga-os-release
Normal file
7
tests/data/test-qga-os-release
Normal file
|
@ -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
|
|
@ -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'}");
|
||||
|
@ -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;
|
||||
|
@ -943,7 +997,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);
|
||||
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue