1
0
mirror of https://github.com/systemd/systemd synced 2026-03-25 08:14:54 +01:00

Compare commits

..

No commits in common. "23e026de254df54119e6565bf7b638b9b83ec4fd" and "d178203d8e2dae9bba48790a93031f2e776a2f83" have entirely different histories.

27 changed files with 290 additions and 318 deletions

View File

@ -288,16 +288,12 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>--setenv=</option><replaceable>VARIABLE</replaceable>[=<replaceable>VALUE</replaceable>]</term> <term><option>--setenv=</option><replaceable>VARIABLE</replaceable>=<replaceable>VALUE</replaceable></term>
<listitem><para>Takes an environment variable assignment to set for all user processes. May be used <listitem><para>Takes an environment variable assignment to set for all user processes. Note that a
multiple times to set multiple environment variables. When <literal>=</literal> and number of other settings also result in environment variables to be set for the user, including
<replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the <option>--email=</option>, <option>--timezone=</option> and <option>--language=</option>. May be used
program environment will be used.</para> multiple times to set multiple environment variables.</para></listitem>
<para>Note that a number of other settings also result in environment variables to be set for the
user, including <option>--email=</option>, <option>--timezone=</option> and
<option>--language=</option>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -712,16 +712,14 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term> <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term> <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<listitem><para>When used with the <command>shell</command> command, sets an environment variable for <listitem><para>When used with the <command>shell</command> command, sets an environment
the executed shell. This option may be used more than once to set multiple variables. When variable to pass to the executed shell. Takes an environment variable name and value,
<literal>=</literal> and <replaceable>VALUE</replaceable> are omitted, the value of the variable with separated by <literal>=</literal>. This switch may be used multiple times to set multiple
the same name in the program environment will be used.</para> environment variables. Note that this switch is not supported for the
<command>login</command> command (see below).</para></listitem>
<para>Note that this option is not supported for the <command>login</command> command.
</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -527,14 +527,14 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term> <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term> <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<listitem><para>Specifies an environment variable to pass to the init process in the container. This <listitem><para>Specifies an environment variable assignment
may be used to override the default variables or to set additional variables. It may be used more to pass to the init process in the container, in the format
than once to set multiple variables. When <literal>=</literal> and <replaceable>VALUE</replaceable> <literal>NAME=VALUE</literal>. This may be used to override
are omitted, the value of the variable with the same name in the program environment will be used. the default variables or to set additional variables. This
</para></listitem> parameter may be used more than once.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -239,15 +239,11 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><option>-E <replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term> <term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term> <term><option>--setenv=<replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<listitem><para>Runs the service process with the specified environment variable set. This parameter <listitem><para>Runs the service process with the specified environment variable set.
may be used more than once to set multiple variables. When <literal>=</literal> and Also see <varname>Environment=</varname> in
<replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
program environment will be used.</para>
<para>Also see <varname>Environment=</varname> in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -9,7 +9,6 @@
#include "sd-daemon.h" #include "sd-daemon.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "env-util.h"
#include "errno-util.h" #include "errno-util.h"
#include "escape.h" #include "escape.h"
#include "fd-util.h" #include "fd-util.h"
@ -122,9 +121,11 @@ static int open_sockets(int *epoll_fd, bool accept) {
return count; return count;
} }
static int exec_process(const char *name, char **argv, int start_fd, size_t n_fds) { static int exec_process(const char *name, char **argv, char **env, int start_fd, size_t n_fds) {
_cleanup_strv_free_ char **envp = NULL; _cleanup_strv_free_ char **envp = NULL;
const char *var; _cleanup_free_ char *joined = NULL;
size_t n_env = 0, length;
const char *tocopy;
char **s; char **s;
int r; int r;
@ -132,16 +133,55 @@ static int exec_process(const char *name, char **argv, int start_fd, size_t n_fd
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--inetd only supported for single file descriptors."); "--inetd only supported for single file descriptors.");
FOREACH_STRING(var, "TERM", "PATH", "USER", "HOME") { length = strv_length(arg_setenv);
/* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
envp = new0(char *, length + 8);
if (!envp)
return log_oom();
STRV_FOREACH(s, arg_setenv) {
if (strchr(*s, '=')) {
char *k;
k = strdup(*s);
if (!k)
return log_oom();
envp[n_env++] = k;
} else {
_cleanup_free_ char *p = NULL;
const char *n; const char *n;
n = strv_find_prefix(environ, var); p = strjoin(*s, "=");
if (!p)
return log_oom();
n = strv_find_prefix(env, p);
if (!n) if (!n)
continue; continue;
r = strv_extend(&envp, n); envp[n_env] = strdup(n);
if (r < 0) if (!envp[n_env])
return r; return log_oom();
n_env++;
}
}
FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") {
const char *n;
n = strv_find_prefix(env, tocopy);
if (!n)
continue;
envp[n_env] = strdup(n);
if (!envp[n_env])
return log_oom();
n_env++;
} }
if (arg_inetd) { if (arg_inetd) {
@ -161,17 +201,16 @@ static int exec_process(const char *name, char **argv, int start_fd, size_t n_fd
safe_close(start_fd); safe_close(start_fd);
} }
r = strv_extendf(&envp, "LISTEN_FDS=%zu", n_fds); if (asprintf((char **) (envp + n_env++), "LISTEN_FDS=%zu", n_fds) < 0)
if (r < 0) return log_oom();
return r;
r = strv_extendf(&envp, "LISTEN_PID=" PID_FMT, getpid_cached()); if (asprintf((char **) (envp + n_env++), "LISTEN_PID=" PID_FMT, getpid_cached()) < 0)
if (r < 0) return log_oom();
return r;
if (arg_fdnames) { if (arg_fdnames) {
_cleanup_free_ char *names = NULL; _cleanup_free_ char *names = NULL;
size_t len; size_t len;
char *e;
len = strv_length(arg_fdnames); len = strv_length(arg_fdnames);
if (len == 1) if (len == 1)
@ -187,23 +226,15 @@ static int exec_process(const char *name, char **argv, int start_fd, size_t n_fd
if (!names) if (!names)
return log_oom(); return log_oom();
char *t = strjoin("LISTEN_FDNAMES=", names); e = strjoin("LISTEN_FDNAMES=", names);
if (!t) if (!e)
return log_oom(); return log_oom();
r = strv_consume(&envp, t); envp[n_env++] = e;
if (r < 0)
return r;
} }
} }
STRV_FOREACH(s, arg_setenv) { joined = strv_join(argv, " ");
r = strv_env_replace_strdup(&envp, *s);
if (r < 0)
return r;
}
_cleanup_free_ char *joined = strv_join(argv, " ");
if (!joined) if (!joined)
return log_oom(); return log_oom();
@ -213,7 +244,7 @@ static int exec_process(const char *name, char **argv, int start_fd, size_t n_fd
return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined); return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
} }
static int fork_and_exec_process(const char *child, char **argv, int fd) { static int fork_and_exec_process(const char *child, char **argv, char **env, int fd) {
_cleanup_free_ char *joined = NULL; _cleanup_free_ char *joined = NULL;
pid_t child_pid; pid_t child_pid;
int r; int r;
@ -229,7 +260,7 @@ static int fork_and_exec_process(const char *child, char **argv, int fd) {
return r; return r;
if (r == 0) { if (r == 0) {
/* In the child */ /* In the child */
exec_process(child, argv, fd, 1); exec_process(child, argv, env, fd, 1);
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
@ -237,7 +268,7 @@ static int fork_and_exec_process(const char *child, char **argv, int fd) {
return 0; return 0;
} }
static int do_accept(const char *name, char **argv, int fd) { static int do_accept(const char *name, char **argv, char **envp, int fd) {
_cleanup_free_ char *local = NULL, *peer = NULL; _cleanup_free_ char *local = NULL, *peer = NULL;
_cleanup_close_ int fd_accepted = -1; _cleanup_close_ int fd_accepted = -1;
@ -253,7 +284,7 @@ static int do_accept(const char *name, char **argv, int fd) {
(void) getpeername_pretty(fd_accepted, true, &peer); (void) getpeername_pretty(fd_accepted, true, &peer);
log_info("Connection from %s to %s", strna(peer), strna(local)); log_info("Connection from %s to %s", strna(peer), strna(local));
return fork_and_exec_process(name, argv, fd_accepted); return fork_and_exec_process(name, argv, envp, fd_accepted);
} }
/* SIGCHLD handler. */ /* SIGCHLD handler. */
@ -383,9 +414,10 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case 'E': case 'E':
r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg); r = strv_extend(&arg_setenv, optarg);
if (r < 0) if (r < 0)
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg); return log_oom();
break; break;
case ARG_FDNAME: { case ARG_FDNAME: {
@ -439,7 +471,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1 /* work to do */; return 1 /* work to do */;
} }
int main(int argc, char **argv) { int main(int argc, char **argv, char **envp) {
int r, n; int r, n;
int epoll_fd = -1; int epoll_fd = -1;
@ -476,14 +508,14 @@ int main(int argc, char **argv) {
log_info("Communication attempt on fd %i.", event.data.fd); log_info("Communication attempt on fd %i.", event.data.fd);
if (arg_accept) { if (arg_accept) {
r = do_accept(argv[optind], argv + optind, event.data.fd); r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
if (r < 0) if (r < 0)
return EXIT_FAILURE; return EXIT_FAILURE;
} else } else
break; break;
} }
exec_process(argv[optind], argv + optind, SD_LISTEN_FDS_START, (size_t) n); exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, (size_t) n);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -183,51 +183,39 @@ static int env_append(char **r, char ***k, char **a) {
return 0; return 0;
} }
char** _strv_env_merge(char **first, ...) { char **strv_env_merge(size_t n_lists, ...) {
_cleanup_strv_free_ char **merged = NULL; _cleanup_strv_free_ char **ret = NULL;
char **k; size_t n = 0;
char **l, **k;
va_list ap; va_list ap;
/* Merges an arbitrary number of environment sets */ /* Merges an arbitrary number of environment sets */
size_t n = strv_length(first); va_start(ap, n_lists);
for (size_t i = 0; i < n_lists; i++) {
va_start(ap, first);
for (;;) {
char **l;
l = va_arg(ap, char**); l = va_arg(ap, char**);
if (l == POINTER_MAX)
break;
n += strv_length(l); n += strv_length(l);
} }
va_end(ap); va_end(ap);
k = merged = new(char*, n + 1); ret = new(char*, n+1);
if (!merged) if (!ret)
return NULL;
merged[0] = NULL;
if (env_append(merged, &k, first) < 0)
return NULL; return NULL;
va_start(ap, first); *ret = NULL;
for (;;) { k = ret;
char **l;
va_start(ap, n_lists);
for (size_t i = 0; i < n_lists; i++) {
l = va_arg(ap, char**); l = va_arg(ap, char**);
if (l == POINTER_MAX) if (env_append(ret, &k, l) < 0) {
break;
if (env_append(merged, &k, l) < 0) {
va_end(ap); va_end(ap);
return NULL; return NULL;
} }
} }
va_end(ap); va_end(ap);
return TAKE_PTR(merged); return TAKE_PTR(ret);
} }
static bool env_match(const char *t, const char *pattern) { static bool env_match(const char *t, const char *pattern) {
@ -421,32 +409,6 @@ int strv_env_replace_strdup(char ***l, const char *assignment) {
return strv_env_replace_consume(l, p); return strv_env_replace_consume(l, p);
} }
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) {
/* Like strv_env_replace_strdup(), but pulls the variable from the environment of
* the calling program, if a variable name without value is specified.
*/
char *p;
if (strchr(assignment, '=')) {
if (!env_assignment_is_valid(assignment))
return -EINVAL;
p = strdup(assignment);
} else {
if (!env_name_is_valid(assignment))
return -EINVAL;
/* If we can't find the variable in our environment, we will use
* the empty string. This way "passthrough" is equivalent to passing
* --setenv=FOO=$FOO in the shell. */
p = strjoin(assignment, "=", secure_getenv(assignment));
}
if (!p)
return -ENOMEM;
return strv_env_replace_consume(l, p);
}
int strv_env_assign(char ***l, const char *key, const char *value) { int strv_env_assign(char ***l, const char *key, const char *value) {
if (!env_name_is_valid(key)) if (!env_name_is_valid(key))
return -EINVAL; return -EINVAL;

View File

@ -39,15 +39,13 @@ char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const cha
bool strv_env_name_is_valid(char **l); bool strv_env_name_is_valid(char **l);
bool strv_env_name_or_assignment_is_valid(char **l); bool strv_env_name_or_assignment_is_valid(char **l);
char** _strv_env_merge(char **first, ...); char **strv_env_merge(size_t n_lists, ...);
#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */ char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char **strv_env_unset(char **l, const char *p); /* In place ... */ char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_; char **strv_env_unset_many(char **l, ...) _sentinel_;
int strv_env_replace_consume(char ***l, char *p); /* In place ... */ int strv_env_replace_consume(char ***l, char *p); /* In place ... */
int strv_env_replace_strdup(char ***l, const char *assignment); int strv_env_replace_strdup(char ***l, const char *assignment);
int strv_env_replace_strdup_passthrough(char ***l, const char *assignment);
int strv_env_assign(char ***l, const char *key, const char *value); int strv_env_assign(char ***l, const char *key, const char *value);
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_; char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;

View File

@ -2888,7 +2888,7 @@ int bus_exec_context_set_transient_property(
if (!joined) if (!joined)
return -ENOMEM; return -ENOMEM;
e = strv_env_merge(c->environment, l); e = strv_env_merge(2, c->environment, l);
if (!e) if (!e)
return -ENOMEM; return -ENOMEM;
@ -2922,7 +2922,7 @@ int bus_exec_context_set_transient_property(
if (!joined) if (!joined)
return -ENOMEM; return -ENOMEM;
e = strv_env_merge(c->unset_environment, l); e = strv_env_merge(2, c->unset_environment, l);
if (!e) if (!e)
return -ENOMEM; return -ENOMEM;

View File

@ -4158,7 +4158,8 @@ static int exec_child(
return log_oom(); return log_oom();
} }
accum_env = strv_env_merge(params->environment, accum_env = strv_env_merge(5,
params->environment,
our_env, our_env,
pass_env, pass_env,
context->environment, context->environment,
@ -5213,7 +5214,7 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c,
else { else {
char **m; char **m;
m = strv_env_merge(r, p); m = strv_env_merge(2, r, p);
strv_free(r); strv_free(r);
strv_free(p); strv_free(p);
if (!m) if (!m)

View File

@ -85,7 +85,7 @@ int locale_setup(char ***environment) {
else { else {
char **merged; char **merged;
merged = strv_env_merge(*environment, add); merged = strv_env_merge(2, *environment, add);
if (!merged) if (!merged)
return -ENOMEM; return -ENOMEM;

View File

@ -3672,7 +3672,7 @@ int manager_transient_environment_add(Manager *m, char **plus) {
if (strv_isempty(plus)) if (strv_isempty(plus))
return 0; return 0;
a = strv_env_merge(m->transient_environment, plus); a = strv_env_merge(2, m->transient_environment, plus);
if (!a) if (!a)
return log_oom(); return log_oom();
@ -3704,7 +3704,7 @@ int manager_client_environment_modify(
} }
if (!strv_isempty(plus)) { if (!strv_isempty(plus)) {
b = strv_env_merge(l, plus); b = strv_env_merge(2, l, plus);
if (!b) { if (!b) {
strv_free(a); strv_free(a);
return -ENOMEM; return -ENOMEM;
@ -3731,7 +3731,7 @@ int manager_get_effective_environment(Manager *m, char ***ret) {
assert(m); assert(m);
assert(ret); assert(ret);
l = strv_env_merge(m->transient_environment, m->client_environment); l = strv_env_merge(2, m->transient_environment, m->client_environment);
if (!l) if (!l)
return -ENOMEM; return -ENOMEM;

View File

@ -1546,7 +1546,7 @@ static int service_spawn(
if (r < 0) if (r < 0)
return r; return r;
final_env = strv_env_merge(exec_params.environment, our_env); final_env = strv_env_merge(2, exec_params.environment, our_env, NULL);
if (!final_env) if (!final_env)
return -ENOMEM; return -ENOMEM;

View File

@ -679,7 +679,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target); return log_error_errno(r, "Failed to copy bytes from %s in mage '%s' to '%s': %m", arg_source, arg_image, arg_target);
(void) copy_xattr(source_fd, target_fd, 0); (void) copy_xattr(source_fd, target_fd);
(void) copy_access(source_fd, target_fd); (void) copy_access(source_fd, target_fd);
(void) copy_times(source_fd, target_fd, 0); (void) copy_times(source_fd, target_fd, 0);
@ -748,7 +748,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image); return log_error_errno(r, "Failed to copy bytes from '%s' to '%s' in image '%s': %m", arg_source, arg_target, arg_image);
(void) copy_xattr(source_fd, target_fd, 0); (void) copy_xattr(source_fd, target_fd);
(void) copy_access(source_fd, target_fd); (void) copy_access(source_fd, target_fd);
(void) copy_times(source_fd, target_fd, 0); (void) copy_times(source_fd, target_fd, 0);

View File

@ -2077,7 +2077,7 @@ static int help(int argc, char *argv[], void *userdata) {
" -G --member-of=GROUP Add user to group\n" " -G --member-of=GROUP Add user to group\n"
" --skel=PATH Skeleton directory to use\n" " --skel=PATH Skeleton directory to use\n"
" --shell=PATH Shell for account\n" " --shell=PATH Shell for account\n"
" --setenv=VARIABLE[=VALUE] Set an environment variable at log-in\n" " --setenv=VARIABLE=VALUE Set an environment variable at log-in\n"
" --timezone=TIMEZONE Set a time-zone\n" " --timezone=TIMEZONE Set a time-zone\n"
" --language=LOCALE Set preferred language\n" " --language=LOCALE Set preferred language\n"
" --ssh-authorized-keys=KEYS\n" " --ssh-authorized-keys=KEYS\n"
@ -2093,8 +2093,8 @@ static int help(int argc, char *argv[], void *userdata) {
" Whether to require user presence to unlock the\n" " Whether to require user presence to unlock the\n"
" account\n" " account\n"
" --fido2-with-user-verification=BOOL\n" " --fido2-with-user-verification=BOOL\n"
" Whether to require user verification to unlock\n" " Whether to require user verification to unlock the\n"
" the account\n" " account\n"
" --recovery-key=BOOL Add a recovery key\n" " --recovery-key=BOOL Add a recovery key\n"
"\n%4$sAccount Management User Record Properties:%5$s\n" "\n%4$sAccount Management User Record Properties:%5$s\n"
" --locked=BOOL Set locked account state\n" " --locked=BOOL Set locked account state\n"
@ -2109,7 +2109,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --enforce-password-policy=BOOL\n" " --enforce-password-policy=BOOL\n"
" Control whether to enforce system's password\n" " Control whether to enforce system's password\n"
" policy for this user\n" " policy for this user\n"
" -P Same as --enforce-password-password=no\n" " -P Equivalent to --enforce-password-password=no\n"
" --password-change-now=BOOL\n" " --password-change-now=BOOL\n"
" Require the password to be changed on next login\n" " Require the password to be changed on next login\n"
" --password-change-min=TIME\n" " --password-change-min=TIME\n"
@ -2673,6 +2673,10 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
} }
if (!env_assignment_is_valid(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Environment assignment '%s' not valid.", optarg);
e = json_variant_by_key(arg_identity_extra, "environment"); e = json_variant_by_key(arg_identity_extra, "environment");
if (e) { if (e) {
r = json_variant_strv(e, &l); r = json_variant_strv(e, &l);
@ -2680,9 +2684,9 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(r, "Failed to parse JSON environment field: %m"); return log_error_errno(r, "Failed to parse JSON environment field: %m");
} }
r = strv_env_replace_strdup_passthrough(&l, optarg); r = strv_env_replace_strdup(&l, optarg);
if (r < 0) if (r < 0)
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg); return log_error_errno(r, "Failed to replace JSON environment field: %m");
strv_sort(l); strv_sort(l);

View File

@ -223,7 +223,7 @@ static int raw_export_process(RawExport *e) {
finish: finish:
if (r >= 0) { if (r >= 0) {
(void) copy_times(e->input_fd, e->output_fd, COPY_CRTIME); (void) copy_times(e->input_fd, e->output_fd, COPY_CRTIME);
(void) copy_xattr(e->input_fd, e->output_fd, 0); (void) copy_xattr(e->input_fd, e->output_fd);
} }
if (e->on_finished) if (e->on_finished)

View File

@ -206,7 +206,7 @@ static int raw_import_finish(RawImport *i) {
if (S_ISREG(i->st.st_mode)) { if (S_ISREG(i->st.st_mode)) {
(void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME); (void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
(void) copy_xattr(i->input_fd, i->output_fd, 0); (void) copy_xattr(i->input_fd, i->output_fd);
} }
if (i->flags & IMPORT_READ_ONLY) { if (i->flags & IMPORT_READ_ONLY) {

View File

@ -371,7 +371,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
} }
(void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME); (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
(void) copy_xattr(i->raw_job->disk_fd, dfd, 0); (void) copy_xattr(i->raw_job->disk_fd, dfd);
dfd = safe_close(dfd); dfd = safe_close(dfd);

View File

@ -845,7 +845,7 @@ int locale_gen_enable_locale(const char *locale) {
r = copy_access(fileno(fr), fileno(fw)); r = copy_access(fileno(fr), fileno(fw));
if (r < 0) if (r < 0)
return r; return r;
r = copy_xattr(fileno(fr), fileno(fw), COPY_ALL_XATTRS); r = copy_xattr(fileno(fr), fileno(fw));
if (r < 0) if (r < 0)
return r; return r;
} }

View File

@ -2510,7 +2510,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --kill-who=WHO Who to send signal to\n" " --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n" " -s --signal=SIGNAL Which signal to send\n"
" --uid=USER Specify user ID to invoke shell as\n" " --uid=USER Specify user ID to invoke shell as\n"
" -E --setenv=VAR[=VALUE] Add an environment variable for shell\n" " -E --setenv=VAR=VALUE Add an environment variable for shell\n"
" --read-only Create read-only bind mount\n" " --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n" " --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n" " -n --lines=INTEGER Number of journal entries to show\n"
@ -2765,9 +2765,13 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case 'E': case 'E':
r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg); if (!env_assignment_is_valid(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Environment assignment invalid: %s", optarg);
r = strv_extend(&arg_setenv, optarg);
if (r < 0) if (r < 0)
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg); return log_oom();
break; break;
case ARG_MAX_ADDRESSES: case ARG_MAX_ADDRESSES:

View File

@ -232,7 +232,7 @@ static int run(int argc, char* argv[]) {
our_env[i++] = NULL; our_env[i++] = NULL;
final_env = strv_env_merge(our_env, argv + optind); final_env = strv_env_merge(2, our_env, argv + optind);
if (!final_env) if (!final_env)
return log_oom(); return log_oom();

View File

@ -338,7 +338,7 @@ static int help(void) {
" -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n" " -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n"
" -b --boot Boot up full system (i.e. invoke init)\n" " -b --boot Boot up full system (i.e. invoke init)\n"
" --chdir=PATH Set working directory in the container\n" " --chdir=PATH Set working directory in the container\n"
" -E --setenv=NAME[=VALUE] Pass an environment variable to PID 1\n" " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
" -u --user=USER Run the command under specified user or UID\n" " -u --user=USER Run the command under specified user or UID\n"
" --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n" " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n"
" --notify-ready=BOOLEAN Receive notifications from the child init process\n\n" " --notify-ready=BOOLEAN Receive notifications from the child init process\n\n"
@ -1121,13 +1121,17 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings_mask |= SETTING_CUSTOM_MOUNTS; arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
break; break;
case 'E': case 'E': {
r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg); if (!env_assignment_is_valid(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Environment variable assignment '%s' is not valid.", optarg);
r = strv_env_replace_strdup(&arg_setenv, optarg);
if (r < 0) if (r < 0)
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg); return r;
arg_settings_mask |= SETTING_ENVIRONMENT; arg_settings_mask |= SETTING_ENVIRONMENT;
break; break;
}
case 'q': case 'q':
arg_quiet = true; arg_quiet = true;
@ -3185,8 +3189,8 @@ static int inner_child(
_cleanup_free_ char *home = NULL; _cleanup_free_ char *home = NULL;
char as_uuid[ID128_UUID_STRING_MAX]; char as_uuid[ID128_UUID_STRING_MAX];
size_t n_env = 1; size_t n_env = 1;
char *envp[] = { const char *envp[] = {
(char*) "PATH=" DEFAULT_PATH_COMPAT, "PATH=" DEFAULT_PATH_COMPAT,
NULL, /* container */ NULL, /* container */
NULL, /* TERM */ NULL, /* TERM */
NULL, /* HOME */ NULL, /* HOME */
@ -3422,17 +3426,17 @@ static int inner_child(
n_env++; n_env++;
if (home || !uid_is_valid(arg_uid) || arg_uid == 0) if (home || !uid_is_valid(arg_uid) || arg_uid == 0)
if (asprintf(envp + n_env++, "HOME=%s", home ?: "/root") < 0) if (asprintf((char**)(envp + n_env++), "HOME=%s", home ?: "/root") < 0)
return log_oom(); return log_oom();
if (arg_user || !uid_is_valid(arg_uid) || arg_uid == 0) if (arg_user || !uid_is_valid(arg_uid) || arg_uid == 0)
if (asprintf(envp + n_env++, "USER=%s", arg_user ?: "root") < 0 || if (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ?: "root") < 0 ||
asprintf(envp + n_env++, "LOGNAME=%s", arg_user ? arg_user : "root") < 0) asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)
return log_oom(); return log_oom();
assert(!sd_id128_is_null(arg_uuid)); assert(!sd_id128_is_null(arg_uuid));
if (asprintf(envp + n_env++, "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0) if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0)
return log_oom(); return log_oom();
if (fdset_size(fds) > 0) { if (fdset_size(fds) > 0) {
@ -3440,11 +3444,11 @@ static int inner_child(
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors."); return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
if ((asprintf(envp + n_env++, "LISTEN_FDS=%u", fdset_size(fds)) < 0) || if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
(asprintf(envp + n_env++, "LISTEN_PID=1") < 0)) (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0))
return log_oom(); return log_oom();
} }
if (asprintf(envp + n_env++, "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0) if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
return log_oom(); return log_oom();
if (arg_n_credentials > 0) { if (arg_n_credentials > 0) {
@ -3454,7 +3458,7 @@ static int inner_child(
n_env++; n_env++;
} }
env_use = strv_env_merge(envp, os_release_pairs, arg_setenv); env_use = strv_env_merge(3, envp, os_release_pairs, arg_setenv);
if (!env_use) if (!env_use)
return log_oom(); return log_oom();

View File

@ -2849,13 +2849,13 @@ static int do_copy_files(Partition *p, const char *fs) {
sfd, ".", sfd, ".",
pfd, fn, pfd, fn,
UID_INVALID, GID_INVALID, UID_INVALID, GID_INVALID,
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS); COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
} else } else
r = copy_tree_at( r = copy_tree_at(
sfd, ".", sfd, ".",
tfd, ".", tfd, ".",
UID_INVALID, GID_INVALID, UID_INVALID, GID_INVALID,
COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS|COPY_ALL_XATTRS); COPY_REFLINK|COPY_MERGE|COPY_REPLACE|COPY_SIGINT|COPY_HARDLINKS);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target); return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
} else { } else {
@ -2890,7 +2890,7 @@ static int do_copy_files(Partition *p, const char *fs) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target); return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", *source, strempty(arg_root), *target);
(void) copy_xattr(sfd, tfd, COPY_ALL_XATTRS); (void) copy_xattr(sfd, tfd);
(void) copy_access(sfd, tfd); (void) copy_access(sfd, tfd);
(void) copy_times(sfd, tfd, 0); (void) copy_times(sfd, tfd, 0);
} }

View File

@ -111,7 +111,7 @@ static int help(void) {
" --nice=NICE Nice level\n" " --nice=NICE Nice level\n"
" --working-directory=PATH Set working directory\n" " --working-directory=PATH Set working directory\n"
" -d --same-dir Inherit working directory from caller\n" " -d --same-dir Inherit working directory from caller\n"
" -E --setenv=NAME[=VALUE] Set environment variable\n" " -E --setenv=NAME=VALUE Set environment\n"
" -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n" " -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n"
" STDERR\n" " STDERR\n"
" -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n" " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
@ -322,9 +322,8 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case 'E': case 'E':
r = strv_env_replace_strdup_passthrough(&arg_environment, optarg); if (strv_extend(&arg_environment, optarg) < 0)
if (r < 0) return log_oom();
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
break; break;
@ -1527,7 +1526,7 @@ static int start_transient_scope(sd_bus *bus) {
return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid); return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
} }
env = strv_env_merge(environ, user_env, arg_environment); env = strv_env_merge(3, environ, user_env, arg_environment);
if (!env) if (!env)
return log_oom(); return log_oom();

View File

@ -1628,7 +1628,6 @@ int btrfs_subvol_snapshot_fd_full(
COPY_REFLINK| COPY_REFLINK|
COPY_SAME_MOUNT| COPY_SAME_MOUNT|
COPY_HARDLINKS| COPY_HARDLINKS|
COPY_ALL_XATTRS|
(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0)| (FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGINT) ? COPY_SIGINT : 0)|
(FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGTERM) ? COPY_SIGTERM : 0), (FLAGS_SET(flags, BTRFS_SNAPSHOT_SIGTERM) ? COPY_SIGTERM : 0),
progress_path, progress_path,

View File

@ -652,7 +652,7 @@ static int fd_copy_regular(
r = -errno; r = -errno;
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
(void) copy_xattr(fdf, fdt, copy_flags); (void) copy_xattr(fdf, fdt);
if (copy_flags & COPY_FSYNC) { if (copy_flags & COPY_FSYNC) {
if (fsync(fdt) < 0) { if (fsync(fdt) < 0) {
@ -945,7 +945,7 @@ static int fd_copy_directory(
if (fchmod(fdt, st->st_mode & 07777) < 0) if (fchmod(fdt, st->st_mode & 07777) < 0)
r = -errno; r = -errno;
(void) copy_xattr(dirfd(d), fdt, copy_flags); (void) copy_xattr(dirfd(d), fdt);
(void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim }); (void) futimens(fdt, (struct timespec[]) { st->st_atim, st->st_mtim });
} }
@ -1139,7 +1139,7 @@ int copy_file_fd_full(
if (S_ISREG(fdt)) { if (S_ISREG(fdt)) {
(void) copy_times(fdf, fdt, copy_flags); (void) copy_times(fdf, fdt, copy_flags);
(void) copy_xattr(fdf, fdt, copy_flags); (void) copy_xattr(fdf, fdt);
} }
if (copy_flags & COPY_FSYNC_FULL) { if (copy_flags & COPY_FSYNC_FULL) {
@ -1211,7 +1211,7 @@ int copy_file_full(
goto fail; goto fail;
(void) copy_times(fdf, fdt, copy_flags); (void) copy_times(fdf, fdt, copy_flags);
(void) copy_xattr(fdf, fdt, copy_flags); (void) copy_xattr(fdf, fdt);
if (chattr_mask != 0) if (chattr_mask != 0)
(void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL);
@ -1399,7 +1399,7 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht) {
return fchmod_and_chown_with_fallback(fdt, patht, st.st_mode & 07777, st.st_uid, st.st_gid); return fchmod_and_chown_with_fallback(fdt, patht, st.st_mode & 07777, st.st_uid, st.st_gid);
} }
int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) { int copy_xattr(int fdf, int fdt) {
_cleanup_free_ char *names = NULL; _cleanup_free_ char *names = NULL;
int ret = 0, r; int ret = 0, r;
const char *p; const char *p;
@ -1411,7 +1411,7 @@ int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) {
NULSTR_FOREACH(p, names) { NULSTR_FOREACH(p, names) {
_cleanup_free_ char *value = NULL; _cleanup_free_ char *value = NULL;
if (!(copy_flags & COPY_ALL_XATTRS) && !startswith(p, "user.")) if (!startswith(p, "user."))
continue; continue;
r = fgetxattr_malloc(fdf, p, &value); r = fgetxattr_malloc(fdf, p, &value);

View File

@ -23,7 +23,6 @@ typedef enum CopyFlags {
COPY_FSYNC = 1 << 10, /* fsync() after we are done */ COPY_FSYNC = 1 << 10, /* fsync() after we are done */
COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */ COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */
COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */ COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */
COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
} CopyFlags; } CopyFlags;
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata); typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
@ -73,4 +72,4 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht);
static inline int copy_rights(int fdf, int fdt) { static inline int copy_rights(int fdf, int fdt) {
return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */ return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */
} }
int copy_xattr(int fdf, int fdt, CopyFlags copy_flags); int copy_xattr(int fdf, int fdt);

View File

@ -73,28 +73,31 @@ static void test_strv_env_unset(void) {
static void test_strv_env_merge(void) { static void test_strv_env_merge(void) {
log_info("/* %s */", __func__); log_info("/* %s */", __func__);
char **a = STRV_MAKE("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF", "EQ==="); _cleanup_strv_free_ char **a = NULL, **b = NULL, **r = NULL;
char **b = STRV_MAKE("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES");
_cleanup_strv_free_ char **r = strv_env_merge(NULL, a, NULL, b, NULL, a, b, b, NULL); a = strv_new("FOO=BAR", "WALDO=WALDO", "WALDO=", "PIEP", "SCHLUMPF=SMURF");
assert_se(a);
b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES");
assert_se(b);
r = strv_env_merge(2, a, b);
assert_se(r); assert_se(r);
assert_se(streq(r[0], "FOO=")); assert_se(streq(r[0], "FOO="));
assert_se(streq(r[1], "WALDO=")); assert_se(streq(r[1], "WALDO="));
assert_se(streq(r[2], "PIEP")); assert_se(streq(r[2], "PIEP"));
assert_se(streq(r[3], "SCHLUMPF=SMURFF")); assert_se(streq(r[3], "SCHLUMPF=SMURFF"));
assert_se(streq(r[4], "EQ===")); assert_se(streq(r[4], "PIEP="));
assert_se(streq(r[5], "PIEP=")); assert_se(streq(r[5], "NANANANA=YES"));
assert_se(streq(r[6], "NANANANA=YES")); assert_se(strv_length(r) == 6);
assert_se(strv_length(r) == 7);
assert_se(strv_env_clean(r) == r); assert_se(strv_env_clean(r) == r);
assert_se(streq(r[0], "FOO=")); assert_se(streq(r[0], "FOO="));
assert_se(streq(r[1], "WALDO=")); assert_se(streq(r[1], "WALDO="));
assert_se(streq(r[2], "SCHLUMPF=SMURFF")); assert_se(streq(r[2], "SCHLUMPF=SMURFF"));
assert_se(streq(r[3], "EQ===")); assert_se(streq(r[3], "PIEP="));
assert_se(streq(r[4], "PIEP=")); assert_se(streq(r[4], "NANANANA=YES"));
assert_se(streq(r[5], "NANANANA=YES")); assert_se(strv_length(r) == 5);
assert_se(strv_length(r) == 6);
} }
static void test_strv_env_replace_strdup(void) { static void test_strv_env_replace_strdup(void) {
@ -105,7 +108,6 @@ static void test_strv_env_replace_strdup(void) {
assert_se(strv_env_replace_strdup(&a, "a=a") == 1); assert_se(strv_env_replace_strdup(&a, "a=a") == 1);
assert_se(strv_env_replace_strdup(&a, "b=b") == 1); assert_se(strv_env_replace_strdup(&a, "b=b") == 1);
assert_se(strv_env_replace_strdup(&a, "a=A") == 0); assert_se(strv_env_replace_strdup(&a, "a=A") == 0);
assert_se(strv_env_replace_strdup(&a, "c") == -EINVAL);
assert_se(strv_length(a) == 2); assert_se(strv_length(a) == 2);
strv_sort(a); strv_sort(a);
@ -113,27 +115,6 @@ static void test_strv_env_replace_strdup(void) {
assert_se(streq(a[1], "b=b")); assert_se(streq(a[1], "b=b"));
} }
static void test_strv_env_replace_strdup_passthrough(void) {
log_info("/* %s */", __func__);
_cleanup_strv_free_ char **a = NULL;
assert_se(putenv((char*) "a=a") == 0);
assert_se(putenv((char*) "b=") == 0);
assert_se(unsetenv("c") == 0);
assert_se(strv_env_replace_strdup_passthrough(&a, "a") == 1);
assert_se(strv_env_replace_strdup_passthrough(&a, "b") == 1);
assert_se(strv_env_replace_strdup_passthrough(&a, "c") == 1);
assert_se(strv_env_replace_strdup_passthrough(&a, "a") == 0);
assert_se(strv_env_replace_strdup_passthrough(&a, "$a") == -EINVAL);
assert_se(strv_length(a) == 3);
assert_se(streq(a[0], "a=a"));
assert_se(streq(a[1], "b="));
assert_se(streq(a[2], "c="));
}
static void test_strv_env_assign(void) { static void test_strv_env_assign(void) {
log_info("/* %s */", __func__); log_info("/* %s */", __func__);
@ -437,7 +418,6 @@ int main(int argc, char *argv[]) {
test_strv_env_unset(); test_strv_env_unset();
test_strv_env_merge(); test_strv_env_merge();
test_strv_env_replace_strdup(); test_strv_env_replace_strdup();
test_strv_env_replace_strdup_passthrough();
test_strv_env_assign(); test_strv_env_assign();
test_env_strv_get_n(); test_env_strv_get_n();
test_replace_env(false); test_replace_env(false);