naccyde / rpms / systemd

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