]>
Commit | Line | Data |
---|---|---|
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 | ||
20 | #include <sys/types.h> | |
21 | #include <sys/wait.h> | |
22 | #include <dirent.h> | |
23 | #include <errno.h> | |
24 | #include <getopt.h> | |
25 | #include <sched.h> | |
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
984e1b7c | 28 | #include <stdbool.h> |
f8aa8e94 | 29 | #include <unistd.h> |
dfd8b117 | 30 | #include <assert.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" |
f8aa8e94 | 37 | |
a167328a | 38 | static struct namespace_file { |
f8aa8e94 | 39 | int nstype; |
f9bbdea6 | 40 | const char *name; |
f8aa8e94 EB |
41 | int fd; |
42 | } namespace_files[] = { | |
ebbc87cd | 43 | /* Careful the order is significant in this array. |
f8aa8e94 EB |
44 | * |
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. | |
48 | */ | |
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 }, | |
9905912f | 55 | { .nstype = 0, .name = NULL, .fd = -1 } |
f8aa8e94 EB |
56 | }; |
57 | ||
58 | static void usage(int status) | |
59 | { | |
60 | FILE *out = status == EXIT_SUCCESS ? stdout : stderr; | |
61 | ||
62 | fputs(USAGE_HEADER, out); | |
63 | fprintf(out, _(" %s [options] <program> [args...]\n"), | |
64 | program_invocation_short_name); | |
65 | ||
66 | fputs(USAGE_OPTIONS, out); | |
620d3f2d KZ |
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" | |
620d3f2d | 74 | " -r, --root [=<dir>] set the root directory\n" |
28384adc ZJS |
75 | " -w, --wd [=<dir>] set the working directory\n" |
76 | " -F, --no-fork don't fork before exec'ing <program>\n"), out); | |
f8aa8e94 EB |
77 | fputs(USAGE_SEPARATOR, out); |
78 | fputs(USAGE_HELP, out); | |
79 | fputs(USAGE_VERSION, out); | |
80 | fprintf(out, USAGE_MAN_TAIL("nsenter(1)")); | |
81 | ||
82 | exit(status); | |
83 | } | |
84 | ||
85 | static pid_t namespace_target_pid = 0; | |
86 | static int root_fd = -1; | |
87 | static int wd_fd = -1; | |
88 | ||
f9bbdea6 | 89 | static void open_target_fd(int *fd, const char *type, const char *path) |
f8aa8e94 EB |
90 | { |
91 | char pathbuf[PATH_MAX]; | |
92 | ||
93 | if (!path && namespace_target_pid) { | |
94 | snprintf(pathbuf, sizeof(pathbuf), "/proc/%u/%s", | |
a167328a | 95 | namespace_target_pid, type); |
f8aa8e94 EB |
96 | path = pathbuf; |
97 | } | |
98 | if (!path) | |
a167328a SK |
99 | errx(EXIT_FAILURE, |
100 | _("neither filename nor target pid supplied for %s"), | |
101 | type); | |
f8aa8e94 EB |
102 | |
103 | if (*fd >= 0) | |
104 | close(*fd); | |
105 | ||
106 | *fd = open(path, O_RDONLY); | |
107 | if (*fd < 0) | |
8b7a7750 | 108 | err(EXIT_FAILURE, _("cannot open %s"), path); |
f8aa8e94 EB |
109 | } |
110 | ||
f9bbdea6 | 111 | static void open_namespace_fd(int nstype, const char *path) |
f8aa8e94 EB |
112 | { |
113 | struct namespace_file *nsfile; | |
114 | ||
115 | for (nsfile = namespace_files; nsfile->nstype; nsfile++) { | |
116 | if (nstype != nsfile->nstype) | |
117 | continue; | |
118 | ||
119 | open_target_fd(&nsfile->fd, nsfile->name, path); | |
120 | return; | |
121 | } | |
122 | /* This should never happen */ | |
dfd8b117 | 123 | assert(nsfile->nstype); |
f8aa8e94 EB |
124 | } |
125 | ||
c9515f86 EB |
126 | static void continue_as_child(void) |
127 | { | |
128 | pid_t child = fork(); | |
129 | int status; | |
130 | pid_t ret; | |
131 | ||
132 | if (child < 0) | |
133 | err(EXIT_FAILURE, _("fork failed")); | |
134 | ||
135 | /* Only the child returns */ | |
136 | if (child == 0) | |
137 | return; | |
138 | ||
139 | for (;;) { | |
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); | |
145 | } else { | |
146 | break; | |
147 | } | |
148 | } | |
149 | /* Return the child's exit code if possible */ | |
150 | if (WIFEXITED(status)) { | |
151 | exit(WEXITSTATUS(status)); | |
a167328a | 152 | } else if (WIFSIGNALED(status)) { |
c9515f86 EB |
153 | kill(getpid(), WTERMSIG(status)); |
154 | } | |
155 | exit(EXIT_FAILURE); | |
156 | } | |
157 | ||
f8aa8e94 EB |
158 | int main(int argc, char *argv[]) |
159 | { | |
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' }, | |
f8aa8e94 EB |
170 | { "root", optional_argument, NULL, 'r' }, |
171 | { "wd", optional_argument, NULL, 'w' }, | |
28384adc | 172 | { "no-fork", no_argument, NULL, 'F' }, |
f8aa8e94 EB |
173 | { NULL, 0, NULL, 0 } |
174 | }; | |
175 | ||
176 | struct namespace_file *nsfile; | |
984e1b7c | 177 | int c, namespaces = 0; |
28384adc | 178 | bool do_rd = false, do_wd = false, do_fork = false; |
f8aa8e94 EB |
179 | |
180 | setlocale(LC_MESSAGES, ""); | |
181 | bindtextdomain(PACKAGE, LOCALEDIR); | |
182 | textdomain(PACKAGE); | |
183 | atexit(close_stdout); | |
184 | ||
28384adc ZJS |
185 | while ((c = |
186 | getopt_long(argc, argv, "hVt:m::u::i::n::p::U::r::w::F", | |
187 | longopts, NULL)) != -1) { | |
188 | switch (c) { | |
f8aa8e94 EB |
189 | case 'h': |
190 | usage(EXIT_SUCCESS); | |
191 | case 'V': | |
192 | printf(UTIL_LINUX_VERSION); | |
193 | return EXIT_SUCCESS; | |
194 | case 't': | |
a167328a SK |
195 | namespace_target_pid = |
196 | strtoul_or_err(optarg, _("failed to parse pid")); | |
f8aa8e94 EB |
197 | break; |
198 | case 'm': | |
984e1b7c ZJS |
199 | if (optarg) |
200 | open_namespace_fd(CLONE_NEWNS, optarg); | |
201 | else | |
202 | namespaces |= CLONE_NEWNS; | |
f8aa8e94 EB |
203 | break; |
204 | case 'u': | |
984e1b7c ZJS |
205 | if (optarg) |
206 | open_namespace_fd(CLONE_NEWUTS, optarg); | |
207 | else | |
208 | namespaces |= CLONE_NEWUTS; | |
f8aa8e94 EB |
209 | break; |
210 | case 'i': | |
984e1b7c ZJS |
211 | if (optarg) |
212 | open_namespace_fd(CLONE_NEWIPC, optarg); | |
213 | else | |
214 | namespaces |= CLONE_NEWIPC; | |
f8aa8e94 EB |
215 | break; |
216 | case 'n': | |
984e1b7c ZJS |
217 | if (optarg) |
218 | open_namespace_fd(CLONE_NEWNET, optarg); | |
219 | else | |
220 | namespaces |= CLONE_NEWNET; | |
f8aa8e94 EB |
221 | break; |
222 | case 'p': | |
28384adc | 223 | do_fork = true; |
984e1b7c ZJS |
224 | if (optarg) |
225 | open_namespace_fd(CLONE_NEWPID, optarg); | |
226 | else | |
227 | namespaces |= CLONE_NEWPID; | |
f8aa8e94 EB |
228 | break; |
229 | case 'U': | |
984e1b7c ZJS |
230 | if (optarg) |
231 | open_namespace_fd(CLONE_NEWUSER, optarg); | |
232 | else | |
233 | namespaces |= CLONE_NEWUSER; | |
f8aa8e94 | 234 | break; |
28384adc ZJS |
235 | case 'F': |
236 | do_fork = false; | |
f8aa8e94 EB |
237 | break; |
238 | case 'r': | |
984e1b7c ZJS |
239 | if (optarg) |
240 | open_target_fd(&root_fd, "root", optarg); | |
241 | else | |
242 | do_rd = true; | |
f8aa8e94 EB |
243 | break; |
244 | case 'w': | |
984e1b7c ZJS |
245 | if (optarg) |
246 | open_target_fd(&wd_fd, "cwd", optarg); | |
247 | else | |
248 | do_wd = true; | |
f8aa8e94 EB |
249 | break; |
250 | default: | |
251 | usage(EXIT_FAILURE); | |
252 | } | |
253 | } | |
254 | ||
a167328a | 255 | if (optind >= argc) |
f8aa8e94 EB |
256 | usage(EXIT_FAILURE); |
257 | ||
984e1b7c ZJS |
258 | /* |
259 | * Open remaining namespace and directory descriptors. | |
260 | */ | |
261 | for (nsfile = namespace_files; nsfile->nstype; nsfile++) | |
262 | if (nsfile->nstype & namespaces) | |
263 | open_namespace_fd(nsfile->nstype, NULL); | |
264 | if (do_rd) | |
265 | open_target_fd(&root_fd, "root", NULL); | |
266 | if (do_wd) | |
267 | open_target_fd(&wd_fd, "cwd", NULL); | |
268 | ||
f8aa8e94 EB |
269 | /* |
270 | * Now that we know which namespaces we want to enter, enter them. | |
271 | */ | |
272 | for (nsfile = namespace_files; nsfile->nstype; nsfile++) { | |
273 | if (nsfile->fd < 0) | |
274 | continue; | |
275 | if (setns(nsfile->fd, nsfile->nstype)) | |
a167328a SK |
276 | err(EXIT_FAILURE, |
277 | _("reassociate to namespace '%s' failed"), | |
f8aa8e94 EB |
278 | nsfile->name); |
279 | close(nsfile->fd); | |
280 | nsfile->fd = -1; | |
281 | } | |
282 | ||
283 | /* Remember the current working directory if I'm not changing it */ | |
284 | if (root_fd >= 0 && wd_fd < 0) { | |
285 | wd_fd = open(".", O_RDONLY); | |
286 | if (wd_fd < 0) | |
a167328a SK |
287 | err(EXIT_FAILURE, |
288 | _("cannot open current working directory")); | |
f8aa8e94 EB |
289 | } |
290 | ||
291 | /* Change the root directory */ | |
292 | if (root_fd >= 0) { | |
293 | if (fchdir(root_fd) < 0) | |
a167328a SK |
294 | err(EXIT_FAILURE, |
295 | _("change directory by root file descriptor failed")); | |
f8aa8e94 EB |
296 | |
297 | if (chroot(".") < 0) | |
298 | err(EXIT_FAILURE, _("chroot failed")); | |
299 | ||
300 | close(root_fd); | |
301 | root_fd = -1; | |
302 | } | |
303 | ||
304 | /* Change the working directory */ | |
305 | if (wd_fd >= 0) { | |
306 | if (fchdir(wd_fd) < 0) | |
a167328a SK |
307 | err(EXIT_FAILURE, |
308 | _("change directory by working directory file descriptor failed")); | |
f8aa8e94 EB |
309 | |
310 | close(wd_fd); | |
311 | wd_fd = -1; | |
312 | } | |
313 | ||
c9515f86 EB |
314 | if (do_fork) |
315 | continue_as_child(); | |
f8aa8e94 EB |
316 | |
317 | execvp(argv[optind], argv + optind); | |
318 | ||
319 | err(EXIT_FAILURE, _("exec %s failed"), argv[optind]); | |
320 | } |