1
0
mirror of https://github.com/systemd/systemd.git synced 2025-02-03 17:47:28 +03:00

Merge pull request #21908 from yonran/environmentfile-docs

man: clarify Environmentfile format
This commit is contained in:
Yu Watanabe 2022-01-24 00:22:50 +09:00 committed by GitHub
commit bb995f747a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 166 additions and 64 deletions

View File

@ -2513,18 +2513,39 @@ SystemCallErrorNumber=EPERM</programlisting>
<varlistentry>
<term><varname>EnvironmentFile=</varname></term>
<listitem><para>Similar to <varname>Environment=</varname> but reads the environment variables from a text
file. The text file should contain new-line-separated variable assignments. Empty lines, lines without an
<literal>=</literal> separator, or lines starting with ; or # will be ignored, which may be used for
commenting. A line ending with a backslash will be concatenated with the following one, allowing multiline
variable definitions. The parser strips leading and trailing whitespace from the values of assignments, unless
you use double quotes (").</para>
<listitem><para>Similar to <varname>Environment=</varname> but reads the environment variables from a text file.
The text file should contain newline-separated variable assignments. Empty lines, lines without an
<literal>=</literal> separator, or lines starting with <literal>;</literal> or <literal>#</literal> will be
ignored, which may be used for commenting. The file must be UTF-8 encoded. Valid characters are <ulink
url="https://www.unicode.org/glossary/#unicode_scalar_value">unicode scalar values</ulink> other than <ulink
url="https://www.unicode.org/glossary/#noncharacter">noncharacters</ulink>, U+0000 NUL, and U+FEFF <ulink
url="https://www.unicode.org/glossary/#byte_order_mark">byte order mark</ulink>. Control codes other than NUL
are allowed.</para>
<para><ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C escapes</ulink>
are supported, but not
<ulink url="https://en.wikipedia.org/wiki/Control_character#In_ASCII">most control characters</ulink>.
<literal>\t</literal> and <literal>\n</literal> can be used to insert tabs and newlines within
<varname>EnvironmentFile=</varname>.</para>
<para>In the file, an unquoted value after the <literal>=</literal> is parsed with the same backslash-escape
rules as <ulink
url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01">unquoted
text</ulink> in a POSIX shell, but unlike in a shell, interior whitespace is preserved and quotes after the
first non-whitespace character are preserved. Leading and trailing whitespace (space, tab, carriage return) is
discarded, but interior whitespace within the line is preserved verbatim. A line ending with a backslash will be
continued to the following one, with the newline itself discarded. A backslash
<literal>\</literal> followed by any character other than newline will preserve the following character, so that
<literal>\\</literal> will become the value <literal>\</literal>.</para>
<para>In the file, a <literal>'</literal>-quoted value after the <literal>=</literal> can span multiple lines
and contain any character verbatim other than single quote, like <ulink
url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_02">single-quoted
text</ulink> in a POSIX shell. No backslash-escape sequences are recognized. Leading and trailing whitespace
outside of the single quotes is discarded.</para>
<para>In the file, a <literal>"</literal>-quoted value after the <literal>=</literal> can span multiple lines,
and the same escape sequences are recognized as in <ulink
url="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_03">double-quoted
text</ulink> of a POSIX shell. Backslash (<literal>\</literal>) followed by any of <literal>"\`$</literal> will
preserve that character. A backslash followed by newline is a line continuation, and the newline itself is
discarded. A backslash followed by any other character is ignored; both the backslash and the following
character are preserved verbatim. Leading and trailing whitespace outside of the double quotes is
discarded.</para>
<para>The argument passed should be an absolute filename or wildcard expression, optionally prefixed with
<literal>-</literal>, which indicates that if the file does not exist, it will not be read and no error or
@ -2557,12 +2578,6 @@ SystemCallErrorNumber=EPERM</programlisting>
<para>Variables set for invoked processes due to this setting are subject to being overridden by those
configured with <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>.</para>
<para><ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C escapes</ulink>
are supported, but not
<ulink url="https://en.wikipedia.org/wiki/Control_character#In_ASCII">most control characters</ulink>.
<literal>\t</literal> and <literal>\n</literal> can be used to insert tabs and newlines within
<varname>EnvironmentFile=</varname>.</para>
<para>Example:
<programlisting>PassEnvironment=VAR1 VAR2 VAR3</programlisting>
passes three variables <literal>VAR1</literal>,

View File

@ -5501,20 +5501,18 @@ static int exec_context_named_iofds(
return targets == 0 ? 0 : -ENOENT;
}
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l) {
char **i, **r = NULL;
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) {
_cleanup_strv_free_ char **v = NULL;
char **i;
int r;
assert(c);
assert(l);
assert(ret);
STRV_FOREACH(i, c->environment_files) {
char *fn;
int k;
bool ignore = false;
char **p;
_cleanup_globfree_ glob_t pglob = {};
fn = *i;
bool ignore = false;
char *fn = *i;
if (fn[0] == '-') {
ignore = true;
@ -5524,33 +5522,30 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
if (!path_is_absolute(fn)) {
if (ignore)
continue;
strv_free(r);
return -EINVAL;
}
/* Filename supports globbing, take all matching files */
k = safe_glob(fn, 0, &pglob);
if (k < 0) {
r = safe_glob(fn, 0, &pglob);
if (r < 0) {
if (ignore)
continue;
strv_free(r);
return k;
return r;
}
/* When we don't match anything, -ENOENT should be returned */
assert(pglob.gl_pathc > 0);
for (unsigned n = 0; n < pglob.gl_pathc; n++) {
k = load_env_file(NULL, pglob.gl_pathv[n], &p);
if (k < 0) {
_cleanup_strv_free_ char **p = NULL;
r = load_env_file(NULL, pglob.gl_pathv[n], &p);
if (r < 0) {
if (ignore)
continue;
strv_free(r);
return k;
return r;
}
/* Log invalid environment variables with filename */
if (p) {
InvalidEnvInfo info = {
@ -5561,23 +5556,19 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
p = strv_env_clean_with_callback(p, invalid_env, &info);
}
if (!r)
r = p;
if (!v)
v = TAKE_PTR(p);
else {
char **m;
m = strv_env_merge(r, p);
strv_free(r);
strv_free(p);
char **m = strv_env_merge(v, p);
if (!m)
return -ENOMEM;
r = m;
strv_free_and_replace(v, m);
}
}
}
*l = r;
*ret = TAKE_PTR(v);
return 0;
}

View File

@ -13,11 +13,11 @@
"a=a\n" \
"b=b\\\n" \
"c\n" \
"d=d\\\n" \
"e\\\n" \
"f\n" \
"d= d\\\n" \
"e \\\n" \
"f \n" \
"g=g\\ \n" \
"h=h\n" \
"h= ąęół\\ śćńźżµ \n" \
"i=i\\"
#define env_file_2 \
@ -26,22 +26,34 @@
#define env_file_3 \
"#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
"#--nouser-config \\\n" \
"normal=line"
"normal=line \\\n" \
";normal=ignored \\\n" \
"normal_ignored \\\n" \
"normal ignored \\\n"
#define env_file_4 \
"# Generated\n" \
"\n" \
"HWMON_MODULES=\"coretemp f71882fg\"\n" \
"\n" \
"# For compatibility reasons\n" \
"\n" \
"MODULE_0=coretemp\n" \
"MODULE_1=f71882fg"
#define env_file_4 \
"# Generated\n" \
"\n" \
"HWMON_MODULES=\"coretemp f71882fg\"\n" \
"\n" \
"# For compatibility reasons\n" \
"\n" \
"MODULE_0=coretemp\n" \
"MODULE_1=f71882fg"
#define env_file_5 \
"a=\n" \
"a=\n" \
"b="
#define env_file_6 \
"a=\\ \\n \\t \\x \\y \\' \n" \
"b= \\$' \n" \
"c= ' \\n\\t\\$\\`\\\\\n" \
"' \n" \
"d= \" \\n\\t\\$\\`\\\\\n" \
"\" \n"
TEST(load_env_file_1) {
_cleanup_strv_free_ char **data = NULL;
int r;
@ -57,9 +69,9 @@ TEST(load_env_file_1) {
assert_se(r == 0);
assert_se(streq(data[0], "a=a"));
assert_se(streq(data[1], "b=bc"));
assert_se(streq(data[2], "d=def"));
assert_se(streq(data[2], "d=de f"));
assert_se(streq(data[3], "g=g "));
assert_se(streq(data[4], "h=h"));
assert_se(streq(data[4], "h=ąęół śćńźżµ"));
assert_se(streq(data[5], "i=i"));
assert_se(data[6] == NULL);
}
@ -133,6 +145,26 @@ TEST(load_env_file_5) {
assert_se(data[2] == NULL);
}
TEST(load_env_file_6) {
_cleanup_strv_free_ char **data = NULL;
int r;
_cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX";
_cleanup_close_ int fd;
fd = mkostemp_safe(name);
assert_se(fd >= 0);
assert_se(write(fd, env_file_6, strlen(env_file_6)) == strlen(env_file_6));
r = load_env_file(NULL, name, &data);
assert_se(r == 0);
assert_se(streq(data[0], "a= n t x y '"));
assert_se(streq(data[1], "b=$'"));
assert_se(streq(data[2], "c= \\n\\t\\$\\`\\\\\n"));
assert_se(streq(data[3], "d= \\n\\t$`\\\n"));
assert_se(data[4] == NULL);
}
TEST(write_and_load_env_file) {
const char *v;

View File

@ -769,6 +769,70 @@ TEST(config_parse_pass_environ) {
assert_se(streq(passenv[0], "normal_name"));
}
TEST(config_parse_unit_env_file) {
/* int config_parse_unit_env_file(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) */
_cleanup_(manager_freep) Manager *m = NULL;
Unit *u;
_cleanup_strv_free_ char **files = NULL;
int r;
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
if (manager_errno_skip_test(r)) {
log_notice_errno(r, "Skipping test: manager_new: %m");
return;
}
assert_se(r >= 0);
assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
assert_se(u = unit_new(m, sizeof(Service)));
assert_se(unit_add_name(u, "foobar.service") == 0);
r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
"EnvironmentFile", 0, "not-absolute",
&files, u);
assert_se(r == 0);
assert_se(strv_length(files) == 0);
r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
"EnvironmentFile", 0, "/absolute1",
&files, u);
assert_se(r == 0);
assert_se(strv_length(files) == 1);
r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
"EnvironmentFile", 0, "/absolute2",
&files, u);
assert_se(r == 0);
assert_se(strv_length(files) == 2);
assert_se(streq(files[0], "/absolute1"));
assert_se(streq(files[1], "/absolute2"));
r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
"EnvironmentFile", 0, "",
&files, u);
assert_se(r == 0);
assert_se(strv_isempty(files));
r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
"EnvironmentFile", 0, "/path/%n.conf",
&files, u);
assert_se(r == 0);
assert_se(strv_length(files) == 1);
assert_se(streq(files[0], "/path/foobar.service.conf"));
}
TEST(unit_dump_config_items) {
unit_dump_config_items(stdout);
}