]> git.ipfire.org Git - thirdparty/glibc.git/blob - support/test-container.c
e9109f9e3d5cf4647244eadf55e77fb284296e32
[thirdparty/glibc.git] / support / test-container.c
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.
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 <https://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 * 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/
80
81 "Per-test" actions:
82 * maybe rsync to $buildroot/testroot.root/
83 * copy support files and test binary
84 * chroot/unshare
85 * set up any mounts (like /proc)
86
87 Magic files:
88
89 For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root
90 and, if found...
91
92 * mytest.root/ is rsync'd into container
93 * mytest.root/preclean.req causes fresh rsync (with delete) before
94 test if present
95 * mytest.root/mytest.script has a list of "commands" to run:
96 syntax:
97 # comment
98 su
99 mv FILE FILE
100 cp FILE FILE
101 rm FILE
102 cwd PATH
103 exec FILE
104 mkdirp MODE DIR
105
106 variables:
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)
112 / container's root
113
114 If FILE begins with any of these variables then they will be
115 substituted for the described value.
116
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.
120
121 details:
122 - '#': A comment.
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.
130
131 * mytest.root/postclean.req causes fresh rsync (with delete) after
132 test if present
133
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.
137
138 Design goals:
139
140 * independent of other packages which may not be installed (like
141 rsync or Docker, or even "cp")
142
143 * Simple, easy to review code (i.e. prefer simple naive code over
144 complex efficient code)
145
146 * The current implementation ist parallel-make-safe, but only in
147 that it uses a lock to prevent parallel access to the testroot. */
148
149 \f
150 /* Utility Functions */
151
152 /* Like xunlink, but it's OK if the file already doesn't exist. */
153 void
154 maybe_xunlink (const char *path)
155 {
156 int rv = unlink (path);
157 if (rv < 0 && errno != ENOENT)
158 FAIL_EXIT1 ("unlink (\"%s\"): %m", path);
159 }
160
161 /* Like xmkdir, but it's OK if the directory already exists. */
162 void
163 maybe_xmkdir (const char *path, mode_t mode)
164 {
165 struct stat st;
166
167 if (stat (path, &st) == 0
168 && S_ISDIR (st.st_mode))
169 return;
170 xmkdir (path, mode);
171 }
172
173 /* Temporarily concatenate multiple strings into one. Allows up to 10
174 temporary results; use xstrdup () if you need them to be
175 permanent. */
176 static char *
177 concat (const char *str, ...)
178 {
179 /* Assume initialized to NULL/zero. */
180 static char *bufs[10];
181 static size_t buflens[10];
182 static int bufn = 0;
183 int n;
184 size_t len;
185 va_list ap, ap2;
186 char *cp;
187 char *next;
188
189 va_start (ap, str);
190 va_copy (ap2, ap);
191
192 n = bufn;
193 bufn = (bufn + 1) % 10;
194 len = strlen (str);
195
196 while ((next = va_arg (ap, char *)) != NULL)
197 len = len + strlen (next);
198
199 va_end (ap);
200
201 if (bufs[n] == NULL)
202 {
203 bufs[n] = xmalloc (len + 1); /* NUL */
204 buflens[n] = len + 1;
205 }
206 else if (buflens[n] < len + 1)
207 {
208 bufs[n] = xrealloc (bufs[n], len + 1); /* NUL */
209 buflens[n] = len + 1;
210 }
211
212 strcpy (bufs[n], str);
213 cp = strchr (bufs[n], '\0');
214 while ((next = va_arg (ap2, char *)) != NULL)
215 {
216 strcpy (cp, next);
217 cp = strchr (cp, '\0');
218 }
219 *cp = 0;
220 va_end (ap2);
221
222 return bufs[n];
223 }
224
225 /* Try to mount SRC onto DEST. */
226 static void
227 trymount (const char *src, const char *dest)
228 {
229 if (mount (src, dest, "", MS_BIND, NULL) < 0)
230 FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
231 }
232
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. */
235 static void
236 devmount (const char *new_root_path, const char *which)
237 {
238 int fd;
239 fd = open (concat (new_root_path, "/dev/", which, NULL),
240 O_CREAT | O_TRUNC | O_RDWR, 0777);
241 xclose (fd);
242
243 trymount (concat ("/dev/", which, NULL),
244 concat (new_root_path, "/dev/", which, NULL));
245 }
246
247 /* Returns true if the string "looks like" an environement variable
248 being set. */
249 static int
250 is_env_setting (const char *a)
251 {
252 int count_name = 0;
253
254 while (*a)
255 {
256 if (isalnum (*a) || *a == '_')
257 ++count_name;
258 else if (*a == '=' && count_name > 0)
259 return 1;
260 else
261 return 0;
262 ++a;
263 }
264 return 0;
265 }
266
267 /* Break the_line into words and store in the_words. Max nwords,
268 returns actual count. */
269 static int
270 tokenize (char *the_line, char **the_words, int nwords)
271 {
272 int rv = 0;
273
274 while (nwords > 0)
275 {
276 /* Skip leading whitespace, if any. */
277 while (*the_line && isspace (*the_line))
278 ++the_line;
279
280 /* End of line? */
281 if (*the_line == 0)
282 return rv;
283
284 /* THE_LINE points to a non-whitespace character, so we have a
285 word. */
286 *the_words = the_line;
287 ++the_words;
288 nwords--;
289 ++rv;
290
291 /* Skip leading whitespace, if any. */
292 while (*the_line && ! isspace (*the_line))
293 ++the_line;
294
295 /* We now point at the trailing NUL *or* some whitespace. */
296 if (*the_line == 0)
297 return rv;
298
299 /* It was whitespace, skip and keep tokenizing. */
300 *the_line++ = 0;
301 }
302
303 /* We get here if we filled the words buffer. */
304 return rv;
305 }
306
307 \f
308 /* Mini-RSYNC implementation. Optimize later. */
309
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. */
313
314 /* We rely on "initialized to zero" to set these up. */
315 typedef struct
316 {
317 char *buf;
318 size_t len;
319 size_t size;
320 } path_buf;
321
322 static path_buf spath, dpath;
323
324 static void
325 r_setup (char *path, path_buf * pb)
326 {
327 size_t len = strlen (path);
328 if (pb->buf == NULL || pb->size < len + 1)
329 {
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);
333 if (pb->buf == NULL)
334 pb->buf = (char *) xmalloc (sz);
335 else
336 pb->buf = (char *) xrealloc (pb->buf, sz);
337 if (pb->buf == NULL)
338 FAIL_EXIT1 ("Out of memory while rsyncing\n");
339
340 pb->size = sz;
341 }
342 strcpy (pb->buf, path);
343 pb->len = len;
344 }
345
346 static void
347 r_append (const char *path, path_buf * pb)
348 {
349 size_t len = strlen (path) + pb->len;
350 if (pb->size < len + 1)
351 {
352 /* Round up */
353 size_t sz = ALIGN_UP (len + 1, 512);
354 pb->buf = (char *) xrealloc (pb->buf, sz);
355 if (pb->buf == NULL)
356 FAIL_EXIT1 ("Out of memory while rsyncing\n");
357
358 pb->size = sz;
359 }
360 strcpy (pb->buf + pb->len, path);
361 pb->len = len;
362 }
363
364 static int
365 file_exists (char *path)
366 {
367 struct stat st;
368 if (lstat (path, &st) == 0)
369 return 1;
370 return 0;
371 }
372
373 static void
374 recursive_remove (char *path)
375 {
376 pid_t child;
377 int status;
378
379 child = fork ();
380
381 switch (child) {
382 case -1:
383 perror("fork");
384 FAIL_EXIT1 ("Unable to fork");
385 case 0:
386 /* Child. */
387 execlp ("rm", "rm", "-rf", path, NULL);
388 FAIL_EXIT1 ("exec rm: %m");
389 default:
390 /* Parent. */
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);
396
397 break;
398 }
399 }
400
401 /* Used for both rsync and the mytest.script "cp" command. */
402 static void
403 copy_one_file (const char *sname, const char *dname)
404 {
405 int sfd, dfd;
406 struct stat st;
407 struct utimbuf times;
408
409 sfd = open (sname, O_RDONLY);
410 if (sfd < 0)
411 FAIL_EXIT1 ("unable to open %s for reading\n", sname);
412
413 if (fstat (sfd, &st) < 0)
414 FAIL_EXIT1 ("unable to fstat %s\n", sname);
415
416 dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
417 if (dfd < 0)
418 FAIL_EXIT1 ("unable to open %s for writing\n", dname);
419
420 xcopy_file_range (sfd, 0, dfd, 0, st.st_size, 0);
421
422 xclose (sfd);
423 xclose (dfd);
424
425 if (chmod (dname, st.st_mode & 0777) < 0)
426 FAIL_EXIT1 ("chmod %s: %s\n", dname, strerror (errno));
427
428 times.actime = st.st_atime;
429 times.modtime = st.st_mtime;
430 if (utime (dname, &times) < 0)
431 FAIL_EXIT1 ("utime %s: %s\n", dname, strerror (errno));
432 }
433
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. */
436 static int
437 need_sync (char *ap, char *bp, struct stat *a, struct stat *b)
438 {
439 if ((a->st_mode & S_IFMT) != (b->st_mode & S_IFMT))
440 return 1;
441
442 if (S_ISLNK (a->st_mode))
443 {
444 int rv;
445 char *al, *bl;
446
447 if (a->st_size != b->st_size)
448 return 1;
449
450 al = xreadlink (ap);
451 bl = xreadlink (bp);
452 rv = strcmp (al, bl);
453 free (al);
454 free (bl);
455 if (rv == 0)
456 return 0; /* links are same */
457 return 1; /* links differ */
458 }
459
460 if (verbose)
461 {
462 if (a->st_size != b->st_size)
463 printf ("SIZE\n");
464 if ((a->st_mode & 0777) != (b->st_mode & 0777))
465 printf ("MODE\n");
466 if (a->st_mtime != b->st_mtime)
467 printf ("TIME\n");
468 }
469
470 if (a->st_size == b->st_size
471 && ((a->st_mode & 0777) == (b->st_mode & 0777))
472 && a->st_mtime == b->st_mtime)
473 return 0;
474
475 return 1;
476 }
477
478 static void
479 rsync_1 (path_buf * src, path_buf * dest, int and_delete)
480 {
481 DIR *dir;
482 struct dirent *de;
483 struct stat s, d;
484
485 r_append ("/", src);
486 r_append ("/", dest);
487
488 if (verbose)
489 printf ("sync %s to %s %s\n", src->buf, dest->buf,
490 and_delete ? "and delete" : "");
491
492 size_t staillen = src->len;
493
494 size_t dtaillen = dest->len;
495
496 dir = opendir (src->buf);
497
498 while ((de = readdir (dir)) != NULL)
499 {
500 if (strcmp (de->d_name, ".") == 0
501 || strcmp (de->d_name, "..") == 0)
502 continue;
503
504 src->len = staillen;
505 r_append (de->d_name, src);
506 dest->len = dtaillen;
507 r_append (de->d_name, dest);
508
509 s.st_mode = ~0;
510 d.st_mode = ~0;
511
512 if (lstat (src->buf, &s) != 0)
513 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src->buf);
514
515 /* It's OK if this one fails, since we know the file might be
516 missing. */
517 lstat (dest->buf, &d);
518
519 if (! need_sync (src->buf, dest->buf, &s, &d))
520 {
521 if (S_ISDIR (s.st_mode))
522 rsync_1 (src, dest, and_delete);
523 continue;
524 }
525
526 if (d.st_mode != ~0)
527 switch (d.st_mode & S_IFMT)
528 {
529 case S_IFDIR:
530 if (!S_ISDIR (s.st_mode))
531 {
532 if (verbose)
533 printf ("-D %s\n", dest->buf);
534 recursive_remove (dest->buf);
535 }
536 break;
537
538 default:
539 if (verbose)
540 printf ("-F %s\n", dest->buf);
541 maybe_xunlink (dest->buf);
542 break;
543 }
544
545 switch (s.st_mode & S_IFMT)
546 {
547 case S_IFREG:
548 if (verbose)
549 printf ("+F %s\n", dest->buf);
550 copy_one_file (src->buf, dest->buf);
551 break;
552
553 case S_IFDIR:
554 if (verbose)
555 printf ("+D %s\n", dest->buf);
556 maybe_xmkdir (dest->buf, (s.st_mode & 0777) | 0700);
557 rsync_1 (src, dest, and_delete);
558 break;
559
560 case S_IFLNK:
561 {
562 char *lp;
563 if (verbose)
564 printf ("+L %s\n", dest->buf);
565 lp = xreadlink (src->buf);
566 xsymlink (lp, dest->buf);
567 free (lp);
568 break;
569 }
570
571 default:
572 break;
573 }
574 }
575
576 closedir (dir);
577 src->len = staillen;
578 src->buf[staillen] = 0;
579 dest->len = dtaillen;
580 dest->buf[dtaillen] = 0;
581
582 if (!and_delete)
583 return;
584
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. */
588
589 dir = opendir (dest->buf);
590
591 while ((de = readdir (dir)) != NULL)
592 {
593 if (strcmp (de->d_name, ".") == 0
594 || strcmp (de->d_name, "..") == 0)
595 continue;
596
597 src->len = staillen;
598 r_append (de->d_name, src);
599 dest->len = dtaillen;
600 r_append (de->d_name, dest);
601
602 s.st_mode = ~0;
603 d.st_mode = ~0;
604
605 lstat (src->buf, &s);
606
607 if (lstat (dest->buf, &d) != 0)
608 FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest->buf);
609
610 if (s.st_mode == ~0)
611 {
612 /* dest exists and src doesn't, clean it. */
613 switch (d.st_mode & S_IFMT)
614 {
615 case S_IFDIR:
616 if (!S_ISDIR (s.st_mode))
617 {
618 if (verbose)
619 printf ("-D %s\n", dest->buf);
620 recursive_remove (dest->buf);
621 }
622 break;
623
624 default:
625 if (verbose)
626 printf ("-F %s\n", dest->buf);
627 maybe_xunlink (dest->buf);
628 break;
629 }
630 }
631 }
632
633 closedir (dir);
634 }
635
636 static void
637 rsync (char *src, char *dest, int and_delete)
638 {
639 r_setup (src, &spath);
640 r_setup (dest, &dpath);
641
642 rsync_1 (&spath, &dpath, and_delete);
643 }
644
645 \f
646
647 /* See if we can detect what the user needs to do to get unshare
648 support working for us. */
649 void
650 check_for_unshare_hints (void)
651 {
652 FILE *f;
653 int i;
654
655 /* Default Debian Linux disables user namespaces, but allows a way
656 to enable them. */
657 f = fopen ("/proc/sys/kernel/unprivileged_userns_clone", "r");
658 if (f != NULL)
659 {
660 i = 99; /* Sentinel. */
661 fscanf (f, "%d", &i);
662 if (i == 0)
663 {
664 printf ("To enable test-container, please run this as root:\n");
665 printf (" echo 1 > /proc/sys/kernel/unprivileged_userns_clone\n");
666 }
667 fclose (f);
668 return;
669 }
670
671 /* ALT Linux has an alternate way of doing the same. */
672 f = fopen ("/proc/sys/kernel/userns_restrict", "r");
673 if (f != NULL)
674 {
675 i = 99; /* Sentinel. */
676 fscanf (f, "%d", &i);
677 if (i == 1)
678 {
679 printf ("To enable test-container, please run this as root:\n");
680 printf (" echo 0 > /proc/sys/kernel/userns_restrict\n");
681 }
682 fclose (f);
683 return;
684 }
685 }
686
687 int
688 main (int argc, char **argv)
689 {
690 pid_t child;
691 char *pristine_root_path;
692 char *new_root_path;
693 char *new_cwd_path;
694 char *new_objdir_path;
695 char *new_srcdir_path;
696 char **new_child_proc;
697 char *new_child_exec;
698 char *command_root;
699 char *command_base;
700 char *command_basename;
701 char *so_base;
702 int do_postclean = 0;
703 char *change_cwd = NULL;
704
705 int pipes[2];
706 char pid_buf[20];
707
708 uid_t original_uid;
709 gid_t original_gid;
710 /* If set, the test runs as root instead of the user running the testsuite. */
711 int be_su = 0;
712 int UMAP;
713 int GMAP;
714 /* Used for "%lld %lld 1" so need not be large. */
715 char tmp[100];
716 struct stat st;
717 int lock_fd;
718
719 setbuf (stdout, NULL);
720
721 /* The command line we're expecting looks like this:
722 env <set some vars> ld.so <library path> test-binary
723
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
726 which we drop. */
727
728 if (argc < 2)
729 {
730 fprintf (stderr, "Usage: test-container <program to run> <args...>\n");
731 exit (1);
732 }
733
734 if (strcmp (argv[1], "-v") == 0)
735 {
736 verbose = 1;
737 ++argv;
738 --argc;
739 }
740
741 if (strcmp (argv[1], "env") == 0)
742 {
743 ++argv;
744 --argc;
745 while (is_env_setting (argv[1]))
746 {
747 /* If there are variables we do NOT want to propogate, this
748 is where the test for them goes. */
749 {
750 /* Need to keep these. Note that putenv stores a
751 pointer to our argv. */
752 putenv (argv[1]);
753 }
754 ++argv;
755 --argc;
756 }
757 }
758
759 if (strcmp (argv[1], support_objdir_elf_ldso) == 0)
760 {
761 ++argv;
762 --argc;
763 while (argv[1][0] == '-')
764 {
765 if (strcmp (argv[1], "--library-path") == 0)
766 {
767 ++argv;
768 --argc;
769 }
770 ++argv;
771 --argc;
772 }
773 }
774
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];
782
783 lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
784 O_CREAT | O_TRUNC | O_RDWR, 0666);
785 if (lock_fd < 0)
786 FAIL_EXIT1 ("Cannot create testroot lock.\n");
787
788 while (flock (lock_fd, LOCK_EX) != 0)
789 {
790 if (errno != EINTR)
791 FAIL_EXIT1 ("Cannot lock testroot.\n");
792 }
793
794 xmkdirp (new_root_path, 0755);
795
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),
807 ".root", NULL);
808 command_root = xstrdup (command_root);
809
810 /* This cuts off the ".root" we appended above. */
811 command_base = xstrdup (command_root);
812 command_base[strlen (command_base) - 5] = 0;
813
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;
818 else
819 ++command_basename;
820
821 /* Shared object base directory. */
822 so_base = xstrdup (argv[1]);
823 if (strrchr (so_base, '/') != NULL)
824 strrchr (so_base, '/')[1] = 0;
825
826 if (file_exists (concat (command_root, "/postclean.req", NULL)))
827 do_postclean = 1;
828
829 rsync (pristine_root_path, new_root_path,
830 file_exists (concat (command_root, "/preclean.req", NULL)));
831
832 if (stat (command_root, &st) >= 0
833 && S_ISDIR (st.st_mode))
834 rsync (command_root, new_root_path, 0);
835
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));
840
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);
845
846 original_uid = getuid ();
847 original_gid = getgid ();
848
849 /* Handle the cp/mv/rm "script" here. */
850 {
851 char *the_line = NULL;
852 size_t line_len = 0;
853 char *fname = concat (command_root, "/",
854 command_basename, ".script", NULL);
855 char *the_words[3];
856 FILE *f = fopen (fname, "r");
857
858 if (verbose && f)
859 fprintf (stderr, "running %s\n", fname);
860
861 if (f == NULL)
862 {
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");
866 if (verbose && f)
867 fprintf (stderr, "running %s\n", fname);
868 }
869
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
872 to add it. */
873
874 /* This is where we "interpret" the mini-script which is <test>.script. */
875 if (f != NULL)
876 {
877 while (getline (&the_line, &line_len, f) > 0)
878 {
879 int nt = tokenize (the_line, the_words, 3);
880 int i;
881
882 /* Expand variables. */
883 for (i = 1; i < nt; ++i)
884 {
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,
908 the_words[i], NULL);
909 }
910
911 if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
912 {
913 char *r = strrchr (the_words[1], '/');
914 if (r)
915 the_words[2] = concat (the_words[2], r + 1, NULL);
916 else
917 the_words[2] = concat (the_words[2], the_words[1], NULL);
918 }
919
920 /* Run the following commands in the_words[0] with NT number of
921 arguments (including the command). */
922
923 if (nt == 2 && strcmp (the_words[0], "so") == 0)
924 {
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]);
929 }
930 else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
931 {
932 copy_one_file (the_words[1], the_words[2]);
933 }
934 else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
935 {
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));
939 }
940 else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
941 {
942 long int m;
943 errno = 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));
949
950 }
951 else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
952 {
953 maybe_xunlink (the_words[1]);
954 }
955 else if (nt >= 2 && strcmp (the_words[0], "exec") == 0)
956 {
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];
963
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]),
969 NULL);
970
971
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
975 chroot. */
976 copy_one_file (new_child_proc[0], concat (new_root_path,
977 new_exec_path,
978 NULL));
979
980 new_child_exec = xstrdup (new_exec_path);
981 if (the_words[2])
982 new_child_proc[0] = xstrdup (the_words[2]);
983 else
984 new_child_proc[0] = new_child_exec;
985 }
986 else if (nt == 2 && strcmp (the_words[0], "cwd") == 0)
987 {
988 change_cwd = xstrdup (the_words[1]);
989 }
990 else if (nt == 1 && strcmp (the_words[0], "su") == 0)
991 {
992 be_su = 1;
993 }
994 else if (nt == 3 && strcmp (the_words[0], "mkdirp") == 0)
995 {
996 long int m;
997 errno = 0;
998 m = strtol (the_words[1], NULL, 0);
999 TEST_COMPARE (errno, 0);
1000 xmkdirp (the_words[2], m);
1001 }
1002 else if (nt > 0 && the_words[0][0] != '#')
1003 {
1004 fprintf (stderr, "\033[31minvalid [%s]\033[0m\n", the_words[0]);
1005 exit (1);
1006 }
1007 }
1008 fclose (f);
1009 }
1010 }
1011
1012 if (do_postclean)
1013 {
1014 pid_t pc_pid = fork ();
1015
1016 if (pc_pid < 0)
1017 {
1018 FAIL_EXIT1 ("Can't fork for post-clean");
1019 }
1020 else if (pc_pid > 0)
1021 {
1022 /* Parent. */
1023 int status;
1024 waitpid (pc_pid, &status, 0);
1025
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);
1029
1030 if (WIFEXITED (status))
1031 exit (WEXITSTATUS (status));
1032
1033 if (WIFSIGNALED (status))
1034 {
1035 printf ("%%SIGNALLED%%\n");
1036 exit (77);
1037 }
1038
1039 printf ("%%EXITERROR%%\n");
1040 exit (78);
1041 }
1042
1043 /* Child continues. */
1044 }
1045
1046 /* This is the last point in the program where we're still in the
1047 "normal" namespace. */
1048
1049 #ifdef CLONE_NEWNS
1050 /* The unshare here gives us our own spaces and capabilities. */
1051 if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
1052 {
1053 /* Older kernels may not support all the options, or security
1054 policy may block this call. */
1055 if (errno == EINVAL || errno == EPERM)
1056 {
1057 int saved_errno = errno;
1058 if (errno == EPERM)
1059 check_for_unshare_hints ();
1060 FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno));
1061 }
1062 else
1063 FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
1064 }
1065 #else
1066 /* Some targets may not support unshare at all. */
1067 FAIL_UNSUPPORTED ("unshare support missing");
1068 #endif
1069
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");
1073
1074 trymount (support_srcdir_root, new_srcdir_path);
1075 trymount (support_objdir_root, new_objdir_path);
1076
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");
1081
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);
1085
1086 if (chdir (new_cwd_path) < 0)
1087 FAIL_EXIT1 ("Can't cd to new %s - ", new_cwd_path);
1088
1089 /* This is to pass the "outside" PID to the child, which will be PID
1090 1. */
1091 if (pipe2 (pipes, O_CLOEXEC) < 0)
1092 FAIL_EXIT1 ("Can't create pid pipe");
1093
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
1097 up. */
1098 child = fork ();
1099 if (child < 0)
1100 FAIL_EXIT1 ("Unable to fork");
1101 else if (child > 0)
1102 {
1103 /* Parent. */
1104 int status;
1105
1106 /* Send the child's "outside" pid to it. */
1107 write (pipes[1], &child, sizeof(child));
1108 close (pipes[0]);
1109 close (pipes[1]);
1110
1111 waitpid (child, &status, 0);
1112
1113 if (WIFEXITED (status))
1114 exit (WEXITSTATUS (status));
1115
1116 if (WIFSIGNALED (status))
1117 {
1118 printf ("%%SIGNALLED%%\n");
1119 exit (77);
1120 }
1121
1122 printf ("%%EXITERROR%%\n");
1123 exit (78);
1124 }
1125
1126 /* The rest is the child process, which is now PID 1 and "in" the
1127 new root. */
1128
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));
1132 close (pipes[0]);
1133 close (pipes[1]);
1134 sprintf (pid_buf, "%lu", (long unsigned)child);
1135 setenv ("PID_OUTSIDE_CONTAINER", pid_buf, 0);
1136
1137 maybe_xmkdir ("/tmp", 0755);
1138
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: ");
1143
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);
1147 if (UMAP < 0)
1148 FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
1149
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));
1153 xclose (UMAP);
1154
1155 /* We must disable setgroups () before we can map our groups, else we
1156 get EPERM. */
1157 GMAP = open ("/proc/self/setgroups", O_WRONLY);
1158 if (GMAP >= 0)
1159 {
1160 /* We support kernels old enough to not have this. */
1161 write (GMAP, "deny\n", 5);
1162 xclose (GMAP);
1163 }
1164
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);
1168 if (GMAP < 0)
1169 FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
1170
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));
1174 xclose (GMAP);
1175
1176 if (change_cwd)
1177 {
1178 if (chdir (change_cwd) < 0)
1179 FAIL_EXIT1 ("Can't cd to %s inside container - ", change_cwd);
1180 }
1181
1182 /* Now run the child. */
1183 execvp (new_child_exec, new_child_proc);
1184
1185 /* Or don't run the child? */
1186 FAIL_EXIT1 ("Unable to exec %s: %s\n", new_child_exec, strerror (errno));
1187
1188 /* Because gcc won't know error () never returns... */
1189 exit (EXIT_UNSUPPORTED);
1190 }