Rebase mingw-qemu-ga-win to QEMU 10.1.0

Resolves: RHEL-111001 Rebase mingw-qemu-ga-win to QEMU 10.1.0
Resolves: RHEL-107458 [mingw-qemu-ga-win] QAPI error desc does not contain Windows error
Resolves: RHEL-107446 QGA VSS wasn't removed if the QGA installation fail and quit
Resolves: RHEL-107215 Fix don't daemonize before channel is initialized patch for Windows behavior

Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com>
This commit is contained in:
Konstantin Kostiuk 2025-09-04 10:42:43 +03:00
parent d8b52e40dc
commit b59c32d3aa
10 changed files with 484 additions and 632 deletions

3
.gitignore vendored
View File

@ -9,4 +9,5 @@
/qemu-8.2.0.tar.bz2
/qemu-9.0.0.tar.bz2
/qemu-v9.1.0.tar.bz2
/qemu-v10.0.0.tar.bz2
/qemu-v10.0.0.tar.bz2
/qemu-v10.1.0.tar.bz2

View File

@ -13,7 +13,7 @@ index 91ff57278e..586de296f5 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-10.0.0
+110.0.2
-10.1.0
+110.1.0
--
2.17.2

View File

@ -1,78 +0,0 @@
From 2ae7fcc6de174f0741ee563e20e99db249af5ecc Mon Sep 17 00:00:00 2001
From: Kostiantyn Kostiuk <kostyanf14@live.com>
Date: Mon, 4 Aug 2025 13:24:34 +0300
Subject: [PATCH] Revert "qga: Don't daemonize before channel is initialized"
This reverts commit c6f5dd7ac8ef62dcdec4cdeda1467c658161afff.
Resolves: RHEL-107174
---
qga/main.c | 24 ++++++------------------
1 file changed, 6 insertions(+), 18 deletions(-)
diff --git a/qga/main.c b/qga/main.c
index 6c02f3ec38..e7fb94e973 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1457,6 +1457,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
if (config->daemonize) {
/* delay opening/locking of pidfile till filesystems are unfrozen */
s->deferred_options.pid_filepath = config->pid_filepath;
+ become_daemon(NULL);
}
if (config->log_filepath) {
/* delay opening the log file till filesystems are unfrozen */
@@ -1464,6 +1465,9 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
}
ga_disable_logging(s);
} else {
+ if (config->daemonize) {
+ become_daemon(config->pid_filepath);
+ }
if (config->log_filepath) {
FILE *log_file = ga_open_logfile(config->log_filepath);
if (!log_file) {
@@ -1510,20 +1514,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
ga_apply_command_filters(s);
- if (!channel_init(s, s->config->method, s->config->channel_path,
- s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) {
- g_critical("failed to initialize guest agent channel");
- return NULL;
- }
-
- if (config->daemonize) {
- if (ga_is_frozen(s)) {
- become_daemon(NULL);
- } else {
- become_daemon(config->pid_filepath);
- }
- }
-
ga_state = s;
return s;
}
@@ -1562,9 +1552,8 @@ static void cleanup_agent(GAState *s)
static int run_agent_once(GAState *s)
{
- if (!s->channel &&
- channel_init(s, s->config->method, s->config->channel_path,
- s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) {
+ if (!channel_init(s, s->config->method, s->config->channel_path,
+ s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) {
g_critical("failed to initialize guest agent channel");
return EXIT_FAILURE;
}
@@ -1573,7 +1562,6 @@ static int run_agent_once(GAState *s)
if (s->channel) {
ga_channel_free(s->channel);
- s->channel = NULL;
}
return EXIT_SUCCESS;
--
2.48.1

View File

@ -1,45 +0,0 @@
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH] qga-vss: Exit with non-zero code when register fail
From: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Date: Fri, 20 Jun 2025 11:31:32 +0300
Message-Id: <20250620083132.28347-1-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
QGA installer uses rundll32 to run the DLLCOMRegister function
from qga-vss.dll and perform VSS provider registration.
rundll32 ignores the return value of the function and always
exits with a zero exit code. This causes a situation where
the installer does not know the status of VSS provider registration.
This commit forces to change exit code when the VSS provider
registration fails.
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/rundll32
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
---
qga/vss-win32/install.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp
index 5cea5bcf74..6ee2f44a10 100644
--- a/qga/vss-win32/install.cpp
+++ b/qga/vss-win32/install.cpp
@@ -385,7 +385,10 @@ out:
STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int);
STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int)
{
- COMRegister();
+ HRESULT hr = COMRegister();
+ if (FAILED(hr)) {
+ exit(hr);
+ }
}
STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int);
--
2.48.1

View File

@ -1,44 +0,0 @@
From 3276284b43f48d8a513f15cc33b8e16e922463d5 Mon Sep 17 00:00:00 2001
From: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Date: Thu, 17 Jul 2025 17:47:03 +0300
Subject: [PATCH 1/2] util: win32: Write hex value when can't get error message
g_win32_error_message - translate a Win32 error code
(as returned by GetLastError()) into the corresponding message.
In the same time, we call error_setg_win32_internal with
error codes from different Windows componets like VSS or
Performance monitor that provides different codes and
can't be converted with g_win32_error_message. In this
case, the empty suffix will be returned so error will be
masked.
QGA error example:
- before changes:
{"error": {"class": "GenericError", "desc": "failed to add D:\\ to snapshot set: "}}
- after changes:
{"error": {"class": "GenericError", "desc": "failed to add D:\\ to snapshot set: unknown Windows error 0x8004230e"}}
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
util/error.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/util/error.c b/util/error.c
index daea2142f3..b1342558ae 100644
--- a/util/error.c
+++ b/util/error.c
@@ -188,6 +188,11 @@ void error_setg_win32_internal(Error **errp,
if (win32_err != 0) {
suffix = g_win32_error_message(win32_err);
+ // g_win32_error_message() failed
+ if (!suffix[0]) {
+ g_free(suffix);
+ suffix = g_strdup_printf("unknown Windows error 0x%x", win32_err);
+ }
}
va_start(ap, fmt);
--
2.48.1

View File

@ -1,12 +1,12 @@
%{?mingw_package_header}
%define with_vss 1
%define qemu_version 10.0.0
%define qemu_version 10.1.0
%define ga_manufacturer "RedHat"
%define ga_distro "RHEL"
Name: mingw-qemu-ga-win
Version: 110.0.2
Version: 110.1.0
Release: 1%{?dist}
Summary: Qemus Guest agent for Windows
@ -19,12 +19,8 @@ Requires(postun): systemd-units
Source0: https://gitlab.com/qemu-project/qemu/-/archive/v%{qemu_version}/qemu-v%{qemu_version}.tar.bz2
Patch0001: 0001-Change-Version.patch
Patch0002: v2_20250324_kkostiuk_qga_add_guest_get_load_command.mbx
Patch0003: v2_20250618_eashurov_qga_vss_win32_add_vss_provider_unregistration_retry.mbx
Patch0004: 20250620_kkostiuk_qga_vss_exit_with_non_zero_code_when_register_fail.mbx
Patch0005: 2025_07_17_kkostiuk_util_win32_write_hex_value_when_can_t_get_error_message.patch
Patch0006: 0002-qga-win-Add-additional-VSS-logs.patch
Patch0007: 0001-Revert-qga-Don-t-daemonize-before-channel-is-initial.patch
Patch0002: 0002-qga-win-Add-additional-VSS-logs.patch
Patch0003: v2_20250901_kkostiuk_misc_qga_fixes_for_2025_08_29.mbx
BuildArch: noarch
# RHEL-57753 - mingw-qemu-ga-win failed to build on s390x
@ -74,10 +70,6 @@ This package does not need to be installed on the host OS.
%patch0001 -p1
%patch0002 -p1
%patch0003 -p1
%patch0004 -p1
%patch0005 -p1
%patch0006 -p1
%patch0007 -p1
%build
@ -134,6 +126,12 @@ cp build/qga/qemu-ga-x86_64.msi $RPM_BUILD_ROOT%{mingw64_bindir}
%{mingw64_bindir}/qemu-ga*
%changelog
* Thu Sep 4 2025 Kostiantyn Kostiuk <kkostiuk@redhat.com> 110.1.0-1
- RHEL-111001 Rebase mingw-qemu-ga-win to QEMU 10.1.0
- RHEL-107458 [mingw-qemu-ga-win] QAPI error desc does not contain Windows error
- RHEL-107446 QGA VSS wasn't removed if the QGA installation fail and quit
- RHEL-107215 Fix don't daemonize before channel is initialized patch for Windows behavior
* Mon Aug 4 2025 Kostiantyn Kostiuk <kkostiuk@redhat.com> 110.0.2-1
- RHEL-107174 QGA can't be installed before vioserial driver or without serial port configured

View File

@ -1 +1 @@
SHA512 (qemu-v10.0.0.tar.bz2) = 1f7e1c0f19a46a0bbf1e1d73a3e41f5f91841dc40f4416619cc011b27ac527a14e3ae4b009fc787c40ee1c6b7892c492d879dec6f15d2396adad1206369e2b37
SHA512 (qemu-v10.1.0.tar.bz2) = 03975a58b460fa89a0af7710e531dbcb643fa66dbfef4f29b2f4d0a1ddc1e5ca671d463c0e4d3628153d10f03658d8ac2a57181dfc3bfae4ebb808de6c3ab590

View File

@ -1,405 +0,0 @@
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 1/2] qga-win: implement a 'guest-get-load' command
From: Konstantin Kostiuk <kkostiuk@redhat.com>
Date: Mon, 24 Mar 2025 15:17:28 +0200
Message-Id: <20250324131729.70992-2-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
Windows has no native equivalent API, but it would be possible to
simulate it as illustrated here (BSD-3-Clause):
https://github.com/giampaolo/psutil/pull/1485
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Tested-by: Dehan Meng <demeng@redhat.com>
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com>
---
qga/commands-win32.c | 148 +++++++++++++++++++++++++++++++++++++++++
qga/guest-agent-core.h | 10 +++
qga/main.c | 39 +++++++++++
qga/meson.build | 2 +-
qga/qapi-schema.json | 9 ++-
5 files changed, 205 insertions(+), 3 deletions(-)
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 749fdf8895..ba72bface7 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -27,6 +27,7 @@
#include <lm.h>
#include <wtsapi32.h>
#include <wininet.h>
+#include <pdh.h>
#include "guest-agent-core.h"
#include "vss-win32.h"
@@ -119,6 +120,28 @@ static OpenFlags guest_file_open_modes[] = {
{"a+b", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS }
};
+/*
+ * We use an exponentially weighted moving average, just like Unix systems do
+ * https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation
+ *
+ * These constants serve as the damping factor and are calculated with
+ * 1 / exp(sampling interval in seconds / window size in seconds)
+ *
+ * This formula comes from linux's include/linux/sched/loadavg.h
+ * https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23
+ */
+#define LOADAVG_FACTOR_1F 0.9200444146293232478931553241
+#define LOADAVG_FACTOR_5F 0.9834714538216174894737477501
+#define LOADAVG_FACTOR_15F 0.9944598480048967508795473394
+/*
+ * The time interval in seconds between taking load counts, same as Linux
+ */
+#define LOADAVG_SAMPLING_INTERVAL 5
+
+double load_avg_1m;
+double load_avg_5m;
+double load_avg_15m;
+
#define debug_error(msg) do { \
char *suffix = g_win32_error_message(GetLastError()); \
g_debug("%s: %s", (msg), suffix); \
@@ -2448,3 +2471,128 @@ char *qga_get_host_name(Error **errp)
return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL);
}
+
+
+static VOID CALLBACK load_avg_callback(PVOID hCounter, BOOLEAN timedOut)
+{
+ PDH_FMT_COUNTERVALUE displayValue;
+ double currentLoad;
+ PDH_STATUS err;
+
+ err = PdhGetFormattedCounterValue(
+ (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &displayValue);
+ /* Skip updating the load if we can't get the value successfully */
+ if (err != ERROR_SUCCESS) {
+ slog("PdhGetFormattedCounterValue failed to get load value with 0x%lx",
+ err);
+ return;
+ }
+ currentLoad = displayValue.doubleValue;
+
+ load_avg_1m = load_avg_1m * LOADAVG_FACTOR_1F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_1F);
+ load_avg_5m = load_avg_5m * LOADAVG_FACTOR_5F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_5F);
+ load_avg_15m = load_avg_15m * LOADAVG_FACTOR_15F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_15F);
+}
+
+static BOOL init_load_avg_counter(Error **errp)
+{
+ CONST WCHAR *szCounterPath = L"\\System\\Processor Queue Length";
+ PDH_STATUS status;
+ BOOL ret;
+ HQUERY hQuery;
+ HCOUNTER hCounter;
+ HANDLE event;
+ HANDLE waitHandle;
+
+ status = PdhOpenQueryW(NULL, 0, &hQuery);
+ if (status != ERROR_SUCCESS) {
+ /*
+ * If the function fails, the return value is a system error code or
+ * a PDH error code. error_setg_win32 cant translate PDH error code
+ * properly, so just report it as is.
+ */
+ error_setg_win32(errp, (DWORD)status,
+ "PdhOpenQueryW failed with 0x%lx", status);
+ return FALSE;
+ }
+
+ status = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter);
+ if (status != ERROR_SUCCESS) {
+ error_setg_win32(errp, (DWORD)status,
+ "PdhAddEnglishCounterW failed with 0x%lx. Performance counters may be disabled.",
+ status);
+ PdhCloseQuery(hQuery);
+ return FALSE;
+ }
+
+ event = CreateEventW(NULL, FALSE, FALSE, L"LoadUpdateEvent");
+ if (event == NULL) {
+ error_setg_win32(errp, GetLastError(), "Create LoadUpdateEvent failed");
+ PdhCloseQuery(hQuery);
+ return FALSE;
+ }
+
+ status = PdhCollectQueryDataEx(hQuery, LOADAVG_SAMPLING_INTERVAL, event);
+ if (status != ERROR_SUCCESS) {
+ error_setg_win32(errp, (DWORD)status,
+ "PdhCollectQueryDataEx failed with 0x%lx", status);
+ CloseHandle(event);
+ PdhCloseQuery(hQuery);
+ return FALSE;
+ }
+
+ ret = RegisterWaitForSingleObject(
+ &waitHandle,
+ event,
+ (WAITORTIMERCALLBACK)load_avg_callback,
+ (PVOID)hCounter,
+ INFINITE,
+ WT_EXECUTEDEFAULT);
+
+ if (ret == 0) {
+ error_setg_win32(errp, GetLastError(),
+ "RegisterWaitForSingleObject failed");
+ CloseHandle(event);
+ PdhCloseQuery(hQuery);
+ return FALSE;
+ }
+
+ ga_set_load_avg_wait_handle(ga_state, waitHandle);
+ ga_set_load_avg_event(ga_state, event);
+ ga_set_load_avg_pdh_query(ga_state, hQuery);
+
+ return TRUE;
+}
+
+GuestLoadAverage *qmp_guest_get_load(Error **errp)
+{
+ /*
+ * The load average logic calls PerformaceCounterAPI, which can result
+ * in a performance penalty. This avoids running the load average logic
+ * until a management application actually requests it. The load average
+ * will not initially be very accurate, but assuming that any interested
+ * management application will request it repeatedly throughout the lifetime
+ * of the VM, this seems like a good mitigation.
+ */
+ if (ga_get_load_avg_pdh_query(ga_state) == NULL) {
+ /* set initial values */
+ load_avg_1m = 0;
+ load_avg_5m = 0;
+ load_avg_15m = 0;
+
+ if (init_load_avg_counter(errp) == false) {
+ return NULL;
+ }
+ }
+
+ GuestLoadAverage *ret = NULL;
+
+ ret = g_new0(GuestLoadAverage, 1);
+ ret->load1m = load_avg_1m;
+ ret->load5m = load_avg_5m;
+ ret->load15m = load_avg_15m;
+ return ret;
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index a536d07d0d..d9f3922adf 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -13,6 +13,10 @@
#ifndef GUEST_AGENT_CORE_H
#define GUEST_AGENT_CORE_H
+#ifdef _WIN32
+#include <pdh.h>
+#endif
+
#include "qapi/qmp-registry.h"
#include "qga-qapi-types.h"
@@ -41,6 +45,12 @@ void ga_set_response_delimited(GAState *s);
bool ga_is_frozen(GAState *s);
void ga_set_frozen(GAState *s);
void ga_unset_frozen(GAState *s);
+#ifdef _WIN32
+void ga_set_load_avg_event(GAState *s, HANDLE event);
+void ga_set_load_avg_wait_handle(GAState *s, HANDLE wait_handle);
+void ga_set_load_avg_pdh_query(GAState *s, HQUERY query);
+HQUERY ga_get_load_avg_pdh_query(GAState *s);
+#endif
const char *ga_fsfreeze_hook(GAState *s);
int64_t ga_get_fd_handle(GAState *s, Error **errp);
int ga_parse_whence(GuestFileWhence *whence, Error **errp);
diff --git a/qga/main.c b/qga/main.c
index 72c39b042f..6c02f3ec38 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -33,6 +33,7 @@
#include "qemu-version.h"
#ifdef _WIN32
#include <dbt.h>
+#include <pdh.h>
#include "qga/service-win32.h"
#include "qga/vss-win32.h"
#endif
@@ -105,6 +106,9 @@ struct GAState {
GAService service;
HANDLE wakeup_event;
HANDLE event_log;
+ HANDLE load_avg_wait_handle;
+ HANDLE load_avg_event;
+ HQUERY load_avg_pdh_query;
#endif
bool delimit_response;
bool frozen;
@@ -582,6 +586,25 @@ const char *ga_fsfreeze_hook(GAState *s)
}
#endif
+#ifdef _WIN32
+void ga_set_load_avg_wait_handle(GAState *s, HANDLE wait_handle)
+{
+ s->load_avg_wait_handle = wait_handle;
+}
+void ga_set_load_avg_event(GAState *s, HANDLE event)
+{
+ s->load_avg_event = event;
+}
+void ga_set_load_avg_pdh_query(GAState *s, HQUERY query)
+{
+ s->load_avg_pdh_query = query;
+}
+HQUERY ga_get_load_avg_pdh_query(GAState *s)
+{
+ return s->load_avg_pdh_query;
+}
+#endif
+
static void become_daemon(const char *pidfile)
{
#ifndef _WIN32
@@ -1402,6 +1425,10 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
g_debug("Guest agent version %s started", QEMU_FULL_VERSION);
#ifdef _WIN32
+ s->load_avg_wait_handle = INVALID_HANDLE_VALUE;
+ s->load_avg_event = INVALID_HANDLE_VALUE;
+ s->load_avg_pdh_query = NULL;
+
s->event_log = RegisterEventSource(NULL, "qemu-ga");
if (!s->event_log) {
g_autofree gchar *errmsg = g_win32_error_message(GetLastError());
@@ -1506,6 +1533,18 @@ static void cleanup_agent(GAState *s)
#ifdef _WIN32
CloseHandle(s->wakeup_event);
CloseHandle(s->event_log);
+
+ if (s->load_avg_wait_handle != INVALID_HANDLE_VALUE) {
+ UnregisterWait(s->load_avg_wait_handle);
+ }
+
+ if (s->load_avg_event != INVALID_HANDLE_VALUE) {
+ CloseHandle(s->load_avg_event);
+ }
+
+ if (s->load_avg_pdh_query) {
+ PdhCloseQuery(s->load_avg_pdh_query);
+ }
#endif
if (s->command_state) {
ga_command_state_cleanup_all(s->command_state);
diff --git a/qga/meson.build b/qga/meson.build
index 587ec4e5e8..89a4a8f713 100644
--- a/qga/meson.build
+++ b/qga/meson.build
@@ -95,7 +95,7 @@ gen_tlb = []
qga_libs = []
if host_os == 'windows'
qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32',
- '-lsetupapi', '-lcfgmgr32', '-luserenv']
+ '-lsetupapi', '-lcfgmgr32', '-luserenv', '-lpdh' ]
if have_qga_vss
qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup']
subdir('vss-win32')
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 995594aaf4..e96a4b7957 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1863,7 +1863,7 @@
'load5m': 'number',
'load15m': 'number'
},
- 'if': 'CONFIG_GETLOADAVG'
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_GETLOADAVG'] }
}
##
@@ -1871,13 +1871,18 @@
#
# Retrieve CPU process load information
#
+# .. note:: Windows does not have load average API, so QGA emulates it by
+# calculating the average CPU usage in the last 1, 5, 15 minutes
+# similar as Linux does this.
+# Calculation starts from the first time this command is called.
+#
# Returns: load information
#
# Since: 10.0
##
{ 'command': 'guest-get-load',
'returns': 'GuestLoadAverage',
- 'if': 'CONFIG_GETLOADAVG'
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_GETLOADAVG'] }
}
##
--
2.48.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 2/2] qga: Add tests for guest-get-load command
From: Konstantin Kostiuk <kkostiuk@redhat.com>
Date: Mon, 24 Mar 2025 15:17:29 +0200
Message-Id: <20250324131729.70992-3-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Tested-by: Dehan Meng <demeng@redhat.com>
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
Signed-off-by: Konstantin Kostiuk <kkostiuk@redhat.com>
---
tests/unit/test-qga.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/tests/unit/test-qga.c b/tests/unit/test-qga.c
index 541b08a5e7..587e30c7e4 100644
--- a/tests/unit/test-qga.c
+++ b/tests/unit/test-qga.c
@@ -332,6 +332,22 @@ static void test_qga_get_fsinfo(gconstpointer fix)
}
}
+static void test_qga_get_load(gconstpointer fix)
+{
+ const TestFixture *fixture = fix;
+ g_autoptr(QDict) ret = NULL;
+ QDict *load;
+
+ ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-load'}");
+ g_assert_nonnull(ret);
+ qmp_assert_no_error(ret);
+
+ load = qdict_get_qdict(ret, "return");
+ g_assert(qdict_haskey(load, "load1m"));
+ g_assert(qdict_haskey(load, "load5m"));
+ g_assert(qdict_haskey(load, "load15m"));
+}
+
static void test_qga_get_memory_block_info(gconstpointer fix)
{
const TestFixture *fixture = fix;
@@ -1105,6 +1121,7 @@ int main(int argc, char **argv)
g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus);
}
g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo);
+ g_test_add_data_func("/qga/get-load", &fix, test_qga_get_load);
g_test_add_data_func("/qga/get-memory-block-info", &fix,
test_qga_get_memory_block_info);
g_test_add_data_func("/qga/get-memory-blocks", &fix,
--
2.48.1

View File

@ -1,44 +0,0 @@
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2] qga/vss-win32: Add VSS provider unregistration retry
From: Elizabeth Ashurov <eashurov@redhat.com>
Date: Wed, 18 Jun 2025 12:18:06 +0300
Message-Id: <20250618091806.170110-1-eashurov@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
This commit improves the QGA VSS provider installation flow by attempting to unregister the VSS provider if it's already
found during installation. This allows for a retry of installation even if a previous unregistration failed or was not performed.
This will prevent inconsistencies between QGA and QGA-VSS versions.
Before this commit, QGA can use QGA-VSS from the previous installation.
Signed-off-by: Elizabeth Ashurov <eashurov@redhat.com>
Reviewed-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/vss-win32/install.cpp | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp
index 5cea5bcf74..9fe0dfc384 100644
--- a/qga/vss-win32/install.cpp
+++ b/qga/vss-win32/install.cpp
@@ -287,9 +287,13 @@ STDAPI COMRegister(void)
chk(QGAProviderFind(QGAProviderCount, (void *)&count));
if (count) {
- errmsg(E_ABORT, "QGA VSS Provider is already installed");
- qga_debug_end;
- return E_ABORT;
+ qga_debug("QGA VSS Provider is already installed. Attempting to unregister first.");
+ hr = COMUnregister();
+ if (FAILED(hr)) {
+ errmsg(hr, "Failed to unregister existing QGA VSS Provider. Aborting installation.");
+ qga_debug_end;
+ return E_ABORT;
+ }
}
chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
--
2.49.0

