diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..279b827 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/qemu-2.8.0.tar.bz2 diff --git a/.qemu-guest-agent.metadata b/.qemu-guest-agent.metadata new file mode 100644 index 0000000..001aef0 --- /dev/null +++ b/.qemu-guest-agent.metadata @@ -0,0 +1 @@ +a5602f2ddb51d61b4c0e618b779fdb0dbdc9cc1f SOURCES/qemu-2.8.0.tar.bz2 diff --git a/README.md b/README.md deleted file mode 100644 index 98f42b4..0000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/99-qemu-guest-agent.rules b/SOURCES/99-qemu-guest-agent.rules new file mode 100644 index 0000000..8a290ab --- /dev/null +++ b/SOURCES/99-qemu-guest-agent.rules @@ -0,0 +1,2 @@ +SUBSYSTEM=="virtio-ports", ATTR{name}=="org.qemu.guest_agent.0", \ + TAG+="systemd" ENV{SYSTEMD_WANTS}="qemu-guest-agent.service" diff --git a/SOURCES/build_configure.sh b/SOURCES/build_configure.sh new file mode 100755 index 0000000..00981ee --- /dev/null +++ b/SOURCES/build_configure.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +_prefix=$1 +shift +_libdir=$1 +shift +_sysconfdir=$1 +shift +_localstatedir=$1 +shift +_libexecdir=$1 +shift +pkgname=$1 +shift +arch=$1 +shift +nvr=$1 +shift +optflags=$1 +shift +have_fdt=$1 +shift +have_tcmalloc=$1 +shift + +./configure \ + --prefix=${_prefix} \ + --libdir=${_libdir} \ + --sysconfdir=${_sysconfdir} \ + --interp-prefix=${_prefix}/qemu-%M \ + --localstatedir=${_localstatedir} \ + --libexecdir=${_libexecdir} \ + --extra-ldflags="$extraldflags -pie -Wl,-z,relro -Wl,-z,now" \ + --extra-cflags="${optflags} -fPIE -DPIE" \ + --with-pkgversion=${nvr} \ + --with-confsuffix=/${pkgname} \ + --with-coroutine=ucontext \ + --disable-archipelago \ + --disable-bluez \ + --disable-brlapi \ + --disable-cap-ng \ + --enable-coroutine-pool \ + --disable-curl \ + --disable-curses \ + --disable-debug-tcg \ + --enable-docs \ + --disable-gtk \ + --enable-kvm \ + --disable-libiscsi \ + --disable-libnfs \ + --disable-libssh2 \ + --disable-libusb \ + --disable-bzip2 \ + --disable-linux-aio \ + --disable-lzo \ + --disable-opengl \ + --enable-pie \ + --disable-qom-cast-debug \ + --disable-sdl \ + --disable-smartcard \ + --disable-snappy \ + --disable-sparse \ + --disable-strip \ + --disable-tpm \ + --enable-trace-backend=dtrace \ + --disable-vde \ + --disable-vhdx \ + --disable-vhost-scsi \ + --disable-virtfs \ + --disable-vnc-jpeg \ + --disable-vte \ + --disable-vnc-png \ + --disable-vnc-sasl \ + --enable-werror \ + --disable-xen \ + --disable-xfsctl \ + --${have_fdt}-fdt \ + --disable-glusterfs \ + --enable-guest-agent \ + --disable-numa \ + --disable-rbd \ + --disable-rdma \ + --disable-seccomp \ + --disable-spice \ + --disable-usb-redir \ + --${have_tcmalloc}-tcmalloc \ + --disable-system \ + --disable-tools \ + "$@" diff --git a/SOURCES/qemu-ga.sysconfig b/SOURCES/qemu-ga.sysconfig new file mode 100644 index 0000000..67bad0c --- /dev/null +++ b/SOURCES/qemu-ga.sysconfig @@ -0,0 +1,19 @@ +# This is a systemd environment file, not a shell script. +# It provides settings for "/lib/systemd/system/qemu-guest-agent.service". + +# Comma-separated blacklist of RPCs to disable, or empty list to enable all. +# +# You can get the list of RPC commands using "qemu-ga --blacklist='?'". +# There should be no spaces between commas and commands in the blacklist. +BLACKLIST_RPC=guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush,guest-exec,guest-exec-status + +# Fsfreeze hook script specification. +# +# FSFREEZE_HOOK_PATHNAME=/dev/null : disables the feature. +# +# FSFREEZE_HOOK_PATHNAME=/path/to/executable : enables the feature with the +# specified binary or shell script. +# +# FSFREEZE_HOOK_PATHNAME= : enables the feature with the +# default value (invoke "qemu-ga --help" to interrogate). +FSFREEZE_HOOK_PATHNAME=/etc/qemu-ga/fsfreeze-hook diff --git a/SOURCES/qemu-guest-agent.service b/SOURCES/qemu-guest-agent.service new file mode 100644 index 0000000..ab50e75 --- /dev/null +++ b/SOURCES/qemu-guest-agent.service @@ -0,0 +1,21 @@ +[Unit] +Description=QEMU Guest Agent +BindsTo=dev-virtio\x2dports-org.qemu.guest_agent.0.device +After=dev-virtio\x2dports-org.qemu.guest_agent.0.device +IgnoreOnIsolate=True + +[Service] +UMask=0077 +EnvironmentFile=/etc/sysconfig/qemu-ga +ExecStart=/usr/bin/qemu-ga \ + --method=virtio-serial \ + --path=/dev/virtio-ports/org.qemu.guest_agent.0 \ + --blacklist=${BLACKLIST_RPC} \ + -F${FSFREEZE_HOOK_PATHNAME} +StandardError=syslog +Restart=always +RestartSec=0 + +[Install] +WantedBy=dev-virtio\x2dports-org.qemu.guest_agent.0.device + diff --git a/SOURCES/qemuga-qemu-ga-add-guest-get-osinfo-command.patch b/SOURCES/qemuga-qemu-ga-add-guest-get-osinfo-command.patch new file mode 100644 index 0000000..43e79e9 --- /dev/null +++ b/SOURCES/qemuga-qemu-ga-add-guest-get-osinfo-command.patch @@ -0,0 +1,464 @@ +From 2db11b426c4e16b64926f78baea594792c183f57 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 19 Apr 2018 12:33:46 -0300 +Subject: [PATCH 5/7] qemu-ga: add guest-get-osinfo command +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Miroslav Rezanina +Message-id: <032a36a732e654dc0d6e2043a47eb294db3ac75f.1524139831.git.mrezanin@redhat.com> +Patchwork-id: 79704 +O-Subject: [RHEL-7.5.z qemu-guest-agent PATCH 5/7] qemu-ga: add guest-get-osinfo command +Bugzilla: 1598210 +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Tomáš Golembiovský + +From: 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 +Signed-off-by: Tomáš Golembiovský +* moved declarations to beginning of functions +* dropped unecessary initialization of struct utsname +Signed-off-by: Michael Roth +(cherry picked from commit 9848f79740dc3bd2e0515470d24b8bec53904473) +Signed-off-by: Wainer dos Santos Moschetta +--- + 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 70348aa8c8..add8a51f2b 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" +@@ -2587,3 +2588,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 fa99a8f846..9ae9836df5 100644 +--- a/qga/commands-win32.c ++++ b/qga/commands-win32.c +@@ -1639,3 +1639,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 5af73fedae..43ce3feeaa 100644 +--- a/qga/qapi-schema.json ++++ b/qga/qapi-schema.json +@@ -1104,3 +1104,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' } +-- +2.13.6 + diff --git a/SOURCES/qemuga-qemu-ga-check-if-utmpx.h-is-available-on-the-system.patch b/SOURCES/qemuga-qemu-ga-check-if-utmpx.h-is-available-on-the-system.patch new file mode 100644 index 0000000..22513cb --- /dev/null +++ b/SOURCES/qemuga-qemu-ga-check-if-utmpx.h-is-available-on-the-system.patch @@ -0,0 +1,122 @@ +From f19ba10fbf079a0470ccc0e4b457fd4694c52f7a Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 19 Apr 2018 12:33:45 -0300 +Subject: [PATCH 4/7] 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 + +RH-Author: Miroslav Rezanina +Message-id: +Patchwork-id: 79706 +O-Subject: [RHEL-7.5.z qemu-guest-agent PATCH 4/7] qemu-ga: check if utmpx.h is available on the system +Bugzilla: 1598210 +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Tomáš Golembiovský + +From: Tomáš Golembiovský + +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 + +Conflicts: + configure - Incorrect context due to missing commits + +(cherry picked from commit e674605f9821a275e3ed87ce9accc835d565b753) +Signed-off-by: Wainer dos Santos Moschetta +--- + configure | 19 +++++++++++++++++++ + qga/commands-posix.c | 17 ++++++++++++++++- + 2 files changed, 35 insertions(+), 1 deletion(-) + +diff --git a/configure b/configure +index ccd2a0e230..606368a506 100755 +--- a/configure ++++ b/configure +@@ -4734,6 +4734,21 @@ if test "$modules" = "yes" && test "$LD_REL_FLAGS" = ""; then + 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 + +@@ -5706,6 +5721,10 @@ if test "$have_af_vsock" = "yes" ; then + echo "CONFIG_AF_VSOCK=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 55ac4fe9e7..70348aa8c8 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 +@@ -2514,6 +2517,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) +@@ -2572,3 +2577,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 +-- +2.13.6 + diff --git a/SOURCES/qemuga-qga-Add-guest-get-host-name-command.patch b/SOURCES/qemuga-qga-Add-guest-get-host-name-command.patch new file mode 100644 index 0000000..8479003 --- /dev/null +++ b/SOURCES/qemuga-qga-Add-guest-get-host-name-command.patch @@ -0,0 +1,93 @@ +From 1878782a30dac5279c050a381015d9590d96a22e Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 19 Apr 2018 12:33:42 -0300 +Subject: [PATCH 1/7] qga: Add 'guest-get-host-name' command +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Miroslav Rezanina +Message-id: +Patchwork-id: 79710 +O-Subject: [RHEL-7.5.z qemu-guest-agent PATCH 1/7] qga: Add 'guest-get-host-name' command +Bugzilla: 1598210 +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Tomáš Golembiovský + +From: Vinzenz Feenstra + +Retrieving the guest host name is a very useful feature for virtual management +systems. This information can help to have more user friendly VM access +details, instead of an IP there would be the host name. Also the host name +reported can be used to have automated checks for valid SSL certificates. + +virsh # qemu-agent-command F25 '{ "execute": "guest-get-host-name" }' +{"return":{"host-name":"F25.lab.evilissimo.net"}} + +Signed-off-by: Vinzenz Feenstra +* minor whitespace fix-ups +Signed-off-by: Michael Roth +(cherry picked from commit 0a3d197a71b0508f5ca066488fbbbe45a61c44fe) +Signed-off-by: Wainer dos Santos Moschetta +--- + qga/commands.c | 11 +++++++++++ + qga/qapi-schema.json | 26 ++++++++++++++++++++++++++ + 2 files changed, 37 insertions(+) + +diff --git a/qga/commands.c b/qga/commands.c +index edd3e830e6..8c09938e28 100644 +--- a/qga/commands.c ++++ b/qga/commands.c +@@ -499,3 +499,14 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp) + error_setg(errp, "invalid whence code %"PRId64, whence->u.value); + return -1; + } ++ ++GuestHostName *qmp_guest_get_host_name(Error **err) ++{ ++ GuestHostName *result = NULL; ++ gchar const *hostname = g_get_host_name(); ++ if (hostname != NULL) { ++ result = g_new0(GuestHostName, 1); ++ result->host_name = g_strdup(hostname); ++ } ++ return result; ++} +diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json +index 94c03128fd..8271c1eb84 100644 +--- a/qga/qapi-schema.json ++++ b/qga/qapi-schema.json +@@ -1028,3 +1028,29 @@ + 'data': { 'path': 'str', '*arg': ['str'], '*env': ['str'], + '*input-data': 'str', '*capture-output': 'bool' }, + 'returns': 'GuestExec' } ++ ++ ++## ++# @GuestHostName: ++# @host-name: Fully qualified domain name of the guest OS ++# ++# Since: 2.10 ++## ++{ 'struct': 'GuestHostName', ++ 'data': { 'host-name': 'str' } } ++ ++## ++# @guest-get-host-name: ++# ++# Return a name for the machine. ++# ++# The returned name is not necessarily a fully-qualified domain name, or even ++# present in DNS or some other name service at all. It need not even be unique ++# on your local network or site, but usually it is. ++# ++# Returns: the host name of the machine on success ++# ++# Since: 2.10 ++## ++{ 'command': 'guest-get-host-name', ++ 'returns': 'GuestHostName' } +-- +2.13.6 + diff --git a/SOURCES/qemuga-qga-Add-guest-get-timezone-command.patch b/SOURCES/qemuga-qga-Add-guest-get-timezone-command.patch new file mode 100644 index 0000000..d5c002f --- /dev/null +++ b/SOURCES/qemuga-qga-Add-guest-get-timezone-command.patch @@ -0,0 +1,121 @@ +From c3d3a693c4bb181f6673071de95e79fd97eda526 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 19 Apr 2018 12:33:44 -0300 +Subject: [PATCH 3/7] qga: Add `guest-get-timezone` command +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Miroslav Rezanina +Message-id: <605280b778254624582ec8aa72c72155d2206948.1524139831.git.mrezanin@redhat.com> +Patchwork-id: 79705 +O-Subject: [RHEL-7.5.z qemu-guest-agent PATCH 3/7] qga: Add `guest-get-timezone` command +Bugzilla: 1598210 +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Tomáš Golembiovský + +From: Vinzenz Feenstra + +Adds a new command `guest-get-timezone` reporting the currently +configured timezone on the system. The information on what timezone is +currently is configured is useful in case of Windows VMs where the +offset of the hardware clock is required to have the same offset. This +can be used for management systems like `oVirt` to detect the timezone +difference and warn administrators of the misconfiguration. + +Signed-off-by: Vinzenz Feenstra +Reviewed-by: Sameeh Jubran +Tested-by: Sameeh Jubran +* moved stub implementation to end of function for consistency +* document that timezone names are for informational use only. +Signed-off-by: Michael Roth +(cherry picked from commit 53c58e64d0a27c59d763778faa2b5a522c544719) +Signed-off-by: Wainer dos Santos Moschetta +--- + qga/commands.c | 38 ++++++++++++++++++++++++++++++++++++++ + qga/qapi-schema.json | 25 +++++++++++++++++++++++++ + 2 files changed, 63 insertions(+) + +diff --git a/qga/commands.c b/qga/commands.c +index 8c09938e28..5b73cd0e4d 100644 +--- a/qga/commands.c ++++ b/qga/commands.c +@@ -510,3 +510,41 @@ GuestHostName *qmp_guest_get_host_name(Error **err) + } + return result; + } ++ ++GuestTimezone *qmp_guest_get_timezone(Error **errp) ++{ ++#if GLIB_CHECK_VERSION(2, 28, 0) ++ GuestTimezone *info = NULL; ++ GTimeZone *tz = NULL; ++ gint64 now = 0; ++ gint32 intv = 0; ++ gchar const *name = NULL; ++ ++ info = g_new0(GuestTimezone, 1); ++ tz = g_time_zone_new_local(); ++ if (tz == NULL) { ++ error_setg(errp, QERR_QGA_COMMAND_FAILED, ++ "Couldn't retrieve local timezone"); ++ goto error; ++ } ++ ++ now = g_get_real_time() / G_USEC_PER_SEC; ++ intv = g_time_zone_find_interval(tz, G_TIME_TYPE_UNIVERSAL, now); ++ info->offset = g_time_zone_get_offset(tz, intv); ++ name = g_time_zone_get_abbreviation(tz, intv); ++ if (name != NULL) { ++ info->has_zone = true; ++ info->zone = g_strdup(name); ++ } ++ g_time_zone_unref(tz); ++ ++ return info; ++ ++error: ++ g_free(info); ++ return NULL; ++#else ++ error_setg(errp, QERR_UNSUPPORTED); ++ return NULL; ++#endif ++} +diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json +index 1695afe3a9..5af73fedae 100644 +--- a/qga/qapi-schema.json ++++ b/qga/qapi-schema.json +@@ -1079,3 +1079,28 @@ + ## + { 'command': 'guest-get-users', + 'returns': ['GuestUser'] } ++ ++## ++# @GuestTimezone: ++# ++# @zone: Timezone name. These values may differ depending on guest/OS and ++# should only be used for informational purposes. ++# @offset: Offset to UTC in seconds, negative numbers for time zones west of ++# GMT, positive numbers for east ++# ++# Since: 2.10 ++## ++{ 'struct': 'GuestTimezone', ++ 'data': { '*zone': 'str', 'offset': 'int' } } ++ ++## ++# @guest-get-timezone: ++# ++# Retrieves the timezone information from the guest. ++# ++# Returns: A GuestTimezone dictionary. ++# ++# Since: 2.10 ++## ++{ 'command': 'guest-get-timezone', ++ 'returns': 'GuestTimezone' } +-- +2.13.6 + diff --git a/SOURCES/qemuga-qga-Add-guest-get-users-command.patch b/SOURCES/qemuga-qga-Add-guest-get-users-command.patch new file mode 100644 index 0000000..5e7658d --- /dev/null +++ b/SOURCES/qemuga-qga-Add-guest-get-users-command.patch @@ -0,0 +1,311 @@ +From c456b78c44d3f132b06d5b77de0de5e6e81870e7 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 19 Apr 2018 12:33:43 -0300 +Subject: [PATCH 2/7] qga: Add 'guest-get-users' command +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Miroslav Rezanina +Message-id: <9c0e4bb70a07f0e6cc94bab75d0aedd193f21be8.1524139831.git.mrezanin@redhat.com> +Patchwork-id: 79708 +O-Subject: [RHEL-7.5.z qemu-guest-agent PATCH 2/7] qga: Add 'guest-get-users' command +Bugzilla: 1598210 +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Tomáš Golembiovský + +From: Vinzenz Feenstra + +A command that will list all currently logged in users, and the time +since when they are logged in. + +Examples: + +virsh # qemu-agent-command F25 '{ "execute": "guest-get-users" }' +{"return":[{"login-time":1490622289.903835,"user":"root"}]} + +virsh # qemu-agent-command Win2k12r2 '{ "execute": "guest-get-users" }' +{"return":[{"login-time":1490351044.670552,"domain":"LADIDA", +"user":"Administrator"}]} + +Signed-off-by: Vinzenz Feenstra +* make g_hash_table_contains compat func inline to avoid + unused warnings +Signed-off-by: Michael Roth +(cherry picked from commit 161a56a9065feb6fa2f69cec6237a5c4e714b9d3) +Signed-off-by: Wainer dos Santos Moschetta +--- + configure | 2 +- + include/glib-compat.h | 6 +++ + qga/commands-posix.c | 60 +++++++++++++++++++++++++++++ + qga/commands-win32.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++ + qga/qapi-schema.json | 25 ++++++++++++ + 5 files changed, 195 insertions(+), 1 deletion(-) + +diff --git a/configure b/configure +index 3770d7c263..ccd2a0e230 100755 +--- a/configure ++++ b/configure +@@ -724,7 +724,7 @@ if test "$mingw32" = "yes" ; then + sysconfdir="\${prefix}" + local_statedir= + confsuffix="" +- libs_qga="-lws2_32 -lwinmm -lpowrprof -liphlpapi -lnetapi32 $libs_qga" ++ libs_qga="-lws2_32 -lwinmm -lpowrprof -lwtsapi32 -liphlpapi -lnetapi32 $libs_qga" + fi + + werror="" +diff --git a/include/glib-compat.h b/include/glib-compat.h +index acf254d2a0..f84ed5d64a 100644 +--- a/include/glib-compat.h ++++ b/include/glib-compat.h +@@ -217,6 +217,12 @@ static inline void g_hash_table_add(GHashTable *hash_table, gpointer key) + { + g_hash_table_replace(hash_table, key, key); + } ++ ++static inline gboolean g_hash_table_contains(GHashTable *hash_table, ++ gpointer key) ++{ ++ return g_hash_table_lookup_extended(hash_table, key, NULL, NULL); ++} + #endif + + #ifndef g_assert_true +diff --git a/qga/commands-posix.c b/qga/commands-posix.c +index ea37c097cf..55ac4fe9e7 100644 +--- a/qga/commands-posix.c ++++ b/qga/commands-posix.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include "qga/guest-agent-core.h" + #include "qga-qmp-commands.h" + #include "qapi/qmp/qerror.h" +@@ -2512,3 +2513,62 @@ void ga_command_state_init(GAState *s, GACommandState *cs) + ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); + #endif + } ++ ++#define QGA_MICRO_SECOND_TO_SECOND 1000000 ++ ++static double ga_get_login_time(struct utmpx *user_info) ++{ ++ double seconds = (double)user_info->ut_tv.tv_sec; ++ double useconds = (double)user_info->ut_tv.tv_usec; ++ useconds /= QGA_MICRO_SECOND_TO_SECOND; ++ return seconds + useconds; ++} ++ ++GuestUserList *qmp_guest_get_users(Error **err) ++{ ++ GHashTable *cache = NULL; ++ GuestUserList *head = NULL, *cur_item = NULL; ++ struct utmpx *user_info = NULL; ++ gpointer value = NULL; ++ GuestUser *user = NULL; ++ GuestUserList *item = NULL; ++ double login_time = 0; ++ ++ cache = g_hash_table_new(g_str_hash, g_str_equal); ++ setutxent(); ++ ++ for (;;) { ++ user_info = getutxent(); ++ if (user_info == NULL) { ++ break; ++ } else if (user_info->ut_type != USER_PROCESS) { ++ continue; ++ } else if (g_hash_table_contains(cache, user_info->ut_user)) { ++ value = g_hash_table_lookup(cache, user_info->ut_user); ++ user = (GuestUser *)value; ++ login_time = ga_get_login_time(user_info); ++ /* We're ensuring the earliest login time to be sent */ ++ if (login_time < user->login_time) { ++ user->login_time = login_time; ++ } ++ continue; ++ } ++ ++ item = g_new0(GuestUserList, 1); ++ item->value = g_new0(GuestUser, 1); ++ item->value->user = g_strdup(user_info->ut_user); ++ item->value->login_time = ga_get_login_time(user_info); ++ ++ g_hash_table_insert(cache, item->value->user, item->value); ++ ++ if (!cur_item) { ++ head = cur_item = item; ++ } else { ++ cur_item->next = item; ++ cur_item = item; ++ } ++ } ++ endutxent(); ++ g_hash_table_destroy(cache); ++ return head; ++} +diff --git a/qga/commands-win32.c b/qga/commands-win32.c +index 19d72b2411..fa99a8f846 100644 +--- a/qga/commands-win32.c ++++ b/qga/commands-win32.c +@@ -11,6 +11,9 @@ + * See the COPYING file in the top-level directory. + */ + ++#ifndef _WIN32_WINNT ++# define _WIN32_WINNT 0x0600 ++#endif + #include "qemu/osdep.h" + #include + #include +@@ -25,6 +28,7 @@ + #include + #endif + #include ++#include + + #include "qga/guest-agent-core.h" + #include "qga/vss-win32.h" +@@ -1536,3 +1540,102 @@ void ga_command_state_init(GAState *s, GACommandState *cs) + ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup); + } + } ++ ++/* MINGW is missing two fields: IncomingFrames & OutgoingFrames */ ++typedef struct _GA_WTSINFOA { ++ WTS_CONNECTSTATE_CLASS State; ++ DWORD SessionId; ++ DWORD IncomingBytes; ++ DWORD OutgoingBytes; ++ DWORD IncomingFrames; ++ DWORD OutgoingFrames; ++ DWORD IncomingCompressedBytes; ++ DWORD OutgoingCompressedBy; ++ CHAR WinStationName[WINSTATIONNAME_LENGTH]; ++ CHAR Domain[DOMAIN_LENGTH]; ++ CHAR UserName[USERNAME_LENGTH + 1]; ++ LARGE_INTEGER ConnectTime; ++ LARGE_INTEGER DisconnectTime; ++ LARGE_INTEGER LastInputTime; ++ LARGE_INTEGER LogonTime; ++ LARGE_INTEGER CurrentTime; ++ ++} GA_WTSINFOA; ++ ++GuestUserList *qmp_guest_get_users(Error **err) ++{ ++#if (_WIN32_WINNT >= 0x0600) ++#define QGA_NANOSECONDS 10000000 ++ ++ GHashTable *cache = NULL; ++ GuestUserList *head = NULL, *cur_item = NULL; ++ ++ DWORD buffer_size = 0, count = 0, i = 0; ++ GA_WTSINFOA *info = NULL; ++ WTS_SESSION_INFOA *entries = NULL; ++ GuestUserList *item = NULL; ++ GuestUser *user = NULL; ++ gpointer value = NULL; ++ INT64 login = 0; ++ double login_time = 0; ++ ++ cache = g_hash_table_new(g_str_hash, g_str_equal); ++ ++ if (WTSEnumerateSessionsA(NULL, 0, 1, &entries, &count)) { ++ for (i = 0; i < count; ++i) { ++ buffer_size = 0; ++ info = NULL; ++ if (WTSQuerySessionInformationA( ++ NULL, ++ entries[i].SessionId, ++ WTSSessionInfo, ++ (LPSTR *)&info, ++ &buffer_size ++ )) { ++ ++ if (strlen(info->UserName) == 0) { ++ WTSFreeMemory(info); ++ continue; ++ } ++ ++ login = info->LogonTime.QuadPart; ++ login -= W32_FT_OFFSET; ++ login_time = ((double)login) / QGA_NANOSECONDS; ++ ++ if (g_hash_table_contains(cache, info->UserName)) { ++ value = g_hash_table_lookup(cache, info->UserName); ++ user = (GuestUser *)value; ++ if (user->login_time > login_time) { ++ user->login_time = login_time; ++ } ++ } else { ++ item = g_new0(GuestUserList, 1); ++ item->value = g_new0(GuestUser, 1); ++ ++ item->value->user = g_strdup(info->UserName); ++ item->value->domain = g_strdup(info->Domain); ++ item->value->has_domain = true; ++ ++ item->value->login_time = login_time; ++ ++ g_hash_table_add(cache, item->value->user); ++ ++ if (!cur_item) { ++ head = cur_item = item; ++ } else { ++ cur_item->next = item; ++ cur_item = item; ++ } ++ } ++ } ++ WTSFreeMemory(info); ++ } ++ WTSFreeMemory(entries); ++ } ++ g_hash_table_destroy(cache); ++ return head; ++#else ++ error_setg(err, QERR_UNSUPPORTED); ++ return NULL; ++#endif ++} +diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json +index 8271c1eb84..1695afe3a9 100644 +--- a/qga/qapi-schema.json ++++ b/qga/qapi-schema.json +@@ -1054,3 +1054,28 @@ + ## + { 'command': 'guest-get-host-name', + 'returns': 'GuestHostName' } ++ ++ ++## ++# @GuestUser: ++# @user: Username ++# @domain: Logon domain (windows only) ++# @login-time: Time of login of this user on the computer. If multiple ++# instances of the user are logged in, the earliest login time is ++# reported. The value is in fractional seconds since epoch time. ++# ++# Since: 2.10 ++## ++{ 'struct': 'GuestUser', ++ 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' } } ++ ++## ++# @guest-get-users: ++# Retrieves a list of currently active users on the VM. ++# ++# Returns: A unique list of users. ++# ++# Since: 2.10 ++## ++{ 'command': 'guest-get-users', ++ 'returns': ['GuestUser'] } +-- +2.13.6 + diff --git a/SOURCES/qemuga-test-qga-add-test-for-guest-get-osinfo.patch b/SOURCES/qemuga-test-qga-add-test-for-guest-get-osinfo.patch new file mode 100644 index 0000000..29baf30 --- /dev/null +++ b/SOURCES/qemuga-test-qga-add-test-for-guest-get-osinfo.patch @@ -0,0 +1,159 @@ +From e3bfadeaac6a46966becef50302e2ad797c1977a Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 19 Apr 2018 12:33:48 -0300 +Subject: [PATCH 7/7] test-qga: add test for guest-get-osinfo +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Miroslav Rezanina +Message-id: <4fbd9f39de7c623e464fe5a2fe36b6aa254218ac.1524139831.git.mrezanin@redhat.com> +Patchwork-id: 79709 +O-Subject: [RHEL-7.5.z qemu-guest-agent PATCH 7/7] test-qga: add test for guest-get-osinfo +Bugzilla: 1598210 +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Tomáš Golembiovský + +From: Tomáš Golembiovský + +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 +(cherry picked from commit 339ca68bef9f30dd18e84b7d92398327e3f819a3) +Signed-off-by: Wainer dos Santos Moschetta +--- + 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 add8a51f2b..8c38dd3f0a 100644 +--- a/qga/commands-posix.c ++++ b/qga/commands-posix.c +@@ -2677,7 +2677,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); + +@@ -2692,9 +2693,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 ef8616f89d..3542a015a5 100644 +--- a/tests/test-qga.c ++++ b/tests/test-qga.c +@@ -909,6 +909,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,6 +997,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, +-- +2.13.6 + diff --git a/SOURCES/qemuga-test-qga-pass-environemnt-to-qemu-ga.patch b/SOURCES/qemuga-test-qga-pass-environemnt-to-qemu-ga.patch new file mode 100644 index 0000000..8d08f30 --- /dev/null +++ b/SOURCES/qemuga-test-qga-pass-environemnt-to-qemu-ga.patch @@ -0,0 +1,74 @@ +From e2cab87d83c5c285132dd036835ac106cb747a05 Mon Sep 17 00:00:00 2001 +From: Miroslav Rezanina +Date: Thu, 19 Apr 2018 12:33:47 -0300 +Subject: [PATCH 6/7] test-qga: pass environemnt to qemu-ga +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RH-Author: Miroslav Rezanina +Message-id: +Patchwork-id: 79707 +O-Subject: [RHEL-7.5.z qemu-guest-agent PATCH 6/7] test-qga: pass environemnt to qemu-ga +Bugzilla: 1598210 +RH-Acked-by: Marc-André Lureau +RH-Acked-by: Stefan Hajnoczi +RH-Acked-by: Tomáš Golembiovský + +From: Tomáš Golembiovský + +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 +(cherry picked from commit c28afa76c0e2829d1ebe5ad33062d1697bf2710b) +Signed-off-by: Wainer dos Santos Moschetta +--- + 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 868b02a40f..ef8616f89d 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); +@@ -680,7 +680,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'}"); +@@ -916,7 +916,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); +-- +2.13.6 + diff --git a/SPECS/qemu-guest-agent.spec b/SPECS/qemu-guest-agent.spec new file mode 100644 index 0000000..4770f39 --- /dev/null +++ b/SPECS/qemu-guest-agent.spec @@ -0,0 +1,359 @@ +# Build time setting +%global SLOF_gittagdate 20140630 + +%global have_usbredir 1 +%global have_spice 1 +%global have_fdt 0 +%global have_gluster 1 +%global have_numa 1 +%global have_librdma 1 +%global have_seccomp 1 +%global have_tcmalloc 1 + +%ifnarch %{ix86} x86_64 aarch64 + %global have_seccomp 0 +%endif + +%ifnarch %{ix86} x86_64 + %global have_usbredir 0 +%endif + +%ifarch s390 s390x + %global have_librdma 0 + %global have_numa 0 + %global have_tcmalloc 0 +%endif + +%ifarch %{ix86} + %global kvm_target i386 +%endif +%ifarch x86_64 + %global kvm_target x86_64 +%else + %global have_spice 0 + %global have_gluster 0 +%endif +%ifarch %{power64} + %global kvm_target ppc64 + %global have_fdt 1 +%endif +%ifarch s390 + %global kvm_target s390 +%endif +%ifarch s390x + %global kvm_target s390x +%endif +%ifarch ppc + %global kvm_target ppc + %global have_fdt 1 +%endif +%ifarch aarch64 + %global kvm_target aarch64 + %global have_fdt 1 +%endif + +#Versions of various parts: + +%define pkgname qemu-kvm + +Summary: QEMU guest agent +Name: qemu-guest-agent +Version: 2.8.0 +Release: 2%{?dist}.1 +# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped +Epoch: 10 +License: GPLv2+ and LGPLv2+ and BSD +Group: System Environment/Daemons +URL: http://www.qemu.org/ +Requires(post): systemd-units +Requires(preun): systemd-units +Requires(postun): systemd-units + +# OOM killer breaks builds with parallel make on s390(x) +%ifarch s390 s390x + %define _smp_mflags %{nil} +%endif + +Source0: http://wiki.qemu.org/download/qemu-2.8.0.tar.bz2 + +Source1: qemu-guest-agent.service +Source2: 99-qemu-guest-agent.rules +Source3: qemu-ga.sysconfig +Source4: build_configure.sh + +# For bz#1598210 - Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z] +Patch1: qemuga-qga-Add-guest-get-host-name-command.patch +# For bz#1598210 - Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z] +Patch2: qemuga-qga-Add-guest-get-users-command.patch +# For bz#1598210 - Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z] +Patch3: qemuga-qga-Add-guest-get-timezone-command.patch +# For bz#1598210 - Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z] +Patch4: qemuga-qemu-ga-check-if-utmpx.h-is-available-on-the-system.patch +# For bz#1598210 - Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z] +Patch5: qemuga-qemu-ga-add-guest-get-osinfo-command.patch +# For bz#1598210 - Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z] +Patch6: qemuga-test-qga-pass-environemnt-to-qemu-ga.patch +# For bz#1598210 - Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z] +Patch7: qemuga-test-qga-add-test-for-guest-get-osinfo.patch + +BuildRequires: zlib-devel +BuildRequires: glib2-devel +BuildRequires: systemd +BuildRequires: python +BuildRequires: systemtap-sdt-devel +BuildRequires: perl-podlators +BuildRequires: texinfo +%if 0%{have_tcmalloc} +BuildRequires: gperftools-devel +%endif + +%define qemudocdir %{_docdir}/%{pkgname} + +%description +qemu-kvm is an open source virtualizer that provides hardware emulation for +the KVM hypervisor. + +This package provides an agent to run inside guests, which communicates +with the host over a virtio-serial channel named "org.qemu.guest_agent.0" + +This package does not need to be installed on the host OS. + +%post +%systemd_post qemu-guest-agent.service + +%preun +%systemd_preun qemu-guest-agent.service + +%postun +%systemd_postun_with_restart qemu-guest-agent.service + +%prep +%setup -q -n qemu-%{version} + +# if patch fuzzy patch applying will be forbidden +%define with_fuzzy_patches 0 +%if %{with_fuzzy_patches} + patch_command='patch -p1 -s' +%else + patch_command='patch -p1 -F1 -s' +%endif + +ApplyPatch() +{ + local patch=$1 + shift + if [ ! -f $RPM_SOURCE_DIR/$patch ]; then + exit 1 + fi + case "$patch" in + *.bz2) bunzip2 < "$RPM_SOURCE_DIR/$patch" | $patch_command ${1+"$@"} ;; + *.gz) gunzip < "$RPM_SOURCE_DIR/$patch" | $patch_command ${1+"$@"} ;; + *) $patch_command ${1+"$@"} < "$RPM_SOURCE_DIR/$patch" ;; + esac +} + +# don't apply patch if it's empty or does not exist +ApplyOptionalPatch() +{ + local patch=$1 + shift + if [ ! -f $RPM_SOURCE_DIR/$patch ]; then + return 0 + fi + local C=$(wc -l $RPM_SOURCE_DIR/$patch | awk '{print $1}') + if [ "$C" -gt 9 ]; then + ApplyPatch $patch ${1+"$@"} + fi +} + + + +ApplyOptionalPatch qemu-kvm-test.patch + +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 + +%build +buildarch="%{kvm_target}-softmmu" + +# --build-id option is used for giving info to the debug packages. +extraldflags="-Wl,--build-id"; +buildldflags="VL_LDFLAGS=-Wl,--build-id" + +# QEMU already knows how to set _FORTIFY_SOURCE +%global optflags %(echo %{optflags} | sed 's/-Wp,-D_FORTIFY_SOURCE=2//') + +%ifarch s390 + # drop -g flag to prevent memory exhaustion by linker + %global optflags %(echo %{optflags} | sed 's/-g//') + sed -i.debug 's/"-g $CFLAGS"/"$CFLAGS"/g' configure +%endif + +cp %{SOURCE4} build_configure.sh +./build_configure.sh \ + "%{_prefix}" \ + "%{_libdir}" \ + "%{_sysconfdir}" \ + "%{_localstatedir}" \ + "%{_libexecdir}" \ + "qemu-ga" \ + "%{kvm_target}" \ + "%{name}-%{version}-%{release}" \ + "%{optflags}" \ +%if 0%{have_fdt} + enable \ +%else + disable \ +%endif +%if 0%{have_tcmalloc} + enable \ +%else + disable \ +%endif + --target-list= \ + --cpu="%{kvm_target}" + +echo "config-host.mak contents:" +echo "===" +cat config-host.mak +echo "===" + +make qemu-ga %{?_smp_mflags} $buildldflags +make qemu-ga.8 + +%install +%define _udevdir %(pkg-config --variable=udevdir udev)/rules.d + + +# For the qemu-guest-agent subpackage, install: +# - the systemd service file and the udev rules: +mkdir -p $RPM_BUILD_ROOT%{_unitdir} +mkdir -p $RPM_BUILD_ROOT%{_udevdir} +install -m 0644 %{SOURCE1} $RPM_BUILD_ROOT%{_unitdir} +install -m 0644 %{SOURCE2} $RPM_BUILD_ROOT%{_udevdir} + +# - the environment file for the systemd service: +install -D -p -m 0644 %{SOURCE3} \ + $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/qemu-ga + +# - the fsfreeze hook script: +install -D --preserve-timestamps \ + scripts/qemu-guest-agent/fsfreeze-hook \ + $RPM_BUILD_ROOT%{_sysconfdir}/qemu-ga/fsfreeze-hook + +# - the directory for user scripts: +mkdir $RPM_BUILD_ROOT%{_sysconfdir}/qemu-ga/fsfreeze-hook.d + +# - and the fsfreeze script samples: +mkdir --parents $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/qemu-ga/fsfreeze-hook.d/ +install --preserve-timestamps --mode=0644 \ + scripts/qemu-guest-agent/fsfreeze-hook.d/*.sample \ + $RPM_BUILD_ROOT%{_datadir}/%{pkgname}/qemu-ga/fsfreeze-hook.d/ + +# - Install dedicated log directory: +mkdir -p -v $RPM_BUILD_ROOT%{_localstatedir}/log/qemu-ga/ + +mkdir -p $RPM_BUILD_ROOT%{_bindir} +install -c -m 0755 qemu-ga ${RPM_BUILD_ROOT}%{_bindir}/qemu-ga + +mkdir -p $RPM_BUILD_ROOT%{_mandir}/man8 +install -m 0644 qemu-ga.8 ${RPM_BUILD_ROOT}%{_mandir}/man8/ + +%files + %defattr(-,root,root,-) + %doc COPYING README + %{_bindir}/qemu-ga + %{_unitdir}/qemu-guest-agent.service + %{_udevdir}/99-qemu-guest-agent.rules + %config(noreplace) %{_sysconfdir}/sysconfig/qemu-ga + %{_sysconfdir}/qemu-ga + %{_datadir}/%{pkgname}/qemu-ga + %{_mandir}/man8/qemu-ga.8* + %dir %{_localstatedir}/log/qemu-ga + + +%changelog +* Tue Jul 17 2018 Wainer dos Santos Moschetta - 2.8.0-2.el7_5.1 +- Corrected the package version from 2.8.0-3.el7 to 2.8.0-2.el7_5.1 +- Resolves: bz#1598210 + (Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z]) + +* Fri Jul 13 2018 Wainer dos Santos Moschetta - 2.8.0-3.el7 +- qemuga-qga-Add-guest-get-host-name-command.patch [bz#1598210] +- qemuga-qga-Add-guest-get-users-command.patch [bz#1598210] +- qemuga-qga-Add-guest-get-timezone-command.patch [bz#1598210] +- qemuga-qemu-ga-check-if-utmpx.h-is-available-on-the-system.patch [bz#1598210] +- qemuga-qemu-ga-add-guest-get-osinfo-command.patch [bz#1598210] +- qemuga-test-qga-pass-environemnt-to-qemu-ga.patch [bz#1598210] +- qemuga-test-qga-add-test-for-guest-get-osinfo.patch [bz#1598210] +- Resolves: bz#1598210 + (Backport some features to 2.8 in RHEL 7.5 [rhel-7.5.z]) + +* Fri May 19 2017 Miroslav Rezanina - 2.8.0-2.el7 +- qemuga-Remove-unnecessary-dependencies.patch [bz#1441999] +- Resolves: bz#1441999 + (Clean qemu-ga dependencies) + +* Tue Feb 07 2017 Miroslav Rezanina - 2.8.0-1.el7 +- Rebase to 2.8.0 base [bz#1414676] +- Resolves: bz#1414676 + (Rebase qemu-guest-agent to 2.8.0 base) + +* Thu Sep 01 2016 Miroslav Rezanina - 2.5.0-3.el7 +- qemuga-spec-add-qemu-ga-man-page.patch [bz#1101556] +- Resolves: bz#1101556 + ([RFE] qemu-ga should have a config file) + +* Wed Jun 15 2016 Miroslav Rezanina - 2.5.0-2.el7 +- qemuga-redhat-blacklist-guest-exec-commands.patch [bz#1340346] +- Resolves: bz#1340346 + (blacklist guest-exec in newer version of qemu-guest-agent) + +* Tue Jan 12 2016 Miroslav Rezanina - 2.5.0-1.el7 +- Rebase to QEMU 2.5.0 [bz#1297673] +- Resolves: bz#1297673 + (Rebase to QEMU 2.5) +* Tue Aug 25 2015 Miroslav Rezanina - 2.3.0-3.el7 +- qemuga-Do-not-stop-qemu-guest-agent-service-on-target-switc.patch [bz#1160930] +- Resolves: bz#1160930 + (The guest agent service in rhel7 guest will be stopped after run the init command) + +* Mon Jul 13 2015 Miroslav Rezanina - 2.3.0-2.el7 +- synchronized with qemu-kvm-rhev-2.3.0-9.el7 +- qemuga-Change-fsreeze-hook-default-location.patch [bz#1210707] +- qemuga-qga-commands-posix-Fix-bug-in-guest-fstrim.patch [bz#1211973] +- Resolves: bz#1210707 + (The default path '/etc/qemu/fsfreeze-hook' for 'fsfreeze-hook' script doesn't exist) +- Resolves: bz#1211973 + ('guest-fstrim' failed for guest with os on spapr-vscsi disk) + +* Tue Apr 28 2015 Miroslav Rezanina - 2.3.0-1.el7 +- Rebase to 2.3.0 [bz#1194152] +- Resolves: bz#1194152 + (Rebase to 2.3.0) + +* Tue Oct 21 2014 Miroslav Rezanina - 2.1.0-4.el7 +- kvm-Mark-etc-sysconfig-qemu-ga-as-config-noreplace.patch [bz#1150924] +- Resolves: bz#1150924 + (/etc/sysconfig/qemu-ga is replaced when updated) + +* Thu Aug 28 2014 Miroslav Rezanina - 2.1.0-3.el7 +- Allow building qemu-guest-agent on ppc64le +- Synchronize with qemu-kvm-rhev-2.1.0-3.el7 +- Resolves: bz#1132718 + (qemu-guest-agent fails to build for ppc64le) + +* Sat Aug 02 2014 Miroslav Rezanina - 2.1.0-2.el7 +- Create separate qemu-guest-agent package based on qemu-kvm-rhev-2.1.0-1.el7 [bz#1117096] +- Resolves: #bz1117096 + +* Sat Aug 02 2014 Miroslav Rezanina - 2.1.0-1.el7 +- Rebase to 2.1.0 [bz#1121609] +- Resolves: bz#1121609 + (Rebase qemu-kvm-rhev to qemu 2.1) +