mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 08:56:15 +09:00
Merge pull request #28168 from poettering/xdg-state-home
properly support XDG_STATE_HOME
This commit is contained in:
@@ -505,69 +505,64 @@
|
||||
<varlistentry>
|
||||
<term><filename>~/.cache/</filename></term>
|
||||
|
||||
<listitem><para>Persistent user cache data. User programs may
|
||||
place non-essential data in this directory. Flushing this
|
||||
directory should have no effect on operation of programs,
|
||||
except for increased runtimes necessary to rebuild these
|
||||
caches. If an application finds
|
||||
<varname>$XDG_CACHE_HOME</varname> set, it should use the
|
||||
directory specified in it instead of this
|
||||
<listitem><para>Persistent user cache data. User programs may place non-essential data in this
|
||||
directory. Flushing this directory should have no effect on operation of programs, except for
|
||||
increased runtimes necessary to rebuild these caches. If an application finds
|
||||
<varname>$XDG_CACHE_HOME</varname> set, it should use the directory specified in it instead of this
|
||||
directory.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>~/.config/</filename></term>
|
||||
|
||||
<listitem><para>Application configuration and state. When a
|
||||
new user is created, this directory will be empty or not exist
|
||||
at all. Applications should fall back to defaults should their
|
||||
configuration or state in this directory be missing. If an
|
||||
application finds <varname>$XDG_CONFIG_HOME</varname> set, it
|
||||
should use the directory specified in it instead of this
|
||||
directory.</para></listitem>
|
||||
<listitem><para>Application configuration. When a new user is created, this directory will be empty
|
||||
or not exist at all. Applications should fall back to defaults should their configuration in this
|
||||
directory be missing. If an application finds <varname>$XDG_CONFIG_HOME</varname> set, it should use
|
||||
the directory specified in it instead of this directory.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>~/.local/bin/</filename></term>
|
||||
|
||||
<listitem><para>Executables that shall appear in the user's
|
||||
<varname>$PATH</varname> search path. It is recommended not to
|
||||
place executables in this directory that are not useful for
|
||||
invocation from a shell; these should be placed in a
|
||||
subdirectory of <filename>~/.local/lib/</filename> instead.
|
||||
Care should be taken when placing architecture-dependent
|
||||
binaries in this place, which might be problematic if the home
|
||||
directory is shared between multiple hosts with different
|
||||
<listitem><para>Executables that shall appear in the user's <varname>$PATH</varname> search path. It
|
||||
is recommended not to place executables in this directory that are not useful for invocation from a
|
||||
shell; these should be placed in a subdirectory of <filename>~/.local/lib/</filename> instead. Care
|
||||
should be taken when placing architecture-dependent binaries in this place, which might be
|
||||
problematic if the home directory is shared between multiple hosts with different
|
||||
architectures.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>~/.local/lib/</filename></term>
|
||||
|
||||
<listitem><para>Static, private vendor data that is compatible
|
||||
with all architectures.</para></listitem>
|
||||
<listitem><para>Static, private vendor data that is compatible with all
|
||||
architectures.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>~/.local/lib/<replaceable>arch-id</replaceable>/</filename></term>
|
||||
|
||||
<listitem><para>Location for placing public dynamic libraries.
|
||||
The architecture identifier to use is defined on <ulink
|
||||
url="https://wiki.debian.org/Multiarch/Tuples">Multiarch
|
||||
Architecture Specifiers (Tuples)</ulink>
|
||||
list.</para></listitem>
|
||||
<listitem><para>Location for placing public dynamic libraries. The architecture identifier to use is
|
||||
defined on <ulink url="https://wiki.debian.org/Multiarch/Tuples">Multiarch Architecture Specifiers
|
||||
(Tuples)</ulink> list.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>~/.local/share/</filename></term>
|
||||
|
||||
<listitem><para>Resources shared between multiple packages,
|
||||
such as fonts or artwork. Usually, the precise location and
|
||||
format of files stored below this directory is subject to
|
||||
specifications that ensure interoperability. If an application
|
||||
finds <varname>$XDG_DATA_HOME</varname> set, it should use the
|
||||
directory specified in it instead of this
|
||||
directory.</para></listitem>
|
||||
<listitem><para>Resources shared between multiple packages, such as fonts or artwork. Usually, the
|
||||
precise location and format of files stored below this directory is subject to specifications that
|
||||
ensure interoperability. If an application finds <varname>$XDG_DATA_HOME</varname> set, it should use
|
||||
the directory specified in it instead of this directory.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><filename>~/.local/state/</filename></term>
|
||||
|
||||
<listitem><para>Application state. When a new user is created, this directory will be empty or not
|
||||
exist at all. Applications should fall back to defaults should their state in this directory be
|
||||
missing. If an application finds <varname>$XDG_STATE_HOME</varname> set, it should use the directory
|
||||
specified in it instead of this directory.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
|
||||
<constant>SD_PATH_USER_CONFIGURATION</constant>,
|
||||
<constant>SD_PATH_USER_RUNTIME</constant>,
|
||||
<constant>SD_PATH_USER_STATE_PRIVATE</constant>,
|
||||
<constant>SD_PATH_USER_STATE_CACHE</constant>,
|
||||
|
||||
<constant>SD_PATH_USER</constant>,
|
||||
|
||||
@@ -1378,7 +1378,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
|
||||
<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>
|
||||
@@ -1390,7 +1390,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
|
||||
<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>
|
||||
@@ -1447,7 +1447,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
|
||||
The second parameter will be interpreted as a destination path that will be created as a symlink to the directory.
|
||||
The symlinks will be created after any <varname>BindPaths=</varname> or <varname>TemporaryFileSystem=</varname>
|
||||
options have been set up, to make ephemeral symlinking possible. The same source can have multiple symlinks, by
|
||||
using the same first parameter, but a different second parameter.</para></listitem>
|
||||
using the same first parameter, but a different second parameter.</para>
|
||||
|
||||
<para>The directories defined by these options are always created under the standard paths used by systemd
|
||||
(<filename>/var/</filename>, <filename>/run/</filename>, <filename>/etc/</filename>, …). If the service needs
|
||||
@@ -1483,7 +1483,7 @@ StateDirectory=aaa/bbb ccc</programlisting>
|
||||
<programlisting>RuntimeDirectory=foo:bar foo:baz</programlisting>
|
||||
the service manager creates <filename index='false'>/run/foo</filename> (if it does not exist), and
|
||||
<filename index='false'>/run/bar</filename> plus <filename index='false'>/run/baz</filename> as symlinks to
|
||||
<filename index='false'>/run/foo</filename>.</para>
|
||||
<filename index='false'>/run/foo</filename>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
||||
@@ -2131,7 +2131,7 @@ Note that this setting is <emphasis>not</emphasis> influenced by the <varname>Us
|
||||
<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"/>
|
||||
@@ -2171,7 +2171,7 @@ Note that this setting is <emphasis>not</emphasis> influenced by the <varname>Us
|
||||
<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>
|
||||
|
||||
@@ -736,7 +736,7 @@ d /tmp/foo/bar - - - bmA:1h -</programlisting></para>
|
||||
<row>
|
||||
<entry><literal>%L</literal></entry>
|
||||
<entry>System or user log directory</entry>
|
||||
<entry>In <option>--user</option> mode, this is the same as <varname>$XDG_CONFIG_HOME</varname> with <filename index="false">/log</filename> appended, and <filename>/var/log</filename> otherwise.</entry>
|
||||
<entry>In <option>--user</option> mode, this is the same as <varname>$XDG_STATE_HOME</varname> with <filename index="false">/log</filename> appended, and <filename>/var/log</filename> otherwise.</entry>
|
||||
</row>
|
||||
<xi:include href="standard-specifiers.xml" xpointer="m"/>
|
||||
<xi:include href="standard-specifiers.xml" xpointer="M"/>
|
||||
@@ -744,7 +744,7 @@ d /tmp/foo/bar - - - bmA:1h -</programlisting></para>
|
||||
<row>
|
||||
<entry><literal>%S</literal></entry>
|
||||
<entry>System or user state directory</entry>
|
||||
<entry>In <option>--user</option> mode, this is the same as <varname>$XDG_CONFIG_HOME</varname>, and <filename>/var/lib</filename> otherwise.</entry>
|
||||
<entry>In <option>--user</option> mode, this is the same as <varname>$XDG_STATE_HOME</varname>, and <filename>/var/lib</filename> otherwise.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>%t</literal></entry>
|
||||
|
||||
@@ -2466,6 +2466,7 @@ static int create_many_symlinks(const char *root, const char *source, char **sym
|
||||
}
|
||||
|
||||
static int setup_exec_directory(
|
||||
Unit *u,
|
||||
const ExecContext *context,
|
||||
const ExecParameters *params,
|
||||
uid_t uid,
|
||||
@@ -2511,6 +2512,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_unit_notice(u, "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_unit_warning_errno(u, r, "Unable to detect whether unit configuration directory '%s' exists, assuming not: %m", q);
|
||||
|
||||
} else if (r < 0)
|
||||
log_unit_warning_errno(u, 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
|
||||
@@ -2559,20 +2615,19 @@ static int setup_exec_directory(
|
||||
goto fail;
|
||||
|
||||
if (is_dir(p, false) > 0 &&
|
||||
(laccess(pp, F_OK) < 0 && errno == ENOENT)) {
|
||||
(laccess(pp, F_OK) == -ENOENT)) {
|
||||
|
||||
/* Hmm, the private directory doesn't exist yet, but the normal one exists? If so, move
|
||||
* it over. Most likely the service has been upgraded from one that didn't use
|
||||
* DynamicUser=1, to one that does. */
|
||||
|
||||
log_info("Found pre-existing public %s= directory %s, migrating to %s.\n"
|
||||
"Apparently, service previously had DynamicUser= turned off, and has now turned it on.",
|
||||
exec_directory_type_to_string(type), p, pp);
|
||||
log_unit_info(u, "Found pre-existing public %s= directory %s, migrating to %s.\n"
|
||||
"Apparently, service previously had DynamicUser= turned off, and has now turned it on.",
|
||||
exec_directory_type_to_string(type), p, pp);
|
||||
|
||||
if (rename(p, pp) < 0) {
|
||||
r = -errno;
|
||||
r = RET_NERRNO(rename(p, pp));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* Otherwise, create the actual directory for the service */
|
||||
|
||||
@@ -2634,19 +2689,17 @@ static int setup_exec_directory(
|
||||
/* Hmm, apparently DynamicUser= was once turned on for this service,
|
||||
* but is no longer. Let's move the directory back up. */
|
||||
|
||||
log_info("Found pre-existing private %s= directory %s, migrating to %s.\n"
|
||||
"Apparently, service previously had DynamicUser= turned on, and has now turned it off.",
|
||||
exec_directory_type_to_string(type), q, p);
|
||||
log_unit_info(u, "Found pre-existing private %s= directory %s, migrating to %s.\n"
|
||||
"Apparently, service previously had DynamicUser= turned on, and has now turned it off.",
|
||||
exec_directory_type_to_string(type), q, p);
|
||||
|
||||
if (unlink(p) < 0) {
|
||||
r = -errno;
|
||||
r = RET_NERRNO(unlink(p));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (rename(q, p) < 0) {
|
||||
r = -errno;
|
||||
r = RET_NERRNO(rename(q, p));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2662,17 +2715,16 @@ static int setup_exec_directory(
|
||||
* as in the common case it is not written to by a service, and shall
|
||||
* not be writable. */
|
||||
|
||||
if (stat(p, &st) < 0) {
|
||||
r = -errno;
|
||||
r = RET_NERRNO(stat(p, &st));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Still complain if the access mode doesn't match */
|
||||
if (((st.st_mode ^ context->directories[type].mode) & 07777) != 0)
|
||||
log_warning("%s \'%s\' already exists but the mode is different. "
|
||||
"(File system: %o %sMode: %o)",
|
||||
exec_directory_type_to_string(type), context->directories[type].items[i].path,
|
||||
st.st_mode & 07777, exec_directory_type_to_string(type), context->directories[type].mode & 07777);
|
||||
log_unit_warning(u, "%s \'%s\' already exists but the mode is different. "
|
||||
"(File system: %o %sMode: %o)",
|
||||
exec_directory_type_to_string(type), context->directories[type].items[i].path,
|
||||
st.st_mode & 07777, exec_directory_type_to_string(type), context->directories[type].mode & 07777);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -2686,10 +2738,15 @@ static int setup_exec_directory(
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
/* Skip the rest (which deals with ownership) in user mode, since ownership changes are not
|
||||
* available to user code anyway */
|
||||
if (params->runtime_scope != RUNTIME_SCOPE_SYSTEM)
|
||||
continue;
|
||||
|
||||
/* Then, change the ownership of the whole tree, if necessary. When dynamic users are used we
|
||||
* drop the suid/sgid bits, since we really don't want SUID/SGID files for dynamic UID/GID
|
||||
* assignments to exist. */
|
||||
r = path_chown_recursive(pp ?: p, uid, gid, context->dynamic_user ? 01777 : 07777);
|
||||
r = path_chown_recursive(pp ?: p, uid, gid, context->dynamic_user ? 01777 : 07777, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
@@ -4066,7 +4123,7 @@ static int apply_mount_namespace(
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (MANAGER_IS_SYSTEM(u->manager)) {
|
||||
if (params->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
|
||||
propagate_dir = path_join("/run/systemd/propagate/", u->id);
|
||||
if (!propagate_dir)
|
||||
return -ENOMEM;
|
||||
@@ -4078,9 +4135,12 @@ static int apply_mount_namespace(
|
||||
extension_dir = strdup("/run/systemd/unit-extensions");
|
||||
if (!extension_dir)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
} else {
|
||||
assert(params->runtime_scope == RUNTIME_SCOPE_USER);
|
||||
|
||||
if (asprintf(&extension_dir, "/run/user/" UID_FMT "/systemd/unit-extensions", geteuid()) < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (root_image) {
|
||||
r = verity_settings_prepare(
|
||||
@@ -4707,14 +4767,17 @@ static void log_command_line(Unit *unit, const char *msg, const char *executable
|
||||
LOG_UNIT_INVOCATION_ID(unit));
|
||||
}
|
||||
|
||||
static bool exec_context_need_unprivileged_private_users(const ExecContext *context, const Manager *manager) {
|
||||
static bool exec_context_need_unprivileged_private_users(
|
||||
const ExecContext *context,
|
||||
const ExecParameters *params) {
|
||||
|
||||
assert(context);
|
||||
assert(manager);
|
||||
assert(params);
|
||||
|
||||
/* These options require PrivateUsers= when used in user units, as we need to be in a user namespace
|
||||
* to have permission to enable them when not running as root. If we have effective CAP_SYS_ADMIN
|
||||
* (system manager) then we have privileges and don't need this. */
|
||||
if (MANAGER_IS_SYSTEM(manager))
|
||||
if (params->runtime_scope != RUNTIME_SCOPE_USER)
|
||||
return false;
|
||||
|
||||
return context->private_users ||
|
||||
@@ -4924,7 +4987,7 @@ static int exec_child(
|
||||
* invocations themselves. Also note that while we'll only invoke NSS modules involved in user management they
|
||||
* might internally call into other NSS modules that are involved in hostname resolution, we never know. */
|
||||
if (setenv("SYSTEMD_ACTIVATION_UNIT", unit->id, true) != 0 ||
|
||||
setenv("SYSTEMD_ACTIVATION_SCOPE", runtime_scope_to_string(unit->manager->runtime_scope), true) != 0) {
|
||||
setenv("SYSTEMD_ACTIVATION_SCOPE", runtime_scope_to_string(params->runtime_scope), true) != 0) {
|
||||
*exit_status = EXIT_MEMORY;
|
||||
return log_unit_error_errno(unit, errno, "Failed to update environment: %m");
|
||||
}
|
||||
@@ -5236,7 +5299,7 @@ static int exec_child(
|
||||
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
|
||||
|
||||
for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
|
||||
r = setup_exec_directory(context, params, uid, gid, dt, needs_mount_namespace, exit_status);
|
||||
r = setup_exec_directory(unit, context, params, uid, gid, dt, needs_mount_namespace, exit_status);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(unit, r, "Failed to set up special execution directory in %s: %m", params->prefix[dt]);
|
||||
}
|
||||
@@ -5392,7 +5455,7 @@ static int exec_child(
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_sandboxing && exec_context_need_unprivileged_private_users(context, unit->manager)) {
|
||||
if (needs_sandboxing && exec_context_need_unprivileged_private_users(context, params)) {
|
||||
/* If we're unprivileged, set up the user namespace first to enable use of the other namespaces.
|
||||
* Users with CAP_SYS_ADMIN can set up user namespaces last because they will be able to
|
||||
* set up the all of the other namespaces (i.e. network, mount, UTS) without a user namespace. */
|
||||
|
||||
@@ -27,6 +27,7 @@ typedef struct Manager Manager;
|
||||
#include "numa-util.h"
|
||||
#include "open-file.h"
|
||||
#include "path-util.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "set.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@@ -140,7 +141,7 @@ struct ExecRuntime {
|
||||
};
|
||||
|
||||
typedef enum ExecDirectoryType {
|
||||
EXEC_DIRECTORY_RUNTIME = 0,
|
||||
EXEC_DIRECTORY_RUNTIME,
|
||||
EXEC_DIRECTORY_STATE,
|
||||
EXEC_DIRECTORY_CACHE,
|
||||
EXEC_DIRECTORY_LOGS,
|
||||
@@ -418,6 +419,8 @@ typedef enum ExecFlags {
|
||||
/* Parameters for a specific invocation of a command. This structure is put together right before a command is
|
||||
* executed. */
|
||||
struct ExecParameters {
|
||||
RuntimeScope runtime_scope;
|
||||
|
||||
char **environment;
|
||||
|
||||
int *fds;
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5309,6 +5309,8 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
p->runtime_scope = u->manager->runtime_scope;
|
||||
|
||||
p->confirm_spawn = manager_get_confirm_spawn(u->manager);
|
||||
p->cgroup_supported = u->manager->cgroup_supported;
|
||||
p->prefix = u->manager->prefix;
|
||||
|
||||
@@ -281,6 +281,9 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
||||
case SD_PATH_USER_STATE_CACHE:
|
||||
return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret);
|
||||
|
||||
case SD_PATH_USER_STATE_PRIVATE:
|
||||
return from_home_dir("XDG_STATE_HOME", ".local/state", buffer, ret);
|
||||
|
||||
case SD_PATH_USER:
|
||||
r = get_home_dir(buffer);
|
||||
if (r < 0)
|
||||
|
||||
@@ -20,6 +20,7 @@ static const char *arg_suffix = NULL;
|
||||
static const char* const path_table[_SD_PATH_MAX] = {
|
||||
[SD_PATH_TEMPORARY] = "temporary",
|
||||
[SD_PATH_TEMPORARY_LARGE] = "temporary-large",
|
||||
|
||||
[SD_PATH_SYSTEM_BINARIES] = "system-binaries",
|
||||
[SD_PATH_SYSTEM_INCLUDE] = "system-include",
|
||||
[SD_PATH_SYSTEM_LIBRARY_PRIVATE] = "system-library-private",
|
||||
@@ -27,6 +28,7 @@ static const char* const path_table[_SD_PATH_MAX] = {
|
||||
[SD_PATH_SYSTEM_SHARED] = "system-shared",
|
||||
[SD_PATH_SYSTEM_CONFIGURATION_FACTORY] = "system-configuration-factory",
|
||||
[SD_PATH_SYSTEM_STATE_FACTORY] = "system-state-factory",
|
||||
|
||||
[SD_PATH_SYSTEM_CONFIGURATION] = "system-configuration",
|
||||
[SD_PATH_SYSTEM_RUNTIME] = "system-runtime",
|
||||
[SD_PATH_SYSTEM_RUNTIME_LOGS] = "system-runtime-logs",
|
||||
@@ -34,13 +36,17 @@ static const char* const path_table[_SD_PATH_MAX] = {
|
||||
[SD_PATH_SYSTEM_STATE_LOGS] = "system-state-logs",
|
||||
[SD_PATH_SYSTEM_STATE_CACHE] = "system-state-cache",
|
||||
[SD_PATH_SYSTEM_STATE_SPOOL] = "system-state-spool",
|
||||
|
||||
[SD_PATH_USER_BINARIES] = "user-binaries",
|
||||
[SD_PATH_USER_LIBRARY_PRIVATE] = "user-library-private",
|
||||
[SD_PATH_USER_LIBRARY_ARCH] = "user-library-arch",
|
||||
[SD_PATH_USER_SHARED] = "user-shared",
|
||||
|
||||
[SD_PATH_USER_CONFIGURATION] = "user-configuration",
|
||||
[SD_PATH_USER_RUNTIME] = "user-runtime",
|
||||
[SD_PATH_USER_STATE_CACHE] = "user-state-cache",
|
||||
[SD_PATH_USER_STATE_PRIVATE] = "user-state-private",
|
||||
|
||||
[SD_PATH_USER] = "user",
|
||||
[SD_PATH_USER_DOCUMENTS] = "user-documents",
|
||||
[SD_PATH_USER_MUSIC] = "user-music",
|
||||
@@ -50,6 +56,7 @@ static const char* const path_table[_SD_PATH_MAX] = {
|
||||
[SD_PATH_USER_PUBLIC] = "user-public",
|
||||
[SD_PATH_USER_TEMPLATES] = "user-templates",
|
||||
[SD_PATH_USER_DESKTOP] = "user-desktop",
|
||||
|
||||
[SD_PATH_SEARCH_BINARIES] = "search-binaries",
|
||||
[SD_PATH_SEARCH_BINARIES_DEFAULT] = "search-binaries-default",
|
||||
[SD_PATH_SEARCH_LIBRARY_PRIVATE] = "search-library-private",
|
||||
@@ -60,18 +67,22 @@ static const char* const path_table[_SD_PATH_MAX] = {
|
||||
[SD_PATH_SEARCH_CONFIGURATION] = "search-configuration",
|
||||
|
||||
[SD_PATH_SYSTEMD_UTIL] = "systemd-util",
|
||||
|
||||
[SD_PATH_SYSTEMD_SYSTEM_UNIT] = "systemd-system-unit",
|
||||
[SD_PATH_SYSTEMD_SYSTEM_PRESET] = "systemd-system-preset",
|
||||
[SD_PATH_SYSTEMD_SYSTEM_CONF] = "systemd-system-conf",
|
||||
[SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT] = "systemd-search-system-unit",
|
||||
[SD_PATH_SYSTEMD_SYSTEM_GENERATOR] = "systemd-system-generator",
|
||||
[SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR] = "systemd-search-system-generator",
|
||||
[SD_PATH_SYSTEMD_USER_UNIT] = "systemd-user-unit",
|
||||
[SD_PATH_SYSTEMD_USER_PRESET] = "systemd-user-preset",
|
||||
[SD_PATH_SYSTEMD_USER_CONF] = "systemd-user-conf",
|
||||
|
||||
[SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT] = "systemd-search-system-unit",
|
||||
[SD_PATH_SYSTEMD_SEARCH_USER_UNIT] = "systemd-search-user-unit",
|
||||
[SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR] = "systemd-search-user-generator",
|
||||
|
||||
[SD_PATH_SYSTEMD_SYSTEM_GENERATOR] = "systemd-system-generator",
|
||||
[SD_PATH_SYSTEMD_USER_GENERATOR] = "systemd-user-generator",
|
||||
[SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR] = "systemd-search-system-generator",
|
||||
[SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR] = "systemd-search-user-generator",
|
||||
|
||||
[SD_PATH_SYSTEMD_SLEEP] = "systemd-sleep",
|
||||
[SD_PATH_SYSTEMD_SHUTDOWN] = "systemd-shutdown",
|
||||
|
||||
@@ -107,7 +118,7 @@ static int list_homes(void) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%s: %s\n", path_table[i], p);
|
||||
printf("%s%s:%s %s\n", ansi_highlight(), path_table[i], ansi_normal(), p);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
@@ -111,12 +111,15 @@ int path_chown_recursive(
|
||||
const char *path,
|
||||
uid_t uid,
|
||||
gid_t gid,
|
||||
mode_t mask) {
|
||||
mode_t mask,
|
||||
int flags) {
|
||||
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
struct stat st;
|
||||
|
||||
fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||
assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
|
||||
|
||||
fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOATIME|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
int path_chown_recursive(const char *path, uid_t uid, gid_t gid, mode_t mask);
|
||||
int path_chown_recursive(const char *path, uid_t uid, gid_t gid, mode_t mask, int flags);
|
||||
|
||||
int fd_chown_recursive(int fd, uid_t uid, gid_t gid, mode_t mask);
|
||||
|
||||
@@ -53,9 +53,10 @@ enum {
|
||||
SD_PATH_USER_SHARED,
|
||||
|
||||
/* User configuration, state, runtime ... */
|
||||
SD_PATH_USER_CONFIGURATION, /* takes both actual configuration (like /etc) and state (like /var/lib) */
|
||||
SD_PATH_USER_CONFIGURATION,
|
||||
SD_PATH_USER_RUNTIME,
|
||||
SD_PATH_USER_STATE_CACHE,
|
||||
/* → SD_PATH_USER_STATE_PRIVATE is added at the bottom */
|
||||
|
||||
/* User resources */
|
||||
SD_PATH_USER, /* $HOME itself */
|
||||
@@ -82,6 +83,7 @@ enum {
|
||||
* replaces "path" by "search"), since this API is about dirs/paths anyway, and contains "path"
|
||||
* already in the prefix */
|
||||
SD_PATH_SYSTEMD_UTIL,
|
||||
|
||||
SD_PATH_SYSTEMD_SYSTEM_UNIT,
|
||||
SD_PATH_SYSTEMD_SYSTEM_PRESET,
|
||||
SD_PATH_SYSTEMD_SYSTEM_CONF,
|
||||
@@ -116,6 +118,8 @@ enum {
|
||||
SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR,
|
||||
SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR,
|
||||
|
||||
SD_PATH_USER_STATE_PRIVATE,
|
||||
|
||||
_SD_PATH_MAX
|
||||
};
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ TEST(chown_recursive) {
|
||||
assert_se(st.st_gid == gid);
|
||||
assert_se(has_xattr(p));
|
||||
|
||||
assert_se(path_chown_recursive(t, 1, 2, 07777) >= 0);
|
||||
assert_se(path_chown_recursive(t, 1, 2, 07777, 0) >= 0);
|
||||
|
||||
p = strjoina(t, "/dir");
|
||||
assert_se(lstat(p, &st) >= 0);
|
||||
|
||||
@@ -255,9 +255,9 @@ static int specifier_directory(char specifier, const void *data, const char *roo
|
||||
|
||||
static const struct table_entry paths_user[] = {
|
||||
[DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME },
|
||||
[DIRECTORY_STATE] = { SD_PATH_USER_CONFIGURATION },
|
||||
[DIRECTORY_STATE] = { SD_PATH_USER_STATE_PRIVATE },
|
||||
[DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE },
|
||||
[DIRECTORY_LOGS] = { SD_PATH_USER_CONFIGURATION, "log" },
|
||||
[DIRECTORY_LOGS] = { SD_PATH_USER_STATE_PRIVATE, "log" },
|
||||
};
|
||||
|
||||
const struct table_entry *paths;
|
||||
|
||||
@@ -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'
|
||||
|
||||
60
test/units/testsuite-23.statedir.sh
Executable file
60
test/units/testsuite-23.statedir.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# shellcheck disable=SC2235
|
||||
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
|
||||
# ex: ts=8 sw=4 sts=4 et filetype=sh
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# Test unit configuration/state/cache/log/runtime data cleanup
|
||||
|
||||
export HOME=/root
|
||||
export XDG_RUNTIME_DIR=/run/user/0
|
||||
|
||||
systemctl start user@0.service
|
||||
|
||||
( ! test -d "$HOME"/.local/state/foo)
|
||||
( ! test -d "$HOME"/.config/foo)
|
||||
|
||||
systemd-run --user -p StateDirectory=foo --wait /bin/true
|
||||
|
||||
test -d "$HOME"/.local/state/foo
|
||||
( ! test -L "$HOME"/.local/state/foo)
|
||||
( ! test -d "$HOME"/.config/foo)
|
||||
|
||||
systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
|
||||
|
||||
test -d "$HOME"/.local/state/foo
|
||||
( ! test -L "$HOME"/.local/state/foo)
|
||||
test -d "$HOME"/.config/foo
|
||||
|
||||
rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
|
||||
|
||||
systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
|
||||
|
||||
test -d "$HOME"/.local/state/foo
|
||||
( ! test -L "$HOME"/.local/state/foo)
|
||||
test -d "$HOME"/.config/foo
|
||||
|
||||
rmdir "$HOME"/.local/state/foo "$HOME"/.config/foo
|
||||
|
||||
# Now trigger an update scenario by creating a config dir first
|
||||
systemd-run --user -p ConfigurationDirectory=foo --wait /bin/true
|
||||
|
||||
( ! test -d "$HOME"/.local/state/foo)
|
||||
test -d "$HOME"/.config/foo
|
||||
|
||||
# This will look like an update and result in a symlink
|
||||
systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
|
||||
|
||||
test -d "$HOME"/.local/state/foo
|
||||
test -L "$HOME"/.local/state/foo
|
||||
test -d "$HOME"/.config/foo
|
||||
|
||||
test "$(readlink "$HOME"/.local/state/foo)" = ../../.config/foo
|
||||
|
||||
# Check that this will work safely a second time
|
||||
systemd-run --user -p StateDirectory=foo -p ConfigurationDirectory=foo --wait /bin/true
|
||||
|
||||
rm "$HOME"/.local/state/foo
|
||||
rmdir "$HOME"/.config/foo
|
||||
Reference in New Issue
Block a user