* [PATCH v3 1/2] qga: replace slog() with standard GLib logging
@ 2026-03-25 11:08 Elizabeth Ashurov
2026-03-25 11:09 ` [PATCH v3 2/2] qga: add --audit option for command logging control Elizabeth Ashurov
0 siblings, 1 reply; 2+ messages in thread
From: Elizabeth Ashurov @ 2026-03-25 11:08 UTC (permalink / raw)
To: qemu-devel; +Cc: berrange, kkostiuk, yvugenfi, Elizabeth Ashurov
Replace qga-specific slog() calls with standard GLib logging APIs.
Use g_info() for command execution messages and g_warning() for
non-fatal failures, and remove the use of "syslog" as a log domain.
Rework ga_log() to route messages to the log file and
syslog/Event Viewer, filtering by log level. Debug messages are
excluded from syslog even with --debug.
- Default log level includes ERROR, CRITICAL, and WARNING
- -v/--verbose enables MESSAGE and INFO
- --debug enables all levels including DEBUG
Signed-off-by: Elizabeth Ashurov <eashurov@redhat.com>
---
qga/commands-linux.c | 6 ++---
qga/commands-posix.c | 26 +++++++++---------
qga/commands-win32.c | 60 +++++++++++++++++++++---------------------
qga/commands.c | 29 ++++++--------------
qga/guest-agent-core.h | 1 -
qga/main.c | 39 +++++++++++++++++++--------
6 files changed, 82 insertions(+), 79 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index 378f4d080c..a722de2e6a 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -44,7 +44,7 @@ static int dev_major_minor(const char *devpath,
*devminor = 0;
if (stat(devpath, &st) < 0) {
- slog("failed to stat device file '%s': %s", devpath, strerror(errno));
+ g_warning("failed to stat device file '%s': %s", devpath, strerror(errno));
return -1;
}
if (S_ISDIR(st.st_mode)) {
@@ -1158,7 +1158,7 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
int fd;
struct fstrim_range r;
- slog("guest-fstrim called");
+ g_info("guest-fstrim called");
QTAILQ_INIT(&mounts);
if (!build_fs_mount_list(&mounts, errp)) {
@@ -2072,7 +2072,7 @@ GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
}
if (i < 5) {
- slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
+ g_warning("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
break;
}
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 837be51c40..96939a6f36 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -240,7 +240,7 @@ void qmp_guest_shutdown(const char *mode, Error **errp)
const char *reboot_flag = "-r";
#endif
- slog("guest-shutdown called, mode: %s", mode);
+ g_info("guest-shutdown called, mode: %s", mode);
if (!mode || strcmp(mode, "powerdown") == 0) {
if (access(POWEROFF_CMD_PATH, X_OK) == 0) {
shutdown_cmd = POWEROFF_CMD_PATH;
@@ -519,7 +519,7 @@ int64_t qmp_guest_file_open(const char *path, const char *mode,
if (!mode) {
mode = "r";
}
- slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
+ g_info("guest-file-open called, filepath: %s, mode: %s", path, mode);
fh = safe_open_or_create(path, mode, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -540,7 +540,7 @@ int64_t qmp_guest_file_open(const char *path, const char *mode,
return -1;
}
- slog("guest-file-open, handle: %" PRId64, handle);
+ g_info("guest-file-open, handle: %" PRId64, handle);
return handle;
}
@@ -549,7 +549,7 @@ void qmp_guest_file_close(int64_t handle, Error **errp)
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
int ret;
- slog("guest-file-close called, handle: %" PRId64, handle);
+ g_info("guest-file-close called, handle: %" PRId64, handle);
if (!gfh) {
return;
}
@@ -645,7 +645,7 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
write_count = fwrite(buf, 1, count, fh);
if (ferror(fh)) {
error_setg_errno(errp, errno, "failed to write to file");
- slog("guest-file-write failed, handle: %" PRId64, handle);
+ g_warning("guest-file-write failed, handle: %" PRId64, handle);
} else {
write_data = g_new0(GuestFileWrite, 1);
write_data->count = write_count;
@@ -760,7 +760,7 @@ static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
const char *argv[] = {hook, arg_str, NULL};
- slog("executing fsfreeze hook with arg '%s'", arg_str);
+ g_info("executing fsfreeze hook with arg '%s'", arg_str);
ga_run_command(argv, NULL, "execute fsfreeze hook", &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -793,7 +793,7 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
FsMountList mounts;
Error *local_err = NULL;
- slog("guest-fsfreeze called");
+ g_info("guest-fsfreeze called");
execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
if (local_err) {
@@ -833,7 +833,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
if (ret >= 0) {
ga_unset_frozen(ga_state);
- slog("guest-fsthaw called");
+ g_info("guest-fsthaw called");
execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
} else {
ret = 0;
@@ -849,8 +849,8 @@ static void guest_fsfreeze_cleanup(void)
if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
qmp_guest_fsfreeze_thaw(&err);
if (err) {
- slog("failed to clean up frozen filesystems: %s",
- error_get_pretty(err));
+ g_warning("failed to clean up frozen filesystems: %s",
+ error_get_pretty(err));
error_free(err);
}
}
@@ -1282,19 +1282,19 @@ static GKeyFile *ga_parse_osrelease(const char *fname)
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);
+ g_warning("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);
+ g_warning("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);
+ g_warning("failed to parse file '%s', error: %s", fname, err->message);
goto fail;
}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index c0bf3467bd..d26b0041ce 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -231,7 +231,7 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp)
if (!mode) {
mode = "r";
}
- slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
+ g_info("guest-file-open called, filepath: %s, mode: %s", path, mode);
guest_flags = find_open_flag(mode);
if (guest_flags == NULL) {
error_setg(errp, "invalid file open mode");
@@ -267,7 +267,7 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp)
goto done;
}
- slog("guest-file-open, handle: % " PRId64, fd);
+ g_info("guest-file-open, handle: % " PRId64, fd);
done:
g_free(w_path);
@@ -278,7 +278,7 @@ void qmp_guest_file_close(int64_t handle, Error **errp)
{
bool ret;
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- slog("guest-file-close called, handle: %" PRId64, handle);
+ g_info("guest-file-close called, handle: %" PRId64, handle);
if (gfh == NULL) {
return;
}
@@ -337,7 +337,7 @@ void qmp_guest_shutdown(const char *mode, Error **errp)
Error *local_err = NULL;
UINT shutdown_flag = EWX_FORCE;
- slog("guest-shutdown called, mode: %s", mode);
+ g_info("guest-shutdown called, mode: %s", mode);
if (!mode || strcmp(mode, "powerdown") == 0) {
shutdown_flag |= EWX_POWEROFF;
@@ -361,7 +361,7 @@ void qmp_guest_shutdown(const char *mode, Error **errp)
if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
g_autofree gchar *emsg = g_win32_error_message(GetLastError());
- slog("guest-shutdown failed: %s", emsg);
+ g_warning("guest-shutdown failed: %s", emsg);
error_setg_win32(errp, GetLastError(), "guest-shutdown failed");
}
}
@@ -426,7 +426,7 @@ GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
is_ok = WriteFile(fh, buf, count, &write_count, NULL);
if (!is_ok) {
error_setg_win32(errp, GetLastError(), "failed to write to file");
- slog("guest-file-write-failed, handle: %" PRId64, handle);
+ g_warning("guest-file-write failed, handle: %" PRId64, handle);
} else {
write_data = g_new0(GuestFileWrite, 1);
write_data->count = (size_t) write_count;
@@ -1255,7 +1255,7 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
return 0;
}
- slog("guest-fsfreeze called");
+ g_info("guest-fsfreeze called");
/* cannot risk guest agent blocking itself on a write in this state */
ga_set_frozen(ga_state);
@@ -1294,7 +1294,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
ga_unset_frozen(ga_state);
- slog("guest-fsthaw called");
+ g_info("guest-fsthaw called");
return i;
}
@@ -1310,8 +1310,8 @@ static void guest_fsfreeze_cleanup(void)
if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
qmp_guest_fsfreeze_thaw(&err);
if (err) {
- slog("failed to clean up frozen filesystems: %s",
- error_get_pretty(err));
+ g_warning("failed to clean up frozen filesystems: %s",
+ error_get_pretty(err));
error_free(err);
}
}
@@ -1464,7 +1464,7 @@ static DWORD WINAPI do_suspend(LPVOID opaque)
if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) {
g_autofree gchar *emsg = g_win32_error_message(GetLastError());
- slog("failed to suspend guest: %s", emsg);
+ g_warning("failed to suspend guest: %s", emsg);
ret = -1;
}
g_free(mode);
@@ -2174,8 +2174,8 @@ static char *ga_get_win_name(const OSVERSIONINFOEXW *os_version, bool id)
}
++table;
}
- slog("failed to lookup Windows version: major=%lu, minor=%lu",
- major, minor);
+ g_warning("failed to lookup Windows version: major=%lu, minor=%lu",
+ major, minor);
return g_strdup("N/A");
}
@@ -2198,8 +2198,8 @@ static char *ga_get_win_product_name(Error **errp)
err = RegQueryValueExA(key, "ProductName", NULL, NULL,
(LPBYTE)result, &size);
if (err == ERROR_MORE_DATA) {
- slog("ProductName longer than expected (%lu bytes), retrying",
- size);
+ g_info("ProductName longer than expected (%lu bytes), retrying",
+ size);
g_free(result);
result = NULL;
if (size > 0) {
@@ -2244,8 +2244,8 @@ static char *ga_get_current_arch(void)
break;
case PROCESSOR_ARCHITECTURE_UNKNOWN:
default:
- slog("unknown processor architecture 0x%0x",
- info.wProcessorArchitecture);
+ g_warning("unknown processor architecture 0x%0x",
+ info.wProcessorArchitecture);
result = g_strdup("unknown");
break;
}
@@ -2308,14 +2308,14 @@ static LPBYTE cm_get_property(DEVINST devInst, const DEVPROPKEY *propName,
buffer, &buffer_len, 0);
if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) {
- slog("failed to get property size, error=0x%lx", cr);
+ g_warning("failed to get property size, error=0x%lx", cr);
return NULL;
}
buffer = g_new0(BYTE, buffer_len + 1);
cr = CM_Get_DevNode_PropertyW(devInst, propName, propType,
buffer, &buffer_len, 0);
if (cr != CR_SUCCESS) {
- slog("failed to get device property, error=0x%lx", cr);
+ g_warning("failed to get device property, error=0x%lx", cr);
return NULL;
}
return g_steal_pointer(&buffer);
@@ -2329,7 +2329,7 @@ static GStrv ga_get_hardware_ids(DEVINST devInstance)
g_autofree LPWSTR property = (LPWSTR)cm_get_property(devInstance,
&qga_DEVPKEY_Device_HardwareIds, &cm_type);
if (property == NULL) {
- slog("failed to get hardware IDs");
+ g_warning("failed to get hardware IDs");
return NULL;
}
if (*property == '\0') {
@@ -2371,7 +2371,7 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
return NULL;
}
- slog("enumerating devices");
+ g_info("enumerating devices");
for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
bool skip = true;
g_autofree LPWSTR name = NULL;
@@ -2385,7 +2385,7 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
name = (LPWSTR)cm_get_property(dev_info_data.DevInst,
&qga_DEVPKEY_NAME, &cm_type);
if (name == NULL) {
- slog("failed to get device description");
+ g_warning("failed to get device description");
continue;
}
device->driver_name = g_utf16_to_utf8(name, -1, NULL, NULL, NULL);
@@ -2393,7 +2393,7 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
error_setg(errp, "conversion to utf8 failed (driver name)");
return NULL;
}
- slog("querying device: %s", device->driver_name);
+ g_info("querying device: %s", device->driver_name);
hw_ids = ga_get_hardware_ids(dev_info_data.DevInst);
if (hw_ids == NULL) {
continue;
@@ -2424,7 +2424,7 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
version = (LPWSTR)cm_get_property(dev_info_data.DevInst,
&qga_DEVPKEY_Device_DriverVersion, &cm_type);
if (version == NULL) {
- slog("failed to get driver version");
+ g_warning("failed to get driver version");
continue;
}
device->driver_version = g_utf16_to_utf8(version, -1, NULL,
@@ -2437,15 +2437,15 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
date = (LPFILETIME)cm_get_property(dev_info_data.DevInst,
&qga_DEVPKEY_Device_DriverDate, &cm_type);
if (date == NULL) {
- slog("failed to get driver date");
+ g_warning("failed to get driver date");
continue;
}
device->driver_date = filetime_to_ns(date);
device->has_driver_date = true;
- slog("driver: %s\ndriver version: %" PRId64 ",%s\n",
- device->driver_name, device->driver_date,
- device->driver_version);
+ g_info("driver: %s\ndriver version: %" PRId64 ",%s\n",
+ device->driver_name, device->driver_date,
+ device->driver_version);
QAPI_LIST_APPEND(tail, g_steal_pointer(&device));
}
@@ -2479,8 +2479,8 @@ static VOID CALLBACK load_avg_callback(PVOID hCounter, BOOLEAN timedOut)
(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);
+ g_warning("PdhGetFormattedCounterValue failed to get load value"
+ " with 0x%lx", err);
return;
}
currentLoad = displayValue.doubleValue;
diff --git a/qga/commands.c b/qga/commands.c
index 5f20af25d3..55edd9fd4c 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -30,19 +30,6 @@
*/
#define GUEST_FILE_READ_COUNT_MAX (48 * MiB)
-/* Note: in some situations, like with the fsfreeze, logging may be
- * temporarily disabled. if it is necessary that a command be able
- * to log for accounting purposes, check ga_logging_enabled() beforehand.
- */
-void slog(const gchar *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
- va_end(ap);
-}
-
int64_t qmp_guest_sync_delimited(int64_t id, Error **errp)
{
ga_set_response_delimited(ga_state);
@@ -56,7 +43,7 @@ int64_t qmp_guest_sync(int64_t id, Error **errp)
void qmp_guest_ping(Error **errp)
{
- slog("guest-ping called");
+ g_info("guest-ping called");
}
static void qmp_command_info(const QmpCommand *cmd, void *opaque)
@@ -149,7 +136,7 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp)
GuestExecInfo *gei;
GuestExecStatus *ges;
- slog("guest-exec-status called, pid: %u", (uint32_t)pid);
+ g_info("guest-exec-status called, pid: %u", (uint32_t)pid);
gei = guest_exec_info_find(pid);
if (gei == NULL) {
@@ -251,7 +238,7 @@ static char **guest_exec_get_args(const strList *entry, bool log)
args[i] = NULL;
if (log) {
- slog("guest-exec called: \"%s\"", str);
+ g_info("guest-exec called: \"%s\"", str);
}
g_free(str);
@@ -285,8 +272,8 @@ static void guest_exec_task_setup(gpointer data)
* inside the parent, not the child.
*/
if (dup2(STDOUT_FILENO, STDERR_FILENO) != 0) {
- slog("dup2() failed to merge stderr into stdout: %s",
- strerror(errno));
+ g_warning("dup2() failed to merge stderr into stdout: %s",
+ strerror(errno));
}
}
@@ -295,8 +282,8 @@ static void guest_exec_task_setup(gpointer data)
sigact.sa_handler = SIG_DFL;
if (sigaction(SIGPIPE, &sigact, NULL) != 0) {
- slog("sigaction() failed to reset child process's SIGPIPE: %s",
- strerror(errno));
+ g_warning("sigaction() failed to reset child process's SIGPIPE: %s",
+ strerror(errno));
}
#endif
}
@@ -626,7 +613,7 @@ GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
read_data = guest_file_read_unsafe(gfh, count, errp);
if (!read_data) {
- slog("guest-file-write failed, handle: %" PRId64, handle);
+ g_warning("guest-file-read failed, handle: %" PRId64, handle);
}
return read_data;
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index d9f3922adf..0f2d1b3415 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -40,7 +40,6 @@ void ga_command_state_free(GACommandState *cs);
bool ga_logging_enabled(GAState *s);
void ga_disable_logging(GAState *s);
void ga_enable_logging(GAState *s);
-void G_GNUC_PRINTF(1, 2) slog(const gchar *fmt, ...);
void ga_set_response_delimited(GAState *s);
bool ga_is_frozen(GAState *s);
void ga_set_frozen(GAState *s);
diff --git a/qga/main.c b/qga/main.c
index fd19c7037d..40b92ec686 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -286,7 +286,8 @@ QEMU_COPYRIGHT "\n"
#endif
" -t, --statedir specify dir to store state information (absolute paths\n"
" only, default is %s)\n"
-" -v, --verbose log extra debugging information\n"
+" -v, --verbose enable verbose logging (info and above)\n"
+" --debug enable debug logging (all messages)\n"
" -V, --version print version information and exit\n"
" -d, --daemonize become a daemon\n"
#ifdef _WIN32
@@ -391,18 +392,24 @@ static void ga_log(const gchar *domain, GLogLevelFlags level,
}
level &= G_LOG_LEVEL_MASK;
- if (g_strcmp0(domain, "syslog") == 0) {
+ if (!(level & s->log_level)) {
+ return;
+ }
+
+ if (s->log_file) {
+ g_autoptr(GDateTime) now = g_date_time_new_now_utc();
+ g_autofree char *nowstr = g_date_time_format(now, "%s.%f");
+ fprintf(s->log_file, "%s: %s: %s\n", nowstr, level_str, msg);
+ fflush(s->log_file);
+ }
+
+ if (level & ~G_LOG_LEVEL_DEBUG) {
#ifndef _WIN32
syslog(glib_log_level_to_system(level), "%s: %s", level_str, msg);
#else
ReportEvent(s->event_log, glib_log_level_to_system(level),
0, 1, NULL, 1, 0, &msg, NULL);
#endif
- } else if (level & s->log_level) {
- g_autoptr(GDateTime) now = g_date_time_new_now_utc();
- g_autofree char *nowstr = g_date_time_format(now, "%s.%f");
- fprintf(s->log_file, "%s: %s: %s\n", nowstr, level_str, msg);
- fflush(s->log_file);
}
}
@@ -1140,7 +1147,10 @@ static void config_load(GAConfig *config, const char *confpath, bool required)
}
if (g_key_file_has_key(keyfile, "general", "verbose", NULL) &&
g_key_file_get_boolean(keyfile, "general", "verbose", &gerr)) {
- /* enable all log levels */
+ config->log_level |= G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO;
+ }
+ if (g_key_file_has_key(keyfile, "general", "debug", NULL) &&
+ g_key_file_get_boolean(keyfile, "general", "debug", &gerr)) {
config->log_level = G_LOG_LEVEL_MASK;
}
if (g_key_file_has_key(keyfile, "general", "retry-path", NULL)) {
@@ -1214,7 +1224,9 @@ static void config_dump(GAConfig *config)
#endif
g_key_file_set_string(keyfile, "general", "statedir", config->state_dir);
g_key_file_set_boolean(keyfile, "general", "verbose",
- config->log_level == G_LOG_LEVEL_MASK);
+ config->log_level & G_LOG_LEVEL_INFO);
+ g_key_file_set_boolean(keyfile, "general", "debug",
+ config->log_level & G_LOG_LEVEL_DEBUG);
g_key_file_set_boolean(keyfile, "general", "retry-path",
config->retry_path);
tmp = list_join(config->blockedrpcs, ',');
@@ -1238,6 +1250,7 @@ static void config_dump(GAConfig *config)
static void config_parse(GAConfig *config, int argc, char **argv)
{
+ enum { OPT_DEBUG = 256 };
const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr";
int opt_ind = 0, ch;
const struct option lopt[] = {
@@ -1251,6 +1264,7 @@ static void config_parse(GAConfig *config, int argc, char **argv)
{ "fsfreeze-hook", 2, NULL, 'F' },
#endif
{ "verbose", 0, NULL, 'v' },
+ { "debug", 0, NULL, OPT_DEBUG },
{ "method", 1, NULL, 'm' },
{ "path", 1, NULL, 'p' },
{ "daemonize", 0, NULL, 'd' },
@@ -1313,7 +1327,9 @@ static void config_parse(GAConfig *config, int argc, char **argv)
config->state_dir = g_strdup(optarg);
break;
case 'v':
- /* enable all log levels */
+ config->log_level |= G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO;
+ break;
+ case OPT_DEBUG:
config->log_level = G_LOG_LEVEL_MASK;
break;
case 'V':
@@ -1673,7 +1689,8 @@ int main(int argc, char **argv)
GAConfig *config = g_new0(GAConfig, 1);
int socket_activation;
- config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
+ config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL
+ | G_LOG_LEVEL_WARNING;
qemu_init_exec_dir(argv[0]);
qga_qmp_init_marshal(&ga_commands);
--
2.51.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH v3 2/2] qga: add --audit option for command logging control
2026-03-25 11:08 [PATCH v3 1/2] qga: replace slog() with standard GLib logging Elizabeth Ashurov
@ 2026-03-25 11:09 ` Elizabeth Ashurov
0 siblings, 0 replies; 2+ messages in thread
From: Elizabeth Ashurov @ 2026-03-25 11:09 UTC (permalink / raw)
To: qemu-devel; +Cc: berrange, kkostiuk, yvugenfi, Elizabeth Ashurov
Add -A/--audit=LIST option to control which guest agent commands
are logged at info level (visible with --verbose) and which at
debug level (visible only with --debug).
Patterns are comma-separated and checked in order; the first match
wins. Patterns starting with '!' log the command at debug level
instead of info.
For example: --audit=!guest-ping,* logs all commands
at info level except guest-ping.
Move command logging from individual handlers into process_event()
so all commands are logged in one place. Keep g_debug() calls in
handlers for useful details like file paths, handles, and PIDs.
The default pattern is '*', so all commands are logged at info
level unless configured otherwise.
Signed-off-by: Elizabeth Ashurov <eashurov@redhat.com>
---
qga/commands-linux.c | 2 --
qga/commands-posix.c | 11 +++---
qga/commands-win32.c | 14 +++-----
qga/commands.c | 5 ++-
qga/main.c | 82 ++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 90 insertions(+), 24 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index a722de2e6a..8df83963fa 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -1158,8 +1158,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
int fd;
struct fstrim_range r;
- g_info("guest-fstrim called");
-
QTAILQ_INIT(&mounts);
if (!build_fs_mount_list(&mounts, errp)) {
return NULL;
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 96939a6f36..6a3e6c78e3 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -240,7 +240,7 @@ void qmp_guest_shutdown(const char *mode, Error **errp)
const char *reboot_flag = "-r";
#endif
- g_info("guest-shutdown called, mode: %s", mode);
+ g_debug("guest-shutdown mode: %s", mode);
if (!mode || strcmp(mode, "powerdown") == 0) {
if (access(POWEROFF_CMD_PATH, X_OK) == 0) {
shutdown_cmd = POWEROFF_CMD_PATH;
@@ -519,7 +519,7 @@ int64_t qmp_guest_file_open(const char *path, const char *mode,
if (!mode) {
mode = "r";
}
- g_info("guest-file-open called, filepath: %s, mode: %s", path, mode);
+ g_debug("guest-file-open filepath: %s, mode: %s", path, mode);
fh = safe_open_or_create(path, mode, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -540,7 +540,7 @@ int64_t qmp_guest_file_open(const char *path, const char *mode,
return -1;
}
- g_info("guest-file-open, handle: %" PRId64, handle);
+ g_debug("guest-file-open handle: %" PRId64, handle);
return handle;
}
@@ -549,7 +549,7 @@ void qmp_guest_file_close(int64_t handle, Error **errp)
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
int ret;
- g_info("guest-file-close called, handle: %" PRId64, handle);
+ g_debug("guest-file-close handle: %" PRId64, handle);
if (!gfh) {
return;
}
@@ -793,8 +793,6 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
FsMountList mounts;
Error *local_err = NULL;
- g_info("guest-fsfreeze called");
-
execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
if (local_err) {
error_propagate(errp, local_err);
@@ -833,7 +831,6 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
if (ret >= 0) {
ga_unset_frozen(ga_state);
- g_info("guest-fsthaw called");
execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
} else {
ret = 0;
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index d26b0041ce..e916d081f5 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -231,7 +231,7 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp)
if (!mode) {
mode = "r";
}
- g_info("guest-file-open called, filepath: %s, mode: %s", path, mode);
+ g_debug("guest-file-open filepath: %s, mode: %s", path, mode);
guest_flags = find_open_flag(mode);
if (guest_flags == NULL) {
error_setg(errp, "invalid file open mode");
@@ -267,8 +267,7 @@ int64_t qmp_guest_file_open(const char *path, const char *mode, Error **errp)
goto done;
}
- g_info("guest-file-open, handle: % " PRId64, fd);
-
+ g_debug("guest-file-open handle: %" PRId64, fd);
done:
g_free(w_path);
return fd;
@@ -278,7 +277,7 @@ void qmp_guest_file_close(int64_t handle, Error **errp)
{
bool ret;
GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- g_info("guest-file-close called, handle: %" PRId64, handle);
+ g_debug("guest-file-close handle: %" PRId64, handle);
if (gfh == NULL) {
return;
}
@@ -337,8 +336,7 @@ void qmp_guest_shutdown(const char *mode, Error **errp)
Error *local_err = NULL;
UINT shutdown_flag = EWX_FORCE;
- g_info("guest-shutdown called, mode: %s", mode);
-
+ g_debug("guest-shutdown mode: %s", mode);
if (!mode || strcmp(mode, "powerdown") == 0) {
shutdown_flag |= EWX_POWEROFF;
} else if (strcmp(mode, "halt") == 0) {
@@ -1255,8 +1253,6 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
return 0;
}
- g_info("guest-fsfreeze called");
-
/* cannot risk guest agent blocking itself on a write in this state */
ga_set_frozen(ga_state);
@@ -1294,8 +1290,6 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
ga_unset_frozen(ga_state);
- g_info("guest-fsthaw called");
-
return i;
}
diff --git a/qga/commands.c b/qga/commands.c
index 55edd9fd4c..36b99819af 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -43,7 +43,6 @@ int64_t qmp_guest_sync(int64_t id, Error **errp)
void qmp_guest_ping(Error **errp)
{
- g_info("guest-ping called");
}
static void qmp_command_info(const QmpCommand *cmd, void *opaque)
@@ -136,7 +135,7 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp)
GuestExecInfo *gei;
GuestExecStatus *ges;
- g_info("guest-exec-status called, pid: %u", (uint32_t)pid);
+ g_debug("guest-exec-status pid: %u", (uint32_t)pid);
gei = guest_exec_info_find(pid);
if (gei == NULL) {
@@ -238,7 +237,7 @@ static char **guest_exec_get_args(const strList *entry, bool log)
args[i] = NULL;
if (log) {
- g_info("guest-exec called: \"%s\"", str);
+ g_debug("guest-exec called: \"%s\"", str);
}
g_free(str);
diff --git a/qga/main.c b/qga/main.c
index 40b92ec686..8392d74451 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -87,8 +87,10 @@ struct GAConfig {
#endif
gchar *bliststr; /* blockedrpcs may point to this string */
gchar *aliststr; /* allowedrpcs may point to this string */
+ gchar *auditstr;
GList *blockedrpcs;
GList *allowedrpcs;
+ GList *audit_patterns;
int daemonize;
GLogLevelFlags log_level;
int dumpconf;
@@ -116,6 +118,7 @@ struct GAState {
bool frozen;
GList *blockedrpcs;
GList *allowedrpcs;
+ GList *audit_patterns;
char *state_filepath_isfrozen;
struct {
const char *log_filepath;
@@ -288,6 +291,11 @@ QEMU_COPYRIGHT "\n"
" only, default is %s)\n"
" -v, --verbose enable verbose logging (info and above)\n"
" --debug enable debug logging (all messages)\n"
+" -A, --audit=LIST comma-separated list of command patterns to log at\n"
+" info level (default: *, no spaces).\n"
+" Patterns prefixed with '!' are logged at debug level.\n"
+" Patterns are evaluated in order; the first match wins.\n"
+" Example: --audit=!guest-ping,*\n"
" -V, --version print version information and exit\n"
" -d, --daemonize become a daemon\n"
#ifdef _WIN32
@@ -413,6 +421,33 @@ static void ga_log(const gchar *domain, GLogLevelFlags level,
}
}
+static void ga_audit_log(GAState *s, const char *command)
+{
+ GList *l;
+
+ if (!command) {
+ return;
+ }
+
+ for (l = s->audit_patterns; l; l = l->next) {
+ const char *pattern = l->data;
+
+ if (pattern[0] == '!') {
+ if (g_pattern_match_simple(pattern + 1, command)) {
+ g_debug("%s called", command);
+ return;
+ }
+ } else {
+ if (g_pattern_match_simple(pattern, command)) {
+ g_info("%s called", command);
+ return;
+ }
+ }
+ }
+
+ g_debug("%s called", command);
+}
+
void ga_set_response_delimited(GAState *s)
{
s->delimit_response = true;
@@ -706,7 +741,27 @@ static void process_event(void *opaque, QObject *obj, Error *err)
}
g_debug("processing command");
- rsp = qmp_dispatch(&ga_commands, obj, false, NULL);
+ {
+ QDict *dict = qobject_to(QDict, obj);
+ const char *command = dict ? qdict_get_try_str(dict, "execute") : NULL;
+ /*
+ * Remember if logging was enabled.
+ * fs-freeze disables logs, so when fs-thaw re-enables
+ * them we re-audit to make sure the thaw is logged.
+ */
+ bool logging_before = ga_logging_enabled(s);
+ bool audit = command && qmp_find_command(&ga_commands, command);
+
+ if (audit) {
+ ga_audit_log(s, command);
+ }
+
+ rsp = qmp_dispatch(&ga_commands, obj, false, NULL);
+
+ if (!logging_before && audit) {
+ ga_audit_log(s, command);
+ }
+ }
end:
ret = send_response(s, rsp);
@@ -1157,6 +1212,14 @@ static void config_load(GAConfig *config, const char *confpath, bool required)
config->retry_path =
g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr);
}
+ if (g_key_file_has_key(keyfile, "general", "audit", NULL)) {
+ config->auditstr =
+ g_key_file_get_string(keyfile, "general", "audit", &gerr);
+ config->audit_patterns = g_list_concat(config->audit_patterns,
+ g_list_reverse(
+ split_list(config->auditstr,
+ ",")));
+ }
if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL)) {
config->bliststr =
@@ -1229,6 +1292,9 @@ static void config_dump(GAConfig *config)
config->log_level & G_LOG_LEVEL_DEBUG);
g_key_file_set_boolean(keyfile, "general", "retry-path",
config->retry_path);
+ tmp = list_join(config->audit_patterns, ',');
+ g_key_file_set_string(keyfile, "general", "audit", tmp);
+ g_free(tmp);
tmp = list_join(config->blockedrpcs, ',');
g_key_file_set_string(keyfile, "general", "block-rpcs", tmp);
g_free(tmp);
@@ -1251,7 +1317,7 @@ static void config_dump(GAConfig *config)
static void config_parse(GAConfig *config, int argc, char **argv)
{
enum { OPT_DEBUG = 256 };
- const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr";
+ const char *sopt = "hVvdc:m:p:l:f:F::b:a:A:s:t:Dr";
int opt_ind = 0, ch;
const struct option lopt[] = {
{ "help", 0, NULL, 'h' },
@@ -1270,6 +1336,7 @@ static void config_parse(GAConfig *config, int argc, char **argv)
{ "daemonize", 0, NULL, 'd' },
{ "block-rpcs", 1, NULL, 'b' },
{ "allow-rpcs", 1, NULL, 'a' },
+ { "audit", 1, NULL, 'A' },
#ifdef _WIN32
{ "service", 1, NULL, 's' },
#endif
@@ -1362,6 +1429,10 @@ static void config_parse(GAConfig *config, int argc, char **argv)
split_list(optarg, ","));
break;
}
+ case 'A':
+ g_list_free_full(config->audit_patterns, g_free);
+ config->audit_patterns = g_list_reverse(split_list(optarg, ","));
+ break;
#ifdef _WIN32
case 's':
config->service = optarg;
@@ -1416,6 +1487,8 @@ static void config_free(GAConfig *config)
#endif
g_list_free_full(config->blockedrpcs, g_free);
g_list_free_full(config->allowedrpcs, g_free);
+ g_list_free_full(config->audit_patterns, g_free);
+ g_free(config->auditstr);
g_free(config);
}
@@ -1459,6 +1532,7 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
g_assert(ga_state == NULL);
s->log_level = config->log_level;
+ s->audit_patterns = config->audit_patterns;
s->log_file = stderr;
#ifdef CONFIG_FSFREEZE
s->fsfreeze_hook = config->fsfreeze_hook;
@@ -1698,6 +1772,10 @@ int main(int argc, char **argv)
init_dfl_pathnames();
config_parse(config, argc, argv);
+ if (config->audit_patterns == NULL) {
+ config->audit_patterns = g_list_append(NULL, g_strdup("*"));
+ }
+
if (config->pid_filepath == NULL) {
config->pid_filepath = g_strdup(dfl_pathnames.pidfile);
}
--
2.51.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-03-25 11:10 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-25 11:08 [PATCH v3 1/2] qga: replace slog() with standard GLib logging Elizabeth Ashurov
2026-03-25 11:09 ` [PATCH v3 2/2] qga: add --audit option for command logging control Elizabeth Ashurov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox