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

Compare commits

...

11 Commits

Author SHA1 Message Date
Andrej Lajovic
23e026de25 shared/copy: add a new flag COPY_ALL_XATTRS
When the flag COPY_ALL_XATTRS is set, it causes the complete set of xattrs
to be copied. If the flag is unset, only xattrs from the "user" namespace
are copied.

Fixes #17178.
2021-08-11 17:48:10 +02:00
Lennart Poettering
a0c5a3f0c0
Merge pull request #20419 from keszybz/setenv-no-value
Allow --setenv=FOO in various programs
2021-08-11 17:47:45 +02:00
Zbigniew Jędrzejewski-Szmek
76e68b3db3 activate: simplify/rework implementation of --setenv
Previous implementation is simplified by using the new helper. The new code
does more looping, but considering that it's unlikely that people set more
than a handful of variables through commandline options, this should be OK.

If a variable is specified on the command line, it overrides any automatically
set variable. Effective behaviour was already were like this, because we would
specify two variables, both would be set, and since glibc will return
the first matching entry.
('systemd-socket-activate -E TERM=FOO -l 2000 --inetd -a env' would give
'TERM=FOO TERM=xterm-256color PATH=...', and getenv("TERM") returns "FOO".)
But it's nicer to filter out any duplicate entries and only pass the intended
variable to the child process.
2021-08-11 10:17:50 +02:00
Zbigniew Jędrzejewski-Szmek
2f400671da activate: use global variable instead of passing char **envp around
The effect should be the same, but the code is less verbose.
In particular, the variable was called envp in parts of the code,
but in other parts, we had a local envp variable, and envp was called
env.
2021-08-11 09:34:45 +02:00
Zbigniew Jędrzejewski-Szmek
89bf86e015 machinectl: allow --setenv=FOO 2021-08-11 09:34:45 +02:00
Zbigniew Jędrzejewski-Szmek
0337b3d51c run: allow --setenv=FOO 2021-08-11 09:34:45 +02:00
Zbigniew Jędrzejewski-Szmek
4bbafcc359 homectl: allow --setenv=FOO 2021-08-11 09:34:45 +02:00
Zbigniew Jędrzejewski-Szmek
0d2a017986 nspawn: allow --setenv=FOO as equivalent to --setenv=FOO=$FOO
systemd-socket-activate has supported such a mode since
5e65c93a433447b15180249166f7b3944c3e6156. '--setenv=FOO=$FOO' is a fairly
common use in scripts, and it's nicer to do this automatically without worrying
about quoting and whatnot.

https://github.com/systemd/mkosi/pull/765 added the same to 'mkosi --environment='.
2021-08-11 09:34:45 +02:00
Zbigniew Jędrzejewski-Szmek
a14af47e64 basic/env-util: add a mode where we pull in the variable value from environment 2021-08-11 09:16:05 +02:00
Zbigniew Jędrzejewski-Szmek
43cadb64d4 test-env-util: extend the test for strv_env_merge() a bit 2021-08-11 09:11:58 +02:00
Zbigniew Jędrzejewski-Szmek
4ab3d29ff0 Add implicit sentinel to strv_env_merge()
Just to make it a tiny bit nicer to use.
2021-08-11 09:11:42 +02:00
27 changed files with 318 additions and 290 deletions

View File

@ -288,12 +288,16 @@
</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. 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>. May be used
multiple times to set multiple environment variables.</para></listitem>
<listitem><para>Takes an environment variable assignment to set for all user processes. May be used
multiple times to set multiple environment variables. When <literal>=</literal> and
<replaceable>VALUE</replaceable> are omitted, the value of the variable with the same name in the
program environment will be used.</para>
<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>

View File

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

View File

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

View File

@ -239,11 +239,15 @@
</varlistentry>
<varlistentry>
<term><option>-E <replaceable>NAME</replaceable>=<replaceable>VALUE</replaceable></option></term>
<term><option>--setenv=<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>
<listitem><para>Runs the service process with the specified environment variable set.
Also see <varname>Environment=</varname> in
<listitem><para>Runs the service process with the specified environment variable set. This parameter
may be used more than once to set multiple variables. When <literal>=</literal> and
<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>
</listitem>
</varlistentry>

View File

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

View File

@ -183,39 +183,51 @@ static int env_append(char **r, char ***k, char **a) {
return 0;
}
char **strv_env_merge(size_t n_lists, ...) {
_cleanup_strv_free_ char **ret = NULL;
size_t n = 0;
char **l, **k;
char** _strv_env_merge(char **first, ...) {
_cleanup_strv_free_ char **merged = NULL;
char **k;
va_list ap;
/* Merges an arbitrary number of environment sets */
va_start(ap, n_lists);
for (size_t i = 0; i < n_lists; i++) {
size_t n = strv_length(first);
va_start(ap, first);
for (;;) {
char **l;
l = va_arg(ap, char**);
if (l == POINTER_MAX)
break;
n += strv_length(l);
}
va_end(ap);
ret = new(char*, n+1);
if (!ret)
k = merged = new(char*, n + 1);
if (!merged)
return NULL;
merged[0] = NULL;
if (env_append(merged, &k, first) < 0)
return NULL;
*ret = NULL;
k = ret;
va_start(ap, first);
for (;;) {
char **l;
va_start(ap, n_lists);
for (size_t i = 0; i < n_lists; i++) {
l = va_arg(ap, char**);
if (env_append(ret, &k, l) < 0) {
if (l == POINTER_MAX)
break;
if (env_append(merged, &k, l) < 0) {
va_end(ap);
return NULL;
}
}
va_end(ap);
return TAKE_PTR(ret);
return TAKE_PTR(merged);
}
static bool env_match(const char *t, const char *pattern) {
@ -409,6 +421,32 @@ int strv_env_replace_strdup(char ***l, const char *assignment) {
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) {
if (!env_name_is_valid(key))
return -EINVAL;

View File

@ -39,13 +39,15 @@ 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_or_assignment_is_valid(char **l);
char **strv_env_merge(size_t n_lists, ...);
char** _strv_env_merge(char **first, ...);
#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_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
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_passthrough(char ***l, const char *assignment);
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_;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -679,7 +679,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
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);
(void) copy_xattr(source_fd, target_fd);
(void) copy_xattr(source_fd, target_fd, 0);
(void) copy_access(source_fd, target_fd);
(void) copy_times(source_fd, target_fd, 0);
@ -748,7 +748,7 @@ static int action_copy(DissectedImage *m, LoopDevice *d) {
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);
(void) copy_xattr(source_fd, target_fd);
(void) copy_xattr(source_fd, target_fd, 0);
(void) copy_access(source_fd, target_fd);
(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"
" --skel=PATH Skeleton directory to use\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"
" --language=LOCALE Set preferred language\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"
" account\n"
" --fido2-with-user-verification=BOOL\n"
" Whether to require user verification to unlock the\n"
" account\n"
" Whether to require user verification to unlock\n"
" the account\n"
" --recovery-key=BOOL Add a recovery key\n"
"\n%4$sAccount Management User Record Properties:%5$s\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"
" Control whether to enforce system's password\n"
" policy for this user\n"
" -P Equivalent to --enforce-password-password=no\n"
" -P Same as --enforce-password-password=no\n"
" --password-change-now=BOOL\n"
" Require the password to be changed on next login\n"
" --password-change-min=TIME\n"
@ -2673,10 +2673,6 @@ static int parse_argv(int argc, char *argv[]) {
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");
if (e) {
r = json_variant_strv(e, &l);
@ -2684,9 +2680,9 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(r, "Failed to parse JSON environment field: %m");
}
r = strv_env_replace_strdup(&l, optarg);
r = strv_env_replace_strdup_passthrough(&l, optarg);
if (r < 0)
return log_error_errno(r, "Failed to replace JSON environment field: %m");
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
strv_sort(l);

View File

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

View File

@ -206,7 +206,7 @@ static int raw_import_finish(RawImport *i) {
if (S_ISREG(i->st.st_mode)) {
(void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME);
(void) copy_xattr(i->input_fd, i->output_fd);
(void) copy_xattr(i->input_fd, i->output_fd, 0);
}
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_xattr(i->raw_job->disk_fd, dfd);
(void) copy_xattr(i->raw_job->disk_fd, dfd, 0);
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));
if (r < 0)
return r;
r = copy_xattr(fileno(fr), fileno(fw));
r = copy_xattr(fileno(fr), fileno(fw), COPY_ALL_XATTRS);
if (r < 0)
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"
" -s --signal=SIGNAL Which signal to send\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"
" --mkdir Create directory before bind mounting, if missing\n"
" -n --lines=INTEGER Number of journal entries to show\n"
@ -2765,13 +2765,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'E':
if (!env_assignment_is_valid(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Environment assignment invalid: %s", optarg);
r = strv_extend(&arg_setenv, optarg);
r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
if (r < 0)
return log_oom();
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
break;
case ARG_MAX_ADDRESSES:

View File

@ -232,7 +232,7 @@ static int run(int argc, char* argv[]) {
our_env[i++] = NULL;
final_env = strv_env_merge(2, our_env, argv + optind);
final_env = strv_env_merge(our_env, argv + optind);
if (!final_env)
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"
" -b --boot Boot up full system (i.e. invoke init)\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"
" --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"
@ -1121,17 +1121,13 @@ static int parse_argv(int argc, char *argv[]) {
arg_settings_mask |= SETTING_CUSTOM_MOUNTS;
break;
case 'E': {
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);
case 'E':
r = strv_env_replace_strdup_passthrough(&arg_setenv, optarg);
if (r < 0)
return r;
return log_error_errno(r, "Cannot assign environment variable %s: %m", optarg);
arg_settings_mask |= SETTING_ENVIRONMENT;
break;
}
case 'q':
arg_quiet = true;
@ -3189,8 +3185,8 @@ static int inner_child(
_cleanup_free_ char *home = NULL;
char as_uuid[ID128_UUID_STRING_MAX];
size_t n_env = 1;
const char *envp[] = {
"PATH=" DEFAULT_PATH_COMPAT,
char *envp[] = {
(char*) "PATH=" DEFAULT_PATH_COMPAT,
NULL, /* container */
NULL, /* TERM */
NULL, /* HOME */
@ -3426,17 +3422,17 @@ static int inner_child(
n_env++;
if (home || !uid_is_valid(arg_uid) || arg_uid == 0)
if (asprintf((char**)(envp + n_env++), "HOME=%s", home ?: "/root") < 0)
if (asprintf(envp + n_env++, "HOME=%s", home ?: "/root") < 0)
return log_oom();
if (arg_user || !uid_is_valid(arg_uid) || arg_uid == 0)
if (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ?: "root") < 0 ||
asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)
if (asprintf(envp + n_env++, "USER=%s", arg_user ?: "root") < 0 ||
asprintf(envp + n_env++, "LOGNAME=%s", arg_user ? arg_user : "root") < 0)
return log_oom();
assert(!sd_id128_is_null(arg_uuid));
if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0)
if (asprintf(envp + n_env++, "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0)
return log_oom();
if (fdset_size(fds) > 0) {
@ -3444,11 +3440,11 @@ static int inner_child(
if (r < 0)
return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
(asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0))
if ((asprintf(envp + n_env++, "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
(asprintf(envp + n_env++, "LISTEN_PID=1") < 0))
return log_oom();
}
if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
if (asprintf(envp + n_env++, "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
return log_oom();
if (arg_n_credentials > 0) {
@ -3458,7 +3454,7 @@ static int inner_child(
n_env++;
}
env_use = strv_env_merge(3, envp, os_release_pairs, arg_setenv);
env_use = strv_env_merge(envp, os_release_pairs, arg_setenv);
if (!env_use)
return log_oom();

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@ typedef enum CopyFlags {
COPY_FSYNC = 1 << 10, /* fsync() 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_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */
} CopyFlags;
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);
@ -72,4 +73,4 @@ int copy_rights_with_fallback(int fdf, int fdt, const char *patht);
static inline int copy_rights(int fdf, int fdt) {
return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */
}
int copy_xattr(int fdf, int fdt);
int copy_xattr(int fdf, int fdt, CopyFlags copy_flags);

View File

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