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 for all units. It should be both a way to pin units into memory as well as a
wait to retrieve their exit data. 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 * expose IO accounting data on the bus, show it in systemd-run --wait and log
about it in the resource log message 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 <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 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 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> <table>
<title>Automatic directory creation</title> <title>Automatic directory creation and environment variables</title>
<tgroup cols='3'> <tgroup cols='4'>
<thead> <thead>
<row> <row>
<entry>Locations</entry> <entry>Locations</entry>
<entry>for system</entry> <entry>for system</entry>
<entry>for users</entry> <entry>for users</entry>
<entry>Environment variable</entry>
</row> </row>
</thead> </thead>
<tbody> <tbody>
@ -830,26 +833,31 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
<entry><varname>RuntimeDirectory=</varname></entry> <entry><varname>RuntimeDirectory=</varname></entry>
<entry><filename>/run</filename></entry> <entry><filename>/run</filename></entry>
<entry><varname>$XDG_RUNTIME_DIR</varname></entry> <entry><varname>$XDG_RUNTIME_DIR</varname></entry>
<entry><varname>$RUNTIME_DIRECTORY</varname></entry>
</row> </row>
<row> <row>
<entry><varname>StateDirectory=</varname></entry> <entry><varname>StateDirectory=</varname></entry>
<entry><filename>/var/lib</filename></entry> <entry><filename>/var/lib</filename></entry>
<entry><varname>$XDG_CONFIG_HOME</varname></entry> <entry><varname>$XDG_CONFIG_HOME</varname></entry>
<entry><varname>$STATE_DIRECTORY</varname></entry>
</row> </row>
<row> <row>
<entry><varname>CacheDirectory=</varname></entry> <entry><varname>CacheDirectory=</varname></entry>
<entry><filename>/var/cache</filename></entry> <entry><filename>/var/cache</filename></entry>
<entry><varname>$XDG_CACHE_HOME</varname></entry> <entry><varname>$XDG_CACHE_HOME</varname></entry>
<entry><varname>$CACHE_DIRECTORY</varname></entry>
</row> </row>
<row> <row>
<entry><varname>LogsDirectory=</varname></entry> <entry><varname>LogsDirectory=</varname></entry>
<entry><filename>/var/log</filename></entry> <entry><filename>/var/log</filename></entry>
<entry><varname>$XDG_CONFIG_HOME</varname><filename>/log</filename></entry> <entry><varname>$XDG_CONFIG_HOME</varname><filename>/log</filename></entry>
<entry><varname>$LOGS_DIRECTORY</varname></entry>
</row> </row>
<row> <row>
<entry><varname>ConfigurationDirectory=</varname></entry> <entry><varname>ConfigurationDirectory=</varname></entry>
<entry><filename>/etc</filename></entry> <entry><filename>/etc</filename></entry>
<entry><varname>$XDG_CONFIG_HOME</varname></entry> <entry><varname>$XDG_CONFIG_HOME</varname></entry>
<entry><varname>$CONFIGURATION_DIRECTORY</varname></entry>
</row> </row>
</tbody> </tbody>
</tgroup> </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>. The directories
<filename>/run/foo/bar</filename> and <filename>/run/baz</filename> except <filename>/run/foo</filename> are <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 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>
<varlistentry> <varlistentry>

View File