View File

@ -0,0 +1,469 @@
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 1/9] qga: Fix ubsan warning
From: Thomas Huth <thuth@redhat.com>
Date: Mon, 01 Sep 2025 14:24:05 +0300
Message-Id: <20250901112413.114314-2-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
When compiling QEMU with --enable-ubsan there is a undefined behavior
warning when running "make check":
.../qga/commands-linux.c:452:15: runtime error: applying non-zero offset 5 to null pointer
#0 0x55ea7b89450c in build_guest_fsinfo_for_pci_dev ..../qga/commands-linux.c:452:15
Fix it by avoiding the additional pointer variable here and use an
"offset" integer variable instead.
Signed-off-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250730072709.27077-1-thuth@redhat.com
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/commands-linux.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index 9dc0c82503..4a09ddc760 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -400,10 +400,10 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
Error **errp)
{
unsigned int pci[4], host, hosts[8], tgt[3];
- int i, nhosts = 0, pcilen;
+ int i, offset, nhosts = 0, pcilen;
GuestPCIAddress *pciaddr = disk->pci_controller;
bool has_ata = false, has_host = false, has_tgt = false;
- char *p, *q, *driver = NULL;
+ char *p, *driver = NULL;
bool ret = false;
p = strstr(syspath, "/devices/pci");
@@ -445,13 +445,13 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
p = strstr(syspath, "/ata");
if (p) {
- q = p + 4;
+ offset = 4;
has_ata = true;
} else {
p = strstr(syspath, "/host");
- q = p + 5;
+ offset = 5;
}
- if (p && sscanf(q, "%u", &host) == 1) {
+ if (p && sscanf(p + offset, "%u", &host) == 1) {
has_host = true;
nhosts = build_hosts(syspath, p, has_ata, hosts,
ARRAY_SIZE(hosts), errp);
--
2.50.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 2/9] qga: fix potentially not initialized nr_volumes in
qga_vss_fsfreeze()
From: "Denis V. Lunev" <den@openvz.org>
Date: Mon, 01 Sep 2025 14:24:06 +0300
Message-Id: <20250901112413.114314-3-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
In this function we could have this variable not initialized. If this
could be acceptable on error, the variable could be left not initialized
f.e. as follows:
void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
{
...
if (mountpoints) {
...
if (num_mount_points == 0) {
/* If there is no valid mount points, just exit. */
goto out;
}
}
...
if (!mountpoints) {
...
if (num_fixed_drives == 0) {
goto out; /* If there is no fixed drive, just exit. */
}
}
...
}
Stay on safe side, initialize the variable at the beginning.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Kostiantyn Kostiuk <kkostiuk@redhat.com>
CC: Michael Roth <michael.roth@amd.com>
Reviewed-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250807133221.1135453-1-den@openvz.org
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/vss-win32.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/qga/vss-win32.c b/qga/vss-win32.c
index f444a25a70..b272bfc782 100644
--- a/qga/vss-win32.c
+++ b/qga/vss-win32.c
@@ -157,6 +157,8 @@ void qga_vss_fsfreeze(int *nr_volume, bool freeze,
.errp = errp,
};
+ *nr_volume = 0;
+
g_assert(errp); /* requester.cpp requires it */
func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name);
if (!func) {
--
2.50.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 3/9] qga-vss: Replace asserts with condition and report
error
From: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Date: Mon, 01 Sep 2025 14:24:07 +0300
Message-Id: <20250901112413.114314-4-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250825145241.170717-2-kkostiuk@redhat.com
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/vss-win32/requester.cpp | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp
index 4401d55e3a..bc260abb96 100644
--- a/qga/vss-win32/requester.cpp
+++ b/qga/vss-win32/requester.cpp
@@ -347,7 +347,12 @@ void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
goto out;
}
- assert(pCreateVssBackupComponents != NULL);
+ if (!pCreateVssBackupComponents) {
+ err_set(errset, (HRESULT)ERROR_PROC_NOT_FOUND,
+ "CreateVssBackupComponents proc address absent. Did you call requester_init()?");
+ goto out;
+ }
+
hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
if (FAILED(hr)) {
err_set(errset, hr, "failed to create VSS backup components");
@@ -579,8 +584,16 @@ void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset)
/* Tell the provider that the snapshot is finished. */
SetEvent(vss_ctx.hEventThaw);
- assert(vss_ctx.pVssbc);
- assert(vss_ctx.pAsyncSnapshot);
+ if (!vss_ctx.pVssbc) {
+ err_set(errset, (HRESULT)VSS_E_BAD_STATE,
+ "CreateVssBackupComponents is missing. Did you freeze the volumes?");
+ return;
+ }
+ if (!vss_ctx.pAsyncSnapshot) {
+ err_set(errset, (HRESULT)VSS_E_BAD_STATE,
+ "AsyncSnapshot set is missing. Did you freeze the volumes?");
+ return;
+ }
HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot);
switch (hr) {
--
2.50.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 4/9] qga-vss: Remove unused dependencies
From: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Date: Mon, 01 Sep 2025 14:24:08 +0300
Message-Id: <20250901112413.114314-5-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250825145241.170717-3-kkostiuk@redhat.com
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/vss-win32/meson.build | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/qga/vss-win32/meson.build b/qga/vss-win32/meson.build
index 0ac918910b..a6b810f12a 100644
--- a/qga/vss-win32/meson.build
+++ b/qga/vss-win32/meson.build
@@ -13,13 +13,11 @@ qga_vss = shared_module(
link_args: link_args,
vs_module_defs: 'qga-vss.def',
dependencies: [
- glib,
socket,
cc.find_library('ole32'),
cc.find_library('oleaut32'),
cc.find_library('shlwapi'),
- cc.find_library('uuid'),
- cc.find_library('intl')
+ cc.find_library('uuid')
]
)
--
2.50.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 5/9] qga: Fix channel initialization check in
run_agent_once
From: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Date: Mon, 01 Sep 2025 14:24:09 +0300
Message-Id: <20250901112413.114314-6-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250825140549.146617-2-kkostiuk@redhat.com
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/qga/main.c b/qga/main.c
index 6c02f3ec38..a1bf8f53ac 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1563,7 +1563,7 @@ static void cleanup_agent(GAState *s)
static int run_agent_once(GAState *s)
{
if (!s->channel &&
- channel_init(s, s->config->method, s->config->channel_path,
+ !channel_init(s, s->config->method, s->config->channel_path,
s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) {
g_critical("failed to initialize guest agent channel");
return EXIT_FAILURE;
--
2.50.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 6/9] qga: ignore channel_init() fail if 'retry_path' is
set
From: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Date: Mon, 01 Sep 2025 14:24:10 +0300
Message-Id: <20250901112413.114314-7-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
On Windows, we run QGA with `-d --retry-path` options by default,
and expect that QGA will start even without the vioserial driver
and will wait for communication forever.
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250825140549.146617-3-kkostiuk@redhat.com
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/main.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/qga/main.c b/qga/main.c
index a1bf8f53ac..dd1c216f9a 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1512,8 +1512,12 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
if (!channel_init(s, s->config->method, s->config->channel_path,
s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) {
- g_critical("failed to initialize guest agent channel");
- return NULL;
+ if (s->config->retry_path) {
+ g_info("failed to initialize guest agent channel, will retry");
+ } else {
+ g_critical("failed to initialize guest agent channel");
+ return NULL;
+ }
}
if (config->daemonize) {
--
2.50.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 7/9] qga-vss: Write hex value of error in log
From: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Date: Mon, 01 Sep 2025 14:24:11 +0300
Message-Id: <20250901112413.114314-8-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
QGA-VSS writes error using error_setg_win32_internal,
which call g_win32_error_message.
g_win32_error_message - translate a Win32 error code
(as returned by GetLastError()) into the corresponding message.
In the same time, we call error_setg_win32_internal with
error codes from different Windows componets like VSS or
Performance monitor that provides different codes and
can't be converted with g_win32_error_message. In this
case, the empty suffix will be returned so error will be
masked.
This commit directly add hex value of error code.
Reproduce:
- Run QGA command: {"execute": "guest-fsfreeze-freeze-list", "arguments": {"mountpoints": ["D:"]}}
QGA error example:
- before changes:
{"error": {"class": "GenericError", "desc": "failed to add D: to snapshot set: "}}
- after changes:
{"error": {"class": "GenericError", "desc": "failed to add D: to snapshot set: Windows error 0x8004230e: "}}
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250825135311.138330-1-kkostiuk@redhat.com
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/vss-win32/requester.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp
index bc260abb96..5615955b6f 100644
--- a/qga/vss-win32/requester.cpp
+++ b/qga/vss-win32/requester.cpp
@@ -28,8 +28,9 @@
#define err_set(e, err, fmt, ...) { \
(e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \
- err, fmt, ## __VA_ARGS__); \
- qga_debug(fmt, ## __VA_ARGS__); \
+ err, fmt ": Windows error 0x%lx", \
+ ## __VA_ARGS__, err); \
+ qga_debug(fmt ": Windows error 0x%lx", ## __VA_ARGS__, err); \
}
/* Bad idea, works only when (e)->errp != NULL: */
#define err_is_set(e) ((e)->errp && *(e)->errp)
--
2.50.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 8/9] qga/installer: Remove QGA VSS if QGA installation
failed
From: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Date: Mon, 01 Sep 2025 14:24:12 +0300
Message-Id: <20250901112413.114314-9-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
When QGA Installer failed to install QGA service but install
QGA VSS provider, provider should be removed before installer
exits. Otherwise QGA VSS will has broken infomation and
prevent QGA installation in next run.
Reviewed-by: Yan Vugenfirer <yvugenfi@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250825143155.160913-1-kkostiuk@redhat.com
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/installer/qemu-ga.wxs | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs
index df572adb4a..32b8308728 100644
--- a/qga/installer/qemu-ga.wxs
+++ b/qga/installer/qemu-ga.wxs
@@ -151,6 +151,14 @@
Return="check"
>
</CustomAction>
+ <CustomAction Id="UnRegisterCom_Rollback"
+ ExeCommand='"[qemu_ga_directory]qga-vss.dll",DLLCOMUnregister'
+ Execute="rollback"
+ Property="rundll"
+ Impersonate="no"
+ Return="check"
+ >
+ </CustomAction>
<?endif?>
<Feature Id="QEMUFeature" Title="QEMU Guest Agent" Level="1">
@@ -174,8 +182,19 @@
<InstallExecuteSequence>
<?ifdef var.InstallVss?>
- <Custom Action="UnRegisterCom" After="StopServices">Installed</Custom>
- <Custom Action="RegisterCom" After="InstallServices">NOT REMOVE</Custom>
+ <!-- Use explicit Sequence number to provide an absolute position in the sequence-->
+ <!-- This is needed to set "UnRegisterCom_Rollback" before "RegisterCom" and after "InstallFiles"-->
+ <!-- but, Wix detect this double condition incorrectly -->
+
+ <!-- UnRegisterCom_Rollback (for install rollback): at 5849, right before RegisterCom (5850)-->
+ <!-- Runs only if the installation fails and rolls back-->
+ <Custom Action="UnRegisterCom_Rollback" Sequence="5849">NOT REMOVE</Custom>
+
+ <!-- RegisterCom (for install): at 5850, right after InstallFiles (5849) (old: After="InstallServices")-->
+ <Custom Action="RegisterCom" Sequence="5850">NOT REMOVE</Custom>
+
+ <!-- UnRegisterCom (for uninstall): at 1901, right after StopServices (1900) (old: After="StopServices")-->
+ <Custom Action="UnRegisterCom" Sequence="1901">Installed</Custom>
<?endif?>
</InstallExecuteSequence>
</Product>
--
2.50.1
From git@z Thu Jan 1 00:00:00 1970
Subject: [PATCH v2 9/9] qga: Fix truncated output handling in guest-exec
status reporting
From: "minglei.liu" <minglei.liu@smartx.com>
Date: Mon, 01 Sep 2025 14:24:13 +0300
Message-Id: <20250901112413.114314-10-kkostiuk@redhat.com>
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 8bit
Signed-off-by: minglei.liu <minglei.liu@smartx.com>
Fixes: a1853dca743
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250711021714.91258-1-minglei.liu@smartx.com
Signed-off-by: Kostiantyn Kostiuk <kkostiuk@redhat.com>
---
qga/commands.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/qga/commands.c b/qga/commands.c
index 5a5fad31f8..5f20af25d3 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -205,13 +205,15 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp)
#endif
if (gei->out.length > 0) {
ges->out_data = g_base64_encode(gei->out.data, gei->out.length);
- ges->has_out_truncated = gei->out.truncated;
+ ges->has_out_truncated = true;
+ ges->out_truncated = gei->out.truncated;
}
g_free(gei->out.data);
if (gei->err.length > 0) {
ges->err_data = g_base64_encode(gei->err.data, gei->err.length);
- ges->has_err_truncated = gei->err.truncated;
+ ges->has_err_truncated = true;
+ ges->err_truncated = gei->err.truncated;
}
g_free(gei->err.data);
--
2.50.1