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
next prev parent 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