]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/tools/lxc_unshare.c
2 * lxc: linux Container library
4 * (C) Copyright IBM Corp. 2007, 2008
7 * Daniel Lezcano <daniel.lezcano at free.fr>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <netinet/in.h>
38 #include <sys/eventfd.h>
39 #include <sys/socket.h>
40 #include <sys/types.h>
44 #include "arguments.h"
49 #include "namespace.h"
52 lxc_log_define(lxc_unshare
, lxc
);
59 int want_default_mounts
;
61 const char *want_hostname
;
64 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
);
65 static inline int sethostname_including_android(const char *name
, size_t len
);
66 static int get_namespace_flags(char *namespaces
);
67 static bool lookup_user(const char *oparg
, uid_t
*uid
);
68 static int mount_fs(const char *source
, const char *target
, const char *type
);
69 static void lxc_setup_fs(void);
70 static int do_start(void *arg
);
71 static void free_ifname_list(void);
73 static struct lxc_list ifnames
;
75 static const struct option my_longopts
[] = {
76 {"namespaces", required_argument
, 0, 's'},
77 {"user", required_argument
, 0, 'u'},
78 {"hostname", required_argument
, 0, 'H'},
79 {"ifname", required_argument
, 0, 'i'},
80 {"daemon", no_argument
, 0, 'd'},
81 {"remount", no_argument
, 0, 'M'},
85 static struct lxc_arguments my_args
= {
86 .progname
= "lxc-unshare",
88 -s NAMESPACES COMMAND\n\
90 lxc-unshare run a COMMAND in a new set of NAMESPACES\n\
93 -s, --namespaces=FLAGS\n\
94 ORed list of flags to unshare:\n\
95 MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n\
97 new id to be set if -s USER is specified\n\
98 -H, --hostname=HOSTNAME\n\
99 Set the hostname in the container\n\
100 -i, --ifname=IFNAME\n\
101 Interface name to be moved into container (presumably with NETWORK unsharing set)\n\
102 -d, --daemon Daemonize (do not wait for container to exit)\n\
103 -M, --remount Remount default fs inside container (/proc /dev/shm /dev/mqueue)\n\
105 .options
= my_longopts
,
108 .log_priority
= "ERROR",
113 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
)
115 struct lxc_list
*tmplist
;
119 args
->flags
= get_namespace_flags(arg
);
124 if (!lookup_user(arg
, &args
->uid
))
130 args
->want_hostname
= arg
;
133 tmplist
= malloc(sizeof(*tmplist
));
135 SYSERROR("Failed to alloc lxc list");
140 lxc_list_add_elem(tmplist
, arg
);
141 lxc_list_add_tail(&ifnames
, tmplist
);
147 args
->want_default_mounts
= 1;
153 /* Define sethostname() if missing from the C library also workaround some
154 * quirky with having this defined in multiple places.
156 static inline int sethostname_including_android(const char *name
, size_t len
)
158 #ifndef HAVE_SETHOSTNAME
159 #ifdef __NR_sethostname
160 return syscall(__NR_sethostname
, name
, len
);
166 return sethostname(name
, len
);
170 static int get_namespace_flags(char *namespaces
)
174 if (lxc_namespace_2_std_identifiers(namespaces
) < 0)
177 if (lxc_fill_namespace_flags(namespaces
, &flags
) < 0)
183 static bool lookup_user(const char *oparg
, uid_t
*uid
)
187 struct passwd
*pwentp
= NULL
;
192 if (!oparg
|| (oparg
[0] == '\0'))
195 bufsize
= sysconf(_SC_GETPW_R_SIZE_MAX
);
199 buf
= malloc(bufsize
);
203 if (sscanf(oparg
, "%u", uid
) < 1) {
204 /* not a uid -- perhaps a username */
205 if (sscanf(oparg
, "%s", name
) < 1) {
210 ret
= getpwnam_r(name
, &pwent
, buf
, bufsize
, &pwentp
);
213 SYSERROR("Could not find matched password record");
215 SYSERROR("Invalid username \"%s\"", name
);
222 ret
= getpwuid_r(*uid
, &pwent
, buf
, bufsize
, &pwentp
);
225 SYSERROR("Could not find matched password record");
227 SYSERROR("Invalid uid : %u", *uid
);
237 static int mount_fs(const char *source
, const char *target
, const char *type
)
239 /* the umount may fail */
240 if (umount(target
) < 0)
242 if (mount(source
, target
, type
, 0, NULL
) < 0)
248 static void lxc_setup_fs(void)
250 (void)mount_fs("proc", "/proc", "proc");
252 /* if /dev has been populated by us, /dev/shm does not exist */
253 if (access("/dev/shm", F_OK
))
254 (void)mkdir("/dev/shm", 0770);
256 /* if we can't mount /dev/shm, continue anyway */
257 (void)mount_fs("shmfs", "/dev/shm", "tmpfs");
259 /* If we were able to mount /dev/shm, then /dev exists */
260 /* Sure, but it's read-only per config :) */
261 if (access("/dev/mqueue", F_OK
))
262 (void)mkdir("/dev/mqueue", 0660);
264 /* continue even without posix message queue support */
265 (void)mount_fs("mqueue", "/dev/mqueue", "mqueue");
268 static int do_start(void *arg
)
272 struct start_arg
*start_arg
= arg
;
273 char *const *args
= start_arg
->args
;
274 const char *want_hostname
= start_arg
->want_hostname
;
276 if (start_arg
->setuid
) {
277 /* waiting until uid maps is set */
278 ret
= lxc_read_nointr(start_arg
->wait_fd
, &wait_val
, sizeof(wait_val
));
280 SYSERROR("Failed to read eventfd");
281 close(start_arg
->wait_fd
);
286 if ((start_arg
->flags
& CLONE_NEWNS
) && start_arg
->want_default_mounts
)
289 if ((start_arg
->flags
& CLONE_NEWUTS
) && want_hostname
)
290 if (sethostname_including_android(want_hostname
, strlen(want_hostname
)) < 0) {
291 SYSERROR("Failed to set hostname %s", want_hostname
);
295 /* Setuid is useful even without a new user id space. */
296 if (start_arg
->setuid
&& setuid(start_arg
->uid
)) {
297 SYSERROR("Failed to set uid %d", start_arg
->uid
);
301 execvp(args
[0], args
);
303 SYSERROR("Failed to exec: '%s'", args
[0]);
307 static void free_ifname_list(void)
309 struct lxc_list
*iterator
, *next
;
311 lxc_list_for_each_safe (iterator
, &ifnames
, next
) {
312 lxc_list_del(iterator
);
317 int main(int argc
, char *argv
[])
322 struct start_arg start_arg
;
324 lxc_list_init(&ifnames
);
329 if (lxc_arguments_parse(&my_args
, argc
, argv
))
332 log
.name
= my_args
.name
;
333 log
.file
= my_args
.log_file
;
334 log
.level
= my_args
.log_priority
;
335 log
.prefix
= my_args
.progname
;
336 log
.quiet
= my_args
.quiet
;
337 log
.lxcpath
= my_args
.lxcpath
[0];
339 if (lxc_log_init(&log
)) {
344 if (!*my_args
.argv
) {
345 ERROR("A command to execute in the new namespace is required");
350 if (my_args
.flags
== 0) {
351 ERROR("A namespace to execute command is required");
356 if (!(my_args
.flags
& CLONE_NEWNET
) && lxc_list_len(&ifnames
) > 0) {
357 ERROR("-i <interfacename> needs -s NETWORK option");
362 if (!(my_args
.flags
& CLONE_NEWUTS
) && my_args
.want_hostname
) {
363 ERROR("-H <hostname> needs -s UTSNAME option");
368 if (!(my_args
.flags
& CLONE_NEWNS
) && my_args
.want_default_mounts
) {
369 ERROR("-M needs -s MOUNT option");
374 if (my_args
.setuid
) {
375 start_arg
.wait_fd
= eventfd(0, EFD_CLOEXEC
);
376 if (start_arg
.wait_fd
< 0) {
377 SYSERROR("Failed to create eventfd");
383 /* set start arguments for lxc_clone from lxc_arguments */
384 start_arg
.args
= my_args
.argv
;
385 start_arg
.uid
= my_args
.uid
; /* valid only if (flags & CLONE_NEWUSER) */
386 start_arg
.setuid
= my_args
.setuid
;
387 start_arg
.flags
= my_args
.flags
;
388 start_arg
.want_hostname
= my_args
.want_hostname
;
389 start_arg
.want_default_mounts
= my_args
.want_default_mounts
;
391 pid
= lxc_clone(do_start
, &start_arg
, my_args
.flags
, NULL
);
393 ERROR("Failed to clone");
398 if (my_args
.setuid
) {
399 uint64_t wait_val
= 1;
400 /* enough space to accommodate uids */
403 /* create new uid mapping using current UID and the one
404 * specified as parameter
406 ret
= snprintf(umap
, 100, "%d %d 1\n" , my_args
.uid
, getuid());
407 if (ret
< 0 || ret
>= 100) {
408 ERROR("snprintf is failed");
410 close(start_arg
.wait_fd
);
414 ret
= write_id_mapping(ID_TYPE_UID
, pid
, umap
, strlen(umap
));
416 ERROR("Failed to map uid");
418 close(start_arg
.wait_fd
);
422 ret
= lxc_write_nointr(start_arg
.wait_fd
, &wait_val
, sizeof(wait_val
));
424 SYSERROR("Failed to write eventfd");
426 close(start_arg
.wait_fd
);
431 if (lxc_list_len(&ifnames
) > 0) {
432 struct lxc_list
*iterator
;
436 lxc_list_for_each(iterator
, &ifnames
) {
437 ifname
= iterator
->elem
;
443 SYSERROR("Failed to move network device \"%s\" to network namespace",
451 ret
= snprintf(buf
, 256, "%d", lpid
);
452 if (ret
< 0 || ret
>= 256)
455 execlp("ip", "ip", "link", "set", "dev", ifname
, "netns", buf
, (char *)NULL
);
459 if (wait_for_pid(lpid
) != 0)
460 SYSERROR("Could not move interface \"%s\" into container %d",
467 if (my_args
.daemonize
)
470 if (wait_for_pid(pid
) != 0) {
471 SYSERROR("Failed to wait for '%d'", pid
);
475 /* Call exit() directly on this function because it returns an exit code. */