]>
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
35 #include <netinet/in.h>
37 #include <sys/eventfd.h>
38 #include <sys/socket.h>
39 #include <sys/types.h>
42 #include "arguments.h"
43 #include "tool_utils.h"
45 /* Define sethostname() if missing from the C library also workaround some
46 * quirky with having this defined in multiple places.
48 static inline int sethostname_including_android(const char *name
, size_t len
)
50 #ifndef HAVE_SETHOSTNAME
51 #ifdef __NR_sethostname
52 return syscall(__NR_sethostname
, name
, len
);
58 return sethostname(name
, len
);
65 struct my_iflist
*mi_next
;
68 static void usage(char *cmd
)
70 fprintf(stderr
, "%s <options> command [command_arguments]\n", basename(cmd
));
71 fprintf(stderr
, "Options are:\n");
72 fprintf(stderr
, "\t -s flags : ORed list of flags to unshare:\n" \
73 "\t MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n");
74 fprintf(stderr
, "\t -u <id> : new id to be set if -s USER is specified\n");
75 fprintf(stderr
, "\t -i <iface> : Interface name to be moved into container (presumably with NETWORK unsharing set)\n");
76 fprintf(stderr
, "\t -H <hostname>: Set the hostname in the container\n");
77 fprintf(stderr
, "\t -d : Daemonize (do not wait for container to exit)\n");
78 fprintf(stderr
, "\t -M : Remount default fs inside container (/proc /dev/shm /dev/mqueue)\n");
82 static bool lookup_user(const char *optarg
, uid_t
*uid
)
84 char name
[TOOL_MAXPATHLEN
];
85 struct passwd
*pwent
= NULL
;
87 if (!optarg
|| (optarg
[0] == '\0'))
90 if (sscanf(optarg
, "%u", uid
) < 1) {
91 /* not a uid -- perhaps a username */
92 if (sscanf(optarg
, "%s", name
) < 1)
95 pwent
= getpwnam(name
);
97 fprintf(stderr
, "invalid username %s\n", name
);
100 *uid
= pwent
->pw_uid
;
102 pwent
= getpwuid(*uid
);
104 fprintf(stderr
, "invalid uid %u\n", *uid
);
116 int want_default_mounts
;
118 const char *want_hostname
;
121 static int do_start(void *arg
)
125 struct start_arg
*start_arg
= arg
;
126 char **args
= *start_arg
->args
;
127 int flags
= *start_arg
->flags
;
128 uid_t uid
= *start_arg
->uid
;
129 int want_default_mounts
= start_arg
->want_default_mounts
;
130 const char *want_hostname
= start_arg
->want_hostname
;
131 int wait_fd
= start_arg
->wait_fd
;
133 if (start_arg
->setuid
) {
134 /* waiting until uid maps is set */
135 ret
= read(wait_fd
, &wait_val
, sizeof(wait_val
));
138 fprintf(stderr
, "read eventfd failed\n");
143 if ((flags
& CLONE_NEWNS
) && want_default_mounts
)
146 if ((flags
& CLONE_NEWUTS
) && want_hostname
)
147 if (sethostname_including_android(want_hostname
, strlen(want_hostname
)) < 0) {
148 fprintf(stderr
, "failed to set hostname %s: %s\n", want_hostname
, strerror(errno
));
152 /* Setuid is useful even without a new user id space. */
153 if (start_arg
->setuid
&& setuid(uid
)) {
154 fprintf(stderr
, "failed to set uid %d: %s\n", uid
, strerror(errno
));
158 execvp(args
[0], args
);
160 fprintf(stderr
, "failed to exec: '%s': %s\n", args
[0], strerror(errno
));
164 int write_id_mapping(pid_t pid
, const char *buf
, size_t buf_size
)
166 char path
[TOOL_MAXPATHLEN
];
170 ret
= snprintf(path
, TOOL_MAXPATHLEN
, "/proc/%d/uid_map", pid
);
171 if (ret
< 0 || ret
>= TOOL_MAXPATHLEN
)
174 fd
= open(path
, O_WRONLY
);
179 ret
= lxc_write_nointr(fd
, buf
, buf_size
);
181 if (ret
< 0 || (size_t)ret
!= buf_size
)
187 int main(int argc
, char *argv
[])
193 char *namespaces
= NULL
;
194 int flags
= 0, daemonize
= 0;
195 uid_t uid
= 0; /* valid only if (flags & CLONE_NEWUSER) */
197 uint64_t wait_val
= 1;
198 struct my_iflist
*tmpif
, *my_iflist
= NULL
;
199 struct start_arg start_arg
= {
204 .want_hostname
= NULL
,
205 .want_default_mounts
= 0,
208 while ((opt
= getopt(argc
, argv
, "s:u:hH:i:dM")) != -1) {
214 if (!(tmpif
= malloc(sizeof(*tmpif
)))) {
218 tmpif
->mi_ifname
= optarg
;
219 tmpif
->mi_next
= my_iflist
;
226 start_arg
.want_default_mounts
= 1;
229 start_arg
.want_hostname
= optarg
;
235 if (!lookup_user(optarg
, &uid
))
237 start_arg
.setuid
= true;
241 if (argv
[optind
] == NULL
) {
242 fprintf(stderr
, "a command to execute in the new namespace is required\n");
246 args
= &argv
[optind
];
248 ret
= lxc_caps_init();
252 /* The identifiers for namespaces used with lxc-unshare as given on the
253 * manpage do not align with the standard identifiers. This affects
254 * network, mount, and uts namespaces. The standard identifiers are:
255 * "mnt", "uts", and "net" whereas lxc-unshare uses "MOUNT", "UTSNAME",
256 * and "NETWORK". So let's use some cheap memmove()s to replace them by
257 * their standard identifiers. Let's illustrate this with an example:
264 * dest: del + 1 == OUNT|PID
265 * src: del + 3 == NT|PID
270 while ((del
= strstr(namespaces
, "MOUNT")))
271 memmove(del
+ 1, del
+ 3, strlen(del
) - 2);
273 for (it
= (char *[]){"NETWORK", "UTSNAME", NULL
}; it
&& *it
; it
++)
274 while ((del
= strstr(namespaces
, *it
)))
275 memmove(del
+ 3, del
+ 7, strlen(del
) - 6);
277 ret
= lxc_fill_namespace_flags(namespaces
, &flags
);
281 if (!(flags
& CLONE_NEWNET
) && my_iflist
) {
282 fprintf(stderr
, "-i <interfacename> needs -s NETWORK option\n");
286 if (!(flags
& CLONE_NEWUTS
) && start_arg
.want_hostname
) {
287 fprintf(stderr
, "-H <hostname> needs -s UTSNAME option\n");
291 if (!(flags
& CLONE_NEWNS
) && start_arg
.want_default_mounts
) {
292 fprintf(stderr
, "-M needs -s MOUNT option\n");
296 if (start_arg
.setuid
) {
297 start_arg
.wait_fd
= eventfd(0, EFD_CLOEXEC
);
298 if (start_arg
.wait_fd
< 0) {
299 fprintf(stderr
, "failed to create eventfd\n");
304 pid
= lxc_clone(do_start
, &start_arg
, flags
);
306 fprintf(stderr
, "failed to clone\n");
310 if (start_arg
.setuid
) {
311 /* enough space to accommodate uids */
312 char *umap
= (char *)alloca(100);
314 /* create new uid mapping using current UID and the one
315 * specified as parameter
317 ret
= snprintf(umap
, 100, "%d %d 1\n" , *(start_arg
.uid
), getuid());
318 if (ret
< 0 || ret
>= 100) {
319 close(start_arg
.wait_fd
);
320 fprintf(stderr
, "snprintf failed");
324 ret
= write_id_mapping(pid
, umap
, strlen(umap
));
326 close(start_arg
.wait_fd
);
327 fprintf(stderr
, "uid mapping failed\n");
331 ret
= write(start_arg
.wait_fd
, &wait_val
, sizeof(wait_val
));
333 close(start_arg
.wait_fd
);
334 fprintf(stderr
, "write to eventfd failed\n");
340 for (tmpif
= my_iflist
; tmpif
; tmpif
= tmpif
->mi_next
) {
345 fprintf(stderr
, "Failed to move network device "
346 "\"%s\" to network namespace\n",
352 ret
= snprintf(buf
, 256, "%d", pid
);
353 if (ret
< 0 || ret
>= 256)
356 execlp("ip", "ip", "link", "set", "dev", tmpif
->mi_ifname
, "netns", buf
, (char *)NULL
);
360 if (wait_for_pid(pid
) != 0)
361 fprintf(stderr
, "Could not move interface %s "
362 "into container %d: %s\n",
363 tmpif
->mi_ifname
, pid
, strerror(errno
));
370 if (wait_for_pid(pid
) != 0) {
371 fprintf(stderr
, "failed to wait for '%d'\n", pid
);
375 /* Call exit() directly on this function because it retuns an exit code. */