diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 4752e0e0f3..32128d4fab 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1378,7 +1378,7 @@ CapabilityBoundingSet=~CAP_B CAP_C StateDirectory= /var/lib/ - $XDG_CONFIG_HOME + $XDG_STATE_HOME $STATE_DIRECTORY @@ -1390,7 +1390,7 @@ CapabilityBoundingSet=~CAP_B CAP_C LogsDirectory= /var/log/ - $XDG_CONFIG_HOME/log/ + $XDG_STATE_HOME/log/ $LOGS_DIRECTORY diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index e9c7cb238c..8c3329995d 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -2131,7 +2131,7 @@ Note that this setting is not influenced by the Us %L Log directory root - This is either /var/log (for the system manager) or the path $XDG_CONFIG_HOME resolves to with /log appended (for user managers). + This is either /var/log (for the system manager) or the path $XDG_STATE_HOME resolves to with /log appended (for user managers). @@ -2171,7 +2171,7 @@ Note that this setting is not influenced by the Us %S State directory root - This is either /var/lib (for the system manager) or the path $XDG_CONFIG_HOME resolves to (for user managers). + This is either /var/lib (for the system manager) or the path $XDG_STATE_HOME resolves to (for user managers). %t 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 diff --git a/src/core/manager.c b/src/core/manager.c index 23df5ce191..8a081d0056 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -720,9 +720,9 @@ static int manager_setup_prefix(Manager *m) { static const struct table_entry paths_user[_EXEC_DIRECTORY_TYPE_MAX] = { [EXEC_DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME, NULL }, - [EXEC_DIRECTORY_STATE] = { SD_PATH_USER_CONFIGURATION, NULL }, + [EXEC_DIRECTORY_STATE] = { SD_PATH_USER_STATE_PRIVATE, NULL }, [EXEC_DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE, NULL }, - [EXEC_DIRECTORY_LOGS] = { SD_PATH_USER_CONFIGURATION, "log" }, + [EXEC_DIRECTORY_LOGS] = { SD_PATH_USER_STATE_PRIVATE, "log" }, [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_USER_CONFIGURATION, NULL }, }; diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 3977082cc1..9f95984eb6 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -209,8 +209,8 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length, * %C: the cache directory root (e.g. /var/cache or $XDG_CACHE_HOME) * %d: the credentials directory ($CREDENTIALS_DIRECTORY) * %E: the configuration directory root (e.g. /etc or $XDG_CONFIG_HOME) - * %L: the log directory root (e.g. /var/log or $XDG_CONFIG_HOME/log) - * %S: the state directory root (e.g. /var/lib or $XDG_CONFIG_HOME) + * %L: the log directory root (e.g. /var/log or $XDG_STATE_HOME/log) + * %S: the state directory root (e.g. /var/lib or $XDG_STATE_HOME) * %t: the runtime directory root (e.g. /run or $XDG_RUNTIME_DIR) * * %h: the homedir of the running user diff --git a/test/test-execute/exec-specifier-user.service b/test/test-execute/exec-specifier-user.service index ee0301a426..ab565fb4fb 100644 --- a/test/test-execute/exec-specifier-user.service +++ b/test/test-execute/exec-specifier-user.service @@ -5,7 +5,7 @@ Description=Test for specifiers [Service] Type=oneshot ExecStart=sh -c 'test %t = $$XDG_RUNTIME_DIR' -ExecStart=sh -c 'test %S = %h/.config' +ExecStart=sh -c 'test %S = %h/.local/state' ExecStart=sh -c 'test %C = %h/.cache' -ExecStart=sh -c 'test %L = %h/.config/log' +ExecStart=sh -c 'test %L = %h/.local/state/log' ExecStart=sh -c 'test %E = %h/.config'