@ -339,21 +339,22 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract
return (int) n; 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 *r, *e;
char **s; char **s;
size_t n, k; size_t n, k, m;
if (!separator) if (!separator)
separator = " "; separator = " ";
k = strlen(separator); k = strlen(separator);
m = strlen_ptr(prefix);
n = 0; n = 0;
STRV_FOREACH(s, l) { STRV_FOREACH(s, l) {
if (s != l) if (s != l)
n += k; n += k;
n += strlen(*s); n += m + strlen(*s);
} }
r = new(char, n+1); r = new(char, n+1);
@ -365,6 +366,9 @@ char *strv_join(char **l, const char *separator) {
if (s != l) if (s != l)
e = stpcpy(e, separator); e = stpcpy(e, separator);
if (prefix)
e = stpcpy(e, prefix);
e = stpcpy(e, *s); 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); 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_parse_nulstr(const char *s, size_t l);
char **strv_split_nulstr(const char *s); 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]); idle_pipe[3] = safe_close(idle_pipe[3]);
} }
static const char *exec_directory_env_name_to_string(ExecDirectoryType t);
static int build_environment( static int build_environment(
const Unit *u, const Unit *u,
const ExecContext *c, const ExecContext *c,
@ -1615,14 +1617,16 @@ static int build_environment(
char ***ret) { char ***ret) {
_cleanup_strv_free_ char **our_env = NULL; _cleanup_strv_free_ char **our_env = NULL;
ExecDirectoryType t;
size_t n_env = 0; size_t n_env = 0;
char *x; char *x;
assert(u); assert(u);
assert(c); assert(c);
assert(p);
assert(ret); assert(ret);
our_env = new0(char*, 14); our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX);
if (!our_env) if (!our_env)
return -ENOMEM; return -ENOMEM;
@ -1727,8 +1731,37 @@ static int build_environment(
our_env[n_env++] = x; 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; our_env[n_env++] = NULL;
assert(n_env <= 12); assert(n_env <= 14 + _EXEC_DIRECTORY_TYPE_MAX);
*ret = TAKE_PTR(our_env); *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); 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] = { static const char* const exec_keyring_mode_table[_EXEC_KEYRING_MODE_MAX] = {
[EXEC_KEYRING_INHERIT] = "inherit", [EXEC_KEYRING_INHERIT] = "inherit",
[EXEC_KEYRING_PRIVATE] = "private", [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", "", "/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) { static void test_mask_supported(void) {

View File

@ -144,6 +144,38 @@ static void test_strv_join(void) {
assert_se(streq(w, "")); 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) { static void test_strv_unquote(const char *quoted, char **list) {
_cleanup_strv_free_ char **s; _cleanup_strv_free_ char **s;
_cleanup_free_ char *j; _cleanup_free_ char *j;
@ -252,18 +284,18 @@ static void test_strv_split_nulstr(void) {
static void test_strv_parse_nulstr(void) { static void test_strv_parse_nulstr(void) {
_cleanup_strv_free_ char **l = NULL; _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); l = strv_parse_nulstr(nulstr, sizeof(nulstr)-1);
assert_se(l); assert_se(l);
puts("Parse nulstr:"); puts("Parse nulstr:");
strv_print(l); strv_print(l);
assert_se(streq(l[0], "fuck")); assert_se(streq(l[0], "hoge"));
assert_se(streq(l[1], "fuck2")); assert_se(streq(l[1], "hoge2"));
assert_se(streq(l[2], "fuck3")); assert_se(streq(l[2], "hoge3"));
assert_se(streq(l[3], "")); 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[5], ""));
assert_se(streq(l[6], "xxx")); assert_se(streq(l[6], "xxx"));
} }
@ -726,6 +758,7 @@ int main(int argc, char *argv[]) {
test_strv_find_prefix(); test_strv_find_prefix();
test_strv_find_startswith(); test_strv_find_startswith();
test_strv_join(); test_strv_join();
test_strv_join_prefix();
test_strv_unquote(" foo=bar \"waldo\" zzz ", STRV_MAKE("foo=bar", "waldo", "zzz")); test_strv_unquote(" foo=bar \"waldo\" zzz ", STRV_MAKE("foo=bar", "waldo", "zzz"));
test_strv_unquote("", STRV_MAKE_EMPTY); 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=test -d /var/lib/test-dynamicuser-migrate2/hoge
ExecStart=touch /var/lib/test-dynamicuser-migrate/yay ExecStart=touch /var/lib/test-dynamicuser-migrate/yay
ExecStart=touch /var/lib/test-dynamicuser-migrate2/hoge/yayyay 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 Type=oneshot
DynamicUser=no 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/test-dynamicuser-migrate2/hoge/yayyay
ExecStart=touch /var/lib/private/test-dynamicuser-migrate/yay ExecStart=touch /var/lib/private/test-dynamicuser-migrate/yay
ExecStart=touch /var/lib/private/test-dynamicuser-migrate2/hoge/yayyay 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 Type=oneshot
DynamicUser=yes 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/quux/pief/yayyay
ExecStart=test -f /var/lib/private/waldo/yay ExecStart=test -f /var/lib/private/waldo/yay
ExecStart=test -f /var/lib/private/quux/pief/yayyay 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 # 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' 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] [Service]
ExecStart=/bin/sh -x -c 'mode=$$(stat -c %%a %t/test-exec_runtimedirectory-mode); test "$$mode" = "750"' 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 Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory-mode RuntimeDirectory=test-exec_runtimedirectory-mode
RuntimeDirectoryMode=0750 RuntimeDirectoryMode=0750

View File

@ -4,6 +4,7 @@ Description=Test for RuntimeDirectory
[Service] [Service]
ExecStart=/bin/sh -x -c 'test -d %t/test-exec_runtimedirectory' 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 -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 Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory RuntimeDirectory=test-exec_runtimedirectory
RuntimeDirectory=./test-exec_runtimedirectory2///./hogehoge/. RuntimeDirectory=./test-exec_runtimedirectory2///./hogehoge/.