]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/tools/lxc_unshare.c
cc00dc0dff39f590ad713d02bfceb3566f97c65b
[mirror_lxc.git] / src / lxc / tools / lxc_unshare.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
8 *
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.
13 *
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.
18 *
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
22 */
23
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE 1
26 #endif
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <getopt.h>
30 #include <libgen.h>
31 #include <netinet/in.h>
32 #include <pwd.h>
33 #include <sched.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/eventfd.h>
39 #include <sys/socket.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <unistd.h>
43
44 #include "arguments.h"
45 #include "caps.h"
46 #include "config.h"
47 #include "list.h"
48 #include "log.h"
49 #include "namespace.h"
50 #include "utils.h"
51
52 lxc_log_define(lxc_unshare, lxc);
53
54 struct start_arg {
55 char *const *args;
56 int flags;
57 uid_t uid;
58 bool setuid;
59 int want_default_mounts;
60 int wait_fd;
61 const char *want_hostname;
62 };
63
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 *optarg, 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);
72
73 static struct lxc_list ifnames;
74
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'},
82 LXC_COMMON_OPTIONS
83 };
84
85 static struct lxc_arguments my_args = {
86 .progname = "lxc-unshare",
87 .help = "\
88 -s NAMESPACES COMMAND\n\
89 \n\
90 lxc-unshare run a COMMAND in a new set of NAMESPACES\n\
91 \n\
92 Options :\n\
93 -s, --namespaces=FLAGS\n\
94 ORed list of flags to unshare:\n\
95 MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n\
96 -u, --user=USERID\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\
104 ",
105 .options = my_longopts,
106 .parser = my_parser,
107 .checker = NULL,
108 .log_priority = "ERROR",
109 .log_file = "none",
110 .daemonize = 0,
111 };
112
113 static int my_parser(struct lxc_arguments *args, int c, char *arg)
114 {
115 struct lxc_list *tmplist;
116
117 switch (c) {
118 case 's':
119 args->flags = get_namespace_flags(arg);
120 if (args->flags < 0)
121 return -1;
122 break;
123 case 'u':
124 if (!lookup_user(arg, &args->uid))
125 return -1;
126
127 args->setuid = true;
128 break;
129 case 'H':
130 args->want_hostname = arg;
131 break;
132 case 'i':
133 tmplist = malloc(sizeof(*tmplist));
134 if (!tmplist) {
135 SYSERROR("Failed to alloc lxc list");
136 free_ifname_list();
137 return -1;
138 }
139
140 lxc_list_add_elem(tmplist, arg);
141 lxc_list_add_tail(&ifnames, tmplist);
142 break;
143 case 'd':
144 args->daemonize = 1;
145 break;
146 case 'M':
147 args->want_default_mounts = 1;
148 break;
149 }
150 return 0;
151 }
152
153 /* Define sethostname() if missing from the C library also workaround some
154 * quirky with having this defined in multiple places.
155 */
156 static inline int sethostname_including_android(const char *name, size_t len)
157 {
158 #ifndef HAVE_SETHOSTNAME
159 #ifdef __NR_sethostname
160 return syscall(__NR_sethostname, name, len);
161 #else
162 errno = ENOSYS;
163 return -1;
164 #endif
165 #else
166 return sethostname(name, len);
167 #endif
168 }
169
170 static int get_namespace_flags(char *namespaces)
171 {
172 int flags = 0;
173
174 if (lxc_namespace_2_std_identifiers(namespaces) < 0)
175 return -1;
176
177 if (lxc_fill_namespace_flags(namespaces, &flags) < 0)
178 return -1;
179
180 return flags;
181 }
182
183 static bool lookup_user(const char *optarg, uid_t *uid)
184 {
185 char name[PATH_MAX];
186 struct passwd pwent;
187 struct passwd *pwentp = NULL;
188 char *buf;
189 size_t bufsize;
190 int ret;
191
192 if (!optarg || (optarg[0] == '\0'))
193 return false;
194
195 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
196 if (bufsize == -1)
197 bufsize = 1024;
198
199 buf = malloc(bufsize);
200 if (!buf)
201 return false;
202
203 if (sscanf(optarg, "%u", uid) < 1) {
204 /* not a uid -- perhaps a username */
205 if (sscanf(optarg, "%s", name) < 1) {
206 free(buf);
207 return false;
208 }
209
210 ret = getpwnam_r(name, &pwent, buf, bufsize, &pwentp);
211 if (!pwentp) {
212 if (ret == 0)
213 SYSERROR("Could not find matched password record");
214
215 SYSERROR("Invalid username \"%s\"", name);
216 free(buf);
217 return false;
218 }
219
220 *uid = pwent.pw_uid;
221 } else {
222 ret = getpwuid_r(*uid, &pwent, buf, bufsize, &pwentp);
223 if (!pwentp) {
224 if (ret == 0)
225 SYSERROR("Could not find matched password record");
226
227 SYSERROR("Invalid uid : %u", *uid);
228 free(buf);
229 return false;
230 }
231 }
232
233 free(buf);
234 return true;
235 }
236
237 static int mount_fs(const char *source, const char *target, const char *type)
238 {
239 /* the umount may fail */
240 if (umount(target) < 0)
241
242 if (mount(source, target, type, 0, NULL) < 0)
243 return -1;
244
245 return 0;
246 }
247
248 static void lxc_setup_fs(void)
249 {
250 (void)mount_fs("proc", "/proc", "proc");
251
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);
255
256 /* if we can't mount /dev/shm, continue anyway */
257 (void)mount_fs("shmfs", "/dev/shm", "tmpfs");
258
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);
263
264 /* continue even without posix message queue support */
265 (void)mount_fs("mqueue", "/dev/mqueue", "mqueue");
266 }
267
268 static int do_start(void *arg)
269 {
270 int ret;
271 uint64_t wait_val;
272 struct start_arg *start_arg = arg;
273 char *const *args = start_arg->args;
274 const char *want_hostname = start_arg->want_hostname;
275
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));
279 if (ret == -1) {
280 SYSERROR("Failed to read eventfd");
281 close(start_arg->wait_fd);
282 _exit(EXIT_FAILURE);
283 }
284 }
285
286 if ((start_arg->flags & CLONE_NEWNS) && start_arg->want_default_mounts)
287 lxc_setup_fs();
288
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);
292 _exit(EXIT_FAILURE);
293 }
294
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);
298 _exit(EXIT_FAILURE);
299 }
300
301 execvp(args[0], args);
302
303 SYSERROR("Failed to exec: '%s'", args[0]);
304 return 1;
305 }
306
307 static void free_ifname_list(void)
308 {
309 struct lxc_list *iterator, *next;
310
311 lxc_list_for_each_safe (iterator, &ifnames, next) {
312 lxc_list_del(iterator);
313 free(iterator);
314 }
315 }
316
317 int main(int argc, char *argv[])
318 {
319 int ret;
320 pid_t pid;
321 struct lxc_log log;
322 struct start_arg start_arg;
323
324 lxc_list_init(&ifnames);
325
326 if (lxc_caps_init())
327 exit(EXIT_FAILURE);
328
329 if (lxc_arguments_parse(&my_args, argc, argv))
330 exit(EXIT_FAILURE);
331
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];
338
339 if (lxc_log_init(&log)) {
340 free_ifname_list();
341 exit(EXIT_FAILURE);
342 }
343
344 if (!*my_args.argv) {
345 ERROR("A command to execute in the new namespace is required");
346 free_ifname_list();
347 exit(EXIT_FAILURE);
348 }
349
350 if (my_args.flags == 0) {
351 ERROR("A namespace to execute command is required");
352 free_ifname_list();
353 exit(EXIT_FAILURE);
354 }
355
356 if (!(my_args.flags & CLONE_NEWNET) && lxc_list_len(&ifnames) > 0) {
357 ERROR("-i <interfacename> needs -s NETWORK option");
358 free_ifname_list();
359 exit(EXIT_FAILURE);
360 }
361
362 if (!(my_args.flags & CLONE_NEWUTS) && my_args.want_hostname) {
363 ERROR("-H <hostname> needs -s UTSNAME option");
364 free_ifname_list();
365 exit(EXIT_FAILURE);
366 }
367
368 if (!(my_args.flags & CLONE_NEWNS) && my_args.want_default_mounts) {
369 ERROR("-M needs -s MOUNT option");
370 free_ifname_list();
371 exit(EXIT_FAILURE);
372 }
373
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");
378 free_ifname_list();
379 exit(EXIT_FAILURE);
380 }
381 }
382
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;
390
391 pid = lxc_clone(do_start, &start_arg, my_args.flags);
392 if (pid < 0) {
393 ERROR("Failed to clone");
394 free_ifname_list();
395 exit(EXIT_FAILURE);
396 }
397
398 if (my_args.setuid) {
399 uint64_t wait_val = 1;
400 /* enough space to accommodate uids */
401 char *umap = (char *)alloca(100);
402
403 /* create new uid mapping using current UID and the one
404 * specified as parameter
405 */
406 ret = snprintf(umap, 100, "%d %d 1\n" , my_args.uid, getuid());
407 if (ret < 0 || ret >= 100) {
408 ERROR("snprintf is failed");
409 free_ifname_list();
410 close(start_arg.wait_fd);
411 exit(EXIT_FAILURE);
412 }
413
414 ret = write_id_mapping(ID_TYPE_UID, pid, umap, strlen(umap));
415 if (ret < 0) {
416 ERROR("Failed to map uid");
417 free_ifname_list();
418 close(start_arg.wait_fd);
419 exit(EXIT_FAILURE);
420 }
421
422 ret = lxc_write_nointr(start_arg.wait_fd, &wait_val, sizeof(wait_val));
423 if (ret < 0) {
424 SYSERROR("Failed to write eventfd");
425 free_ifname_list();
426 close(start_arg.wait_fd);
427 exit(EXIT_FAILURE);
428 }
429 }
430
431 if (lxc_list_len(&ifnames) > 0) {
432 struct lxc_list *iterator;
433 char* ifname;
434 pid_t pid;
435
436 lxc_list_for_each(iterator, &ifnames) {
437 ifname = iterator->elem;
438 if (!ifname)
439 continue;
440
441 pid = fork();
442 if (pid < 0) {
443 SYSERROR("Failed to move network device \"%s\" to network namespace",
444 ifname);
445 continue;
446 }
447
448 if (pid == 0) {
449 char buf[256];
450
451 ret = snprintf(buf, 256, "%d", pid);
452 if (ret < 0 || ret >= 256)
453 _exit(EXIT_FAILURE);
454
455 execlp("ip", "ip", "link", "set", "dev", ifname, "netns", buf, (char *)NULL);
456 _exit(EXIT_FAILURE);
457 }
458
459 if (wait_for_pid(pid) != 0)
460 SYSERROR("Could not move interface \"%s\" into container %d",
461 ifname, pid);
462 }
463
464 free_ifname_list();
465 }
466
467 if (my_args.daemonize)
468 exit(EXIT_SUCCESS);
469
470 if (wait_for_pid(pid) != 0) {
471 SYSERROR("Failed to wait for '%d'", pid);
472 exit(EXIT_FAILURE);
473 }
474
475 /* Call exit() directly on this function because it retuns an exit code. */
476 exit(EXIT_SUCCESS);
477 }