On Wed, Apr 08, 2026 at 00:10:38 -0400, Laine Stump via Devel wrote:
From: Laine Stump <laine@redhat.com>
When running in session/unprivileged mode, nearly all paths are prefixed with the returns from one of glib's g_get_user_*_dir() functions, which in turn base their selected paths on the settings of a few items in the user's environment ($XDG_*, or a subdirectory of $HOME if the relevant $XDG_* isn't set).
This patch logs the settings of these directories in the log banner in an attempt to help diagnose the problem when a file/socket open/create fails.
An example of the banner:
libvirt version: 12.3.0, package: 1.fc43 (Unknown, 2026-04-07-22:43:30, vhost) hostname: 83be0e173e02, user: qemu, uid: 107 home dir: '/' (HOME='/') runtime dir: '/.cache' (XDG_RUNTIME_DIR='(unset)') config dir: '/.config' (XDG_CONFIG_HOME='(unset)') log dir: '/.cache' (XDG_CACHE_HOME='(unset)') libvirt: XML-RPC error : Cannot create user runtime directory '/.cache/libvirt': Permission denied
Resolves: https://redhat.atlassian.net/browse/RHEL-70222 Resolves: https://redhat.atlassian.net/browse/RHEL-105490 Signed-off-by: Laine Stump <laine@redhat.com> ---
We could obviously add more information here (or less); it's difficult to know where to draw the line. Also, the astute reviewer will notice that all this code is executed once for each log target - we could do it all once at a higher level and cache it if we really wanted to. I'm not sure if it's worth the trouble though).
Changes in V2: modified the format/labeling of the data as Peter suggested, and included an example in the commit log.
src/util/virlog.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
diff --git a/src/util/virlog.c b/src/util/virlog.c index ccdf66c396..d2882d16ee 100644 --- a/src/util/virlog.c +++ b/src/util/virlog.c @@ -540,6 +540,43 @@ virLogToOneTarget(virLogSource *source, g_get_host_name(), username, uid); virLogOneInitMsg(timestamp, hoststr, outputFunc, data);
+ /* This info is only relevant when running as something other than root */ + if (uid != 0) { + g_autofree char *envHOME = NULL; + g_autofree char *envXDG_RUNTIME_DIR = NULL; + g_autofree char *envXDG_CONFIG_HOME = NULL; + g_autofree char *envXDG_CACHE_HOME = NULL; + + g_autofree char *envstr1 = NULL; + g_autofree char *envstr2 = NULL; + g_autofree char *envstr3 = NULL; + g_autofree char *envstr4 = NULL; + + if (!(envHOME = g_strdup(g_getenv("HOME")))) + envHOME = g_strdup("(unset)"); + if (!(envXDG_RUNTIME_DIR = g_strdup(g_getenv("XDG_RUNTIME_DIR")))) + envXDG_RUNTIME_DIR = g_strdup("(unset)"); + if (!(envXDG_CONFIG_HOME = g_strdup(g_getenv("XDG_CONFIG_HOME")))) + envXDG_CONFIG_HOME = g_strdup("(unset)"); + if (!(envXDG_CACHE_HOME = g_strdup(g_getenv("XDG_CACHE_HOME")))) + envXDG_CACHE_HOME = g_strdup("(unset)"); + + envstr1 = g_strdup_printf("home dir: '%s' (HOME='%s')", + g_get_home_dir(), envHOME);
I wonder if the '(unset)' string is even required. The code could be simplified, if we decide that e.g. empty string is okay to denote we won't need the extra variables: envstr1 = g_strdup_printf("home dir: '%s' (HOME='%s')", g_get_home_dir(), NULLSTR_EMPTY(g_getenv("HOME")));
+ virLogOneInitMsg(timestamp, envstr1, outputFunc, data); + + envstr2 = g_strdup_printf("runtime dir: '%s' (XDG_RUNTIME_DIR='%s')", + g_get_user_runtime_dir(), envXDG_RUNTIME_DIR); + virLogOneInitMsg(timestamp, envstr2, outputFunc, data); + + envstr3 = g_strdup_printf("config dir: '%s' (XDG_CONFIG_HOME='%s')", + g_get_user_config_dir(), envXDG_CONFIG_HOME); + virLogOneInitMsg(timestamp, envstr3, outputFunc, data); + + envstr4 = g_strdup_printf("log dir: '%s' (XDG_CACHE_HOME='%s')", + g_get_user_cache_dir(), envXDG_CACHE_HOME); + virLogOneInitMsg(timestamp, envstr4, outputFunc, data); + } *needInit = false; }
However you decide about the '(unset)' vs '' for unset env variables: Reviewed-by: Peter Krempa <pkrempa@redhat.com>