2 * unshare(1) - command-line interface for unshare(2)
4 * Copyright (C) 2009 Mikhail Gusarov <dottedmag@dottedmag.net>
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; either version 2, or (at your option) any
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <sys/mount.h>
29 #include <sys/types.h>
31 #include <sys/prctl.h>
33 /* we only need some defines missing in sys/mount.h, no libmount linkage */
38 #include "closestream.h"
39 #include "namespace.h"
40 #include "exec_shell.h"
42 #include "pathnames.h"
46 /* synchronize parent and child by pipe */
47 #define PIPE_SYNC_BYTE 0x06
49 /* 'private' is kernel default */
50 #define UNSHARE_PROPAGATION_DEFAULT (MS_REC | MS_PRIVATE)
52 /* /proc namespace files and mountpoints for binds */
53 static struct namespace_file
{
54 int type
; /* CLONE_NEW* */
55 const char *name
; /* ns/<type> */
56 const char *target
; /* user specified target for bind mount */
57 } namespace_files
[] = {
58 { .type
= CLONE_NEWUSER
, .name
= "ns/user" },
59 { .type
= CLONE_NEWCGROUP
,.name
= "ns/cgroup" },
60 { .type
= CLONE_NEWIPC
, .name
= "ns/ipc" },
61 { .type
= CLONE_NEWUTS
, .name
= "ns/uts" },
62 { .type
= CLONE_NEWNET
, .name
= "ns/net" },
63 { .type
= CLONE_NEWPID
, .name
= "ns/pid" },
64 { .type
= CLONE_NEWNS
, .name
= "ns/mnt" },
68 static int npersists
; /* number of persistent namespaces */
77 static const char *setgroups_strings
[] =
79 [SETGROUPS_DENY
] = "deny",
80 [SETGROUPS_ALLOW
] = "allow"
83 static int setgroups_str2id(const char *str
)
87 for (i
= 0; i
< ARRAY_SIZE(setgroups_strings
); i
++)
88 if (strcmp(str
, setgroups_strings
[i
]) == 0)
91 errx(EXIT_FAILURE
, _("unsupported --setgroups argument '%s'"), str
);
94 static void setgroups_control(int action
)
96 const char *file
= _PATH_PROC_SETGROUPS
;
100 if (action
< 0 || (size_t) action
>= ARRAY_SIZE(setgroups_strings
))
102 cmd
= setgroups_strings
[action
];
104 fd
= open(file
, O_WRONLY
);
108 err(EXIT_FAILURE
, _("cannot open %s"), file
);
111 if (write_all(fd
, cmd
, strlen(cmd
)))
112 err(EXIT_FAILURE
, _("write failed %s"), file
);
116 static void map_id(const char *file
, uint32_t from
, uint32_t to
)
121 fd
= open(file
, O_WRONLY
);
123 err(EXIT_FAILURE
, _("cannot open %s"), file
);
125 xasprintf(&buf
, "%u %u 1", from
, to
);
126 if (write_all(fd
, buf
, strlen(buf
)))
127 err(EXIT_FAILURE
, _("write failed %s"), file
);
132 static unsigned long parse_propagation(const char *str
)
135 static const struct prop_opts
{
139 { "slave", MS_REC
| MS_SLAVE
},
140 { "private", MS_REC
| MS_PRIVATE
},
141 { "shared", MS_REC
| MS_SHARED
},
145 for (i
= 0; i
< ARRAY_SIZE(opts
); i
++) {
146 if (strcmp(opts
[i
].name
, str
) == 0)
150 errx(EXIT_FAILURE
, _("unsupported propagation mode: %s"), str
);
153 static void set_propagation(unsigned long flags
)
158 if (mount("none", "/", NULL
, flags
, NULL
) != 0)
159 err(EXIT_FAILURE
, _("cannot change root filesystem propagation"));
163 static int set_ns_target(int type
, const char *path
)
165 struct namespace_file
*ns
;
167 for (ns
= namespace_files
; ns
->name
; ns
++) {
168 if (ns
->type
!= type
)
178 static int bind_ns_files(pid_t pid
)
180 struct namespace_file
*ns
;
183 for (ns
= namespace_files
; ns
->name
; ns
++) {
187 snprintf(src
, sizeof(src
), "/proc/%u/%s", (unsigned) pid
, ns
->name
);
189 if (mount(src
, ns
->target
, NULL
, MS_BIND
, NULL
) != 0)
190 err(EXIT_FAILURE
, _("mount %s on %s failed"), src
, ns
->target
);
196 static ino_t
get_mnt_ino(pid_t pid
)
201 snprintf(path
, sizeof(path
), "/proc/%u/ns/mnt", (unsigned) pid
);
203 if (stat(path
, &st
) != 0)
204 err(EXIT_FAILURE
, _("cannot stat %s"), path
);
208 static void bind_ns_files_from_child(pid_t
*child
, int fds
[2])
211 pid_t ppid
= getpid();
212 ino_t ino
= get_mnt_ino(ppid
);
215 err(EXIT_FAILURE
, _("pipe failed"));
221 err(EXIT_FAILURE
, _("fork failed"));
227 /* wait for parent */
228 if (read_all(fds
[0], &ch
, 1) != 1 && ch
!= PIPE_SYNC_BYTE
)
229 err(EXIT_FAILURE
, _("failed to read pipe"));
230 if (get_mnt_ino(ppid
) == ino
)
236 default: /* parent */
243 static void __attribute__((__noreturn__
)) usage(void)
247 fputs(USAGE_HEADER
, out
);
248 fprintf(out
, _(" %s [options] [<program> [<argument>...]]\n"),
249 program_invocation_short_name
);
251 fputs(USAGE_SEPARATOR
, out
);
252 fputs(_("Run a program with some namespaces unshared from the parent.\n"), out
);
254 fputs(USAGE_OPTIONS
, out
);
255 fputs(_(" -m, --mount[=<file>] unshare mounts namespace\n"), out
);
256 fputs(_(" -u, --uts[=<file>] unshare UTS namespace (hostname etc)\n"), out
);
257 fputs(_(" -i, --ipc[=<file>] unshare System V IPC namespace\n"), out
);
258 fputs(_(" -n, --net[=<file>] unshare network namespace\n"), out
);
259 fputs(_(" -p, --pid[=<file>] unshare pid namespace\n"), out
);
260 fputs(_(" -U, --user[=<file>] unshare user namespace\n"), out
);
261 fputs(_(" -C, --cgroup[=<file>] unshare cgroup namespace\n"), out
);
262 fputs(_(" -f, --fork fork before launching <program>\n"), out
);
263 fputs(_(" --kill-child[=<signame>] when dying, kill the forked child (implies --fork); defaults to SIGKILL\n"), out
);
264 fputs(_(" --mount-proc[=<dir>] mount proc filesystem first (implies --mount)\n"), out
);
265 fputs(_(" -r, --map-root-user map current user to root (implies --user)\n"), out
);
266 fputs(_(" --propagation slave|shared|private|unchanged\n"
267 " modify mount propagation in mount namespace\n"), out
);
268 fputs(_(" -s, --setgroups allow|deny control the setgroups syscall in user namespaces\n"), out
);
270 fputs(USAGE_SEPARATOR
, out
);
271 printf(USAGE_HELP_OPTIONS(27));
272 printf(USAGE_MAN_TAIL("unshare(1)"));
277 int main(int argc
, char *argv
[])
280 OPT_MOUNTPROC
= CHAR_MAX
+ 1,
285 static const struct option longopts
[] = {
286 { "help", no_argument
, NULL
, 'h' },
287 { "version", no_argument
, NULL
, 'V' },
289 { "mount", optional_argument
, NULL
, 'm' },
290 { "uts", optional_argument
, NULL
, 'u' },
291 { "ipc", optional_argument
, NULL
, 'i' },
292 { "net", optional_argument
, NULL
, 'n' },
293 { "pid", optional_argument
, NULL
, 'p' },
294 { "user", optional_argument
, NULL
, 'U' },
295 { "cgroup", optional_argument
, NULL
, 'C' },
297 { "fork", no_argument
, NULL
, 'f' },
298 { "kill-child", optional_argument
, NULL
, OPT_KILLCHILD
},
299 { "mount-proc", optional_argument
, NULL
, OPT_MOUNTPROC
},
300 { "map-root-user", no_argument
, NULL
, 'r' },
301 { "propagation", required_argument
, NULL
, OPT_PROPAGATION
},
302 { "setgroups", required_argument
, NULL
, OPT_SETGROUPS
},
306 int setgrpcmd
= SETGROUPS_NONE
;
307 int unshare_flags
= 0;
308 int c
, forkit
= 0, maproot
= 0;
309 int kill_child_signo
= 0; /* 0 means --kill-child was not used */
310 const char *procmnt
= NULL
;
314 unsigned long propagation
= UNSHARE_PROPAGATION_DEFAULT
;
315 uid_t real_euid
= geteuid();
316 gid_t real_egid
= getegid();
318 setlocale(LC_ALL
, "");
319 bindtextdomain(PACKAGE
, LOCALEDIR
);
321 atexit(close_stdout
);
323 while ((c
= getopt_long(argc
, argv
, "+fhVmuinpCUr", longopts
, NULL
)) != -1) {
331 printf(UTIL_LINUX_VERSION
);
334 unshare_flags
|= CLONE_NEWNS
;
336 set_ns_target(CLONE_NEWNS
, optarg
);
339 unshare_flags
|= CLONE_NEWUTS
;
341 set_ns_target(CLONE_NEWUTS
, optarg
);
344 unshare_flags
|= CLONE_NEWIPC
;
346 set_ns_target(CLONE_NEWIPC
, optarg
);
349 unshare_flags
|= CLONE_NEWNET
;
351 set_ns_target(CLONE_NEWNET
, optarg
);
354 unshare_flags
|= CLONE_NEWPID
;
356 set_ns_target(CLONE_NEWPID
, optarg
);
359 unshare_flags
|= CLONE_NEWUSER
;
361 set_ns_target(CLONE_NEWUSER
, optarg
);
364 unshare_flags
|= CLONE_NEWCGROUP
;
366 set_ns_target(CLONE_NEWCGROUP
, optarg
);
369 unshare_flags
|= CLONE_NEWNS
;
370 procmnt
= optarg
? optarg
: "/proc";
373 unshare_flags
|= CLONE_NEWUSER
;
377 setgrpcmd
= setgroups_str2id(optarg
);
379 case OPT_PROPAGATION
:
380 propagation
= parse_propagation(optarg
);
385 if ((kill_child_signo
= signame_to_signum(optarg
)) < 0)
386 errx(EXIT_FAILURE
, _("unknown signal: %s"),
389 kill_child_signo
= SIGKILL
;
393 errtryhelp(EXIT_FAILURE
);
397 if (npersists
&& (unshare_flags
& CLONE_NEWNS
))
398 bind_ns_files_from_child(&pid
, fds
);
400 if (-1 == unshare(unshare_flags
))
401 err(EXIT_FAILURE
, _("unshare failed"));
404 if (pid
&& (unshare_flags
& CLONE_NEWNS
)) {
406 char ch
= PIPE_SYNC_BYTE
;
408 /* signal child we are ready */
409 write_all(fds
[1], &ch
, 1);
413 /* wait for bind_ns_files_from_child() */
415 rc
= waitpid(pid
, &status
, 0);
419 err(EXIT_FAILURE
, _("waitpid failed"));
421 if (WIFEXITED(status
) &&
422 WEXITSTATUS(status
) != EXIT_SUCCESS
)
423 return WEXITSTATUS(status
);
426 /* simple way, just bind */
427 bind_ns_files(getpid());
435 err(EXIT_FAILURE
, _("fork failed"));
438 default: /* parent */
439 if (waitpid(pid
, &status
, 0) == -1)
440 err(EXIT_FAILURE
, _("waitpid failed"));
441 if (WIFEXITED(status
))
442 return WEXITSTATUS(status
);
443 else if (WIFSIGNALED(status
))
444 kill(getpid(), WTERMSIG(status
));
445 err(EXIT_FAILURE
, _("child exit failed"));
449 if (kill_child_signo
!= 0 && prctl(PR_SET_PDEATHSIG
, kill_child_signo
) < 0)
450 err(EXIT_FAILURE
, "prctl failed");
453 if (setgrpcmd
== SETGROUPS_ALLOW
)
454 errx(EXIT_FAILURE
, _("options --setgroups=allow and "
455 "--map-root-user are mutually exclusive"));
457 /* since Linux 3.19 unprivileged writing of /proc/self/gid_map
458 * has s been disabled unless /proc/self/setgroups is written
459 * first to permanently disable the ability to call setgroups
460 * in that user namespace. */
461 setgroups_control(SETGROUPS_DENY
);
462 map_id(_PATH_PROC_UIDMAP
, 0, real_euid
);
463 map_id(_PATH_PROC_GIDMAP
, 0, real_egid
);
465 } else if (setgrpcmd
!= SETGROUPS_NONE
)
466 setgroups_control(setgrpcmd
);
468 if ((unshare_flags
& CLONE_NEWNS
) && propagation
)
469 set_propagation(propagation
);
472 (mount("none", procmnt
, NULL
, MS_PRIVATE
|MS_REC
, NULL
) != 0 ||
473 mount("proc", procmnt
, "proc", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
) != 0))
474 err(EXIT_FAILURE
, _("mount %s failed"), procmnt
);
477 execvp(argv
[optind
], argv
+ optind
);
478 errexec(argv
[optind
]);