From 974cc006f122f36e2187cedb9d3e58dc2d24814c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 13 Dec 2016 14:44:33 +0100 Subject: nsenter: add --all option Let's make it easy for users to enter target process namespaces. Addresses: https://github.com/karelzak/util-linux/issues/382 Signed-off-by: Karel Zak --- sys-utils/nsenter.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) (limited to 'sys-utils/nsenter.c') diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index f85ace9fc..fc301d9f3 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef HAVE_LIBSELINUX # include @@ -76,6 +77,7 @@ static void usage(int status) fputs(_("Run a program with namespaces of other processes.\n"), out); fputs(USAGE_OPTIONS, out); + fputs(_(" -a, --all enter all namespaces\n"), out); fputs(_(" -t, --target target process to get namespaces from\n"), out); fputs(_(" -m, --mount[=] enter mount namespace\n"), out); fputs(_(" -u, --uts[=] enter UTS namespace (hostname etc)\n"), out); @@ -143,6 +145,32 @@ static void open_namespace_fd(int nstype, const char *path) assert(nsfile->nstype); } +static int get_ns_ino(const char *path, ino_t *ino) +{ + struct stat st; + + if (stat(path, &st) != 0) + return -errno; + *ino = st.st_ino; + return 0; +} + +static int is_same_namespace(pid_t a, pid_t b, const char *type) +{ + char path[PATH_MAX]; + ino_t a_ino, b_ino; + + snprintf(path, sizeof(path), "/proc/%u/%s", a, type); + if (get_ns_ino(path, &a_ino) != 0) + err(EXIT_FAILURE, _("stat of %s failed"), path); + + snprintf(path, sizeof(path), "/proc/%u/%s", b, type); + if (get_ns_ino(path, &b_ino) != 0) + err(EXIT_FAILURE, _("stat of %s failed"), path); + + return a_ino == b_ino; +} + static void continue_as_child(void) { pid_t child = fork(); @@ -181,6 +209,7 @@ int main(int argc, char *argv[]) OPT_PRESERVE_CRED = CHAR_MAX + 1 }; static const struct option longopts[] = { + { "all", no_argument, NULL, 'a' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V'}, { "target", required_argument, NULL, 't' }, @@ -206,6 +235,7 @@ 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_all = false; int do_fork = -1; /* unknown yet */ uid_t uid = 0; gid_t gid = 0; @@ -219,7 +249,7 @@ int main(int argc, char *argv[]) atexit(close_stdout); while ((c = - getopt_long(argc, argv, "+hVt:m::u::i::n::p::C::U::S:G:r::w::FZ", + getopt_long(argc, argv, "+ahVt:m::u::i::n::p::C::U::S:G:r::w::FZ", longopts, NULL)) != -1) { switch (c) { case 'h': @@ -227,6 +257,9 @@ int main(int argc, char *argv[]) case 'V': printf(UTIL_LINUX_VERSION); return EXIT_SUCCESS; + case 'a': + do_all = true; + break; case 't': namespace_target_pid = strtoul_or_err(optarg, _("failed to parse pid")); @@ -323,6 +356,25 @@ int main(int argc, char *argv[]) freecon(scon); } #endif + + if (do_all) { + if (!namespace_target_pid) + errx(EXIT_FAILURE, _("no target PID specified for --all")); + for (nsfile = namespace_files; nsfile->nstype; nsfile++) { + if (nsfile->fd >= 0) + continue; /* namespace already specified */ + + /* It is not permitted to use setns(2) to reenter the caller's + * current user namespace; see setns(2) man page for more details. + */ + if (nsfile->nstype & CLONE_NEWUSER + && is_same_namespace(getpid(), namespace_target_pid, nsfile->name)) + continue; + + namespaces |= nsfile->nstype; + } + } + /* * Open remaining namespace and directory descriptors. */ -- cgit v1.2.3-55-g7522