Merge pull request #10059 from yuwata/env-exec-directory

core: introduce $RUNTIME_DIRECTORY= or friends
This commit is contained in:
Lennart Poettering 2018-09-25 12:34:30 +02:00 committed by GitHub
commit 7c428bb5d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 118 additions and 25 deletions

9
TODO
View File

@ -249,15 +249,6 @@ Features:
for all units. It should be both a way to pin units into memory as well as a
wait to retrieve their exit data.
* maybe set a new set of env vars for services, based on RuntimeDirectory=,
StateDirectory=, LogsDirectory=, CacheDirectory= and ConfigurationDirectory=
automatically. For example, there could be $RUNTIME_DIRECTORY,
$STATE_DIRECTORY, $LOGS_DIRECTORY=, $CACHE_DIRECTORY and
$CONFIGURATION_DIRECTORY or so. This could be useful to write services that
can adapt to varying directories for these purposes. Special care has to be
taken if multiple dirs are configured. Maybe avoid setting the env vars in
that case?
* expose IO accounting data on the bus, show it in systemd-run --wait and log
about it in the resource log message

View File

@ -814,15 +814,18 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<listitem><para>These options take a whitespace-separated list of directory names. The specified directory
names must be relative, and may not include <literal>..</literal>. If set, one or more
directories by the specified names will be created (including their parents) below the locations
defined in the following table, when the unit is started.</para>
defined in the following table, when the unit is started. Also, the corresponding environment variable
is defined with the full path of directories. If multiple directories are set, then int the environment variable
the paths are concatenated with colon (<literal>:</literal>).</para>
<table>
<title>Automatic directory creation</title>
<tgroup cols='3'>
<title>Automatic directory creation and environment variables</title>
<tgroup cols='4'>
<thead>
<row>
<entry>Locations</entry>
<entry>for system</entry>
<entry>for users</entry>
<entry>Environment variable</entry>
</row>
</thead>
<tbody>
@ -830,26 +833,31 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<entry><varname>RuntimeDirectory=</varname></entry>
<entry><filename>/run</filename></entry>
<entry><varname>$XDG_RUNTIME_DIR</varname></entry>
<entry><varname>$RUNTIME_DIRECTORY</varname></entry>
</row>
<row>
<entry><varname>StateDirectory=</varname></entry>
<entry><filename>/var/lib</filename></entry>
<entry><varname>$XDG_CONFIG_HOME</varname></entry>
<entry><varname>$STATE_DIRECTORY</varname></entry>
</row>
<row>
<entry><varname>CacheDirectory=</varname></entry>
<entry><filename>/var/cache</filename></entry>
<entry><varname>$XDG_CACHE_HOME</varname></entry>
<entry><varname>$CACHE_DIRECTORY</varname></entry>
</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>$LOGS_DIRECTORY</varname></entry>
</row>
<row>
<entry><varname>ConfigurationDirectory=</varname></entry>
<entry><filename>/etc</filename></entry>
<entry><varname>$XDG_CONFIG_HOME</varname></entry>
<entry><varname>$CONFIGURATION_DIRECTORY</varname></entry>
</row>
</tbody>
</tgroup>
@ -899,7 +907,13 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<filename>/run/foo/bar</filename>, and <filename>/run/baz</filename>. The directories
<filename>/run/foo/bar</filename> and <filename>/run/baz</filename> except <filename>/run/foo</filename> are
owned by the user and group specified in <varname>User=</varname> and <varname>Group=</varname>, and removed
when the service is stopped.</para></listitem>
when the service is stopped.</para>
<para>Example: if a system service unit has the following,
<programlisting>RuntimeDirectory=foo/bar
StateDirectory=aaa/bbb ccc</programlisting>
then the environment variable <literal>RUNTIME_DIRECTORY</literal> is set with <literal>/run/foo/bar</literal>, and
<literal>STATE_DIRECTORY</literal> is set with <literal>/var/lib/aaa/bbb:/var/lib/ccc</literal>.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -339,21 +339,22 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
return (int) n;
}
char *strv_join(char **l, const char *separator) {
char *strv_join_prefix(char **l, const char *separator, const char *prefix) {
char *r, *e;
char **s;
size_t n, k;
size_t n, k, m;
if (!separator)
separator = " ";
k = strlen(separator);
m = strlen_ptr(prefix);
n = 0;
STRV_FOREACH(s, l) {
if (s != l)
n += k;
n += strlen(*s);
n += m + strlen(*s);
}
r = new(char, n+1);
@ -365,6 +366,9 @@ char *strv_join(char **l, const char *separator) {
if (s != l)
e = stpcpy(e, separator);
if (prefix)
e = stpcpy(e, prefix);
e = stpcpy(e, *s);
}

View File

@ -71,7 +71,10 @@ char **strv_split_newlines(const char *s);
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
char *strv_join(char **l, const char *separator);
char *strv_join_prefix(char **l, const char *separator, const char *prefix);
static inline char *strv_join(char **l, const char *separator) {
return strv_join_prefix(l, separator, NULL);
}
char **strv_parse_nulstr(const char *s, size_t l);
char **strv_split_nulstr(const char *s);

View File

@ -1602,6 +1602,8 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
idle_pipe[3] = safe_close(idle_pipe[3]);
}
static const char *exec_directory_env_name_to_string(ExecDirectoryType t);
static int build_environment(
const Unit *u,
const ExecContext *c,
@ -1615,14 +1617,16 @@ static int build_environment(
char ***ret) {
_cleanup_strv_free_ char **our_env = NULL;
ExecDirectoryType t;
size_t n_env = 0;
char *x;
assert(u);
assert(c);
assert(p);
assert(ret);
our_env = new0(char*, 14);
our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX);
if (!our_env)
return -ENOMEM;
@ -1727,8 +1731,37 @@ static int build_environment(
our_env[n_env++] = x;
}
for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
_cleanup_free_ char *pre = NULL, *joined = NULL;
const char *n;
if (!p->prefix[t])
continue;
if (strv_isempty(c->directories[t].paths))
continue;
n = exec_directory_env_name_to_string(t);
if (!n)
continue;
pre = strjoin(p->prefix[t], "/");
if (!pre)
return -ENOMEM;
joined = strv_join_prefix(c->directories[t].paths, ":", pre);
if (!joined)
return -ENOMEM;
x = strjoin(n, "=", joined);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
our_env[n_env++] = NULL;
assert(n_env <= 12);
assert(n_env <= 14 + _EXEC_DIRECTORY_TYPE_MAX);
*ret = TAKE_PTR(our_env);
@ -5121,6 +5154,16 @@ static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
static const char* const exec_directory_env_name_table[_EXEC_DIRECTORY_TYPE_MAX] = {
[EXEC_DIRECTORY_RUNTIME] = "RUNTIME_DIRECTORY",
[EXEC_DIRECTORY_STATE] = "STATE_DIRECTORY",
[EXEC_DIRECTORY_CACHE] = "CACHE_DIRECTORY",
[EXEC_DIRECTORY_LOGS] = "LOGS_DIRECTORY",
[EXEC_DIRECTORY_CONFIGURATION] = "CONFIGURATION_DIRECTORY",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(exec_directory_env_name, ExecDirectoryType);
static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
[EXEC_KEYRING_INHERIT] = "inherit",
[EXEC_KEYRING_PRIVATE] = "private",

View File

@ -297,7 +297,7 @@ static void test_shift_path(void) {
test_shift_path_one("/foobar/waldo", "/", "/foobar/waldo");
test_shift_path_one("/foobar/waldo", "", "/foobar/waldo");
test_shift_path_one("/foobar/waldo", "/foobar", "/waldo");
test_shift_path_one("/foobar/waldo", "/fuckfuck", "/foobar/waldo");
test_shift_path_one("/foobar/waldo", "/hogehoge", "/foobar/waldo");
}
static void test_mask_supported(void) {

View File

@ -144,6 +144,38 @@ static void test_strv_join(void) {
assert_se(streq(w, ""));
}
static void test_strv_join_prefix(void) {
_cleanup_free_ char *p = NULL, *q = NULL, *r = NULL, *s = NULL, *t = NULL, *v = NULL, *w = NULL;
p = strv_join_prefix((char **)input_table_multiple, ", ", "foo");
assert_se(p);
assert_se(streq(p, "fooone, footwo, foothree"));
q = strv_join_prefix((char **)input_table_multiple, ";", "foo");
assert_se(q);
assert_se(streq(q, "fooone;footwo;foothree"));
r = strv_join_prefix((char **)input_table_multiple, NULL, "foo");
assert_se(r);
assert_se(streq(r, "fooone footwo foothree"));
s = strv_join_prefix((char **)input_table_one, ", ", "foo");
assert_se(s);
assert_se(streq(s, "fooone"));
t = strv_join_prefix((char **)input_table_none, ", ", "foo");
assert_se(t);
assert_se(streq(t, ""));
v = strv_join_prefix((char **)input_table_two_empties, ", ", "foo");
assert_se(v);
assert_se(streq(v, "foo, foo"));
w = strv_join_prefix((char **)input_table_one_empty, ", ", "foo");
assert_se(w);
assert_se(streq(w, "foo"));
}
static void test_strv_unquote(const char *quoted, char **list) {
_cleanup_strv_free_ char **s;
_cleanup_free_ char *j;
@ -252,18 +284,18 @@ static void test_strv_split_nulstr(void) {
static void test_strv_parse_nulstr(void) {
_cleanup_strv_free_ char **l = NULL;
const char nulstr[] = "fuck\0fuck2\0fuck3\0\0fuck5\0\0xxx";
const char nulstr[] = "hoge\0hoge2\0hoge3\0\0hoge5\0\0xxx";
l = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
assert_se(l);
puts("Parse nulstr:");
strv_print(l);
assert_se(streq(l[0], "fuck"));
assert_se(streq(l[1], "fuck2"));
assert_se(streq(l[2], "fuck3"));
assert_se(streq(l[0], "hoge"));
assert_se(streq(l[1], "hoge2"));
assert_se(streq(l[2], "hoge3"));
assert_se(streq(l[3], ""));
assert_se(streq(l[4], "fuck5"));
assert_se(streq(l[4], "hoge5"));
assert_se(streq(l[5], ""));
assert_se(streq(l[6], "xxx"));
}
@ -726,6 +758,7 @@ int main(int argc, char *argv[]) {
test_strv_find_prefix();
test_strv_find_startswith();
test_strv_join();
test_strv_join_prefix();
test_strv_unquote(" foo=bar \"waldo\" zzz ", STRV_MAKE("foo=bar", "waldo", "zzz"));
test_strv_unquote("", STRV_MAKE_EMPTY);

View File

@ -10,6 +10,7 @@ ExecStart=test -d /var/lib/test-dynamicuser-migrate
ExecStart=test -d /var/lib/test-dynamicuser-migrate2/hoge
ExecStart=touch /var/lib/test-dynamicuser-migrate/yay
ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay
ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
Type=oneshot
DynamicUser=no

View File

@ -18,6 +18,7 @@ ExecStart=touch /var/lib/test-dynamicuser-migrate/yay
ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay
ExecStart=touch /var/lib/private/test-dynamicuser-migrate/yay
ExecStart=touch /var/lib/private/test-dynamicuser-migrate2/hoge/yayyay
ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/test-dynamicuser-migrate:%S/test-dynamicuser-migrate2/hoge"'
Type=oneshot
DynamicUser=yes

View File

@ -10,6 +10,7 @@ ExecStart=test -f /var/lib/waldo/yay
ExecStart=test -f /var/lib/quux/pief/yayyay
ExecStart=test -f /var/lib/private/waldo/yay
ExecStart=test -f /var/lib/private/quux/pief/yayyay
ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/waldo:%S/quux/pief"'
# Make sure that /var/lib/private/waldo is really the only writable directory besides the obvious candidates
ExecStart=sh -x -c 'test $$(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o -path /sys/fs/bpf \) -prune -o -type d -writable -print 2>/dev/null | sort -u | tr -d '\\\\n') = /var/lib/private/quux/pief/var/lib/private/waldo'

View File

@ -3,6 +3,7 @@ Description=Test for RuntimeDirectoryMode
[Service]
ExecStart=/bin/sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"'
ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory-mode"'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory-mode
RuntimeDirectoryMode=0750

View File

@ -4,6 +4,7 @@ Description=Test for RuntimeDirectory
[Service]
ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory'
ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory2/hogehoge'
ExecStart=/bin/sh -x -c 'test "$$RUNTIME_DIRECTORY" = "%t/test-exec_runtimedirectory:%t/test-exec_runtimedirectory2/hogehoge"'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory
RuntimeDirectory=./test-exec_runtimedirectory2///./hogehoge/.