* [PATCH v1] qga: rework slog to support multiple severity levels
@ 2026-03-12 14:16 Elizabeth Ashurov
[not found] ` <abLVj2eLZmKbPs9J@redhat.com>
2026-03-18 15:47 ` [PATCH v2 1/2] qga: replace slog() with standard GLib logging Elizabeth Ashurov
0 siblings, 2 replies; 11+ messages in thread
From: Elizabeth Ashurov @ 2026-03-12 14:16 UTC (permalink / raw)
To: qemu-devel; +Cc: kkostiuk, berrange, Elizabeth Ashurov
ga_log() forwarded all "syslog" domain messages unconditionally,
bypassing the log level filter. Furthermore, slog() hardcoded
all messages to G_LOG_LEVEL_INFO, causing log spam from frequent
guest-ping calls.
- Split slog() into slog_error() (WARNING), slog() (MESSAGE), and slog_trace() (INFO)
- Apply s->log_level filtering to "syslog" in ga_log()
- Move guest-ping to slog_trace()
- Move error messages to slog_error()
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3214
Signed-off-by: Elizabeth Ashurov <eashurov@redhat.com>
---
qga/commands-linux.c | 6 ++++--
qga/commands-posix.c | 13 +++++++------
qga/commands-win32.c | 34 +++++++++++++++++-----------------
qga/commands.c | 30 ++++++++++++++++++++++++------
qga/guest-agent-core.h | 2 ++
qga/main.c | 6 +++++-
6 files changed, 59 insertions(+), 32 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index 378f4d080c..b924e44517 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -44,7 +44,8 @@ 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));
+ slog_error("failed to stat device file '%s': %s",
+ devpath, strerror(errno));
return -1;
}
if (S_ISDIR(st.st_mode)) {
@@ -2072,7 +2073,8 @@ GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
}
if (i < 5) {
- slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
+ slog_error("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..e7dc92fed0 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -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);
+ slog_error("guest-file-write failed, handle: %" PRId64, handle);
} else {
write_data = g_new0(GuestFileWrite, 1);
write_data->count = write_count;
@@ -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));
+ slog_error("failed to clean up frozen filesystems: %s",
+ error_get_pretty(err));
error_free(err);
}
}
@@ -1282,19 +1282,20 @@ 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);
+ slog_error("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);
+ slog_error("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);
+ slog_error("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..cada3135fc 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -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);
+ slog_error("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);
+ slog_error("guest-file-write-failed, handle: %" PRId64, handle);
} else {
write_data = g_new0(GuestFileWrite, 1);
write_data->count = (size_t) write_count;
@@ -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));
+ slog_error("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);
+ slog_error("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);
+ slog_error("failed to lookup Windows version: major=%lu, minor=%lu",
+ major, minor);
return g_strdup("N/A");
}
@@ -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);
+ slog_error("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);
+ slog_error("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);
+ slog_error("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");
+ slog_error("failed to get hardware IDs");
return NULL;
}
if (*property == '\0') {
@@ -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");
+ slog_error("failed to get device description");
continue;
}
device->driver_name = g_utf16_to_utf8(name, -1, NULL, NULL, NULL);
@@ -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");
+ slog_error("failed to get driver version");
continue;
}
device->driver_version = g_utf16_to_utf8(version, -1, NULL,
@@ -2437,7 +2437,7 @@ 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");
+ slog_error("failed to get driver date");
continue;
}
device->driver_date = filetime_to_ns(date);
@@ -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);
+ slog_error("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..ed52a7d3c2 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -38,6 +38,24 @@ void slog(const gchar *fmt, ...)
{
va_list ap;
+ va_start(ap, fmt);
+ g_logv("syslog", G_LOG_LEVEL_MESSAGE, fmt, ap);
+ va_end(ap);
+}
+
+void slog_error(const gchar *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ g_logv("syslog", G_LOG_LEVEL_WARNING, fmt, ap);
+ va_end(ap);
+}
+
+void slog_trace(const gchar *fmt, ...)
+{
+ va_list ap;
+
va_start(ap, fmt);
g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
va_end(ap);
@@ -56,7 +74,7 @@ int64_t qmp_guest_sync(int64_t id, Error **errp)
void qmp_guest_ping(Error **errp)
{
- slog("guest-ping called");
+ slog_trace("guest-ping called");
}
static void qmp_command_info(const QmpCommand *cmd, void *opaque)
@@ -285,8 +303,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));
+ slog_error("dup2() failed to merge stderr into stdout: %s",
+ strerror(errno));
}
}
@@ -295,8 +313,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));
+ slog_error("sigaction() failed to reset child process's SIGPIPE: %s",
+ strerror(errno));
}
#endif
}
@@ -626,7 +644,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);
+ slog_error("guest-file-write failed, handle: %" PRId64, handle);
}
return read_data;
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index d9f3922adf..7c50e43dfc 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -41,6 +41,8 @@ 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 G_GNUC_PRINTF(1, 2) slog_error(const gchar *fmt, ...);
+void G_GNUC_PRINTF(1, 2) slog_trace(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..bc786593fd 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -392,6 +392,9 @@ 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;
+ }
#ifndef _WIN32
syslog(glib_log_level_to_system(level), "%s: %s", level_str, msg);
#else
@@ -1673,7 +1676,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 | G_LOG_LEVEL_MESSAGE;
qemu_init_exec_dir(argv[0]);
qga_qmp_init_marshal(&ga_commands);
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread[parent not found: <abLVj2eLZmKbPs9J@redhat.com>]
[parent not found: <CAPMcbCq4i8DhwRqPkJxywt6H==NRW=3_7NA0ZQhe8cMFtVGRCA@mail.gmail.com>]
* Re: [PATCH v1] qga: rework slog to support multiple severity levels [not found] ` <CAPMcbCq4i8DhwRqPkJxywt6H==NRW=3_7NA0ZQhe8cMFtVGRCA@mail.gmail.com> @ 2026-03-12 15:36 ` Daniel P. Berrangé 2026-03-18 16:13 ` Elizabeth Ashurov 0 siblings, 1 reply; 11+ messages in thread From: Daniel P. Berrangé @ 2026-03-12 15:36 UTC (permalink / raw) To: Kostiantyn Kostiuk; +Cc: Elizabeth Ashurov, qemu-devel On Thu, Mar 12, 2026 at 05:15:05PM +0200, Kostiantyn Kostiuk wrote: > On Thu, Mar 12, 2026 at 5:02 PM Daniel P. Berrangé <berrange@redhat.com> > wrote: > > > On Thu, Mar 12, 2026 at 04:16:57PM +0200, Elizabeth Ashurov wrote: > > > ga_log() forwarded all "syslog" domain messages unconditionally, > > > bypassing the log level filter. Furthermore, slog() hardcoded > > > all messages to G_LOG_LEVEL_INFO, causing log spam from frequent > > > guest-ping calls. > > > > > > - Split slog() into slog_error() (WARNING), slog() (MESSAGE), and > > slog_trace() (INFO) > > > - Apply s->log_level filtering to "syslog" in ga_log() > > > - Move guest-ping to slog_trace() > > > - Move error messages to slog_error() > > > > > > Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3214 > > > > > > Signed-off-by: Elizabeth Ashurov <eashurov@redhat.com> > > > --- > > > qga/commands-linux.c | 6 ++++-- > > > qga/commands-posix.c | 13 +++++++------ > > > qga/commands-win32.c | 34 +++++++++++++++++----------------- > > > qga/commands.c | 30 ++++++++++++++++++++++++------ > > > qga/guest-agent-core.h | 2 ++ > > > qga/main.c | 6 +++++- > > > 6 files changed, 59 insertions(+), 32 deletions(-) > > > > > > > > diff --git a/qga/commands.c b/qga/commands.c > > > index 5f20af25d3..ed52a7d3c2 100644 > > > --- a/qga/commands.c > > > +++ b/qga/commands.c > > > @@ -38,6 +38,24 @@ void slog(const gchar *fmt, ...) > > > { > > > va_list ap; > > > > > > + va_start(ap, fmt); > > > + g_logv("syslog", G_LOG_LEVEL_MESSAGE, fmt, ap); > > > + va_end(ap); > > > +} > > > + > > > +void slog_error(const gchar *fmt, ...) > > > +{ > > > + va_list ap; > > > + > > > + va_start(ap, fmt); > > > + g_logv("syslog", G_LOG_LEVEL_WARNING, fmt, ap); > > > + va_end(ap); > > > +} > > > + > > > +void slog_trace(const gchar *fmt, ...) > > > +{ > > > + va_list ap; > > > + > > > va_start(ap, fmt); > > > g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap); > > > va_end(ap); > > > > This is really the wrong approach IMHO. > > > > It is bad practice for the call sites to be declaring what log > > output they are targetting. > > > > Also the first parameter of g_log call is supposed to be the > > "domain" which is a name that identicates the *source* of the > > error message. For code in a library it would be the library > > name, or a module name within the library. For applications > > it would usually be the application name. > > > > Passing the name of a desired log target "syslog" is really > > a misuse of the logging framework. > > > > IMHO the existing usage of "slog" should be replaced by > > calls to g_error() (for the cases which are reporting error > > messages) and g_info() (for the cases which are reporting the > > names of commands being executed). > > > > Then, we should offer better command line control over the > > usage of logging > > > > "verbose" should enable g_info() or higher > > "debug" should enable g_debug() or higher > > > > The log file (if configured) should receive *all* messages > > per the verbose/debug flag settings. (currently it misses > > any messages that get sent to syslog which is bad as it > > makes the logfile incomplete. > > > > The syslog output should also receive all messages at > > "info" level of higher (provided the verbose flag is > > set). It shouldn't get debug messages, even if 'debug' > > flag is set. > > > > We should not specialcase the "ping" command either IMHO, > > it should remain "info" like the other commands. Per that > > bug, users can exclude ping messages via a log filter on > > the systemd service config. > > > > What to do with Windows? We definitely want to see a freeze/thaw event in > Event Viewer, but ping is also redundant there. If we think of the recording of commands as more of an "audit" task, we could expose an "audit" argument to control this. It would take a list of command names, optionally with wildcards, and optionally with negation audit = * => current default - all commands at "info" level audit = "!guest-ping,*" => a new default - all commands at "info" level, except ping audit = "!guest-ssh-get*,guest-ssh*" => a restrictive audit, only commands which modify ssh setup anything not matched by the audit setting, would be logged at g_debug instead of g_info With regards, Daniel -- |: https://berrange.com ~~ https://hachyderm.io/@berrange :| |: https://libvirt.org ~~ https://entangle-photo.org :| |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :| ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v1] qga: rework slog to support multiple severity levels 2026-03-12 15:36 ` Daniel P. Berrangé @ 2026-03-18 16:13 ` Elizabeth Ashurov 0 siblings, 0 replies; 11+ messages in thread From: Elizabeth Ashurov @ 2026-03-18 16:13 UTC (permalink / raw) To: Daniel P. Berrangé; +Cc: Kostiantyn Kostiuk, qemu-devel [-- Attachment #1: Type: text/plain, Size: 5211 bytes --] Hi, Thanks for the review and suggestions. I updated the patch based on the discussion and sent v2. Best regards, On Thu, Mar 12, 2026 at 5:36 PM Daniel P. Berrangé <berrange@redhat.com> wrote: > On Thu, Mar 12, 2026 at 05:15:05PM +0200, Kostiantyn Kostiuk wrote: > > On Thu, Mar 12, 2026 at 5:02 PM Daniel P. Berrangé <berrange@redhat.com> > > wrote: > > > > > On Thu, Mar 12, 2026 at 04:16:57PM +0200, Elizabeth Ashurov wrote: > > > > ga_log() forwarded all "syslog" domain messages unconditionally, > > > > bypassing the log level filter. Furthermore, slog() hardcoded > > > > all messages to G_LOG_LEVEL_INFO, causing log spam from frequent > > > > guest-ping calls. > > > > > > > > - Split slog() into slog_error() (WARNING), slog() (MESSAGE), and > > > slog_trace() (INFO) > > > > - Apply s->log_level filtering to "syslog" in ga_log() > > > > - Move guest-ping to slog_trace() > > > > - Move error messages to slog_error() > > > > > > > > Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3214 > > > > > > > > Signed-off-by: Elizabeth Ashurov <eashurov@redhat.com> > > > > --- > > > > qga/commands-linux.c | 6 ++++-- > > > > qga/commands-posix.c | 13 +++++++------ > > > > qga/commands-win32.c | 34 +++++++++++++++++----------------- > > > > qga/commands.c | 30 ++++++++++++++++++++++++------ > > > > qga/guest-agent-core.h | 2 ++ > > > > qga/main.c | 6 +++++- > > > > 6 files changed, 59 insertions(+), 32 deletions(-) > > > > > > > > > > > diff --git a/qga/commands.c b/qga/commands.c > > > > index 5f20af25d3..ed52a7d3c2 100644 > > > > --- a/qga/commands.c > > > > +++ b/qga/commands.c > > > > @@ -38,6 +38,24 @@ void slog(const gchar *fmt, ...) > > > > { > > > > va_list ap; > > > > > > > > + va_start(ap, fmt); > > > > + g_logv("syslog", G_LOG_LEVEL_MESSAGE, fmt, ap); > > > > + va_end(ap); > > > > +} > > > > + > > > > +void slog_error(const gchar *fmt, ...) > > > > +{ > > > > + va_list ap; > > > > + > > > > + va_start(ap, fmt); > > > > + g_logv("syslog", G_LOG_LEVEL_WARNING, fmt, ap); > > > > + va_end(ap); > > > > +} > > > > + > > > > +void slog_trace(const gchar *fmt, ...) > > > > +{ > > > > + va_list ap; > > > > + > > > > va_start(ap, fmt); > > > > g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap); > > > > va_end(ap); > > > > > > This is really the wrong approach IMHO. > > > > > > It is bad practice for the call sites to be declaring what log > > > output they are targetting. > > > > > > Also the first parameter of g_log call is supposed to be the > > > "domain" which is a name that identicates the *source* of the > > > error message. For code in a library it would be the library > > > name, or a module name within the library. For applications > > > it would usually be the application name. > > > > > > Passing the name of a desired log target "syslog" is really > > > a misuse of the logging framework. > > > > > > IMHO the existing usage of "slog" should be replaced by > > > calls to g_error() (for the cases which are reporting error > > > messages) and g_info() (for the cases which are reporting the > > > names of commands being executed). > > > > > > Then, we should offer better command line control over the > > > usage of logging > > > > > > "verbose" should enable g_info() or higher > > > "debug" should enable g_debug() or higher > > > > > > The log file (if configured) should receive *all* messages > > > per the verbose/debug flag settings. (currently it misses > > > any messages that get sent to syslog which is bad as it > > > makes the logfile incomplete. > > > > > > The syslog output should also receive all messages at > > > "info" level of higher (provided the verbose flag is > > > set). It shouldn't get debug messages, even if 'debug' > > > flag is set. > > > > > > We should not specialcase the "ping" command either IMHO, > > > it should remain "info" like the other commands. Per that > > > bug, users can exclude ping messages via a log filter on > > > the systemd service config. > > > > > > > What to do with Windows? We definitely want to see a freeze/thaw event in > > Event Viewer, but ping is also redundant there. > > If we think of the recording of commands as more of an "audit" task, > we could expose an "audit" argument to control this. It would take > a list of command names, optionally with wildcards, and optionally > with negation > > audit = * > > => current default - all commands at "info" level > > audit = "!guest-ping,*" > > => a new default - all commands at "info" level, except ping > > audit = "!guest-ssh-get*,guest-ssh*" > > => a restrictive audit, only commands which modify ssh > setup > > anything not matched by the audit setting, would be logged at > g_debug instead of g_info > > > With regards, > Daniel > -- > |: https://berrange.com ~~ https://hachyderm.io/@berrange :| > |: https://libvirt.org ~~ https://entangle-photo.org :| > |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :| > > [-- Attachment #2: Type: text/html, Size: 7789 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/2] qga: replace slog() with standard GLib logging 2026-03-12 14:16 [PATCH v1] qga: rework slog to support multiple severity levels Elizabeth Ashurov [not found] ` <abLVj2eLZmKbPs9J@redhat.com> @ 2026-03-18 15:47 ` Elizabeth Ashurov 2026-03-18 15:47 ` [PATCH v2 2/2] qga: add --audit option for command logging control Elizabeth Ashurov ` (2 more replies) 1 sibling, 3 replies; 11+ messages in thread From: Elizabeth Ashurov @ 2026-03-18 15:47 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 and CRITICAL - -v/--verbose enables WARNING, MESSAGE, and INFO - -g/--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..04c772b680 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 (warning/info and above)\n" +" -g, --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,11 @@ 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_WARNING | 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 +1225,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,7 +1251,7 @@ static void config_dump(GAConfig *config) static void config_parse(GAConfig *config, int argc, char **argv) { - const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr"; + const char *sopt = "hVvdgc:m:p:l:f:F::b:a:s:t:Dr"; int opt_ind = 0, ch; const struct option lopt[] = { { "help", 0, NULL, 'h' }, @@ -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, 'g' }, { "method", 1, NULL, 'm' }, { "path", 1, NULL, 'p' }, { "daemonize", 0, NULL, 'd' }, @@ -1313,7 +1327,10 @@ 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_WARNING | G_LOG_LEVEL_MESSAGE + | G_LOG_LEVEL_INFO; + break; + case 'g': config->log_level = G_LOG_LEVEL_MASK; break; case 'V': -- 2.51.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 2/2] qga: add --audit option for command logging control 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 2026-03-23 13:46 ` Kostiantyn Kostiuk 2026-03-20 12:58 ` [PATCH v2 1/2] qga: replace slog() with standard GLib logging Daniel P. Berrangé 2026-03-24 18:02 ` Daniel P. Berrangé 2 siblings, 1 reply; 11+ messages in thread From: Elizabeth Ashurov @ 2026-03-18 15:47 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 | 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 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] qga: add --audit option for command logging control 2026-03-18 15:47 ` [PATCH v2 2/2] qga: add --audit option for command logging control Elizabeth Ashurov @ 2026-03-23 13:46 ` Kostiantyn Kostiuk 2026-03-24 14:08 ` Elizabeth Ashurov 0 siblings, 1 reply; 11+ messages in thread From: Kostiantyn Kostiuk @ 2026-03-23 13:46 UTC (permalink / raw) To: Elizabeth Ashurov; +Cc: qemu-devel, berrange, yvugenfi [-- Attachment #1: Type: text/plain, Size: 14121 bytes --] On Wed, Mar 18, 2026 at 5:48 PM Elizabeth Ashurov <eashurov@redhat.com> wrote: > 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); > any reason for g_debug there? will it be printed by ga_audit_log? in any case, the extra line was removed 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); > the same > } > 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); > Please add a comment on why we need this. I know about fs-thaw and disabled logging, but for other people, this can be unclear > + bool audit = command && qmp_find_command(&ga_commands, command); > + > + if (audit) { > + ga_audit_log(s, command); > What if command = NULL? Is ga_audit_log NULL-safe? > + } > + > + 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 > > [-- Attachment #2: Type: text/html, Size: 17939 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] qga: add --audit option for command logging control 2026-03-23 13:46 ` Kostiantyn Kostiuk @ 2026-03-24 14:08 ` Elizabeth Ashurov 2026-03-24 14:18 ` Kostiantyn Kostiuk 0 siblings, 1 reply; 11+ messages in thread From: Elizabeth Ashurov @ 2026-03-24 14:08 UTC (permalink / raw) To: Kostiantyn Kostiuk; +Cc: qemu-devel, berrange, yvugenfi [-- Attachment #1: Type: text/plain, Size: 15301 bytes --] Hi, Thanks for the review. I'm working on v3 and addressed the comments. Regarding the g_debug lines in guest_exec: I kept them because ga_audit_log only logs the command name, while the g_debug lines include the arguments (str) and the PID. This was previously logged at info level, so I wanted to avoid losing that information. Regarding NULL safety of ga_audit_log: the function itself is not NULL-safe, but the current flow does not call it with NULL. I will add an explicit check at the beginning of ga_audit_log for safety. Let me know if you prefer to keep the g_debug lines. Otherwise, I’ll remove them and send v3. On Mon, Mar 23, 2026 at 3:46 PM Kostiantyn Kostiuk <kkostiuk@redhat.com> wrote: > > > On Wed, Mar 18, 2026 at 5:48 PM Elizabeth Ashurov <eashurov@redhat.com> > wrote: > >> 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); >> > > any reason for g_debug there? will it be printed by ga_audit_log? > > in any case, the extra line was removed > > 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); >> > > the same > > >> } >> 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); >> > > Please add a comment on why we need this. > I know about fs-thaw and disabled logging, but for other people, this can > be unclear > > >> + bool audit = command && qmp_find_command(&ga_commands, command); >> + >> + if (audit) { >> + ga_audit_log(s, command); >> > What if command = NULL? Is ga_audit_log NULL-safe? > > >> + } >> + >> + 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 >> >> [-- Attachment #2: Type: text/html, Size: 18989 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] qga: add --audit option for command logging control 2026-03-24 14:08 ` Elizabeth Ashurov @ 2026-03-24 14:18 ` Kostiantyn Kostiuk 0 siblings, 0 replies; 11+ messages in thread From: Kostiantyn Kostiuk @ 2026-03-24 14:18 UTC (permalink / raw) To: Elizabeth Ashurov; +Cc: qemu-devel, berrange, yvugenfi [-- Attachment #1: Type: text/plain, Size: 15872 bytes --] On Tue, Mar 24, 2026 at 4:08 PM Elizabeth Ashurov <eashurov@redhat.com> wrote: > Hi, > > Thanks for the review. I'm working on v3 and addressed the comments. > > Regarding the g_debug lines in guest_exec: I kept them because > ga_audit_log only logs the command name, while the g_debug lines include > the arguments (str) and the PID. This was previously logged at info level, > so I wanted to avoid losing that information. > > Regarding NULL safety of ga_audit_log: the function itself is not > NULL-safe, but the current flow does not call it with NULL. > I will add an explicit check at the beginning of ga_audit_log for safety. > > Let me know if you prefer to keep the g_debug lines. Otherwise, I’ll > remove them and send v3. > You can keep it > > On Mon, Mar 23, 2026 at 3:46 PM Kostiantyn Kostiuk <kkostiuk@redhat.com> > wrote: > >> >> >> On Wed, Mar 18, 2026 at 5:48 PM Elizabeth Ashurov <eashurov@redhat.com> >> wrote: >> >>> 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); >>> >> >> any reason for g_debug there? will it be printed by ga_audit_log? >> >> in any case, the extra line was removed >> >> 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); >>> >> >> the same >> >> >>> } >>> 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); >>> >> >> Please add a comment on why we need this. >> I know about fs-thaw and disabled logging, but for other people, this can >> be unclear >> >> >>> + bool audit = command && qmp_find_command(&ga_commands, command); >>> + >>> + if (audit) { >>> + ga_audit_log(s, command); >>> >> What if command = NULL? Is ga_audit_log NULL-safe? >> >> >>> + } >>> + >>> + 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 >>> >>> [-- Attachment #2: Type: text/html, Size: 19587 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] qga: replace slog() with standard GLib logging 2026-03-18 15:47 ` [PATCH v2 1/2] qga: replace slog() with standard GLib logging Elizabeth Ashurov 2026-03-18 15:47 ` [PATCH v2 2/2] qga: add --audit option for command logging control Elizabeth Ashurov @ 2026-03-20 12:58 ` Daniel P. Berrangé 2026-03-24 14:15 ` Elizabeth Ashurov 2026-03-24 18:02 ` Daniel P. Berrangé 2 siblings, 1 reply; 11+ messages in thread From: Daniel P. Berrangé @ 2026-03-20 12:58 UTC (permalink / raw) To: Elizabeth Ashurov; +Cc: qemu-devel, kkostiuk, yvugenfi On Wed, Mar 18, 2026 at 05:47:51PM +0200, Elizabeth Ashurov wrote: > 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 and CRITICAL > - -v/--verbose enables WARNING, MESSAGE, and INFO IMHO "WARNING" should be enabled by default too. > - -g/--debug enables all levels including DEBUG Since we can't use the natural '-d' short option, we can probably do with only the the long option. > > 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(-) With regards, Daniel -- |: https://berrange.com ~~ https://hachyderm.io/@berrange :| |: https://libvirt.org ~~ https://entangle-photo.org :| |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :| ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] qga: replace slog() with standard GLib logging 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 0 siblings, 0 replies; 11+ messages in thread From: Elizabeth Ashurov @ 2026-03-24 14:15 UTC (permalink / raw) To: Daniel P. Berrangé; +Cc: qemu-devel, kkostiuk, yvugenfi [-- Attachment #1: Type: text/plain, Size: 1656 bytes --] I’ll update accordingly. Thanks. On Fri, Mar 20, 2026 at 2:58 PM Daniel P. Berrangé <berrange@redhat.com> wrote: > On Wed, Mar 18, 2026 at 05:47:51PM +0200, Elizabeth Ashurov wrote: > > 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 and CRITICAL > > - -v/--verbose enables WARNING, MESSAGE, and INFO > > IMHO "WARNING" should be enabled by default too. > > > - -g/--debug enables all levels including DEBUG > > Since we can't use the natural '-d' short option, we can > probably do with only the the long option. > > > > > 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(-) > > > With regards, > Daniel > -- > |: https://berrange.com ~~ https://hachyderm.io/@berrange :| > |: https://libvirt.org ~~ https://entangle-photo.org :| > |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :| > > [-- Attachment #2: Type: text/html, Size: 2688 bytes --] ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] qga: replace slog() with standard GLib logging 2026-03-18 15:47 ` [PATCH v2 1/2] qga: replace slog() with standard GLib logging Elizabeth Ashurov 2026-03-18 15:47 ` [PATCH v2 2/2] qga: add --audit option for command logging control Elizabeth Ashurov 2026-03-20 12:58 ` [PATCH v2 1/2] qga: replace slog() with standard GLib logging Daniel P. Berrangé @ 2026-03-24 18:02 ` Daniel P. Berrangé 2 siblings, 0 replies; 11+ messages in thread From: Daniel P. Berrangé @ 2026-03-24 18:02 UTC (permalink / raw) To: Elizabeth Ashurov; +Cc: qemu-devel, kkostiuk, yvugenfi FWIW, next time, please send new versions as top level mail threads. Don't try to make the mails a reply to a previous posting as that is liable to lead to maintainers overlooking the new posting. On Wed, Mar 18, 2026 at 05:47:51PM +0200, Elizabeth Ashurov wrote: > 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 and CRITICAL > - -v/--verbose enables WARNING, MESSAGE, and INFO > - -g/--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..04c772b680 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 (warning/info and above)\n" > +" -g, --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,11 @@ 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_WARNING | 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 +1225,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,7 +1251,7 @@ static void config_dump(GAConfig *config) > > static void config_parse(GAConfig *config, int argc, char **argv) > { > - const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr"; > + const char *sopt = "hVvdgc:m:p:l:f:F::b:a:s:t:Dr"; > int opt_ind = 0, ch; > const struct option lopt[] = { > { "help", 0, NULL, 'h' }, > @@ -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, 'g' }, > { "method", 1, NULL, 'm' }, > { "path", 1, NULL, 'p' }, > { "daemonize", 0, NULL, 'd' }, > @@ -1313,7 +1327,10 @@ 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_WARNING | G_LOG_LEVEL_MESSAGE > + | G_LOG_LEVEL_INFO; > + break; > + case 'g': > config->log_level = G_LOG_LEVEL_MASK; > break; > case 'V': > -- > 2.51.0 > With regards, Daniel -- |: https://berrange.com ~~ https://hachyderm.io/@berrange :| |: https://libvirt.org ~~ https://entangle-photo.org :| |: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :| ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-03-24 18:02 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH v2 2/2] qga: add --audit option for command logging control Elizabeth Ashurov
2026-03-23 13:46 ` 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é
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox