From: u2386 Date: Mon, 27 Feb 2023 02:18:31 +0000 (+0000) Subject: nsenter: add --env for allowing environment variables inheritance X-Git-Tag: v2.39-rc1~20 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4e9ec856a1b91fb3ff10de10f2dac94f711705e1;p=thirdparty%2Futil-linux.git nsenter: add --env for allowing environment variables inheritance This commit adds support for the -e or --env option in nsenter, allowing a new process to inherit the environment va If the option is not given, the environment variables will stay the same as in the current namespace. Example: Setup the namespace: $ docker run -d -e PROJECT='util linux' --rm alpine sleep 10000 cb0b69aa7aec $ docker inspect --format '{{ .State.Pid }}' cb0b69aa7aec 470012 Enter the namespace: $ nsenter --all -t 470012 --env env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=cb0b69aa7aec PROJECT=util linux HOME=/root Reviewed-by: Thomas Weißschuh Reviewed-by: Karel Zak Signed-off-by: u2386 --- diff --git a/bash-completion/nsenter b/bash-completion/nsenter index d3cb23f0b1..ace7934407 100644 --- a/bash-completion/nsenter +++ b/bash-completion/nsenter @@ -49,6 +49,7 @@ _nsenter_module() --root= --wd= --wdns= + --env --no-fork --help --version diff --git a/include/all-io.h b/include/all-io.h index e05e1e7ed7..168965a877 100644 --- a/include/all-io.h +++ b/include/all-io.h @@ -84,6 +84,36 @@ static inline ssize_t read_all(int fd, char *buf, size_t count) return c; } +static inline ssize_t read_all_alloc(int fd, char **buf) +{ + size_t size = 1024, c = 0; + ssize_t ret; + + *buf = malloc(size); + if (!*buf) + return -1; + + while (1) { + ret = read_all(fd, *buf + c, size - c); + if (ret < 0) { + free(*buf); + *buf = NULL; + return -1; + } + + if (ret == 0) + return c; + + c += ret; + if (c == size) { + size *= 2; + *buf = realloc(*buf, size); + if (!*buf) + return -1; + } + } +} + static inline ssize_t sendfile_all(int out, int in, off_t *off, size_t count) { #if defined(HAVE_SENDFILE) && defined(__linux__) diff --git a/include/env.h b/include/env.h index c2caecbaa0..9c853eac2f 100644 --- a/include/env.h +++ b/include/env.h @@ -11,6 +11,7 @@ extern void __sanitize_env(struct ul_env_list **org); extern int env_list_setenv(struct ul_env_list *ls); extern void env_list_free(struct ul_env_list *ls); +extern struct ul_env_list *env_from_fd(int pid); extern char *safe_getenv(const char *arg); @@ -32,4 +33,3 @@ static inline int remote_entry(char **argv, int remove, int last) } #endif /* UTIL_LINUX_ENV_H */ - diff --git a/lib/env.c b/lib/env.c index 37125f4d5a..5c385f934b 100644 --- a/lib/env.c +++ b/lib/env.c @@ -19,6 +19,7 @@ #include #include "env.h" +#include "all-io.h" #ifndef HAVE_ENVIRON_DECL extern char **environ; @@ -81,6 +82,29 @@ static struct ul_env_list *env_list_add(struct ul_env_list *ls0, const char *str return ls; } +/* + * Use env_from_fd() to read environment from @fd. + * + * @fd must be /proc//environ file. +*/ +struct ul_env_list *env_from_fd(int fd) +{ + char *buf = NULL; + size_t rc = 0; + struct ul_env_list *ls = NULL; + + if ((rc = read_all_alloc(fd, &buf)) < 1) + return NULL; + buf[rc] = '\0'; + + while (rc > 0) { + ls = env_list_add(ls, buf); + buf += strlen(buf) + 1; + rc -= strlen(buf) + 1; + } + return ls; +} + /* * Use setenv() for all stuff in @ls. * diff --git a/sys-utils/nsenter.1.adoc b/sys-utils/nsenter.1.adoc index 2500ff2071..ada249af0d 100644 --- a/sys-utils/nsenter.1.adoc +++ b/sys-utils/nsenter.1.adoc @@ -121,6 +121,9 @@ Set the working directory. If no directory is specified, set the working directo *-W*, *--wdns*[=_directory_]:: Set the working directory. The _directory_ is open after switch to the requested namespaces and after *chroot*(2) call. The options *--wd* and *--wdns* are mutually exclusive. +*-e*, *--env*:: +Pass environment variables from the target process to the new process being created. If this option is not provided, the environment variables will remain the same as in the current namespace.. + *-F*, *--no-fork*:: Do not fork before exec'ing the specified program. By default, when entering a PID namespace, *nsenter* calls *fork* before calling *exec* so that any children will also be in the newly entered PID namespace. diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index 440bf3b9f8..c145669179 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -42,6 +42,9 @@ #include "namespace.h" #include "exec_shell.h" #include "optutils.h" +#include "xalloc.h" +#include "all-io.h" +#include "env.h" static struct namespace_file { int nstype; @@ -94,7 +97,8 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" --preserve-credentials do not touch uids or gids\n"), out); fputs(_(" -r, --root[=] set the root directory\n"), out); fputs(_(" -w, --wd[=] set the working directory\n"), out); - fputs(_(" -W. --wdns set the working directory in namespace\n"), out); + fputs(_(" -W, --wdns set the working directory in namespace\n"), out); + fputs(_(" -e, --env inherit environment variables from target process\n"), out); fputs(_(" -F, --no-fork do not fork before exec'ing \n"), out); #ifdef HAVE_LIBSELINUX fputs(_(" -Z, --follow-context set SELinux context according to --target PID\n"), out); @@ -110,6 +114,7 @@ static void __attribute__((__noreturn__)) usage(void) static pid_t namespace_target_pid = 0; static int root_fd = -1; static int wd_fd = -1; +static int env_fd = -1; static void open_target_fd(int *fd, const char *type, const char *path) { @@ -246,6 +251,7 @@ int main(int argc, char *argv[]) { "root", optional_argument, NULL, 'r' }, { "wd", optional_argument, NULL, 'w' }, { "wdns", optional_argument, NULL, 'W' }, + { "env", no_argument, NULL, 'e' }, { "no-fork", no_argument, NULL, 'F' }, { "preserve-credentials", no_argument, NULL, OPT_PRESERVE_CRED }, #ifdef HAVE_LIBSELINUX @@ -261,12 +267,13 @@ int main(int argc, char *argv[]) struct namespace_file *nsfile; int c, pass, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0; - bool do_rd = false, do_wd = false, force_uid = false, force_gid = false; + bool do_rd = false, do_wd = false, force_uid = false, force_gid = false, do_env = false; bool do_all = false; int do_fork = -1; /* unknown yet */ char *wdns = NULL; uid_t uid = 0; gid_t gid = 0; + struct ul_env_list *envls; #ifdef HAVE_LIBSELINUX bool selinux = 0; #endif @@ -277,7 +284,7 @@ int main(int argc, char *argv[]) close_stdout_atexit(); while ((c = - getopt_long(argc, argv, "+ahVt:m::u::i::n::p::C::U::T::S:G:r::w::W:FZ", + getopt_long(argc, argv, "+ahVt:m::u::i::n::p::C::U::T::S:G:r::w::W::eFZ", longopts, NULL)) != -1) { err_exclusive_options(c, longopts, excl, excl_st); @@ -364,6 +371,9 @@ int main(int argc, char *argv[]) case 'W': wdns = optarg; break; + case 'e': + do_env = true; + break; case OPT_PRESERVE_CRED: preserve_cred = 1; break; @@ -420,6 +430,8 @@ int main(int argc, char *argv[]) open_target_fd(&root_fd, "root", NULL); if (do_wd) open_target_fd(&wd_fd, "cwd", NULL); + if (do_env) + open_target_fd(&env_fd, "environ", NULL); /* * Update namespaces variable to contain all requested namespaces @@ -510,6 +522,17 @@ int main(int argc, char *argv[]) wd_fd = -1; } + /* Pass environment variables of the target process to the spawned process */ + if (env_fd >= 0) { + if ((envls = env_from_fd(env_fd)) == NULL) + err(EXIT_FAILURE, _("failed to get environment variables")); + clearenv(); + if (env_list_setenv(envls) < 0) + err(EXIT_FAILURE, _("failed to set environment variables")); + env_list_free(envls); + close(env_fd); + } + if (do_fork == 1) continue_as_child();