<row>
<entry><varname>StateDirectory=</varname></entry>
<entry><filename>/var/lib/</filename></entry>
- <entry><varname>$XDG_CONFIG_HOME</varname></entry>
+ <entry><varname>$XDG_STATE_HOME</varname></entry>
<entry><varname>$STATE_DIRECTORY</varname></entry>
</row>
<row>
<row>
<entry><varname>LogsDirectory=</varname></entry>
<entry><filename>/var/log/</filename></entry>
- <entry><varname>$XDG_CONFIG_HOME</varname><filename>/log/</filename></entry>
+ <entry><varname>$XDG_STATE_HOME</varname><filename>/log/</filename></entry>
<entry><varname>$LOGS_DIRECTORY</varname></entry>
</row>
<row>
<row>
<entry><literal>%L</literal></entry>
<entry>Log directory root</entry>
- <entry>This is either <filename>/var/log</filename> (for the system manager) or the path <literal>$XDG_CONFIG_HOME</literal> resolves to with <filename index="false">/log</filename> appended (for user managers).</entry>
+ <entry>This is either <filename>/var/log</filename> (for the system manager) or the path <varname>$XDG_STATE_HOME</varname> resolves to with <filename index="false">/log</filename> appended (for user managers).</entry>
</row>
<xi:include href="standard-specifiers.xml" xpointer="m"/>
<xi:include href="standard-specifiers.xml" xpointer="M"/>
<row>
<entry><literal>%S</literal></entry>
<entry>State directory root</entry>
- <entry>This is either <filename>/var/lib</filename> (for the system manager) or the path <literal>$XDG_CONFIG_HOME</literal> resolves to (for user managers).</entry>
+ <entry>This is either <filename>/var/lib</filename> (for the system manager) or the path <varname>$XDG_STATE_HOME</varname> resolves to (for user managers).</entry>
</row>
<row>
<entry><literal>%t</literal></entry>
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
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 },
};
* %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
[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'