]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/nsenter.c
dmesg: -w output not line-buffered
[thirdparty/util-linux.git] / sys-utils / nsenter.c
CommitLineData
f8aa8e94
EB
1/*
2 * nsenter(1) - command-line interface for setns(2)
3 *
4 * Copyright (C) 2012-2013 Eric Biederman <ebiederm@xmission.com>
5 *
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.
9 *
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.
14 *
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.
18 */
19
f8aa8e94
EB
20#include <dirent.h>
21#include <errno.h>
22#include <getopt.h>
23#include <sched.h>
24#include <stdio.h>
25#include <stdlib.h>
984e1b7c 26#include <stdbool.h>
f8aa8e94 27#include <unistd.h>
dfd8b117 28#include <assert.h>
57580694
ZJS
29#include <sys/types.h>
30#include <sys/wait.h>
f8aa8e94 31
0d3ec860 32#include "strutils.h"
f8aa8e94
EB
33#include "nls.h"
34#include "c.h"
35#include "closestream.h"
c91280a4 36#include "namespace.h"
57580694 37#include "exec_shell.h"
f8aa8e94 38
a167328a 39static struct namespace_file {
f8aa8e94 40 int nstype;
f9bbdea6 41 const char *name;
f8aa8e94
EB
42 int fd;
43} namespace_files[] = {
ebbc87cd 44 /* Careful the order is significant in this array.
f8aa8e94
EB
45 *
46 * The user namespace comes first, so that it is entered
47 * first. This gives an unprivileged user the potential to
48 * enter the other namespaces.
49 */
50 { .nstype = CLONE_NEWUSER, .name = "ns/user", .fd = -1 },
51 { .nstype = CLONE_NEWIPC, .name = "ns/ipc", .fd = -1 },
52 { .nstype = CLONE_NEWUTS, .name = "ns/uts", .fd = -1 },
53 { .nstype = CLONE_NEWNET, .name = "ns/net", .fd = -1 },
54 { .nstype = CLONE_NEWPID, .name = "ns/pid", .fd = -1 },
55 { .nstype = CLONE_NEWNS, .name = "ns/mnt", .fd = -1 },
9905912f 56 { .nstype = 0, .name = NULL, .fd = -1 }
f8aa8e94
EB
57};
58
59static void usage(int status)
60{
61 FILE *out = status == EXIT_SUCCESS ? stdout : stderr;
62
63 fputs(USAGE_HEADER, out);
64 fprintf(out, _(" %s [options] <program> [args...]\n"),
65 program_invocation_short_name);
66
67 fputs(USAGE_OPTIONS, out);
26f879ed
SK
68 fputs(_(" -t, --target <pid> target process to get namespaces from\n"), out);
69 fputs(_(" -m, --mount [=<file>] enter mount namespace\n"), out);
70 fputs(_(" -u, --uts [=<file>] enter UTS namespace (hostname etc)\n"), out);
71 fputs(_(" -i, --ipc [=<file>] enter System V IPC namespace\n"), out);
72 fputs(_(" -n, --net [=<file>] enter network namespace\n"), out);
73 fputs(_(" -p, --pid [=<file>] enter pid namespace\n"), out);
74 fputs(_(" -U, --user [=<file>] enter user namespace\n"), out);
6b9e5bf6
RW
75 fputs(_(" -S, --setuid <uid> set uid in user namespace\n"), out);
76 fputs(_(" -G, --setgid <gid> set gid in user namespace\n"), out);
26f879ed
SK
77 fputs(_(" -r, --root [=<dir>] set the root directory\n"), out);
78 fputs(_(" -w, --wd [=<dir>] set the working directory\n"), out);
79 fputs(_(" -F, --no-fork do not fork before exec'ing <program>\n"), out);
80
f8aa8e94
EB
81 fputs(USAGE_SEPARATOR, out);
82 fputs(USAGE_HELP, out);
83 fputs(USAGE_VERSION, out);
84 fprintf(out, USAGE_MAN_TAIL("nsenter(1)"));
85
86 exit(status);
87}
88
89static pid_t namespace_target_pid = 0;
90static int root_fd = -1;
91static int wd_fd = -1;
92
f9bbdea6 93static void open_target_fd(int *fd, const char *type, const char *path)
f8aa8e94
EB
94{
95 char pathbuf[PATH_MAX];
96
97 if (!path && namespace_target_pid) {
98 snprintf(pathbuf, sizeof(pathbuf), "/proc/%u/%s",
a167328a 99 namespace_target_pid, type);
f8aa8e94
EB
100 path = pathbuf;
101 }
102 if (!path)
a167328a
SK
103 errx(EXIT_FAILURE,
104 _("neither filename nor target pid supplied for %s"),
105 type);
f8aa8e94
EB
106
107 if (*fd >= 0)
108 close(*fd);
109
110 *fd = open(path, O_RDONLY);
111 if (*fd < 0)
8b7a7750 112 err(EXIT_FAILURE, _("cannot open %s"), path);
f8aa8e94
EB
113}
114
f9bbdea6 115static void open_namespace_fd(int nstype, const char *path)
f8aa8e94
EB
116{
117 struct namespace_file *nsfile;
118
119 for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
120 if (nstype != nsfile->nstype)
121 continue;
122
123 open_target_fd(&nsfile->fd, nsfile->name, path);
124 return;
125 }
126 /* This should never happen */
dfd8b117 127 assert(nsfile->nstype);
f8aa8e94
EB
128}
129
c9515f86
EB
130static void continue_as_child(void)
131{
132 pid_t child = fork();
133 int status;
134 pid_t ret;
135
136 if (child < 0)
137 err(EXIT_FAILURE, _("fork failed"));
138
139 /* Only the child returns */
140 if (child == 0)
141 return;
142
143 for (;;) {
144 ret = waitpid(child, &status, WUNTRACED);
145 if ((ret == child) && (WIFSTOPPED(status))) {
146 /* The child suspended so suspend us as well */
147 kill(getpid(), SIGSTOP);
148 kill(child, SIGCONT);
149 } else {
150 break;
151 }
152 }
153 /* Return the child's exit code if possible */
154 if (WIFEXITED(status)) {
155 exit(WEXITSTATUS(status));
a167328a 156 } else if (WIFSIGNALED(status)) {
c9515f86
EB
157 kill(getpid(), WTERMSIG(status));
158 }
159 exit(EXIT_FAILURE);
160}
161
f8aa8e94
EB
162int main(int argc, char *argv[])
163{
164 static const struct option longopts[] = {
165 { "help", no_argument, NULL, 'h' },
166 { "version", no_argument, NULL, 'V'},
167 { "target", required_argument, NULL, 't' },
168 { "mount", optional_argument, NULL, 'm' },
169 { "uts", optional_argument, NULL, 'u' },
170 { "ipc", optional_argument, NULL, 'i' },
171 { "net", optional_argument, NULL, 'n' },
172 { "pid", optional_argument, NULL, 'p' },
173 { "user", optional_argument, NULL, 'U' },
6b9e5bf6
RW
174 { "setuid", required_argument, NULL, 'S' },
175 { "setgid", required_argument, NULL, 'G' },
f8aa8e94
EB
176 { "root", optional_argument, NULL, 'r' },
177 { "wd", optional_argument, NULL, 'w' },
28384adc 178 { "no-fork", no_argument, NULL, 'F' },
f8aa8e94
EB
179 { NULL, 0, NULL, 0 }
180 };
181
182 struct namespace_file *nsfile;
984e1b7c 183 int c, namespaces = 0;
57dbcf94
ZJS
184 bool do_rd = false, do_wd = false;
185 int do_fork = -1; /* unknown yet */
6b9e5bf6
RW
186 uid_t uid = 0;
187 gid_t gid = 0;
f8aa8e94 188
999ac5e2 189 setlocale(LC_ALL, "");
f8aa8e94
EB
190 bindtextdomain(PACKAGE, LOCALEDIR);
191 textdomain(PACKAGE);
192 atexit(close_stdout);
193
28384adc 194 while ((c =
6b9e5bf6 195 getopt_long(argc, argv, "hVt:m::u::i::n::p::U::S:G:r::w::F",
28384adc
ZJS
196 longopts, NULL)) != -1) {
197 switch (c) {
f8aa8e94
EB
198 case 'h':
199 usage(EXIT_SUCCESS);
200 case 'V':
201 printf(UTIL_LINUX_VERSION);
202 return EXIT_SUCCESS;
203 case 't':
a167328a
SK
204 namespace_target_pid =
205 strtoul_or_err(optarg, _("failed to parse pid"));
f8aa8e94
EB
206 break;
207 case 'm':
984e1b7c
ZJS
208 if (optarg)
209 open_namespace_fd(CLONE_NEWNS, optarg);
210 else
211 namespaces |= CLONE_NEWNS;
f8aa8e94
EB
212 break;
213 case 'u':
984e1b7c
ZJS
214 if (optarg)
215 open_namespace_fd(CLONE_NEWUTS, optarg);
216 else
217 namespaces |= CLONE_NEWUTS;
f8aa8e94
EB
218 break;
219 case 'i':
984e1b7c
ZJS
220 if (optarg)
221 open_namespace_fd(CLONE_NEWIPC, optarg);
222 else
223 namespaces |= CLONE_NEWIPC;
f8aa8e94
EB
224 break;
225 case 'n':
984e1b7c
ZJS
226 if (optarg)
227 open_namespace_fd(CLONE_NEWNET, optarg);
228 else
229 namespaces |= CLONE_NEWNET;
f8aa8e94
EB
230 break;
231 case 'p':
984e1b7c
ZJS
232 if (optarg)
233 open_namespace_fd(CLONE_NEWPID, optarg);
234 else
235 namespaces |= CLONE_NEWPID;
f8aa8e94
EB
236 break;
237 case 'U':
984e1b7c
ZJS
238 if (optarg)
239 open_namespace_fd(CLONE_NEWUSER, optarg);
240 else
241 namespaces |= CLONE_NEWUSER;
f8aa8e94 242 break;
6b9e5bf6
RW
243 case 'S':
244 uid = strtoul_or_err(optarg, _("failed to parse uid"));
245 break;
246 case 'G':
247 gid = strtoul_or_err(optarg, _("failed to parse gid"));
248 break;
28384adc 249 case 'F':
57dbcf94 250 do_fork = 0;
f8aa8e94
EB
251 break;
252 case 'r':
984e1b7c
ZJS
253 if (optarg)
254 open_target_fd(&root_fd, "root", optarg);
255 else
256 do_rd = true;
f8aa8e94
EB
257 break;
258 case 'w':
984e1b7c
ZJS
259 if (optarg)
260 open_target_fd(&wd_fd, "cwd", optarg);
261 else
262 do_wd = true;
f8aa8e94
EB
263 break;
264 default:
265 usage(EXIT_FAILURE);
266 }
267 }
268
984e1b7c
ZJS
269 /*
270 * Open remaining namespace and directory descriptors.
271 */
272 for (nsfile = namespace_files; nsfile->nstype; nsfile++)
273 if (nsfile->nstype & namespaces)
274 open_namespace_fd(nsfile->nstype, NULL);
275 if (do_rd)
276 open_target_fd(&root_fd, "root", NULL);
277 if (do_wd)
278 open_target_fd(&wd_fd, "cwd", NULL);
279
f8aa8e94
EB
280 /*
281 * Now that we know which namespaces we want to enter, enter them.
282 */
283 for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
284 if (nsfile->fd < 0)
285 continue;
57dbcf94
ZJS
286 if (nsfile->nstype == CLONE_NEWPID && do_fork == -1)
287 do_fork = 1;
f8aa8e94 288 if (setns(nsfile->fd, nsfile->nstype))
a167328a
SK
289 err(EXIT_FAILURE,
290 _("reassociate to namespace '%s' failed"),
f8aa8e94
EB
291 nsfile->name);
292 close(nsfile->fd);
293 nsfile->fd = -1;
294 }
295
296 /* Remember the current working directory if I'm not changing it */
297 if (root_fd >= 0 && wd_fd < 0) {
298 wd_fd = open(".", O_RDONLY);
299 if (wd_fd < 0)
a167328a
SK
300 err(EXIT_FAILURE,
301 _("cannot open current working directory"));
f8aa8e94
EB
302 }
303
304 /* Change the root directory */
305 if (root_fd >= 0) {
306 if (fchdir(root_fd) < 0)
a167328a
SK
307 err(EXIT_FAILURE,
308 _("change directory by root file descriptor failed"));
f8aa8e94
EB
309
310 if (chroot(".") < 0)
311 err(EXIT_FAILURE, _("chroot failed"));
312
313 close(root_fd);
314 root_fd = -1;
315 }
316
317 /* Change the working directory */
318 if (wd_fd >= 0) {
319 if (fchdir(wd_fd) < 0)
a167328a
SK
320 err(EXIT_FAILURE,
321 _("change directory by working directory file descriptor failed"));
f8aa8e94
EB
322
323 close(wd_fd);
324 wd_fd = -1;
325 }
326
57dbcf94 327 if (do_fork == 1)
c9515f86 328 continue_as_child();
f8aa8e94 329
6b9e5bf6
RW
330 if (namespaces & CLONE_NEWUSER) {
331 if (setuid(uid) < 0)
332 err(EXIT_FAILURE, _("setuid failed"));
333 if (setgid(gid) < 0)
334 err(EXIT_FAILURE, _("setgid failed"));
335 }
336
57580694
ZJS
337 if (optind < argc) {
338 execvp(argv[optind], argv + optind);
339 err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]);
340 }
341 exec_shell();
f8aa8e94 342}