You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
391 lines
17 KiB
391 lines
17 KiB
From 88d2ec272d3e503412e477d9abaebfe2ca199e78 Mon Sep 17 00:00:00 2001 |
|
From: Filipe Brandenburger <filbranden@google.com> |
|
Date: Sun, 6 Sep 2015 23:06:53 -0700 |
|
Subject: [PATCH] execute: Add new PassEnvironment= directive |
|
|
|
This directive allows passing environment variables from the system |
|
manager to spawned services. Variables in the system manager can be set |
|
inside a container by passing `--set-env=...` options to systemd-spawn. |
|
|
|
Tested with an on-disk test.service unit. Tested using multiple variable |
|
names on a single line, with an empty setting to clear the current list |
|
of variables, with non-existing variables. |
|
|
|
Tested using `systemd-run -p PassEnvironment=VARNAME` to confirm it |
|
works with transient units. |
|
|
|
Confirmed that `systemctl show` will display the PassEnvironment |
|
settings. |
|
|
|
Checked that man pages are generated correctly. |
|
|
|
No regressions in `make check`. |
|
|
|
(cherry picked from commit b4c14404b3e8753c41bac0b1d49369230a15c544) |
|
|
|
Resolves: #1426214 |
|
--- |
|
man/systemd.exec.xml | 27 +++++++++++ |
|
shell-completion/bash/systemd-run | 2 +- |
|
src/core/dbus-execute.c | 34 ++++++++++++++ |
|
src/core/execute.c | 44 +++++++++++++++++- |
|
src/core/execute.h | 1 + |
|
src/core/load-fragment-gperf.gperf.m4 | 1 + |
|
src/core/load-fragment.c | 65 +++++++++++++++++++++++++++ |
|
src/core/load-fragment.h | 1 + |
|
src/libsystemd/sd-bus/bus-util.c | 2 +- |
|
src/shared/env-util.c | 15 +++++++ |
|
src/shared/env-util.h | 1 + |
|
11 files changed, 189 insertions(+), 4 deletions(-) |
|
|
|
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml |
|
index c5199d3a54..aa5831cc2c 100644 |
|
--- a/man/systemd.exec.xml |
|
+++ b/man/systemd.exec.xml |
|
@@ -293,6 +293,33 @@ |
|
earlier setting.</para></listitem> |
|
</varlistentry> |
|
|
|
+ <varlistentry> |
|
+ <term><varname>PassEnvironment=</varname></term> |
|
+ |
|
+ <listitem><para>Pass environment variables from the systemd system |
|
+ manager to executed processes. Takes a space-separated list of variable |
|
+ names. This option may be specified more than once, in which case all |
|
+ listed variables will be set. If the empty string is assigned to this |
|
+ option, the list of environment variables is reset, all prior |
|
+ assignments have no effect. Variables that are not set in the system |
|
+ manager will not be passed and will be silently ignored.</para> |
|
+ |
|
+ <para>Variables passed from this setting are overridden by those passed |
|
+ from <varname>Environment=</varname> or |
|
+ <varname>EnvironmentFile=</varname>.</para> |
|
+ |
|
+ <para>Example: |
|
+ <programlisting>PassEnvironment=VAR1 VAR2 VAR3</programlisting> |
|
+ passes three variables <literal>VAR1</literal>, |
|
+ <literal>VAR2</literal>, <literal>VAR3</literal> |
|
+ with the values set for those variables in PID1.</para> |
|
+ |
|
+ <para> |
|
+ See |
|
+ <citerefentry project='man-pages'><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry> |
|
+ for details about environment variables.</para></listitem> |
|
+ </varlistentry> |
|
+ |
|
<varlistentry> |
|
<term><varname>StandardInput=</varname></term> |
|
<listitem><para>Controls where file descriptor 0 (STDIN) of |
|
diff --git a/shell-completion/bash/systemd-run b/shell-completion/bash/systemd-run |
|
index 5145cd3f29..36ffa46db5 100644 |
|
--- a/shell-completion/bash/systemd-run |
|
+++ b/shell-completion/bash/systemd-run |
|
@@ -73,7 +73,7 @@ _systemd_run() { |
|
KillSignal= LimitCPU= LimitFSIZE= LimitDATA= LimitSTACK= |
|
LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= |
|
LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE= |
|
- LimitNICE= LimitRTPRIO= LimitRTTIME=' |
|
+ LimitNICE= LimitRTPRIO= LimitRTTIME= PassEnvironment=' |
|
|
|
COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) |
|
return 0 |
|
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c |
|
index a9f7971cde..da8b10d2b3 100644 |
|
--- a/src/core/dbus-execute.c |
|
+++ b/src/core/dbus-execute.c |
|
@@ -597,6 +597,7 @@ const sd_bus_vtable bus_exec_vtable[] = { |
|
SD_BUS_VTABLE_START(0), |
|
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST), |
|
SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST), |
|
+ SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST), |
|
SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST), |
|
SD_BUS_PROPERTY("LimitCPU", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST), |
|
SD_BUS_PROPERTY("LimitFSIZE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST), |
|
@@ -963,6 +964,39 @@ int bus_exec_context_set_transient_property( |
|
|
|
return 1; |
|
|
|
+ } else if (streq(name, "PassEnvironment")) { |
|
+ |
|
+ _cleanup_strv_free_ char **l = NULL; |
|
+ |
|
+ r = sd_bus_message_read_strv(message, &l); |
|
+ if (r < 0) |
|
+ return r; |
|
+ |
|
+ if (!strv_env_name_is_valid(l)) |
|
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block."); |
|
+ |
|
+ if (mode != UNIT_CHECK) { |
|
+ if (strv_isempty(l)) { |
|
+ strv_free(c->pass_environment); |
|
+ c->pass_environment = NULL; |
|
+ unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=\n"); |
|
+ } else { |
|
+ _cleanup_free_ char *joined = NULL; |
|
+ |
|
+ r = strv_extend_strv(&c->pass_environment, l); |
|
+ if (r < 0) |
|
+ return r; |
|
+ |
|
+ joined = strv_join_quoted(c->pass_environment); |
|
+ if (!joined) |
|
+ return -ENOMEM; |
|
+ |
|
+ unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=%s\n", joined); |
|
+ } |
|
+ } |
|
+ |
|
+ return 1; |
|
+ |
|
} else if (rlimit_from_string(name) >= 0) { |
|
uint64_t rl; |
|
rlim_t x; |
|
diff --git a/src/core/execute.c b/src/core/execute.c |
|
index 863babd761..f72b20966f 100644 |
|
--- a/src/core/execute.c |
|
+++ b/src/core/execute.c |
|
@@ -1256,6 +1256,34 @@ static int build_environment( |
|
return 0; |
|
} |
|
|
|
+static int build_pass_environment(const ExecContext *c, char ***ret) { |
|
+ _cleanup_strv_free_ char **pass_env = NULL; |
|
+ size_t n_env = 0, n_bufsize = 0; |
|
+ char **i; |
|
+ |
|
+ STRV_FOREACH(i, c->pass_environment) { |
|
+ _cleanup_free_ char *x = NULL; |
|
+ char *v; |
|
+ |
|
+ v = getenv(*i); |
|
+ if (!v) |
|
+ continue; |
|
+ x = strjoin(*i, "=", v, NULL); |
|
+ if (!x) |
|
+ return -ENOMEM; |
|
+ if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2)) |
|
+ return -ENOMEM; |
|
+ pass_env[n_env++] = x; |
|
+ pass_env[n_env] = NULL; |
|
+ x = NULL; |
|
+ } |
|
+ |
|
+ *ret = pass_env; |
|
+ pass_env = NULL; |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
static bool exec_needs_mount_namespace( |
|
const ExecContext *context, |
|
const ExecParameters *params, |
|
@@ -1297,7 +1325,7 @@ static int exec_child( |
|
char **files_env, |
|
int *exit_status) { |
|
|
|
- _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; |
|
+ _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; |
|
_cleanup_free_ char *mac_selinux_context_net = NULL; |
|
const char *username = NULL, *home = NULL, *shell = NULL; |
|
unsigned n_dont_close = 0; |
|
@@ -1805,9 +1833,16 @@ static int exec_child( |
|
return r; |
|
} |
|
|
|
- final_env = strv_env_merge(5, |
|
+ r = build_pass_environment(context, &pass_env); |
|
+ if (r < 0) { |
|
+ *exit_status = EXIT_MEMORY; |
|
+ return r; |
|
+ } |
|
+ |
|
+ final_env = strv_env_merge(6, |
|
params->environment, |
|
our_env, |
|
+ pass_env, |
|
context->environment, |
|
files_env, |
|
pam_env, |
|
@@ -1965,6 +2000,8 @@ void exec_context_done(ExecContext *c) { |
|
|
|
strv_free(c->environment_files); |
|
c->environment_files = NULL; |
|
+ strv_free(c->pass_environment); |
|
+ c->pass_environment = NULL; |
|
|
|
for (l = 0; l < ELEMENTSOF(c->rlimit); l++) { |
|
free(c->rlimit[l]); |
|
@@ -2267,6 +2304,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { |
|
STRV_FOREACH(e, c->environment_files) |
|
fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e); |
|
|
|
+ STRV_FOREACH(e, c->pass_environment) |
|
+ fprintf(f, "%sPassEnvironment: %s\n", prefix, *e); |
|
+ |
|
if (c->nice_set) |
|
fprintf(f, |
|
"%sNice: %i\n", |
|
diff --git a/src/core/execute.h b/src/core/execute.h |
|
index 6e0c9faa75..cadd0e6b47 100644 |
|
--- a/src/core/execute.h |
|
+++ b/src/core/execute.h |
|
@@ -96,6 +96,7 @@ struct ExecRuntime { |
|
struct ExecContext { |
|
char **environment; |
|
char **environment_files; |
|
+ char **pass_environment; |
|
|
|
struct rlimit *rlimit[_RLIMIT_MAX]; |
|
char *working_directory, *root_directory; |
|
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 |
|
index c866a9cd02..b50fe45b47 100644 |
|
--- a/src/core/load-fragment-gperf.gperf.m4 |
|
+++ b/src/core/load-fragment-gperf.gperf.m4 |
|
@@ -33,6 +33,7 @@ $1.CPUAffinity, config_parse_exec_cpu_affinity, 0, |
|
$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask) |
|
$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment) |
|
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) |
|
+$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment) |
|
$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input) |
|
$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output) |
|
$1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error) |
|
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c |
|
index 3a3c456da5..c450fe2c72 100644 |
|
--- a/src/core/load-fragment.c |
|
+++ b/src/core/load-fragment.c |
|
@@ -2265,6 +2265,71 @@ int config_parse_environ(const char *unit, |
|
return 0; |
|
} |
|
|
|
+int config_parse_pass_environ(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) { |
|
+ |
|
+ const char *whole_rvalue = rvalue; |
|
+ char*** passenv = data; |
|
+ _cleanup_strv_free_ char **n = NULL; |
|
+ size_t nlen = 0, nbufsize = 0; |
|
+ int r; |
|
+ |
|
+ assert(filename); |
|
+ assert(lvalue); |
|
+ assert(rvalue); |
|
+ assert(data); |
|
+ |
|
+ if (isempty(rvalue)) { |
|
+ /* Empty assignment resets the list */ |
|
+ strv_free(*passenv); |
|
+ *passenv = NULL; |
|
+ return 0; |
|
+ } |
|
+ |
|
+ for (;;) { |
|
+ _cleanup_free_ char *word = NULL; |
|
+ |
|
+ r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); |
|
+ if (r == 0) |
|
+ break; |
|
+ if (r == -ENOMEM) |
|
+ return log_oom(); |
|
+ if (r < 0) { |
|
+ log_syntax(unit, LOG_ERR, filename, line, r, |
|
+ "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue); |
|
+ break; |
|
+ } |
|
+ |
|
+ if (!env_name_is_valid(word)) { |
|
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL, |
|
+ "Invalid environment name for %s, ignoring: %s", lvalue, word); |
|
+ continue; |
|
+ } |
|
+ |
|
+ if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) |
|
+ return log_oom(); |
|
+ n[nlen++] = word; |
|
+ n[nlen] = NULL; |
|
+ word = NULL; |
|
+ } |
|
+ |
|
+ if (n) { |
|
+ r = strv_extend_strv(passenv, n); |
|
+ if (r < 0) |
|
+ return r; |
|
+ } |
|
+ |
|
+ return 0; |
|
+} |
|
+ |
|
int config_parse_ip_tos(const char *unit, |
|
const char *filename, |
|
unsigned line, |
|
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h |
|
index 7c69e53699..9dd7d1bda0 100644 |
|
--- a/src/core/load-fragment.h |
|
+++ b/src/core/load-fragment.h |
|
@@ -85,6 +85,7 @@ int config_parse_syscall_filter(const char *unit, const char *filename, unsigned |
|
int config_parse_syscall_archs(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); |
|
int config_parse_syscall_errno(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); |
|
int config_parse_environ(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); |
|
+int config_parse_pass_environ(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); |
|
int config_parse_unit_slice(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); |
|
int config_parse_cpu_shares(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); |
|
int config_parse_memory_limit(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); |
|
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c |
|
index d357760870..ed0849b638 100644 |
|
--- a/src/libsystemd/sd-bus/bus-util.c |
|
+++ b/src/libsystemd/sd-bus/bus-util.c |
|
@@ -1535,7 +1535,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen |
|
|
|
r = sd_bus_message_append(m, "v", "i", i); |
|
|
|
- } else if (streq(field, "Environment")) { |
|
+ } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { |
|
|
|
r = sd_bus_message_append(m, "v", "as", 1, eq); |
|
|
|
diff --git a/src/shared/env-util.c b/src/shared/env-util.c |
|
index e8da4c978a..581d84a20b 100644 |
|
--- a/src/shared/env-util.c |
|
+++ b/src/shared/env-util.c |
|
@@ -136,6 +136,21 @@ bool strv_env_is_valid(char **e) { |
|
return true; |
|
} |
|
|
|
+bool strv_env_name_is_valid(char **l) { |
|
+ char **p, **q; |
|
+ |
|
+ STRV_FOREACH(p, l) { |
|
+ if (!env_name_is_valid(*p)) |
|
+ return false; |
|
+ |
|
+ STRV_FOREACH(q, p + 1) |
|
+ if (streq(*p, *q)) |
|
+ return false; |
|
+ } |
|
+ |
|
+ return true; |
|
+} |
|
+ |
|
bool strv_env_name_or_assignment_is_valid(char **l) { |
|
char **p, **q; |
|
|
|
diff --git a/src/shared/env-util.h b/src/shared/env-util.h |
|
index 252d87be1f..b8c2d81e4a 100644 |
|
--- a/src/shared/env-util.h |
|
+++ b/src/shared/env-util.h |
|
@@ -34,6 +34,7 @@ bool strv_env_is_valid(char **e); |
|
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) |
|
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); |
|
|
|
+bool strv_env_name_is_valid(char **l); |
|
bool strv_env_name_or_assignment_is_valid(char **l); |
|
|
|
char **strv_env_merge(unsigned n_lists, ...);
|
|
|