1 /* Run a test case in an isolated namespace.
2 Copyright (C) 2018-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #define _FILE_OFFSET_BITS 64
25 #include <sys/syscall.h>
27 #include <sys/types.h>
31 #include <sys/fcntl.h>
35 #include <sys/sysmacros.h>
40 #include <libc-pointer-arith.h>
43 #include <sys/mount.h>
46 #include <support/support.h>
47 #include <support/xunistd.h>
49 #include "test-driver.h"
52 #define mount(s,t,fs,f,d) no_mount()
55 FAIL_UNSUPPORTED("mount not supported; port needed");
61 /* Running a test in a container is tricky. There are two main
62 categories of things to do:
64 1. "Once" actions, like setting up the container and doing an
67 2. "Per-test" actions, like copying in support files and
68 configuring the container.
73 * mkdir $buildroot/testroot.pristine/
75 * default glibc install
76 * create /bin for /bin/sh
77 * create $(complocaledir) so localedef tests work with default paths.
78 * install /bin/sh, /bin/echo, and /bin/true.
79 * rsync to $buildroot/testroot.root/
82 * maybe rsync to $buildroot/testroot.root/
83 * copy support files and test binary
85 * set up any mounts (like /proc)
89 For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
92 * mytest.root/ is rsync'd into container
93 * mytest.root/preclean.req causes fresh rsync (with delete) before
95 * mytest.root/mytest.script has a list of "commands" to run:
107 $B/ build dir, equivalent to $(common-objpfx)
108 $S/ source dir, equivalent to $(srcdir)
109 $I/ install dir, equivalent to $(prefix)
110 $L/ library dir (in container), equivalent to $(libdir)
111 $complocaledir/ compiled locale dir, equivalent to $(complocaledir)
114 If FILE begins with any of these variables then they will be
115 substituted for the described value.
117 The goal is to expose as many of the runtime's configured paths
118 via variables so they can be used to setup the container environment
119 before execution reaches the test.
123 - 'su': Enables running test as root in the container.
124 - 'mv': A minimal move files command.
125 - 'cp': A minimal copy files command.
126 - 'rm': A minimal remove files command.
127 - 'cwd': set test working directory
128 - 'exec': change test binary location (may end in /)
129 - 'mkdirp': A minimal "mkdir -p FILE" command.
131 * mytest.root/postclean.req causes fresh rsync (with delete) after
134 Note that $srcdir/foo/mytest.script may be used instead of a
135 $srcdir/foo/mytest.root/mytest.script in the sysroot template, if
136 there is no other reason for a sysroot.
140 * independent of other packages which may not be installed (like
141 rsync or Docker, or even "cp")
143 * Simple, easy to review code (i.e. prefer simple naive code over
144 complex efficient code)
146 * The current implementation ist parallel-make-safe, but only in
147 that it uses a lock to prevent parallel access to the testroot. */
150 /* Utility Functions */
152 /* Like xunlink, but it's OK if the file already doesn't exist. */
154 maybe_xunlink (const char *path
)
156 int rv
= unlink (path
);
157 if (rv
< 0 && errno
!= ENOENT
)
158 FAIL_EXIT1 ("unlink (\"%s\"): %m", path
);
161 /* Like xmkdir, but it's OK if the directory already exists. */
163 maybe_xmkdir (const char *path
, mode_t mode
)
167 if (stat (path
, &st
) == 0
168 && S_ISDIR (st
.st_mode
))
173 /* Temporarily concatenate multiple strings into one. Allows up to 10
174 temporary results; use xstrdup () if you need them to be
177 concat (const char *str
, ...)
179 /* Assume initialized to NULL/zero. */
180 static char *bufs
[10];
181 static size_t buflens
[10];
193 bufn
= (bufn
+ 1) % 10;
196 while ((next
= va_arg (ap
, char *)) != NULL
)
197 len
= len
+ strlen (next
);
203 bufs
[n
] = xmalloc (len
+ 1); /* NUL */
204 buflens
[n
] = len
+ 1;
206 else if (buflens
[n
] < len
+ 1)
208 bufs
[n
] = xrealloc (bufs
[n
], len
+ 1); /* NUL */
209 buflens
[n
] = len
+ 1;
212 strcpy (bufs
[n
], str
);
213 cp
= strchr (bufs
[n
], '\0');
214 while ((next
= va_arg (ap2
, char *)) != NULL
)
217 cp
= strchr (cp
, '\0');
225 /* Try to mount SRC onto DEST. */
227 trymount (const char *src
, const char *dest
)
229 if (mount (src
, dest
, "", MS_BIND
, NULL
) < 0)
230 FAIL_EXIT1 ("can't mount %s onto %s\n", src
, dest
);
233 /* Special case of above for devices like /dev/zero where we have to
234 mount a device over a device, not a directory over a directory. */
236 devmount (const char *new_root_path
, const char *which
)
239 fd
= open (concat (new_root_path
, "/dev/", which
, NULL
),
240 O_CREAT
| O_TRUNC
| O_RDWR
, 0777);
243 trymount (concat ("/dev/", which
, NULL
),
244 concat (new_root_path
, "/dev/", which
, NULL
));
247 /* Returns true if the string "looks like" an environement variable
250 is_env_setting (const char *a
)
256 if (isalnum (*a
) || *a
== '_')
258 else if (*a
== '=' && count_name
> 0)
267 /* Break the_line into words and store in the_words. Max nwords,
268 returns actual count. */
270 tokenize (char *the_line
, char **the_words
, int nwords
)
276 /* Skip leading whitespace, if any. */
277 while (*the_line
&& isspace (*the_line
))
284 /* THE_LINE points to a non-whitespace character, so we have a
286 *the_words
= the_line
;
291 /* Skip leading whitespace, if any. */
292 while (*the_line
&& ! isspace (*the_line
))
295 /* We now point at the trailing NUL *or* some whitespace. */
299 /* It was whitespace, skip and keep tokenizing. */
303 /* We get here if we filled the words buffer. */
308 /* Mini-RSYNC implementation. Optimize later. */
310 /* A few routines for an "rsync buffer" which stores the paths we're
311 working on. We continuously grow and shrink the paths in each
312 buffer so there's lot of re-use. */
314 /* We rely on "initialized to zero" to set these up. */
322 static path_buf spath
, dpath
;
325 r_setup (char *path
, path_buf
* pb
)
327 size_t len
= strlen (path
);
328 if (pb
->buf
== NULL
|| pb
->size
< len
+ 1)
330 /* Round up. This is an arbitrary number, just to keep from
331 reallocing too often. */
332 size_t sz
= ALIGN_UP (len
+ 1, 512);
334 pb
->buf
= (char *) xmalloc (sz
);
336 pb
->buf
= (char *) xrealloc (pb
->buf
, sz
);
338 FAIL_EXIT1 ("Out of memory while rsyncing\n");
342 strcpy (pb
->buf
, path
);
347 r_append (const char *path
, path_buf
* pb
)
349 size_t len
= strlen (path
) + pb
->len
;
350 if (pb
->size
< len
+ 1)
353 size_t sz
= ALIGN_UP (len
+ 1, 512);
354 pb
->buf
= (char *) xrealloc (pb
->buf
, sz
);
356 FAIL_EXIT1 ("Out of memory while rsyncing\n");
360 strcpy (pb
->buf
+ pb
->len
, path
);
365 file_exists (char *path
)
368 if (lstat (path
, &st
) == 0)
374 recursive_remove (char *path
)
384 FAIL_EXIT1 ("Unable to fork");
387 execlp ("rm", "rm", "-rf", path
, NULL
);
388 FAIL_EXIT1 ("exec rm: %m");
391 waitpid (child
, &status
, 0);
392 /* "rm" would have already printed a suitable error message. */
393 if (! WIFEXITED (status
)
394 || WEXITSTATUS (status
) != 0)
395 FAIL_EXIT1 ("exec child returned status: %d", status
);
401 /* Used for both rsync and the mytest.script "cp" command. */
403 copy_one_file (const char *sname
, const char *dname
)
407 struct utimbuf times
;
409 sfd
= open (sname
, O_RDONLY
);
411 FAIL_EXIT1 ("unable to open %s for reading\n", sname
);
413 if (fstat (sfd
, &st
) < 0)
414 FAIL_EXIT1 ("unable to fstat %s\n", sname
);
416 dfd
= open (dname
, O_WRONLY
| O_TRUNC
| O_CREAT
, 0600);
418 FAIL_EXIT1 ("unable to open %s for writing\n", dname
);
420 xcopy_file_range (sfd
, 0, dfd
, 0, st
.st_size
, 0);
425 if (chmod (dname
, st
.st_mode
& 0777) < 0)
426 FAIL_EXIT1 ("chmod %s: %s\n", dname
, strerror (errno
));
428 times
.actime
= st
.st_atime
;
429 times
.modtime
= st
.st_mtime
;
430 if (utime (dname
, ×
) < 0)
431 FAIL_EXIT1 ("utime %s: %s\n", dname
, strerror (errno
));
434 /* We don't check *everything* about the two files to see if a copy is
435 needed, just the minimum to make sure we get the latest copy. */
437 need_sync (char *ap
, char *bp
, struct stat
*a
, struct stat
*b
)
439 if ((a
->st_mode
& S_IFMT
) != (b
->st_mode
& S_IFMT
))
442 if (S_ISLNK (a
->st_mode
))
447 if (a
->st_size
!= b
->st_size
)
452 rv
= strcmp (al
, bl
);
456 return 0; /* links are same */
457 return 1; /* links differ */
462 if (a
->st_size
!= b
->st_size
)
464 if ((a
->st_mode
& 0777) != (b
->st_mode
& 0777))
466 if (a
->st_mtime
!= b
->st_mtime
)
470 if (a
->st_size
== b
->st_size
471 && ((a
->st_mode
& 0777) == (b
->st_mode
& 0777))
472 && a
->st_mtime
== b
->st_mtime
)
479 rsync_1 (path_buf
* src
, path_buf
* dest
, int and_delete
)
486 r_append ("/", dest
);
489 printf ("sync %s to %s %s\n", src
->buf
, dest
->buf
,
490 and_delete
? "and delete" : "");
492 size_t staillen
= src
->len
;
494 size_t dtaillen
= dest
->len
;
496 dir
= opendir (src
->buf
);
498 while ((de
= readdir (dir
)) != NULL
)
500 if (strcmp (de
->d_name
, ".") == 0
501 || strcmp (de
->d_name
, "..") == 0)
505 r_append (de
->d_name
, src
);
506 dest
->len
= dtaillen
;
507 r_append (de
->d_name
, dest
);
512 if (lstat (src
->buf
, &s
) != 0)
513 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src
->buf
);
515 /* It's OK if this one fails, since we know the file might be
517 lstat (dest
->buf
, &d
);
519 if (! need_sync (src
->buf
, dest
->buf
, &s
, &d
))
521 if (S_ISDIR (s
.st_mode
))
522 rsync_1 (src
, dest
, and_delete
);
527 switch (d
.st_mode
& S_IFMT
)
530 if (!S_ISDIR (s
.st_mode
))
533 printf ("-D %s\n", dest
->buf
);
534 recursive_remove (dest
->buf
);
540 printf ("-F %s\n", dest
->buf
);
541 maybe_xunlink (dest
->buf
);
545 switch (s
.st_mode
& S_IFMT
)
549 printf ("+F %s\n", dest
->buf
);
550 copy_one_file (src
->buf
, dest
->buf
);
555 printf ("+D %s\n", dest
->buf
);
556 maybe_xmkdir (dest
->buf
, (s
.st_mode
& 0777) | 0700);
557 rsync_1 (src
, dest
, and_delete
);
564 printf ("+L %s\n", dest
->buf
);
565 lp
= xreadlink (src
->buf
);
566 xsymlink (lp
, dest
->buf
);
578 src
->buf
[staillen
] = 0;
579 dest
->len
= dtaillen
;
580 dest
->buf
[dtaillen
] = 0;
585 /* The rest of this function removes any files/directories in DEST
586 that do not exist in SRC. This is triggered as part of a
587 preclean or postsclean step. */
589 dir
= opendir (dest
->buf
);
591 while ((de
= readdir (dir
)) != NULL
)
593 if (strcmp (de
->d_name
, ".") == 0
594 || strcmp (de
->d_name
, "..") == 0)
598 r_append (de
->d_name
, src
);
599 dest
->len
= dtaillen
;
600 r_append (de
->d_name
, dest
);
605 lstat (src
->buf
, &s
);
607 if (lstat (dest
->buf
, &d
) != 0)
608 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest
->buf
);
612 /* dest exists and src doesn't, clean it. */
613 switch (d
.st_mode
& S_IFMT
)
616 if (!S_ISDIR (s
.st_mode
))
619 printf ("-D %s\n", dest
->buf
);
620 recursive_remove (dest
->buf
);
626 printf ("-F %s\n", dest
->buf
);
627 maybe_xunlink (dest
->buf
);
637 rsync (char *src
, char *dest
, int and_delete
)
639 r_setup (src
, &spath
);
640 r_setup (dest
, &dpath
);
642 rsync_1 (&spath
, &dpath
, and_delete
);
647 /* See if we can detect what the user needs to do to get unshare
648 support working for us. */
650 check_for_unshare_hints (void)
655 /* Default Debian Linux disables user namespaces, but allows a way
657 f
= fopen ("/proc/sys/kernel/unprivileged_userns_clone", "r");
660 i
= 99; /* Sentinel. */
661 fscanf (f
, "%d", &i
);
664 printf ("To enable test-container, please run this as root:\n");
665 printf (" echo 1 > /proc/sys/kernel/unprivileged_userns_clone\n");
671 /* ALT Linux has an alternate way of doing the same. */
672 f
= fopen ("/proc/sys/kernel/userns_restrict", "r");
675 i
= 99; /* Sentinel. */
676 fscanf (f
, "%d", &i
);
679 printf ("To enable test-container, please run this as root:\n");
680 printf (" echo 0 > /proc/sys/kernel/userns_restrict\n");
688 main (int argc
, char **argv
)
691 char *pristine_root_path
;
694 char *new_objdir_path
;
695 char *new_srcdir_path
;
696 char **new_child_proc
;
697 char *new_child_exec
;
700 char *command_basename
;
702 int do_postclean
= 0;
703 char *change_cwd
= NULL
;
710 /* If set, the test runs as root instead of the user running the testsuite. */
714 /* Used for "%lld %lld 1" so need not be large. */
719 setbuf (stdout
, NULL
);
721 /* The command line we're expecting looks like this:
722 env <set some vars> ld.so <library path> test-binary
724 We need to peel off any "env" or "ld.so" portion of the command
725 line, and keep track of which env vars we should preserve and
730 fprintf (stderr
, "Usage: test-container <program to run> <args...>\n");
734 if (strcmp (argv
[1], "-v") == 0)
741 if (strcmp (argv
[1], "env") == 0)
745 while (is_env_setting (argv
[1]))
747 /* If there are variables we do NOT want to propogate, this
748 is where the test for them goes. */
750 /* Need to keep these. Note that putenv stores a
751 pointer to our argv. */
759 if (strcmp (argv
[1], support_objdir_elf_ldso
) == 0)
763 while (argv
[1][0] == '-')
765 if (strcmp (argv
[1], "--library-path") == 0)
775 pristine_root_path
= xstrdup (concat (support_objdir_root
,
776 "/testroot.pristine", NULL
));
777 new_root_path
= xstrdup (concat (support_objdir_root
,
778 "/testroot.root", NULL
));
779 new_cwd_path
= get_current_dir_name ();
780 new_child_proc
= argv
+ 1;
781 new_child_exec
= argv
[1];
783 lock_fd
= open (concat (pristine_root_path
, "/lock.fd", NULL
),
784 O_CREAT
| O_TRUNC
| O_RDWR
, 0666);
786 FAIL_EXIT1 ("Cannot create testroot lock.\n");
788 while (flock (lock_fd
, LOCK_EX
) != 0)
791 FAIL_EXIT1 ("Cannot lock testroot.\n");
794 xmkdirp (new_root_path
, 0755);
796 /* We look for extra setup info in a subdir in the same spot as the
797 test, with the same name but a ".root" extension. This is that
798 directory. We try to look in the source tree if the path we're
799 given refers to the build tree, but we rely on the path to be
800 absolute. This is what the glibc makefiles do. */
801 command_root
= concat (argv
[1], ".root", NULL
);
802 if (strncmp (command_root
, support_objdir_root
,
803 strlen (support_objdir_root
)) == 0
804 && command_root
[strlen (support_objdir_root
)] == '/')
805 command_root
= concat (support_srcdir_root
,
806 argv
[1] + strlen (support_objdir_root
),
808 command_root
= xstrdup (command_root
);
810 /* This cuts off the ".root" we appended above. */
811 command_base
= xstrdup (command_root
);
812 command_base
[strlen (command_base
) - 5] = 0;
814 /* This is the basename of the test we're running. */
815 command_basename
= strrchr (command_base
, '/');
816 if (command_basename
== NULL
)
817 command_basename
= command_base
;
821 /* Shared object base directory. */
822 so_base
= xstrdup (argv
[1]);
823 if (strrchr (so_base
, '/') != NULL
)
824 strrchr (so_base
, '/')[1] = 0;
826 if (file_exists (concat (command_root
, "/postclean.req", NULL
)))
829 rsync (pristine_root_path
, new_root_path
,
830 file_exists (concat (command_root
, "/preclean.req", NULL
)));
832 if (stat (command_root
, &st
) >= 0
833 && S_ISDIR (st
.st_mode
))
834 rsync (command_root
, new_root_path
, 0);
836 new_objdir_path
= xstrdup (concat (new_root_path
,
837 support_objdir_root
, NULL
));
838 new_srcdir_path
= xstrdup (concat (new_root_path
,
839 support_srcdir_root
, NULL
));
841 /* new_cwd_path starts with '/' so no "/" needed between the two. */
842 xmkdirp (concat (new_root_path
, new_cwd_path
, NULL
), 0755);
843 xmkdirp (new_srcdir_path
, 0755);
844 xmkdirp (new_objdir_path
, 0755);
846 original_uid
= getuid ();
847 original_gid
= getgid ();
849 /* Handle the cp/mv/rm "script" here. */
851 char *the_line
= NULL
;
853 char *fname
= concat (command_root
, "/",
854 command_basename
, ".script", NULL
);
856 FILE *f
= fopen (fname
, "r");
859 fprintf (stderr
, "running %s\n", fname
);
863 /* Try foo.script instead of foo.root/foo.script, as a shortcut. */
864 fname
= concat (command_base
, ".script", NULL
);
865 f
= fopen (fname
, "r");
867 fprintf (stderr
, "running %s\n", fname
);
870 /* Note that we do NOT look for a Makefile-generated foo.script in
871 the build directory. If that is ever needed, this is the place
874 /* This is where we "interpret" the mini-script which is <test>.script. */
877 while (getline (&the_line
, &line_len
, f
) > 0)
879 int nt
= tokenize (the_line
, the_words
, 3);
882 /* Expand variables. */
883 for (i
= 1; i
< nt
; ++i
)
885 if (memcmp (the_words
[i
], "$B/", 3) == 0)
886 the_words
[i
] = concat (support_objdir_root
,
887 the_words
[i
] + 2, NULL
);
888 else if (memcmp (the_words
[i
], "$S/", 3) == 0)
889 the_words
[i
] = concat (support_srcdir_root
,
890 the_words
[i
] + 2, NULL
);
891 else if (memcmp (the_words
[i
], "$I/", 3) == 0)
892 the_words
[i
] = concat (new_root_path
,
893 support_install_prefix
,
894 the_words
[i
] + 2, NULL
);
895 else if (memcmp (the_words
[i
], "$L/", 3) == 0)
896 the_words
[i
] = concat (new_root_path
,
897 support_libdir_prefix
,
898 the_words
[i
] + 2, NULL
);
899 else if (memcmp (the_words
[i
], "$complocaledir/", 15) == 0)
900 the_words
[i
] = concat (new_root_path
,
901 support_complocaledir_prefix
,
902 the_words
[i
] + 14, NULL
);
903 /* "exec" and "cwd" use inside-root paths. */
904 else if (strcmp (the_words
[0], "exec") != 0
905 && strcmp (the_words
[0], "cwd") != 0
906 && the_words
[i
][0] == '/')
907 the_words
[i
] = concat (new_root_path
,
911 if (nt
== 3 && the_words
[2][strlen (the_words
[2]) - 1] == '/')
913 char *r
= strrchr (the_words
[1], '/');
915 the_words
[2] = concat (the_words
[2], r
+ 1, NULL
);
917 the_words
[2] = concat (the_words
[2], the_words
[1], NULL
);
920 /* Run the following commands in the_words[0] with NT number of
921 arguments (including the command). */
923 if (nt
== 2 && strcmp (the_words
[0], "so") == 0)
925 the_words
[2] = concat (new_root_path
, support_libdir_prefix
,
926 "/", the_words
[1], NULL
);
927 the_words
[1] = concat (so_base
, the_words
[1], NULL
);
928 copy_one_file (the_words
[1], the_words
[2]);
930 else if (nt
== 3 && strcmp (the_words
[0], "cp") == 0)
932 copy_one_file (the_words
[1], the_words
[2]);
934 else if (nt
== 3 && strcmp (the_words
[0], "mv") == 0)
936 if (rename (the_words
[1], the_words
[2]) < 0)
937 FAIL_EXIT1 ("rename %s -> %s: %s", the_words
[1],
938 the_words
[2], strerror (errno
));
940 else if (nt
== 3 && strcmp (the_words
[0], "chmod") == 0)
944 m
= strtol (the_words
[1], NULL
, 0);
945 TEST_COMPARE (errno
, 0);
946 if (chmod (the_words
[2], m
) < 0)
947 FAIL_EXIT1 ("chmod %s: %s\n",
948 the_words
[2], strerror (errno
));
951 else if (nt
== 2 && strcmp (the_words
[0], "rm") == 0)
953 maybe_xunlink (the_words
[1]);
955 else if (nt
>= 2 && strcmp (the_words
[0], "exec") == 0)
957 /* The first argument is the desired location and name
958 of the test binary as we wish to exec it; we will
959 copy the binary there. The second (optional)
960 argument is the value to pass as argv[0], it
961 defaults to the same as the first argument. */
962 char *new_exec_path
= the_words
[1];
964 /* If the new exec path ends with a slash, that's the
965 * directory, and use the old test base name. */
966 if (new_exec_path
[strlen(new_exec_path
) - 1] == '/')
967 new_exec_path
= concat (new_exec_path
,
968 basename (new_child_proc
[0]),
972 /* new_child_proc is in the build tree, so has the
973 same path inside the chroot as outside. The new
974 exec path is, by definition, relative to the
976 copy_one_file (new_child_proc
[0], concat (new_root_path
,
980 new_child_exec
= xstrdup (new_exec_path
);
982 new_child_proc
[0] = xstrdup (the_words
[2]);
984 new_child_proc
[0] = new_child_exec
;
986 else if (nt
== 2 && strcmp (the_words
[0], "cwd") == 0)
988 change_cwd
= xstrdup (the_words
[1]);
990 else if (nt
== 1 && strcmp (the_words
[0], "su") == 0)
994 else if (nt
== 3 && strcmp (the_words
[0], "mkdirp") == 0)
998 m
= strtol (the_words
[1], NULL
, 0);
999 TEST_COMPARE (errno
, 0);
1000 xmkdirp (the_words
[2], m
);
1002 else if (nt
> 0 && the_words
[0][0] != '#')
1004 fprintf (stderr
, "\033[31minvalid [%s]\033[0m\n", the_words
[0]);
1014 pid_t pc_pid
= fork ();
1018 FAIL_EXIT1 ("Can't fork for post-clean");
1020 else if (pc_pid
> 0)
1024 waitpid (pc_pid
, &status
, 0);
1026 /* Child has exited, we can post-clean the test root. */
1027 printf("running post-clean rsync\n");
1028 rsync (pristine_root_path
, new_root_path
, 1);
1030 if (WIFEXITED (status
))
1031 exit (WEXITSTATUS (status
));
1033 if (WIFSIGNALED (status
))
1035 printf ("%%SIGNALLED%%\n");
1039 printf ("%%EXITERROR%%\n");
1043 /* Child continues. */
1046 /* This is the last point in the program where we're still in the
1047 "normal" namespace. */
1050 /* The unshare here gives us our own spaces and capabilities. */
1051 if (unshare (CLONE_NEWUSER
| CLONE_NEWPID
| CLONE_NEWNS
) < 0)
1053 /* Older kernels may not support all the options, or security
1054 policy may block this call. */
1055 if (errno
== EINVAL
|| errno
== EPERM
)
1057 int saved_errno
= errno
;
1059 check_for_unshare_hints ();
1060 FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno
));
1063 FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno
));
1066 /* Some targets may not support unshare at all. */
1067 FAIL_UNSUPPORTED ("unshare support missing");
1070 /* Some systems, by default, all mounts leak out of the namespace. */
1071 if (mount ("none", "/", NULL
, MS_REC
| MS_PRIVATE
, NULL
) != 0)
1072 FAIL_EXIT1 ("could not create a private mount namespace\n");
1074 trymount (support_srcdir_root
, new_srcdir_path
);
1075 trymount (support_objdir_root
, new_objdir_path
);
1077 xmkdirp (concat (new_root_path
, "/dev", NULL
), 0755);
1078 devmount (new_root_path
, "null");
1079 devmount (new_root_path
, "zero");
1080 devmount (new_root_path
, "urandom");
1082 /* We're done with the "old" root, switch to the new one. */
1083 if (chroot (new_root_path
) < 0)
1084 FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path
);
1086 if (chdir (new_cwd_path
) < 0)
1087 FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path
);
1089 /* This is to pass the "outside" PID to the child, which will be PID
1091 if (pipe2 (pipes
, O_CLOEXEC
) < 0)
1092 FAIL_EXIT1 ("Can't create pid pipe");
1094 /* To complete the containerization, we need to fork () at least
1095 once. We can't exec, nor can we somehow link the new child to
1096 our parent. So we run the child and propogate it's exit status
1100 FAIL_EXIT1 ("Unable to fork");
1106 /* Send the child's "outside" pid to it. */
1107 write (pipes
[1], &child
, sizeof(child
));
1111 waitpid (child
, &status
, 0);
1113 if (WIFEXITED (status
))
1114 exit (WEXITSTATUS (status
));
1116 if (WIFSIGNALED (status
))
1118 printf ("%%SIGNALLED%%\n");
1122 printf ("%%EXITERROR%%\n");
1126 /* The rest is the child process, which is now PID 1 and "in" the
1129 /* Get our "outside" pid from our parent. We use this to help with
1130 debugging from outside the container. */
1131 read (pipes
[0], &child
, sizeof(child
));
1134 sprintf (pid_buf
, "%lu", (long unsigned)child
);
1135 setenv ("PID_OUTSIDE_CONTAINER", pid_buf
, 0);
1137 maybe_xmkdir ("/tmp", 0755);
1139 /* Now that we're pid 1 (effectively "root") we can mount /proc */
1140 maybe_xmkdir ("/proc", 0777);
1141 if (mount ("proc", "/proc", "proc", 0, NULL
) < 0)
1142 FAIL_EXIT1 ("Unable to mount /proc: ");
1144 /* We map our original UID to the same UID in the container so we
1145 can own our own files normally. */
1146 UMAP
= open ("/proc/self/uid_map", O_WRONLY
);
1148 FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
1150 sprintf (tmp
, "%lld %lld 1\n",
1151 (long long) (be_su
? 0 : original_uid
), (long long) original_uid
);
1152 write (UMAP
, tmp
, strlen (tmp
));
1155 /* We must disable setgroups () before we can map our groups, else we
1157 GMAP
= open ("/proc/self/setgroups", O_WRONLY
);
1160 /* We support kernels old enough to not have this. */
1161 write (GMAP
, "deny\n", 5);
1165 /* We map our original GID to the same GID in the container so we
1166 can own our own files normally. */
1167 GMAP
= open ("/proc/self/gid_map", O_WRONLY
);
1169 FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
1171 sprintf (tmp
, "%lld %lld 1\n",
1172 (long long) (be_su
? 0 : original_gid
), (long long) original_gid
);
1173 write (GMAP
, tmp
, strlen (tmp
));
1178 if (chdir (change_cwd
) < 0)
1179 FAIL_EXIT1 ("Can't cd to %s inside container - ", change_cwd
);
1182 /* Now run the child. */
1183 execvp (new_child_exec
, new_child_proc
);
1185 /* Or don't run the child? */
1186 FAIL_EXIT1 ("Unable to exec %s: %s\n", new_child_exec
, strerror (errno
));
1188 /* Because gcc won't know error () never returns... */
1189 exit (EXIT_UNSUPPORTED
);