]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
nsenter: add --env for allowing environment variables inheritance
authoru2386 <hugo.cavan2386@gmail.com>
Mon, 27 Feb 2023 02:18:31 +0000 (02:18 +0000)
committeru2386 <hugo.cavan2386@gmail.com>
Thu, 2 Mar 2023 16:24:05 +0000 (16:24 +0000)
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 <thomas@t-8ch.de>
Reviewed-by: Karel Zak <kzak@redhat.com>
Signed-off-by: u2386 <hugo.cavan2386@gmail.com>
bash-completion/nsenter
include/all-io.h
include/env.h
lib/env.c
sys-utils/nsenter.1.adoc
sys-utils/nsenter.c

index d3cb23f0b12ee8b7a5ebfb4444462ca6142fbf8d..ace7934407fd623dca61737cc4b84d4c615310e8 100644 (file)
@@ -49,6 +49,7 @@ _nsenter_module()
                                --root=
                                --wd=
                                --wdns=
+                               --env
                                --no-fork
                                --help
                                --version
index e05e1e7ed7d000ec22dc0b141299eee6d5bd6c52..168965a877031afcb63dfd2d30181afb6c728841 100644 (file)
@@ -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__)
index c2caecbaa0ebcad0c3a44fd669fb259585c8729a..9c853eac2f4a7b94cd72aeb05d422a773fe800e2 100644 (file)
@@ -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 */
-
index 37125f4d5a0e62e78de839bb6d273a8e10b31e29..5c385f934b91f1272472e2f24189adf269761558 100644 (file)
--- a/lib/env.c
+++ b/lib/env.c
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 
 #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/<pid>/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.
  *
index 2500ff20716961b536c608997ee475571b21d8d1..ada249af0db4542e51370f52224c1bbae48187f2 100644 (file)
@@ -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.
 
index 440bf3b9f877b5e6522b5335530cc2e07f717df5..c1456691794652aa934abe8139ebc02184af132b 100644 (file)
@@ -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[=<dir>]     set the root directory\n"), out);
        fputs(_(" -w, --wd[=<dir>]       set the working directory\n"), out);
-       fputs(_(" -W. --wdns <dir>       set the working directory in namespace\n"), out);
+       fputs(_(" -W, --wdns <dir>       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 <program>\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();