public inbox for qemu-devel@nongnu.org
 help / color / mirror / Atom feed
* [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