diff options
author | Lennart Poettering <lennart@poettering.net> | 2023-06-26 22:42:33 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2023-06-28 22:01:55 +0200 |
commit | f9c91932b4d83faf0f95624dc82db353d0726425 (patch) | |
tree | 98e3fef1790e0b3214832dfc3d84e5bcbb1e8d40 /src/core/execute.c | |
parent | execute: when recursively chowning StateDirectory= when spawning services, fo... (diff) | |
download | systemd-f9c91932b4d83faf0f95624dc82db353d0726425.tar.xz systemd-f9c91932b4d83faf0f95624dc82db353d0726425.zip |
execute: add support for XDG_STATE_HOME for placing service state data in --user mode
This adds support for the new XDG_STATE_HOME env var that was added to
the xdg basedir spec. Previously, because the basedir spec didn't know
the concept we'd alias the backing dir for StateDirectory= to the one
for ConfigurationDirectory= when runnin in --user mode. With this change
we'll make separate. This brings us various benefits, such as proper
"systemctl clean" support, where we can clear service state separately
from service configuration, now in user mode too.
This does not come without complications: retaining compatibility with
older setups is difficult, because we cannot possibly identitfy which
files in existing populated config dirs are actually "state" and which
one are true" configuration.
Hence let's deal with this pragmatically: if we detect that a service
that has both dirs configured only has the configuration dir existing,
then symlink the state dir to the configuration dir to retain
compatibility.
This is not great, but it's the only somewhat reasonable way out I can
see.
Fixes: #25739
Diffstat (limited to 'src/core/execute.c')
-rw-r--r-- | src/core/execute.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/src/core/execute.c b/src/core/execute.c index 11d707b59c..3e065b2ca8 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2511,6 +2511,61 @@ static int setup_exec_directory( if (r < 0) goto fail; + if (IN_SET(type, EXEC_DIRECTORY_STATE, EXEC_DIRECTORY_LOGS) && params->runtime_scope == RUNTIME_SCOPE_USER) { + + /* If we are in user mode, and a configuration directory exists but a state directory + * doesn't exist, then we likely are upgrading from an older systemd version that + * didn't know the more recent addition to the xdg-basedir spec: the $XDG_STATE_HOME + * directory. In older systemd versions EXEC_DIRECTORY_STATE was aliased to + * EXEC_DIRECTORY_CONFIGURATION, with the advent of $XDG_STATE_HOME is is now + * seperated. If a service has both dirs configured but only the configuration dir + * exists and the state dir does not, we assume we are looking at an update + * situation. Hence, create a compatibility symlink, so that all expectations are + * met. + * + * (We also do something similar with the log directory, which still doesn't exist in + * the xdg basedir spec. We'll make it a subdir of the state dir.) */ + + /* this assumes the state dir is always created before the configuration dir */ + assert_cc(EXEC_DIRECTORY_STATE < EXEC_DIRECTORY_LOGS); + assert_cc(EXEC_DIRECTORY_LOGS < EXEC_DIRECTORY_CONFIGURATION); + + r = laccess(p, F_OK); + if (r == -ENOENT) { + _cleanup_free_ char *q = NULL; + + /* OK, we know that the state dir does not exist. Let's see if the dir exists + * under the configuration hierarchy. */ + + if (type == EXEC_DIRECTORY_STATE) + q = path_join(params->prefix[EXEC_DIRECTORY_CONFIGURATION], context->directories[type].items[i].path); + else if (type == EXEC_DIRECTORY_LOGS) + q = path_join(params->prefix[EXEC_DIRECTORY_CONFIGURATION], "log", context->directories[type].items[i].path); + else + assert_not_reached(); + if (!q) { + r = -ENOMEM; + goto fail; + } + + r = laccess(q, F_OK); + if (r >= 0) { + /* It does exist! This hence looks like an update. Symlink the + * configuration directory into the state directory. */ + + r = symlink_idempotent(q, p, /* make_relative= */ true); + if (r < 0) + goto fail; + + log_notice("Unit state directory %s missing but matching configuration directory %s exists, assuming update from systemd 253 or older, creating compatibility symlink.", p, q); + continue; + } else if (r != -ENOENT) + log_warning_errno(r, "Unable to detect whether unit configuration directory '%s' exists, assuming not: %m", q); + + } else if (r < 0) + log_warning_errno(r, "Unable to detect whether unit state directory '%s' is missing, assuming it is: %m", p); + } + if (exec_directory_is_private(context, type)) { /* So, here's one extra complication when dealing with DynamicUser=1 units. In that * case we want to avoid leaving a directory around fully accessible that is owned by |