public inbox for qemu-devel@nongnu.org
 help / color / mirror / Atom feed
From: Elizabeth Ashurov <eashurov@redhat.com>
To: qemu-devel@nongnu.org
Cc: berrange@redhat.com, kkostiuk@redhat.com, yvugenfi@redhat.com,
	Elizabeth Ashurov <eashurov@redhat.com>
Subject: [PATCH v2 2/2] qga: add --audit option for command logging control
Date: Wed, 18 Mar 2026 17:47:52 +0200	[thread overview]
Message-ID: <20260318154752.1880933-2-eashurov@redhat.com> (raw)
In-Reply-To: <20260318154752.1880933-1-eashurov@redhat.com>

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       |  6 ++--
 qga/main.c           | 73 ++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 81 insertions(+), 25 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..4462922005 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,8 +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) {
         error_setg(errp, "PID " PRId64 " does not exist");
@@ -238,7 +236,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 04c772b680..5b289ae7f9 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 (warning/info and above)\n"
 "  -g, --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,29 @@ static void ga_log(const gchar *domain, GLogLevelFlags level,
     }
 }
 
+static void ga_audit_log(GAState *s, const char *command)
+{
+    GList *l;
+
+    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 +737,22 @@ 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;
+        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);
@@ -1158,6 +1204,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 =
@@ -1230,6 +1284,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 +1308,7 @@ static void config_dump(GAConfig *config)
 
 static void config_parse(GAConfig *config, int argc, char **argv)
 {
-    const char *sopt = "hVvdgc:m:p:l:f:F::b:a:s:t:Dr";
+    const char *sopt = "hVvdgc: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 +1327,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
@@ -1363,6 +1421,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;
@@ -1417,6 +1479,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);
 }
 
@@ -1460,6 +1524,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 +1763,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



  reply	other threads:[~2026-03-18 15:49 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-12 14:16 [PATCH v1] qga: rework slog to support multiple severity levels Elizabeth Ashurov
     [not found] ` <abLVj2eLZmKbPs9J@redhat.com>
     [not found]   ` <CAPMcbCq4i8DhwRqPkJxywt6H==NRW=3_7NA0ZQhe8cMFtVGRCA@mail.gmail.com>
2026-03-12 15:36     ` Daniel P. Berrangé
2026-03-18 16:13       ` Elizabeth Ashurov
2026-03-18 15:47 ` [PATCH v2 1/2] qga: replace slog() with standard GLib logging Elizabeth Ashurov
2026-03-18 15:47   ` Elizabeth Ashurov [this message]
2026-03-23 13:46     ` [PATCH v2 2/2] qga: add --audit option for command logging control Kostiantyn Kostiuk
2026-03-24 14:08       ` Elizabeth Ashurov
2026-03-24 14:18         ` Kostiantyn Kostiuk
2026-03-20 12:58   ` [PATCH v2 1/2] qga: replace slog() with standard GLib logging Daniel P. Berrangé
2026-03-24 14:15     ` Elizabeth Ashurov
2026-03-24 18:02   ` Daniel P. Berrangé

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260318154752.1880933-2-eashurov@redhat.com \
    --to=eashurov@redhat.com \
    --cc=berrange@redhat.com \
    --cc=kkostiuk@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=yvugenfi@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox