]> git.ipfire.org Git - thirdparty/glibc.git/blob - support/test-container.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / support / test-container.c
1 /* Run a test case in an isolated namespace.
2 Copyright (C) 2018-2019 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
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.
9
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.
14
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 <http://www.gnu.org/licenses/>. */
18
19 #define _FILE_OFFSET_BITS 64
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sched.h>
25 #include <sys/syscall.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <sys/fcntl.h>
32 #include <sys/file.h>
33 #include <sys/wait.h>
34 #include <stdarg.h>
35 #include <sys/sysmacros.h>
36 #include <ctype.h>
37 #include <utime.h>
38 #include <errno.h>
39 #include <error.h>
40 #include <libc-pointer-arith.h>
41
42 #ifdef __linux__
43 #include <sys/mount.h>
44 #endif
45
46 #include <support/support.h>
47 #include <support/xunistd.h>
48 #include "check.h"
49 #include "test-driver.h"
50
51 #ifndef __linux__
52 #define mount(s,t,fs,f,d) no_mount()
53 int no_mount (void)
54 {
55 FAIL_UNSUPPORTED("mount not supported; port needed");
56 }
57 #endif
58
59 int verbose = 0;
60
61 /* Running a test in a container is tricky. There are two main
62 categories of things to do:
63
64 1. "Once" actions, like setting up the container and doing an
65 install into it.
66
67 2. "Per-test" actions, like copying in support files and
68 configuring the container.
69
70
71 "Once" actions:
72
73 * mkdir $buildroot/testroot.pristine/
74 * install into it
75 * rsync to $buildroot/testroot.root/
76
77 "Per-test" actions:
78 * maybe rsync to $buildroot/testroot.root/
79 * copy support files and test binary
80 * chroot/unshare
81 * set up any mounts (like /proc)
82
83 Magic files:
84
85 For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
86 and, if found...
87
88 * mytest.root/ is rsync'd into container
89 * mytest.root/preclean.req causes fresh rsync (with delete) before
90 test if present
91 * mytest.root/mytest.script has a list of "commands" to run:
92 syntax:
93 # comment
94 su
95 mv FILE FILE
96 cp FILE FILE
97 rm FILE
98 FILE must start with $B/, $S/, $I/, $L/, or /
99 (expands to build dir, source dir, install dir, library dir
100 (in container), or container's root)
101 details:
102 - '#': A comment.
103 - 'su': Enables running test as root in the container.
104 - 'mv': A minimal move files command.
105 - 'cp': A minimal copy files command.
106 - 'rm': A minimal remove files command.
107 * mytest.root/postclean.req causes fresh rsync (with delete) after
108 test if present
109
110 Note that $srcdir/foo/mytest.script may be used instead of a
111 $srcdir/foo/mytest.root/mytest.script in the sysroot template, if
112 there is no other reason for a sysroot.
113
114 Design goals:
115
116 * independent of other packages which may not be installed (like
117 rsync or Docker, or even "cp")
118
119 * Simple, easy to review code (i.e. prefer simple naive code over
120 complex efficient code)
121
122 * The current implementation ist parallel-make-safe, but only in
123 that it uses a lock to prevent parallel access to the testroot. */
124
125 \f
126 /* Utility Functions */
127
128 /* Like xunlink, but it's OK if the file already doesn't exist. */
129 void
130 maybe_xunlink (const char *path)
131 {
132 int rv = unlink (path);
133 if (rv < 0 && errno != ENOENT)
134 FAIL_EXIT1 ("unlink (\"%s\"): %m", path);
135 }
136
137 /* Like xmkdir, but it's OK if the directory already exists. */
138 void
139 maybe_xmkdir (const char *path, mode_t mode)
140 {
141 struct stat st;
142
143 if (stat (path, &st) == 0
144 && S_ISDIR (st.st_mode))
145 return;
146 xmkdir (path, mode);
147 }
148
149 /* Temporarily concatenate multiple strings into one. Allows up to 10
150 temporary results; use strdup () if you need them to be
151 permanent. */
152 static char *
153 concat (const char *str, ...)
154 {
155 /* Assume initialized to NULL/zero. */
156 static char *bufs[10];
157 static size_t buflens[10];
158 static int bufn = 0;
159 int n;
160 size_t len;
161 va_list ap, ap2;
162 char *cp;
163 char *next;
164
165 va_start (ap, str);
166 va_copy (ap2, ap);
167
168 n = bufn;
169 bufn = (bufn + 1) % 10;
170 len = strlen (str);
171
172 while ((next = va_arg (ap, char *)) != NULL)
173 len = len + strlen (next);
174
175 va_end (ap);
176
177 if (bufs[n] == NULL)
178 {
179 bufs[n] = xmalloc (len + 1); /* NUL */
180 buflens[n] = len + 1;
181 }
182 else if (buflens[n] < len + 1)
183 {
184 bufs[n] = xrealloc (bufs[n], len + 1); /* NUL */
185 buflens[n] = len + 1;
186 }
187
188 strcpy (bufs[n], str);
189 cp = strchr (bufs[n], '\0');
190 while ((next = va_arg (ap2, char *)) != NULL)
191 {
192 strcpy (cp, next);
193 cp = strchr (cp, '\0');
194 }
195 *cp = 0;
196 va_end (ap2);
197
198 return bufs[n];
199 }
200
201 /* Try to mount SRC onto DEST. */
202 static void
203 trymount (const char *src, const char *dest)
204 {
205 if (mount (src, dest, "", MS_BIND, NULL) < 0)
206 FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
207 }
208
209 /* Special case of above for devices like /dev/zero where we have to
210 mount a device over a device, not a directory over a directory. */
211 static void
212 devmount (const char *new_root_path, const char *which)
213 {
214 int fd;
215 fd = open (concat (new_root_path, "/dev/", which, NULL),
216 O_CREAT | O_TRUNC | O_RDWR, 0777);
217 xclose (fd);
218
219 trymount (concat ("/dev/", which, NULL),
220 concat (new_root_path, "/dev/", which, NULL));
221 }
222
223 /* Returns true if the string "looks like" an environement variable
224 being set. */
225 static int
226 is_env_setting (const char *a)
227 {
228 int count_name = 0;
229
230 while (*a)
231 {
232 if (isalnum (*a) || *a == '_')
233 ++count_name;
234 else if (*a == '=' && count_name > 0)
235 return 1;
236 else
237 return 0;
238 ++a;
239 }
240 return 0;
241 }
242
243 /* Break the_line into words and store in the_words. Max nwords,
244 returns actual count. */
245 static int
246 tokenize (char *the_line, char **the_words, int nwords)
247 {
248 int rv = 0;
249
250 while (nwords > 0)
251 {
252 /* Skip leading whitespace, if any. */
253 while (*the_line && isspace (*the_line))
254 ++the_line;
255
256 /* End of line? */
257 if (*the_line == 0)
258 return rv;
259
260 /* THE_LINE points to a non-whitespace character, so we have a
261 word. */
262 *the_words = the_line;
263 ++the_words;
264 nwords--;
265 ++rv;
266
267 /* Skip leading whitespace, if any. */
268 while (*the_line && ! isspace (*the_line))
269 ++the_line;
270
271 /* We now point at the trailing NUL *or* some whitespace. */
272 if (*the_line == 0)
273 return rv;
274
275 /* It was whitespace, skip and keep tokenizing. */
276 *the_line++ = 0;
277 }
278
279 /* We get here if we filled the words buffer. */
280 return rv;
281 }
282
283 \f
284 /* Mini-RSYNC implementation. Optimize later. */
285
286 /* A few routines for an "rsync buffer" which stores the paths we're
287 working on. We continuously grow and shrink the paths in each
288 buffer so there's lot of re-use. */
289
290 /* We rely on "initialized to zero" to set these up. */
291 typedef struct
292 {
293 char *buf;
294 size_t len;
295 size_t size;
296 } path_buf;
297
298 static path_buf spath, dpath;
299
300 static void
301 r_setup (char *path, path_buf * pb)
302 {
303 size_t len = strlen (path);
304 if (pb->buf == NULL || pb->size < len + 1)
305 {
306 /* Round up. This is an arbitrary number, just to keep from
307 reallocing too often. */
308 size_t sz = ALIGN_UP (len + 1, 512);
309 if (pb->buf == NULL)
310 pb->buf = (char *) xmalloc (sz);
311 else
312 pb->buf = (char *) xrealloc (pb->buf, sz);
313 if (pb->buf == NULL)
314 FAIL_EXIT1 ("Out of memory while rsyncing\n");
315
316 pb->size = sz;
317 }
318 strcpy (pb->buf, path);
319 pb->len = len;
320 }
321
322 static void
323 r_append (const char *path, path_buf * pb)
324 {
325 size_t len = strlen (path) + pb->len;
326 if (pb->size < len + 1)
327 {
328 /* Round up */
329 size_t sz = ALIGN_UP (len + 1, 512);
330 pb->buf = (char *) xrealloc (pb->buf, sz);
331 if (pb->buf == NULL)
332 FAIL_EXIT1 ("Out of memory while rsyncing\n");
333
334 pb->size = sz;
335 }
336 strcpy (pb->buf + pb->len, path);
337 pb->len = len;
338 }
339
340 static int
341 file_exists (char *path)
342 {
343 struct stat st;
344 if (lstat (path, &st) == 0)
345 return 1;
346 return 0;
347 }
348
349 static void
350 recursive_remove (char *path)
351 {
352 pid_t child;
353 int status;
354
355 child = fork ();
356
357 switch (child) {
358 case -1:
359 perror("fork");
360 FAIL_EXIT1 ("Unable to fork");
361 case 0:
362 /* Child. */
363 execlp ("rm", "rm", "-rf", path, NULL);
364 default:
365 /* Parent. */
366 waitpid (child, &status, 0);
367 /* "rm" would have already printed a suitable error message. */
368 if (! WIFEXITED (status)
369 || WEXITSTATUS (status) != 0)
370 exit (1);
371
372 break;
373 }
374 }
375
376 /* Used for both rsync and the mytest.script "cp" command. */
377 static void
378 copy_one_file (const char *sname, const char *dname)
379 {
380 int sfd, dfd;
381 struct stat st;
382 struct utimbuf times;
383
384 sfd = open (sname, O_RDONLY);
385 if (sfd < 0)
386 FAIL_EXIT1 ("unable to open %s for reading\n", sname);
387
388 if (fstat (sfd, &st) < 0)
389 FAIL_EXIT1 ("unable to fstat %s\n", sname);
390
391 dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
392 if (dfd < 0)
393 FAIL_EXIT1 ("unable to open %s for writing\n", dname);
394
395 xcopy_file_range (sfd, 0, dfd, 0, st.st_size, 0);
396
397 xclose (sfd);
398 xclose (dfd);
399
400 if (chmod (dname, st.st_mode & 0777) < 0)
401 FAIL_EXIT1 ("chmod %s: %s\n", dname, strerror (errno));
402
403 times.actime = st.st_atime;
404 times.modtime = st.st_mtime;
405 if (utime (dname, &times) < 0)
406 FAIL_EXIT1 ("utime %s: %s\n", dname, strerror (errno));
407 }
408
409 /* We don't check *everything* about the two files to see if a copy is
410 needed, just the minimum to make sure we get the latest copy. */
411 static int
412 need_sync (char *ap, char *bp, struct stat *a, struct stat *b)
413 {
414 if ((a->st_mode & S_IFMT) != (b->st_mode & S_IFMT))
415 return 1;
416
417 if (S_ISLNK (a->st_mode))
418 {
419 int rv;
420 char *al, *bl;
421
422 if (a->st_size != b->st_size)
423 return 1;
424
425 al = xreadlink (ap);
426 bl = xreadlink (bp);
427 rv = strcmp (al, bl);
428 free (al);
429 free (bl);
430 if (rv == 0)
431 return 0; /* links are same */
432 return 1; /* links differ */
433 }
434
435 if (verbose)
436 {
437 if (a->st_size != b->st_size)
438 printf ("SIZE\n");
439 if ((a->st_mode & 0777) != (b->st_mode & 0777))
440 printf ("MODE\n");
441 if (a->st_mtime != b->st_mtime)
442 printf ("TIME\n");
443 }
444
445 if (a->st_size == b->st_size
446 && ((a->st_mode & 0777) == (b->st_mode & 0777))
447 && a->st_mtime == b->st_mtime)
448 return 0;
449
450 return 1;
451 }
452
453 static void
454 rsync_1 (path_buf * src, path_buf * dest, int and_delete)
455 {
456 DIR *dir;
457 struct dirent *de;
458 struct stat s, d;
459
460 r_append ("/", src);
461 r_append ("/", dest);
462
463 if (verbose)
464 printf ("sync %s to %s %s\n", src->buf, dest->buf,
465 and_delete ? "and delete" : "");
466
467 size_t staillen = src->len;
468
469 size_t dtaillen = dest->len;
470
471 dir = opendir (src->buf);
472
473 while ((de = readdir (dir)) != NULL)
474 {
475 if (strcmp (de->d_name, ".") == 0
476 || strcmp (de->d_name, "..") == 0)
477 continue;
478
479 src->len = staillen;
480 r_append (de->d_name, src);
481 dest->len = dtaillen;
482 r_append (de->d_name, dest);
483
484 s.st_mode = ~0;
485 d.st_mode = ~0;
486
487 if (lstat (src->buf, &s) != 0)
488 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src->buf);
489
490 /* It's OK if this one fails, since we know the file might be
491 missing. */
492 lstat (dest->buf, &d);
493
494 if (! need_sync (src->buf, dest->buf, &s, &d))
495 {
496 if (S_ISDIR (s.st_mode))
497 rsync_1 (src, dest, and_delete);
498 continue;
499 }
500
501 if (d.st_mode != ~0)
502 switch (d.st_mode & S_IFMT)
503 {
504 case S_IFDIR:
505 if (!S_ISDIR (s.st_mode))
506 {
507 if (verbose)
508 printf ("-D %s\n", dest->buf);
509 recursive_remove (dest->buf);
510 }
511 break;
512
513 default:
514 if (verbose)
515 printf ("-F %s\n", dest->buf);
516 maybe_xunlink (dest->buf);
517 break;
518 }
519
520 switch (s.st_mode & S_IFMT)
521 {
522 case S_IFREG:
523 if (verbose)
524 printf ("+F %s\n", dest->buf);
525 copy_one_file (src->buf, dest->buf);
526 break;
527
528 case S_IFDIR:
529 if (verbose)
530 printf ("+D %s\n", dest->buf);
531 maybe_xmkdir (dest->buf, (s.st_mode & 0777) | 0700);
532 rsync_1 (src, dest, and_delete);
533 break;
534
535 case S_IFLNK:
536 {
537 char *lp;
538 if (verbose)
539 printf ("+L %s\n", dest->buf);
540 lp = xreadlink (src->buf);
541 xsymlink (lp, dest->buf);
542 free (lp);
543 break;
544 }
545
546 default:
547 break;
548 }
549 }
550
551 closedir (dir);
552 src->len = staillen;
553 src->buf[staillen] = 0;
554 dest->len = dtaillen;
555 dest->buf[dtaillen] = 0;
556
557 if (!and_delete)
558 return;
559
560 /* The rest of this function removes any files/directories in DEST
561 that do not exist in SRC. This is triggered as part of a
562 preclean or postsclean step. */
563
564 dir = opendir (dest->buf);
565
566 while ((de = readdir (dir)) != NULL)
567 {
568 if (strcmp (de->d_name, ".") == 0
569 || strcmp (de->d_name, "..") == 0)
570 continue;
571
572 src->len = staillen;
573 r_append (de->d_name, src);
574 dest->len = dtaillen;
575 r_append (de->d_name, dest);
576
577 s.st_mode = ~0;
578 d.st_mode = ~0;
579
580 lstat (src->buf, &s);
581
582 if (lstat (dest->buf, &d) != 0)
583 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest->buf);
584
585 if (s.st_mode == ~0)
586 {
587 /* dest exists and src doesn't, clean it. */
588 switch (d.st_mode & S_IFMT)
589 {
590 case S_IFDIR:
591 if (!S_ISDIR (s.st_mode))
592 {
593 if (verbose)
594 printf ("-D %s\n", dest->buf);
595 recursive_remove (dest->buf);
596 }
597 break;
598
599 default:
600 if (verbose)
601 printf ("-F %s\n", dest->buf);
602 maybe_xunlink (dest->buf);
603 break;
604 }
605 }
606 }
607
608 closedir (dir);
609 }
610
611 static void
612 rsync (char *src, char *dest, int and_delete)
613 {
614 r_setup (src, &spath);
615 r_setup (dest, &dpath);
616
617 rsync_1 (&spath, &dpath, and_delete);
618 }
619
620 \f
621
622 /* See if we can detect what the user needs to do to get unshare
623 support working for us. */
624 void
625 check_for_unshare_hints (void)
626 {
627 FILE *f;
628 int i;
629
630 /* Default Debian Linux disables user namespaces, but allows a way
631 to enable them. */
632 f = fopen ("/proc/sys/kernel/unprivileged_userns_clone", "r");
633 if (f != NULL)
634 {
635 i = 99; /* Sentinel. */
636 fscanf (f, "%d", &i);
637 if (i == 0)
638 {
639 printf ("To enable test-container, please run this as root:\n");
640 printf (" echo 1 > /proc/sys/kernel/unprivileged_userns_clone\n");
641 }
642 fclose (f);
643 return;
644 }
645
646 /* ALT Linux has an alternate way of doing the same. */
647 f = fopen ("/proc/sys/kernel/userns_restrict", "r");
648 if (f != NULL)
649 {
650 i = 99; /* Sentinel. */
651 fscanf (f, "%d", &i);
652 if (i == 1)
653 {
654 printf ("To enable test-container, please run this as root:\n");
655 printf (" echo 0 > /proc/sys/kernel/userns_restrict\n");
656 }
657 fclose (f);
658 return;
659 }
660 }
661
662 int
663 main (int argc, char **argv)
664 {
665 pid_t child;
666 char *pristine_root_path;
667 char *new_root_path;
668 char *new_cwd_path;
669 char *new_objdir_path;
670 char *new_srcdir_path;
671 char **new_child_proc;
672 char *command_root;
673 char *command_base;
674 char *command_basename;
675 char *so_base;
676 int do_postclean = 0;
677
678 uid_t original_uid;
679 gid_t original_gid;
680 /* If set, the test runs as root instead of the user running the testsuite. */
681 int be_su = 0;
682 int UMAP;
683 int GMAP;
684 /* Used for "%lld %lld 1" so need not be large. */
685 char tmp[100];
686 struct stat st;
687 int lock_fd;
688
689 setbuf (stdout, NULL);
690
691 /* The command line we're expecting looks like this:
692 env <set some vars> ld.so <library path> test-binary
693
694 We need to peel off any "env" or "ld.so" portion of the command
695 line, and keep track of which env vars we should preserve and
696 which we drop. */
697
698 if (argc < 2)
699 {
700 fprintf (stderr, "Usage: containerize <program to run> <args...>\n");
701 exit (1);
702 }
703
704 if (strcmp (argv[1], "-v") == 0)
705 {
706 verbose = 1;
707 ++argv;
708 --argc;
709 }
710
711 if (strcmp (argv[1], "env") == 0)
712 {
713 ++argv;
714 --argc;
715 while (is_env_setting (argv[1]))
716 {
717 /* If there are variables we do NOT want to propogate, this
718 is where the test for them goes. */
719 {
720 /* Need to keep these. Note that putenv stores a
721 pointer to our argv. */
722 putenv (argv[1]);
723 }
724 ++argv;
725 --argc;
726 }
727 }
728
729 if (strcmp (argv[1], support_objdir_elf_ldso) == 0)
730 {
731 ++argv;
732 --argc;
733 while (argv[1][0] == '-')
734 {
735 if (strcmp (argv[1], "--library-path") == 0)
736 {
737 ++argv;
738 --argc;
739 }
740 ++argv;
741 --argc;
742 }
743 }
744
745 pristine_root_path = strdup (concat (support_objdir_root,
746 "/testroot.pristine", NULL));
747 new_root_path = strdup (concat (support_objdir_root,
748 "/testroot.root", NULL));
749 new_cwd_path = get_current_dir_name ();
750 new_child_proc = argv + 1;
751
752 lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
753 O_CREAT | O_TRUNC | O_RDWR, 0666);
754 if (lock_fd < 0)
755 FAIL_EXIT1 ("Cannot create testroot lock.\n");
756
757 while (flock (lock_fd, LOCK_EX) != 0)
758 {
759 if (errno != EINTR)
760 FAIL_EXIT1 ("Cannot lock testroot.\n");
761 }
762
763 xmkdirp (new_root_path, 0755);
764
765 /* We look for extra setup info in a subdir in the same spot as the
766 test, with the same name but a ".root" extension. This is that
767 directory. We try to look in the source tree if the path we're
768 given refers to the build tree, but we rely on the path to be
769 absolute. This is what the glibc makefiles do. */
770 command_root = concat (argv[1], ".root", NULL);
771 if (strncmp (command_root, support_objdir_root,
772 strlen (support_objdir_root)) == 0
773 && command_root[strlen (support_objdir_root)] == '/')
774 command_root = concat (support_srcdir_root,
775 argv[1] + strlen (support_objdir_root),
776 ".root", NULL);
777 command_root = strdup (command_root);
778
779 /* This cuts off the ".root" we appended above. */
780 command_base = strdup (command_root);
781 command_base[strlen (command_base) - 5] = 0;
782
783 /* This is the basename of the test we're running. */
784 command_basename = strrchr (command_base, '/');
785 if (command_basename == NULL)
786 command_basename = command_base;
787 else
788 ++command_basename;
789
790 /* Shared object base directory. */
791 so_base = strdup (argv[1]);
792 if (strrchr (so_base, '/') != NULL)
793 strrchr (so_base, '/')[1] = 0;
794
795 if (file_exists (concat (command_root, "/postclean.req", NULL)))
796 do_postclean = 1;
797
798 rsync (pristine_root_path, new_root_path,
799 file_exists (concat (command_root, "/preclean.req", NULL)));
800
801 if (stat (command_root, &st) >= 0
802 && S_ISDIR (st.st_mode))
803 rsync (command_root, new_root_path, 0);
804
805 new_objdir_path = strdup (concat (new_root_path,
806 support_objdir_root, NULL));
807 new_srcdir_path = strdup (concat (new_root_path,
808 support_srcdir_root, NULL));
809
810 /* new_cwd_path starts with '/' so no "/" needed between the two. */
811 xmkdirp (concat (new_root_path, new_cwd_path, NULL), 0755);
812 xmkdirp (new_srcdir_path, 0755);
813 xmkdirp (new_objdir_path, 0755);
814
815 original_uid = getuid ();
816 original_gid = getgid ();
817
818 /* Handle the cp/mv/rm "script" here. */
819 {
820 char *the_line = NULL;
821 size_t line_len = 0;
822 char *fname = concat (command_root, "/",
823 command_basename, ".script", NULL);
824 char *the_words[3];
825 FILE *f = fopen (fname, "r");
826
827 if (verbose && f)
828 fprintf (stderr, "running %s\n", fname);
829
830 if (f == NULL)
831 {
832 /* Try foo.script instead of foo.root/foo.script, as a shortcut. */
833 fname = concat (command_base, ".script", NULL);
834 f = fopen (fname, "r");
835 if (verbose && f)
836 fprintf (stderr, "running %s\n", fname);
837 }
838
839 /* Note that we do NOT look for a Makefile-generated foo.script in
840 the build directory. If that is ever needed, this is the place
841 to add it. */
842
843 /* This is where we "interpret" the mini-script which is <test>.script. */
844 if (f != NULL)
845 {
846 while (getline (&the_line, &line_len, f) > 0)
847 {
848 int nt = tokenize (the_line, the_words, 3);
849 int i;
850
851 for (i = 1; i < nt; ++i)
852 {
853 if (memcmp (the_words[i], "$B/", 3) == 0)
854 the_words[i] = concat (support_objdir_root,
855 the_words[i] + 2, NULL);
856 else if (memcmp (the_words[i], "$S/", 3) == 0)
857 the_words[i] = concat (support_srcdir_root,
858 the_words[i] + 2, NULL);
859 else if (memcmp (the_words[i], "$I/", 3) == 0)
860 the_words[i] = concat (new_root_path,
861 support_install_prefix,
862 the_words[i] + 2, NULL);
863 else if (memcmp (the_words[i], "$L/", 3) == 0)
864 the_words[i] = concat (new_root_path,
865 support_libdir_prefix,
866 the_words[i] + 2, NULL);
867 else if (the_words[i][0] == '/')
868 the_words[i] = concat (new_root_path,
869 the_words[i], NULL);
870 }
871
872 if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
873 {
874 char *r = strrchr (the_words[1], '/');
875 if (r)
876 the_words[2] = concat (the_words[2], r + 1, NULL);
877 else
878 the_words[2] = concat (the_words[2], the_words[1], NULL);
879 }
880
881 if (nt == 2 && strcmp (the_words[0], "so") == 0)
882 {
883 the_words[2] = concat (new_root_path, support_libdir_prefix,
884 "/", the_words[1], NULL);
885 the_words[1] = concat (so_base, the_words[1], NULL);
886 copy_one_file (the_words[1], the_words[2]);
887 }
888 else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
889 {
890 copy_one_file (the_words[1], the_words[2]);
891 }
892 else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
893 {
894 if (rename (the_words[1], the_words[2]) < 0)
895 FAIL_EXIT1 ("rename %s -> %s: %s", the_words[1],
896 the_words[2], strerror (errno));
897 }
898 else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
899 {
900 long int m;
901 m = strtol (the_words[1], NULL, 0);
902 if (chmod (the_words[2], m) < 0)
903 FAIL_EXIT1 ("chmod %s: %s\n",
904 the_words[2], strerror (errno));
905
906 }
907 else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
908 {
909 maybe_xunlink (the_words[1]);
910 }
911 else if (nt == 1 && strcmp (the_words[0], "su") == 0)
912 {
913 be_su = 1;
914 }
915 else if (nt > 0 && the_words[0][0] != '#')
916 {
917 printf ("\033[31minvalid [%s]\033[0m\n", the_words[0]);
918 }
919 }
920 fclose (f);
921 }
922 }
923
924 if (do_postclean)
925 {
926 pid_t pc_pid = fork ();
927
928 if (pc_pid < 0)
929 {
930 FAIL_EXIT1 ("Can't fork for post-clean");
931 }
932 else if (pc_pid > 0)
933 {
934 /* Parent. */
935 int status;
936 waitpid (pc_pid, &status, 0);
937
938 /* Child has exited, we can post-clean the test root. */
939 printf("running post-clean rsync\n");
940 rsync (pristine_root_path, new_root_path, 1);
941
942 if (WIFEXITED (status))
943 exit (WEXITSTATUS (status));
944
945 if (WIFSIGNALED (status))
946 {
947 printf ("%%SIGNALLED%%\n");
948 exit (77);
949 }
950
951 printf ("%%EXITERROR%%\n");
952 exit (78);
953 }
954
955 /* Child continues. */
956 }
957
958 /* This is the last point in the program where we're still in the
959 "normal" namespace. */
960
961 #ifdef CLONE_NEWNS
962 /* The unshare here gives us our own spaces and capabilities. */
963 if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
964 {
965 /* Older kernels may not support all the options, or security
966 policy may block this call. */
967 if (errno == EINVAL || errno == EPERM)
968 {
969 int saved_errno = errno;
970 if (errno == EPERM)
971 check_for_unshare_hints ();
972 FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno));
973 }
974 else
975 FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
976 }
977 #else
978 /* Some targets may not support unshare at all. */
979 FAIL_UNSUPPORTED ("unshare support missing");
980 #endif
981
982 /* Some systems, by default, all mounts leak out of the namespace. */
983 if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
984 FAIL_EXIT1 ("could not create a private mount namespace\n");
985
986 trymount (support_srcdir_root, new_srcdir_path);
987 trymount (support_objdir_root, new_objdir_path);
988
989 xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
990 devmount (new_root_path, "null");
991 devmount (new_root_path, "zero");
992 devmount (new_root_path, "urandom");
993
994 /* We're done with the "old" root, switch to the new one. */
995 if (chroot (new_root_path) < 0)
996 FAIL_EXIT1 ("Can't chroot to %s - ", new_root_path);
997
998 if (chdir (new_cwd_path) < 0)
999 FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
1000
1001 /* To complete the containerization, we need to fork () at least
1002 once. We can't exec, nor can we somehow link the new child to
1003 our parent. So we run the child and propogate it's exit status
1004 up. */
1005 child = fork ();
1006 if (child < 0)
1007 FAIL_EXIT1 ("Unable to fork");
1008 else if (child > 0)
1009 {
1010 /* Parent. */
1011 int status;
1012 waitpid (child, &status, 0);
1013
1014 if (WIFEXITED (status))
1015 exit (WEXITSTATUS (status));
1016
1017 if (WIFSIGNALED (status))
1018 {
1019 printf ("%%SIGNALLED%%\n");
1020 exit (77);
1021 }
1022
1023 printf ("%%EXITERROR%%\n");
1024 exit (78);
1025 }
1026
1027 /* The rest is the child process, which is now PID 1 and "in" the
1028 new root. */
1029
1030 maybe_xmkdir ("/tmp", 0755);
1031
1032 /* Now that we're pid 1 (effectively "root") we can mount /proc */
1033 maybe_xmkdir ("/proc", 0777);
1034 if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
1035 FAIL_EXIT1 ("Unable to mount /proc: ");
1036
1037 /* We map our original UID to the same UID in the container so we
1038 can own our own files normally. */
1039 UMAP = open ("/proc/self/uid_map", O_WRONLY);
1040 if (UMAP < 0)
1041 FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
1042
1043 sprintf (tmp, "%lld %lld 1\n",
1044 (long long) (be_su ? 0 : original_uid), (long long) original_uid);
1045 write (UMAP, tmp, strlen (tmp));
1046 xclose (UMAP);
1047
1048 /* We must disable setgroups () before we can map our groups, else we
1049 get EPERM. */
1050 GMAP = open ("/proc/self/setgroups", O_WRONLY);
1051 if (GMAP >= 0)
1052 {
1053 /* We support kernels old enough to not have this. */
1054 write (GMAP, "deny\n", 5);
1055 xclose (GMAP);
1056 }
1057
1058 /* We map our original GID to the same GID in the container so we
1059 can own our own files normally. */
1060 GMAP = open ("/proc/self/gid_map", O_WRONLY);
1061 if (GMAP < 0)
1062 FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
1063
1064 sprintf (tmp, "%lld %lld 1\n",
1065 (long long) (be_su ? 0 : original_gid), (long long) original_gid);
1066 write (GMAP, tmp, strlen (tmp));
1067 xclose (GMAP);
1068
1069 /* Now run the child. */
1070 execvp (new_child_proc[0], new_child_proc);
1071
1072 /* Or don't run the child? */
1073 FAIL_EXIT1 ("Unable to exec %s\n", new_child_proc[0]);
1074
1075 /* Because gcc won't know error () never returns... */
1076 exit (EXIT_UNSUPPORTED);
1077 }