2 * nsenter(1) - command-line interface for setns(2)
4 * Copyright (C) 2012-2013 Eric Biederman <ebiederm@xmission.com>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; version 2.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <sys/types.h>
35 #include "closestream.h"
36 #include "namespace.h"
38 static struct namespace_file
{
42 } namespace_files
[] = {
43 /* Careful the order is significant in this array.
45 * The user namespace comes first, so that it is entered
46 * first. This gives an unprivileged user the potential to
47 * enter the other namespaces.
49 { .nstype
= CLONE_NEWUSER
, .name
= "ns/user", .fd
= -1 },
50 { .nstype
= CLONE_NEWIPC
, .name
= "ns/ipc", .fd
= -1 },
51 { .nstype
= CLONE_NEWUTS
, .name
= "ns/uts", .fd
= -1 },
52 { .nstype
= CLONE_NEWNET
, .name
= "ns/net", .fd
= -1 },
53 { .nstype
= CLONE_NEWPID
, .name
= "ns/pid", .fd
= -1 },
54 { .nstype
= CLONE_NEWNS
, .name
= "ns/mnt", .fd
= -1 },
55 { .nstype
= 0, .name
= NULL
, .fd
= -1 }
58 static void usage(int status
)
60 FILE *out
= status
== EXIT_SUCCESS
? stdout
: stderr
;
62 fputs(USAGE_HEADER
, out
);
63 fprintf(out
, _(" %s [options] <program> [args...]\n"),
64 program_invocation_short_name
);
66 fputs(USAGE_OPTIONS
, out
);
67 fputs(_(" -t, --target <pid> target process to get namespaces from\n"
68 " -m, --mount [=<file>] enter mount namespace\n"
69 " -u, --uts [=<file>] enter UTS namespace (hostname etc)\n"
70 " -i, --ipc [=<file>] enter System V IPC namespace\n"
71 " -n, --net [=<file>] enter network namespace\n"
72 " -p, --pid [=<file>] enter pid namespace\n"
73 " -U, --user [=<file>] enter user namespace\n"
74 " -r, --root [=<dir>] set the root directory\n"
75 " -w, --wd [=<dir>] set the working directory\n"
76 " -F, --no-fork don't fork before exec'ing <program>\n"), out
);
77 fputs(USAGE_SEPARATOR
, out
);
78 fputs(USAGE_HELP
, out
);
79 fputs(USAGE_VERSION
, out
);
80 fprintf(out
, USAGE_MAN_TAIL("nsenter(1)"));
85 static pid_t namespace_target_pid
= 0;
86 static int root_fd
= -1;
87 static int wd_fd
= -1;
89 static void open_target_fd(int *fd
, const char *type
, const char *path
)
91 char pathbuf
[PATH_MAX
];
93 if (!path
&& namespace_target_pid
) {
94 snprintf(pathbuf
, sizeof(pathbuf
), "/proc/%u/%s",
95 namespace_target_pid
, type
);
100 _("neither filename nor target pid supplied for %s"),
106 *fd
= open(path
, O_RDONLY
);
108 err(EXIT_FAILURE
, _("cannot open %s"), path
);
111 static void open_namespace_fd(int nstype
, const char *path
)
113 struct namespace_file
*nsfile
;
115 for (nsfile
= namespace_files
; nsfile
->nstype
; nsfile
++) {
116 if (nstype
!= nsfile
->nstype
)
119 open_target_fd(&nsfile
->fd
, nsfile
->name
, path
);
122 /* This should never happen */
123 assert(nsfile
->nstype
);
126 static void continue_as_child(void)
128 pid_t child
= fork();
133 err(EXIT_FAILURE
, _("fork failed"));
135 /* Only the child returns */
140 ret
= waitpid(child
, &status
, WUNTRACED
);
141 if ((ret
== child
) && (WIFSTOPPED(status
))) {
142 /* The child suspended so suspend us as well */
143 kill(getpid(), SIGSTOP
);
144 kill(child
, SIGCONT
);
149 /* Return the child's exit code if possible */
150 if (WIFEXITED(status
)) {
151 exit(WEXITSTATUS(status
));
152 } else if (WIFSIGNALED(status
)) {
153 kill(getpid(), WTERMSIG(status
));
158 int main(int argc
, char *argv
[])
160 static const struct option longopts
[] = {
161 { "help", no_argument
, NULL
, 'h' },
162 { "version", no_argument
, NULL
, 'V'},
163 { "target", required_argument
, NULL
, 't' },
164 { "mount", optional_argument
, NULL
, 'm' },
165 { "uts", optional_argument
, NULL
, 'u' },
166 { "ipc", optional_argument
, NULL
, 'i' },
167 { "net", optional_argument
, NULL
, 'n' },
168 { "pid", optional_argument
, NULL
, 'p' },
169 { "user", optional_argument
, NULL
, 'U' },
170 { "root", optional_argument
, NULL
, 'r' },
171 { "wd", optional_argument
, NULL
, 'w' },
172 { "no-fork", no_argument
, NULL
, 'F' },
176 struct namespace_file
*nsfile
;
177 int c
, namespaces
= 0;
178 bool do_rd
= false, do_wd
= false;
179 int do_fork
= -1; /* unknown yet */
181 setlocale(LC_MESSAGES
, "");
182 bindtextdomain(PACKAGE
, LOCALEDIR
);
184 atexit(close_stdout
);
187 getopt_long(argc
, argv
, "hVt:m::u::i::n::p::U::r::w::F",
188 longopts
, NULL
)) != -1) {
193 printf(UTIL_LINUX_VERSION
);
196 namespace_target_pid
=
197 strtoul_or_err(optarg
, _("failed to parse pid"));
201 open_namespace_fd(CLONE_NEWNS
, optarg
);
203 namespaces
|= CLONE_NEWNS
;
207 open_namespace_fd(CLONE_NEWUTS
, optarg
);
209 namespaces
|= CLONE_NEWUTS
;
213 open_namespace_fd(CLONE_NEWIPC
, optarg
);
215 namespaces
|= CLONE_NEWIPC
;
219 open_namespace_fd(CLONE_NEWNET
, optarg
);
221 namespaces
|= CLONE_NEWNET
;
225 open_namespace_fd(CLONE_NEWPID
, optarg
);
227 namespaces
|= CLONE_NEWPID
;
231 open_namespace_fd(CLONE_NEWUSER
, optarg
);
233 namespaces
|= CLONE_NEWUSER
;
240 open_target_fd(&root_fd
, "root", optarg
);
246 open_target_fd(&wd_fd
, "cwd", optarg
);
259 * Open remaining namespace and directory descriptors.
261 for (nsfile
= namespace_files
; nsfile
->nstype
; nsfile
++)
262 if (nsfile
->nstype
& namespaces
)
263 open_namespace_fd(nsfile
->nstype
, NULL
);
265 open_target_fd(&root_fd
, "root", NULL
);
267 open_target_fd(&wd_fd
, "cwd", NULL
);
270 * Now that we know which namespaces we want to enter, enter them.
272 for (nsfile
= namespace_files
; nsfile
->nstype
; nsfile
++) {
275 if (nsfile
->nstype
== CLONE_NEWPID
&& do_fork
== -1)
277 if (setns(nsfile
->fd
, nsfile
->nstype
))
279 _("reassociate to namespace '%s' failed"),
285 /* Remember the current working directory if I'm not changing it */
286 if (root_fd
>= 0 && wd_fd
< 0) {
287 wd_fd
= open(".", O_RDONLY
);
290 _("cannot open current working directory"));
293 /* Change the root directory */
295 if (fchdir(root_fd
) < 0)
297 _("change directory by root file descriptor failed"));
300 err(EXIT_FAILURE
, _("chroot failed"));
306 /* Change the working directory */
308 if (fchdir(wd_fd
) < 0)
310 _("change directory by working directory file descriptor failed"));
319 execvp(argv
[optind
], argv
+ optind
);
321 err(EXIT_FAILURE
, _("exec %s failed"), argv
[optind
]);