====== I'm sending this as an RFC just because what it's doing feels kind of dirty - directly examining XDG_RUNTIME_DIR seems like an "end run" around the Glib API. If anyone has a better idea of what to do, please give details :-) ====== When running unprivileged (i.e. not as root, but as a regular user), libvirt calls g_get_user_runtime_dir() (from Glib) to get the name of a directory where status files can be saved. This is a directory that is 1) writeable by the current user, and 2) will remain there until the host reboots, but then 3) be erased after the reboot. This is used for pidfiles, sockets created to communicate between processes, status XML of active domains, etc. Normally g_get_user_runtime_dir() returns the setting of XDG_RUNTIME_DIR in the user's environment; usually this is set to /run/user/${UID} (e.g. /run/user/1000) - that directory is created when a user first logs in and is owned by the user, but is cleared out when the system reboots (more specifically, this directory usually resides in a tmpfs, and so disappears when that tmpfs is unmounted). But sometimes XDG_RUNTIME_DIR isn't set in the user's environment. In that case, g_get_user_runtime_dir() returns ${HOME}/.config (e.g. /home/laine/.config). This directory fulfills the first 2 criteria above, but fails the 3rd. This isn't just some pedantic complaint - libvirt actually depends on the directory being cleared out during a reboot - otherwise it might think that stale status files are indicating active guests when in fact the guests were shutdown during the reboot). In my opinion this behavior is a bug in Glib - see the requirements for XDG_RUNTIME in the FreeDesktop documentation here: https://specifications.freedesktop.org/basedir-spec/latest/#variables but they've documented the behavior as proper in the Glib docs for g_get_user_runtime_dir(), and so likely will consider it not a bug. Beyond that, aside from failing the "must be cleared out during a reboot" requirement, use of $HOME/.cache in this way also disturbs SELinux, which gives an AVC denial when libvirt (or passt) tries to create a file or socket in that directory (the SELinux policy permits use of /run/user/$UID, but not of $HOME/.config). We *could* add that to the SELinux policy, but since the glib behavior doesn't All of the above is a very long leadup to the functionality in this patch: rather than blindly accepting the path returned from g_get_user_runtime_dir(), we first check if XDG_RUNTIME_DIR is set; if it isn't set then we look to see if /run/user/$UID exists and is writable by this user, if so we use *that* as the directory for our status files. Otherwise (both when XDG_RUNTIME_DIR is set, and when /run/user/$UID isn't usable) we fallback to just using the path returned by g_get_user_runtime_dir() - that isn't perfect, but it's what we were doing before, so at least it's not any worse. Resolves: https://issues.redhat.com/browse/RHEL-70222 Signed-off-by: Laine Stump <laine@xxxxxxxxxx> --- src/util/virutil.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/util/virutil.c b/src/util/virutil.c index 2abcb282fe..4c7f4b62bc 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -538,6 +538,28 @@ char *virGetUserRuntimeDirectory(void) #ifdef WIN32 return g_strdup(g_get_user_runtime_dir()); #else + /* tl;dr - if XDG_RUNTIME_DIR is set, use g_get_user_runtime_dir(). + * if not set, then see if /run/user/$UID works + * if so, use that, else fallback to g_get_user_runtime_dir() + * + * this is done because the directory returned by + * g_get_user_runtime_dir() when XDG_RUNTIME_DIR isn't set is + * "suboptimal" (it's a location that is owned by the user, but + * isn't erased when the user completely logs out) + */ + + if (!getenv("XDG_RUNTIME_DIR")) { + g_autofree char *runtime_dir = NULL; + struct stat sb; + + runtime_dir = g_strdup_printf("/run/user/%d", getuid()); + if (virFileIsDir(runtime_dir) && + (stat(runtime_dir, &sb) == 0) && (sb.st_mode & S_IWUSR)) { + return g_build_filename(runtime_dir, "libvirt", NULL); + } + } + + /* either XDG_RUNTIME_DIR was set, or /run/usr/$UID wasn't writable */ return g_build_filename(g_get_user_runtime_dir(), "libvirt", NULL); #endif } -- 2.47.1