]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/nsenter.c
nsenter: Replace a bare strtoul with strtoul_or_err
[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
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>
28#include <unistd.h>
29
0d3ec860 30#include "strutils.h"
f8aa8e94
EB
31#include "nls.h"
32#include "c.h"
33#include "closestream.h"
34
35#ifndef CLONE_NEWSNS
36# define CLONE_NEWNS 0x00020000
37#endif
38#ifndef CLONE_NEWUTS
39# define CLONE_NEWUTS 0x04000000
40#endif
41#ifndef CLONE_NEWIPC
42# define CLONE_NEWIPC 0x08000000
43#endif
44#ifndef CLONE_NEWNET
45# define CLONE_NEWNET 0x40000000
46#endif
47#ifndef CLONE_NEWUSER
48# define CLONE_NEWUSER 0x10000000
49#endif
50#ifndef CLONE_NEWPID
51# define CLONE_NEWPID 0x20000000
52#endif
53
54#ifndef HAVE_SETNS
55# include <sys/syscall.h>
56static int setns(int fd, int nstype)
57{
58 return syscall(SYS_setns, fd, nstype);
59}
60#endif /* HAVE_SETNS */
61
62static struct namespace_file{
63 int nstype;
f9bbdea6 64 const char *name;
f8aa8e94
EB
65 int fd;
66} namespace_files[] = {
ebbc87cd 67 /* Careful the order is significant in this array.
f8aa8e94
EB
68 *
69 * The user namespace comes first, so that it is entered
70 * first. This gives an unprivileged user the potential to
71 * enter the other namespaces.
72 */
73 { .nstype = CLONE_NEWUSER, .name = "ns/user", .fd = -1 },
74 { .nstype = CLONE_NEWIPC, .name = "ns/ipc", .fd = -1 },
75 { .nstype = CLONE_NEWUTS, .name = "ns/uts", .fd = -1 },
76 { .nstype = CLONE_NEWNET, .name = "ns/net", .fd = -1 },
77 { .nstype = CLONE_NEWPID, .name = "ns/pid", .fd = -1 },
78 { .nstype = CLONE_NEWNS, .name = "ns/mnt", .fd = -1 },
79 {}
80};
81
82static void usage(int status)
83{
84 FILE *out = status == EXIT_SUCCESS ? stdout : stderr;
85
86 fputs(USAGE_HEADER, out);
87 fprintf(out, _(" %s [options] <program> [args...]\n"),
88 program_invocation_short_name);
89
90 fputs(USAGE_OPTIONS, out);
91 fputs(_(" -t, --target <pid> target process to get namespaces from\n"
92 " -m, --mount [<file>] enter mount namespace\n"
93 " -u, --uts [<file>] enter UTS namespace (hostname etc)\n"
94 " -i, --ipc [<file>] enter System V IPC namespace\n"
95 " -n, --net [<file>] enter network namespace\n"
96 " -p, --pid [<file>] enter pid namespace\n"
97 " -U, --user [<file>] enter user namespace\n"
98 " -e, --exec don't fork before exec'ing <program>\n"
99 " -r, --root [<dir>] set the root directory\n"
100 " -w, --wd [<dir>] set the working directory\n"), out);
101 fputs(USAGE_SEPARATOR, out);
102 fputs(USAGE_HELP, out);
103 fputs(USAGE_VERSION, out);
104 fprintf(out, USAGE_MAN_TAIL("nsenter(1)"));
105
106 exit(status);
107}
108
109static pid_t namespace_target_pid = 0;
110static int root_fd = -1;
111static int wd_fd = -1;
112
f9bbdea6 113static void open_target_fd(int *fd, const char *type, const char *path)
f8aa8e94
EB
114{
115 char pathbuf[PATH_MAX];
116
117 if (!path && namespace_target_pid) {
118 snprintf(pathbuf, sizeof(pathbuf), "/proc/%u/%s",
119 namespace_target_pid, type);
120 path = pathbuf;
121 }
122 if (!path)
123 err(EXIT_FAILURE, _("No filename and no target pid supplied for %s"),
124 type);
125
126 if (*fd >= 0)
127 close(*fd);
128
129 *fd = open(path, O_RDONLY);
130 if (*fd < 0)
131 err(EXIT_FAILURE, _("open of '%s' failed"), path);
132}
133
f9bbdea6 134static void open_namespace_fd(int nstype, const char *path)
f8aa8e94
EB
135{
136 struct namespace_file *nsfile;
137
138 for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
139 if (nstype != nsfile->nstype)
140 continue;
141
142 open_target_fd(&nsfile->fd, nsfile->name, path);
143 return;
144 }
145 /* This should never happen */
146 err(EXIT_FAILURE, "Unrecognized namespace type");
147}
148
c9515f86
EB
149static void continue_as_child(void)
150{
151 pid_t child = fork();
152 int status;
153 pid_t ret;
154
155 if (child < 0)
156 err(EXIT_FAILURE, _("fork failed"));
157
158 /* Only the child returns */
159 if (child == 0)
160 return;
161
162 for (;;) {
163 ret = waitpid(child, &status, WUNTRACED);
164 if ((ret == child) && (WIFSTOPPED(status))) {
165 /* The child suspended so suspend us as well */
166 kill(getpid(), SIGSTOP);
167 kill(child, SIGCONT);
168 } else {
169 break;
170 }
171 }
172 /* Return the child's exit code if possible */
173 if (WIFEXITED(status)) {
174 exit(WEXITSTATUS(status));
175 }
176 else if (WIFSIGNALED(status)) {
177 kill(getpid(), WTERMSIG(status));
178 }
179 exit(EXIT_FAILURE);
180}
181
f8aa8e94
EB
182int main(int argc, char *argv[])
183{
184 static const struct option longopts[] = {
185 { "help", no_argument, NULL, 'h' },
186 { "version", no_argument, NULL, 'V'},
187 { "target", required_argument, NULL, 't' },
188 { "mount", optional_argument, NULL, 'm' },
189 { "uts", optional_argument, NULL, 'u' },
190 { "ipc", optional_argument, NULL, 'i' },
191 { "net", optional_argument, NULL, 'n' },
192 { "pid", optional_argument, NULL, 'p' },
193 { "user", optional_argument, NULL, 'U' },
194 { "exec", no_argument, NULL, 'e' },
195 { "root", optional_argument, NULL, 'r' },
196 { "wd", optional_argument, NULL, 'w' },
197 { NULL, 0, NULL, 0 }
198 };
199
200 struct namespace_file *nsfile;
201 int do_fork = 0;
f8aa8e94
EB
202 int c;
203
204 setlocale(LC_MESSAGES, "");
205 bindtextdomain(PACKAGE, LOCALEDIR);
206 textdomain(PACKAGE);
207 atexit(close_stdout);
208
209 while((c = getopt_long(argc, argv, "hVt:m::u::i::n::p::U::er::w::", longopts, NULL)) != -1) {
210 switch(c) {
211 case 'h':
212 usage(EXIT_SUCCESS);
213 case 'V':
214 printf(UTIL_LINUX_VERSION);
215 return EXIT_SUCCESS;
216 case 't':
0d3ec860 217 namespace_target_pid = strtoul_or_err(optarg, _("failed to parse pid"));
f8aa8e94
EB
218 break;
219 case 'm':
220 open_namespace_fd(CLONE_NEWNS, optarg);
221 break;
222 case 'u':
223 open_namespace_fd(CLONE_NEWUTS, optarg);
224 break;
225 case 'i':
226 open_namespace_fd(CLONE_NEWIPC, optarg);
227 break;
228 case 'n':
229 open_namespace_fd(CLONE_NEWNET, optarg);
230 break;
231 case 'p':
232 do_fork = 1;
233 open_namespace_fd(CLONE_NEWPID, optarg);
234 break;
235 case 'U':
236 open_namespace_fd(CLONE_NEWUSER, optarg);
237 break;
238 case 'e':
239 do_fork = 0;
240 break;
241 case 'r':
242 open_target_fd(&root_fd, "root", optarg);
243 break;
244 case 'w':
245 open_target_fd(&wd_fd, "cwd", optarg);
246 break;
247 default:
248 usage(EXIT_FAILURE);
249 }
250 }
251
252 if(optind >= argc)
253 usage(EXIT_FAILURE);
254
255 /*
256 * Now that we know which namespaces we want to enter, enter them.
257 */
258 for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
259 if (nsfile->fd < 0)
260 continue;
261 if (setns(nsfile->fd, nsfile->nstype))
262 err(EXIT_FAILURE, _("setns of '%s' failed"),
263 nsfile->name);
264 close(nsfile->fd);
265 nsfile->fd = -1;
266 }
267
268 /* Remember the current working directory if I'm not changing it */
269 if (root_fd >= 0 && wd_fd < 0) {
270 wd_fd = open(".", O_RDONLY);
271 if (wd_fd < 0)
272 err(EXIT_FAILURE, _("open of . failed"));
273 }
274
275 /* Change the root directory */
276 if (root_fd >= 0) {
277 if (fchdir(root_fd) < 0)
278 err(EXIT_FAILURE, _("fchdir to root_fd failed"));
279
280 if (chroot(".") < 0)
281 err(EXIT_FAILURE, _("chroot failed"));
282
283 close(root_fd);
284 root_fd = -1;
285 }
286
287 /* Change the working directory */
288 if (wd_fd >= 0) {
289 if (fchdir(wd_fd) < 0)
290 err(EXIT_FAILURE, _("fchdir to wd_fd failed"));
291
292 close(wd_fd);
293 wd_fd = -1;
294 }
295
c9515f86
EB
296 if (do_fork)
297 continue_as_child();
f8aa8e94
EB
298
299 execvp(argv[optind], argv + optind);
300
301 err(EXIT_FAILURE, _("exec %s failed"), argv[optind]);
302}