public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* V7 test-in-container patch
@ 2018-08-15  5:21 DJ Delorie
  2018-08-15 14:24 ` Florian Weimer
  0 siblings, 1 reply; 6+ messages in thread
From: DJ Delorie @ 2018-08-15  5:21 UTC (permalink / raw)
  To: libc-alpha; +Cc: Joseph Myers, Florian Weimer, Carlos O'Donell


Includes changes as per https://www.sourceware.org/ml/libc-alpha/2018-08/msg00293.html

	* Makefile (testroot.pristine): New rules to initialize the
	test-in-container "testroot".
	* Makerules (all-testsuite): Add tests-container.
	* Rules (tests): Add tests-container.
	(binaries-all-tests): Likewise.
	(tests-container): New, run these tests in the testroot container.
	* support/Makefile (others): Add *-container, support_paths.c,
	xmkdirp, and links-dso-program.
	* support/links-dso-program-c.c: New.
	* support/links-dso-program.cc: New.
	* support/test-container.c: New.
	* support/shell-container.c: New.
	* support/echo-container.c: New.
	* support/true-container.c: New.
	* support/xmkdirp.c: New.
	* support/xsymlink.c: New.
	* support/support_paths.c: New.
	* support/support.h: Add xmkdirp() and support paths prototypes.
	* support/xunistd.h: Add xsymlink().

	* nss/tst-nss-test3.c: Convert to test-in-container.
	* nss/tst-nss-test3.root/: New.

diff --git a/Makefile b/Makefile
index d3f25a525a..fb428578b6 100644
--- a/Makefile
+++ b/Makefile
@@ -340,6 +340,49 @@ define summarize-tests
 @! egrep -q -v '^(X?PASS|XFAIL|UNSUPPORTED):' $(objpfx)$1
 endef
 
+# The intention here is to do ONE install of our build into the
+# testroot.pristine/ directory, then rsync (internal to
+# support/test-container) that to testroot.root/ at the start of each
+# test.  That way we can promise each test a "clean" install, without
+# having to do the install for each test.
+#
+# In addition, we have to copy some files (which we build) into this
+# root in addition to what glibc installs.  For example, many tests
+# require /bin/sh be present, and any shared objects that /bin/sh
+# depends on.  We also build a "test" program in either C or (if
+# available) C++ just so we can copy in any shared objects (which we
+# do not build) that GCC-compiled programs depend on.
+
+$(tests-container) $(addsuffix /tests,$(subdirs)) : $(objpfx)testroot.pristine/install.stamp
+$(objpfx)testroot.pristine/install.stamp :
+	test -d $(objpfx)testroot.pristine || \
+	  mkdir $(objpfx)testroot.pristine
+	# We need a working /bin/sh for some of the tests.
+	test -d $(objpfx)testroot.pristine/bin || \
+	  mkdir $(objpfx)testroot.pristine/bin
+	cp $(objpfx)support/shell-container $(objpfx)testroot.pristine/bin/sh
+	cp $(objpfx)support/echo-container $(objpfx)testroot.pristine/bin/echo
+	cp $(objpfx)support/true-container $(objpfx)testroot.pristine/bin/true
+	# Copy these DSOs first so we can overwrite them with our own.
+	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  $(objpfx)elf/$(rtld-installed-name) \
+			        $(objpfx)testroot.pristine/bin/sh \
+	                      | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
+	  do \
+	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
+	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
+	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
+	  done
+	for dso in `$(test-wrapper-env) LD_TRACE_LOADED_OBJECTS=1  $(objpfx)elf/$(rtld-installed-name) \
+			        $(objpfx)support/links-dso-program \
+	                      | grep / | sed 's/^[^/]*//' | sed 's/ .*//'` ;\
+	  do \
+	    test -d `dirname $(objpfx)testroot.pristine$$dso` || \
+	      mkdir -p `dirname $(objpfx)testroot.pristine$$dso` ;\
+	    $(test-wrapper) cp $$dso $(objpfx)testroot.pristine$$dso ;\
+	  done
+	$(MAKE) install DESTDIR=$(objpfx)testroot.pristine
+	touch $(objpfx)testroot.pristine/install.stamp
+
 tests-special-notdir = $(patsubst $(objpfx)%, %, $(tests-special))
 tests: $(tests-special)
 	$(..)scripts/merge-test-results.sh -s $(objpfx) "" \
diff --git a/Makerules b/Makerules
index a10a0b4d70..64dab056ea 100644
--- a/Makerules
+++ b/Makerules
@@ -1369,7 +1369,7 @@ xcheck: xtests
 # The only difference between MODULE_NAME=testsuite and MODULE_NAME=nonlib is
 # that almost all internal declarations from config.h, libc-symbols.h, and
 # include/*.h are not available to 'testsuite' code, but are to 'nonlib' code.
-all-testsuite := $(strip $(tests) $(xtests) $(test-srcs) $(test-extras))
+all-testsuite := $(strip $(tests) $(xtests) $(test-srcs) $(test-extras) $(tests-container))
 ifneq (,$(all-testsuite))
 cpp-srcs-left = $(all-testsuite)
 lib := testsuite
diff --git a/Rules b/Rules
index 706c8a749d..c3fd2bb0cb 100644
--- a/Rules
+++ b/Rules
@@ -130,12 +130,12 @@ others: $(py-const)
 
 ifeq ($(run-built-tests),no)
 tests: $(addprefix $(objpfx),$(filter-out $(tests-unsupported), \
-                                          $(tests) $(tests-internal)) \
+                                          $(tests) $(tests-internal) $(tests-container)) \
 			     $(test-srcs)) $(tests-special) \
 			     $(tests-printers-programs)
 xtests: tests $(xtests-special)
 else
-tests: $(tests:%=$(objpfx)%.out) $(tests-internal:%=$(objpfx)%.out) \
+tests: $(tests:%=$(objpfx)%.out) $(tests-internal:%=$(objpfx)%.out) $(tests-container:%=$(objpfx)%.out) \
        $(tests-special) $(tests-printers-out)
 xtests: tests $(xtests:%=$(objpfx)%.out) $(xtests-special)
 endif
@@ -149,7 +149,7 @@ tests-expected = $(tests) $(tests-internal) $(tests-printers)
 endif
 tests:
 	$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
-	  $(sort $(tests-expected) $(tests-special-notdir:.out=)) \
+	  $(sort $(tests-expected) $(tests-special-notdir:.out=) $(tests-container)) \
 	  > $(objpfx)subdir-tests.sum
 xtests:
 	$(..)scripts/merge-test-results.sh -s $(objpfx) $(subdir) \
@@ -158,7 +158,7 @@ xtests:
 
 ifeq ($(build-programs),yes)
 binaries-all-notests = $(others) $(sysdep-others)
-binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs)
+binaries-all-tests = $(tests) $(tests-internal) $(xtests) $(test-srcs) $(tests-container)
 binaries-all = $(binaries-all-notests) $(binaries-all-tests)
 binaries-static-notests = $(others-static)
 binaries-static-tests = $(tests-static) $(xtests-static)
@@ -248,6 +248,16 @@ $(objpfx)%.out: /dev/null $(objpfx)%	# Make it 2nd arg for canned sequence.
 	$(make-test-out) > $@; \
 	$(evaluate-test)
 
+
+# Any tests that require an isolated container (chroot) in which to
+# run, should be added to tests-container.
+$(tests-container:%=$(objpfx)%.out): $(objpfx)%.out : $(if $(wildcard $(objpfx)%.files),$(objpfx)%.files,/dev/null) $(objpfx)%
+	$(test-wrapper-env) $(run-program-env) $(run-via-rtld-prefix) \
+	  $(common-objpfx)support/test-container env $(run-program-env) $($*-ENV) \
+	  $(host-test-program-cmd) $($*-ARGS) > $@; \
+	$(evaluate-test)
+
+
 # tests-unsupported lists tests that we will not try to build at all in
 # this configuration.  Note this runs every time because it does not
 # actually create its target.  The dependency on Makefile is meant to
diff --git a/nss/Makefile b/nss/Makefile
index 5209fc0456..e00a4f768f 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -55,11 +55,13 @@ tests-internal		= tst-field
 tests			= test-netdb test-digits-dots tst-nss-getpwent bug17079 \
 			  tst-nss-test1 \
 			  tst-nss-test2 \
-			  tst-nss-test3 \
 			  tst-nss-test4 \
 			  tst-nss-test5
 xtests			= bug-erange
 
+tests-container = \
+			  tst-nss-test3
+
 # Tests which need libdl
 ifeq (yes,$(build-shared))
 tests += tst-nss-files-hosts-erange
diff --git a/nss/tst-nss-test3.c b/nss/tst-nss-test3.c
index d9d708ae7b..4112231778 100644
--- a/nss/tst-nss-test3.c
+++ b/nss/tst-nss-test3.c
@@ -107,7 +107,11 @@ do_test (void)
   int i;
   struct group *g = NULL;
 
-  __nss_configure_lookup ("group", "test1");
+/* Previously we used __nss_configure_lookup to isolate the test
+   from the host environment and to get it to lookup from our new
+   test1 NSS service module, but now this test is run in a different
+   root filesystem via the test-container support and we directly
+   configure the use of the test1 NSS service.  */
 
   setgrent ();
 
diff --git a/nss/tst-nss-test3.root/etc/nsswitch.conf b/nss/tst-nss-test3.root/etc/nsswitch.conf
new file mode 100644
index 0000000000..5e08fe5eea
--- /dev/null
+++ b/nss/tst-nss-test3.root/etc/nsswitch.conf
@@ -0,0 +1 @@
+group	test1
diff --git a/nss/tst-nss-test3.root/tst-nss-test3.script b/nss/tst-nss-test3.root/tst-nss-test3.script
new file mode 100644
index 0000000000..a10beb1e6c
--- /dev/null
+++ b/nss/tst-nss-test3.root/tst-nss-test3.script
@@ -0,0 +1,2 @@
+cp $B/nss/libnss_test1.so $L/libnss_test1.so.2
+cp $B/nss/libnss_test2.so $L/libnss_test2.so.2
diff --git a/support/Makefile b/support/Makefile
index 652d2cdf69..2c937761ab 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -53,6 +53,7 @@ libsupport-routines = \
   support_format_netent \
   support_isolate_in_subprocess \
   support_openpty \
+  support_paths \
   support_quote_blob \
   support_record_failure \
   support_run_diff \
@@ -84,6 +85,7 @@ libsupport-routines = \
   xmalloc \
   xmemstream \
   xmkdir \
+  xmkdirp \
   xmmap \
   xmprotect \
   xmunmap \
@@ -139,6 +141,7 @@ libsupport-routines = \
   xsocket \
   xstrdup \
   xstrndup \
+  xsymlink \
   xsysconf \
   xunlink \
   xwaitpid \
@@ -151,6 +154,42 @@ ifeq ($(build-shared),yes)
 libsupport-inhibit-o += .o
 endif
 
+CFLAGS-support_paths.c = \
+		-DSRCDIR_PATH=\"`cd .. ; pwd`\" \
+		-DOBJDIR_PATH=\"`cd $(objpfx)/..; pwd`\" \
+		-DINSTDIR_PATH=\"$(prefix)\" \
+		-DLIBDIR_PATH=\"$(libdir)\"
+
+others: \
+	$(objpfx)test-container \
+	$(objpfx)links-dso-program \
+	$(objpfx)shell-container \
+	$(objpfx)echo-container \
+	$(objpfx)true-container
+
+LDLIBS-test-container = $(libsupport)
+
+others += test-container
+others-noinstall += test-container
+
+others += shell-container echo-container true-container
+others-noinstall += shell-container echo-container true-container
+
+$(objpfx)test-container : $(libsupport)
+$(objpfx)shell-container : $(libsupport)
+$(objpfx)echo-container : $(libsupport)
+$(objpfx)true-container : $(libsupport)
+
+# This exists only so we can guess which OS DSOs we need to copy into
+# the testing container.
+ifeq (,$(CXX))
+$(objpfx)links-dso-program : $(objpfx)links-dso-program-c.o
+	$(LINK.o) -o $@ $^
+else
+$(objpfx)links-dso-program : $(objpfx)links-dso-program.o
+	$(LINK.o) -o $@ $^ -lstdc++
+endif
+
 tests = \
   README-testing \
   tst-support-namespace \
diff --git a/support/echo-container.c b/support/echo-container.c
new file mode 100644
index 0000000000..e4d48df957
--- /dev/null
+++ b/support/echo-container.c
@@ -0,0 +1,34 @@
+/* Minimal /bin/echo for in-container use.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+
+int
+main (int argc, const char **argv)
+{
+  int i;
+
+  for (i = 1; i < argc; i++)
+    {
+      if (i > 1)
+	putchar (' ');
+      fputs (argv[i], stdout);
+    }
+  putchar ('\n');
+  return 0;
+}
diff --git a/support/links-dso-program-c.c b/support/links-dso-program-c.c
new file mode 100644
index 0000000000..400b87cab4
--- /dev/null
+++ b/support/links-dso-program-c.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+  /* Complexity to keep gcc from optimizing this away.  */
+  printf("This is a test %s.\n", argc > 1 ? argv[1] : "null");
+  return 0;
+}
diff --git a/support/links-dso-program.cc b/support/links-dso-program.cc
new file mode 100644
index 0000000000..dba6976c06
--- /dev/null
+++ b/support/links-dso-program.cc
@@ -0,0 +1,11 @@
+#include <iostream>
+
+using namespace std;
+
+int
+main (int argc, char **argv)
+{
+  /* Complexity to keep gcc from optimizing this away.  */
+  cout << (argc > 1 ? argv[1] : "null");
+  return 0;
+}
diff --git a/support/shell-container.c b/support/shell-container.c
new file mode 100644
index 0000000000..cf3fcdad70
--- /dev/null
+++ b/support/shell-container.c
@@ -0,0 +1,395 @@
+/* Minimal /bin/sh for in-container use.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define __USE_LARGEFILE64
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <stdarg.h>
+#include <sys/sysmacros.h>
+#include <ctype.h>
+#include <utime.h>
+#include <errno.h>
+#include <error.h>
+
+#include <support/support.h>
+
+/* Design considerations
+
+ General rule: optimize for developer time, not run time.
+
+ Specifically:
+
+ * Don't worry about slow algorithms
+ * Don't worry about free'ing memory
+ * Don't implement anything the testsuite doesn't need.
+ * Line and argument counts are limited, see below.
+
+*/
+
+#define MAX_ARG_COUNT 100
+#define MAX_LINE_LENGTH 1000
+
+/* Debugging is enabled via --debug, which must be the first argument.  */
+static int debug_mode = 0;
+#define dprintf if (debug_mode) fprintf
+
+/* Emulate the "/bin/true" command.  Arguments are ignored.  */
+static int
+true_func (char **argv)
+{
+  return 0;
+}
+
+/* Emulate the "/bin/echo" command.  Options are ignored, arguments
+   are printed to stdout.  */
+static int
+echo_func (char **argv)
+{
+  int i;
+
+  for (i = 0; argv[i]; i++)
+    {
+      if (i > 0)
+	putchar (' ');
+      fputs (argv[i], stdout);
+    }
+  putchar ('\n');
+
+  return 0;
+}
+
+/* Emulate the "/bin/cp" command.  Options are ignored.  Only copies
+   one source file to one destination file.  Directory destinations
+   are not supported.  */
+static int
+copy_func (char **argv)
+{
+  char *sname = argv[0];
+  char *dname = argv[1];
+  int sfd, dfd;
+  struct stat st;
+
+  sfd = open (sname, O_RDONLY);
+  if (sfd < 0)
+    {
+      fprintf (stderr, "cp: unable to open %s for reading: %s\n",
+	       sname, strerror (errno));
+      return 1;
+    }
+
+  if (fstat (sfd, &st) < 0)
+    {
+      fprintf (stderr, "cp: unable to fstat %s: %s\n",
+	       sname, strerror (errno));
+      return 1;
+    }
+
+  dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+  if (dfd < 0)
+    {
+      fprintf (stderr, "cp: unable to open %s for writing: %s\n",
+	       dname, strerror (errno));
+      return 1;
+    }
+
+  if (copy_file_range (sfd, 0, dfd, 0, st.st_size, 0) != st.st_size)
+    {
+      fprintf (stderr, "cp: cannot copy file %s to %s: %s\n",
+	       sname, dname, strerror (errno));
+      return 1;
+    }
+
+  close (sfd);
+  close (dfd);
+
+  chmod (dname, st.st_mode & 0777);
+
+  return 0;
+
+}
+
+/* This is a list of all the built-in commands we understand.  */
+static struct {
+  const char *name;
+  int (*func)(char **argv);
+} builtin_funcs[] = {
+  { "true", true_func },
+  { "echo", echo_func },
+  { "cp", copy_func },
+  { NULL, NULL }
+};
+
+/* Run one tokenized command.  argv[0] is the command.  argv is
+   NULL-terminated.  */
+static void
+run_command_array (char **argv)
+{
+  int i, j;
+  pid_t pid;
+  int status;
+  int (*builtin_func)(char **args);
+
+  if (argv[0] == NULL)
+    return;
+
+  builtin_func = NULL;
+
+  int new_stdin = 0;
+  int new_stdout = 1;
+  int new_stderr = 2;
+
+  dprintf (stderr, "run_command_array starting\n");
+  for (i = 0; argv[i]; i++)
+    dprintf (stderr, "   argv [%d] `%s'\n", i, argv[i]);
+
+  for (j = i = 0; argv[i]; i++)
+    {
+      if (strcmp (argv[i], "<") == 0 && argv[i + 1])
+	{
+	  new_stdin = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
+	  ++i;
+	  continue;
+	}
+      if (strcmp (argv[i], ">") == 0 && argv[i + 1])
+	{
+	  new_stdout = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
+	  ++i;
+	  continue;
+	}
+      if (strcmp (argv[i], ">>") == 0 && argv[i + 1])
+	{
+	  new_stdout = open (argv[i + 1], O_WRONLY|O_CREAT|O_APPEND, 0777);
+	  ++i;
+	  continue;
+	}
+      if (strcmp (argv[i], "2>") == 0 && argv[i + 1])
+	{
+	  new_stderr = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
+	  ++i;
+	  continue;
+	}
+      argv[j++] = argv[i];
+    }
+  argv[j] = NULL;
+
+
+  for (i = 0; builtin_funcs[i].name != NULL; i++)
+    if (strcmp (argv[0], builtin_funcs[i].name) == 0)
+       builtin_func = builtin_funcs[i].func;
+
+  dprintf (stderr, "builtin %p argv0 `%s'\n", builtin_func, argv[0]);
+
+  pid = fork();
+  if (pid < 0)
+    {
+      fprintf (stderr, "sh; fork failed\n");
+      exit(1);
+    }
+
+  if (pid == 0)
+    {
+      if (new_stdin != 0)
+	{
+	  dup2 (new_stdin, 0);
+	  close (new_stdin);
+	}
+      if (new_stdout != 1)
+	{
+	  dup2 (new_stdout, 1);
+	  close (new_stdout);
+	}
+      if (new_stderr != 2)
+	{
+	  dup2 (new_stderr, 2);
+	  close (new_stdout);
+	}
+
+      if (builtin_func != NULL)
+	exit (builtin_func (argv + 1));
+
+      execvp (argv[0], argv);
+
+      fprintf (stderr, "sh: execing %s failed: %s", argv[0], strerror (errno));
+      exit(1);
+    }
+
+  waitpid (pid, &status, 0);
+
+  dprintf (stderr, "exiting run_command_array\n");
+
+  if (WIFEXITED (status))
+    {
+      int rv = WEXITSTATUS (status);
+      if (rv)
+	exit (rv);
+    }
+  else
+    exit (1);
+}
+
+/* Run one command-as-a-string, by tokenizing it.  Limited to
+   MAX_ARG_COUNT arguments.  Simple substitution is done of $1 to $9
+   (as whole separate tokens) from iargs[].  Quoted strings work if
+   the quotes wrap whole tokens; i.e. "foo bar" but not foo" bar".  */
+static void
+run_command_string (const char *cmdline, const char **iargs)
+{
+  char *args[MAX_ARG_COUNT+1];
+  int ap = 0;
+  const char *start, *end;
+  int nargs;
+
+  for (nargs = 0; iargs[nargs] != NULL; ++nargs)
+    ;
+
+  dprintf (stderr, "run_command_string starting: '%s'\n", cmdline);
+
+  while (ap < MAX_ARG_COUNT)
+    {
+      /* If the argument is quoted, this is the quote character, else NUL.  */
+      int in_quote = 0;
+
+      /* Skip whitespace up to the next token.  */
+      while (*cmdline && isspace (*cmdline))
+	cmdline ++;
+      if (*cmdline == 0)
+	break;
+
+      start = cmdline;
+      /* Check for quoted argument.  */
+      in_quote = (*cmdline == '\'' || *cmdline == '"') ? *cmdline : 0;
+
+      /* Skip to end of token; either by whitespace or matching quote.  */
+      dprintf (stderr, "in_quote %d\n", in_quote);
+      while (*cmdline
+	     && (!isspace (*cmdline) || in_quote))
+	{
+	  if (*cmdline == in_quote
+	      && cmdline != start)
+	    in_quote = 0;
+	  dprintf (stderr, "[%c]%d ", *cmdline, in_quote);
+	  cmdline ++;
+	}
+      dprintf (stderr, "\n");
+
+      /* Allocate space for this token and store it in args[].  */
+      end = cmdline;
+      dprintf (stderr, "start<%s> end<%s>\n", start, end);
+      args[ap] = (char *) xmalloc (end - start + 1);
+      memcpy (args[ap], start, end - start);
+      args[ap][end - start] = 0;
+
+      /* Strip off quotes, if found.  */
+      dprintf (stderr, "args[%d] = <%s>\n", ap, args[ap]);
+      if (args[ap][0] == '\''
+	  && args[ap][strlen(args[ap])-1] == '\'')
+	{
+	  args[ap][strlen(args[ap])-1] = 0;
+	  args[ap] ++;
+	}
+
+      else if (args[ap][0] == '"'
+	  && args[ap][strlen(args[ap])-1] == '"')
+	{
+	  args[ap][strlen(args[ap])-1] = 0;
+	  args[ap] ++;
+	}
+
+      /* Replace positional parameters like $4.  */
+      else if (args[ap][0] == '$'
+	       && isdigit (args[ap][1])
+	       && args[ap][2] == 0)
+	{
+	  int a = args[ap][1] - '1';
+	  if (0 <= a && a < nargs)
+	    args[ap] = strdup (iargs[a]);
+	}
+
+      ap ++;
+
+      if (*cmdline == 0)
+	break;
+    }
+
+  /* Lastly, NULL terminate the array and run it.  */
+  args[ap] = NULL;
+  run_command_array (args);
+}
+
+/* Run a script by reading lines and passing them to the above
+   function.  */
+static void
+run_script (const char *filename, const char **args)
+{
+  char line[MAX_LINE_LENGTH + 1];
+  dprintf (stderr, "run_script starting: '%s'\n", filename);
+  FILE *f = fopen (filename, "r");
+  if (f == NULL)
+    {
+      fprintf (stderr, "sh: %s: %s\n", filename, strerror (errno));
+      exit (1);
+    }
+  while (fgets (line, sizeof(line), f) != NULL)
+    {
+      if (line[0] == '#')
+	{
+	  dprintf (stderr, "comment: %s\n", line);
+	  continue;
+	}
+      run_command_string (line, args);
+    }
+  fclose (f);
+}
+
+int
+main (int argc, const char **argv)
+{
+  int i;
+
+  if (strcmp (argv[1], "--debug") == 0)
+    {
+      debug_mode = 1;
+      --argc;
+      ++argv;
+    }
+
+  dprintf (stderr, "container-sh starting:\n");
+  for (i = 0; i < argc; i++)
+    dprintf (stderr, "  argv[%d] is `%s'\n", i, argv[i]);
+
+  if (strcmp (argv[1], "-c") == 0)
+    run_command_string (argv[2], argv+3);
+  else
+    run_script (argv[1], argv+2);
+
+  dprintf (stderr, "normal exit 0\n");
+  return 0;
+}
diff --git a/support/support.h b/support/support.h
index b61fe0735c..04641f3e98 100644
--- a/support/support.h
+++ b/support/support.h
@@ -25,6 +25,8 @@
 
 #include <stddef.h>
 #include <sys/cdefs.h>
+/* For mode_t.  */
+#include <sys/stat.h>
 
 __BEGIN_DECLS
 
@@ -76,6 +78,19 @@ char *xasprintf (const char *format, ...)
 char *xstrdup (const char *);
 char *xstrndup (const char *, size_t);
 
+/* Equivalent of "mkdir -p".  */
+void xmkdirp (const char *, mode_t);
+
+/* These point to the TOP of the source/build tree, not your (or
+   support's) subdirectory.  */
+extern const char support_srcdir_root[];
+extern const char support_objdir_root[];
+
+/* Corresponds to the --prefix= passed to configure.  */
+extern const char support_install_prefix[];
+/* Corresponds to the install's lib/ or lib64/ directory.  */
+extern const char support_libdir_prefix[];
+
 __END_DECLS
 
 #endif /* SUPPORT_H */
diff --git a/support/support_paths.c b/support/support_paths.c
new file mode 100644
index 0000000000..a1c22315bd
--- /dev/null
+++ b/support/support_paths.c
@@ -0,0 +1,51 @@
+/* Various paths that might be needed.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/support.h>
+#include <support/check.h>
+
+/* The idea here is to make various makefile-level paths available to
+   support programs, as canonicalized absolute paths.  */
+
+/* These point to the TOP of the source/build tree, not your (or
+   support's) subdirectory.  */
+#ifdef SRCDIR_PATH
+const char support_srcdir_root[] = SRCDIR_PATH;
+#else
+# error please -DSRCDIR_PATH=something in the Makefile
+#endif
+
+#ifdef OBJDIR_PATH
+const char support_objdir_root[] = OBJDIR_PATH;
+#else
+# error please -DOBJDIR_PATH=something in the Makefile
+#endif
+
+#ifdef INSTDIR_PATH
+/* Corresponds to the --prefix= passed to configure.  */
+const char support_install_prefix[] = INSTDIR_PATH;
+#else
+# error please -DINSTDIR_PATH=something in the Makefile
+#endif
+
+#ifdef LIBDIR_PATH
+/* Corresponds to the install's lib/ or lib64/ directory.  */
+const char support_libdir_prefix[] = LIBDIR_PATH;
+#else
+# error please -DLIBDIR_PATH=something in the Makefile
+#endif
diff --git a/support/test-container.c b/support/test-container.c
new file mode 100644
index 0000000000..fc446f31d6
--- /dev/null
+++ b/support/test-container.c
@@ -0,0 +1,944 @@
+/* Run a test case in an isolated namespace.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#define __USE_LARGEFILE64
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <stdarg.h>
+#include <sys/sysmacros.h>
+#include <ctype.h>
+#include <utime.h>
+#include <errno.h>
+#include <error.h>
+
+#include <support/support.h>
+#include <support/xunistd.h>
+#include "check.h"
+#include "test-driver.h"
+
+int verbose = 0;
+
+/* Running a test in a container is tricky.  There are two main
+   categories of things to do:
+
+   1. "Once" actions, like setting up the container and doing an
+      install into it.
+
+   2. "Per-test" actions, like copying in support files and
+      configuring the container.
+
+
+   "Once" actions:
+
+   * mkdir $buildroot/testroot.pristine/
+   * install into it
+   * rsync to $buildroot/testroot.root/
+
+   "Per-test" actions:
+   * maybe rsync to $buildroot/testroot.root/
+   * copy support files and test binary
+   * chroot/unshare
+   * set up any mounts (like /proc)
+
+   Magic files:
+
+   For test $srcdir/foo/mytest.c we look for $srcdir/foo/mytest.root and, if found...
+
+   * mytest.root/ is rsync'd into container
+   * mytest.root/preclean.req causes fresh rsync (with delete) before test if present
+   * mytest.root/mytset.script has a list of "commands" to run:
+       syntax:
+         # comment
+         mv FILE FILE
+	 cp FILE FILE
+	 rm FILE
+	 FILE must start with $B/, $S/, $I/, $L/, or /
+	  (expands to build dir, source dir, install dir, library dir
+	   (in container), or container's root)
+   * mytest.root/postclean.req causes fresh rsync (with delete) after test if present
+
+   Note that $srcdir/foo/mytest.script may be used instead of a
+   $srcdir/foo/mytest.root/mytest.script in the sysroot template, if
+   there is no other reason for a sysroot.
+
+   Design goals:
+
+   * independent of other packages which may not be installed (like
+     rsync or Docker, or even "cp")
+
+   * Simple, easy to review code (i.e. prefer simple naive code over
+     complex efficient code)
+
+   * The current implementation ist parallel-make-safe, but only in
+     that it uses a lock to prevent parallel access to the testroot.  */
+
+/* Utility Functions */
+
+/* Like xunlink, but it's OK if the file already doesn't exist.  */
+void
+maybe_xunlink (const char *path)
+{
+  int rv = unlink (path);
+  if (rv < 0 && errno != ENOENT)
+    FAIL_EXIT1 ("unlink (\"%s\"): %m", path);
+}
+
+/* Like xmkdir, but it's OK if the directory already exists.  */
+void
+maybe_xmkdir (const char *path, mode_t mode)
+{
+  struct stat st;
+
+  if (stat (path, &st) == 0
+      && S_ISDIR (st.st_mode))
+    return;
+  xmkdir (path, mode);
+}
+
+/* Temporarily concatenate multiple strings into one.  Allows up to 10
+   temporary results; use strdup () if you need them to be
+   permanent.  */
+static char *
+concat (const char *str, ...)
+{
+  /* Assume initialized to NULL/zero.  */
+  static char *bufs[10];
+  static size_t buflens[10];
+  static int bufn = 0;
+  int n;
+  size_t len;
+  va_list ap, ap2;
+  char *cp;
+  char *next;
+
+  va_start (ap, str);
+  va_copy (ap2, ap);
+
+  n = bufn;
+  bufn = (bufn + 1) % 10;
+  len = strlen (str);
+
+  while ((next = va_arg (ap, char *)) != NULL)
+    len = len + strlen (next);
+
+  va_end (ap);
+
+  if (bufs[n] == NULL)
+    {
+      bufs[n] = xmalloc (len + 1); /* NUL */
+      buflens[n] = len + 1;
+    }
+  else if (buflens[n] < len + 1)
+    {
+      bufs[n] = xrealloc (bufs[n], len + 1); /* NUL */
+      buflens[n] = len + 1;
+    }
+
+  strcpy (bufs[n], str);
+  cp = strchr (bufs[n], '\0');
+  while ((next = va_arg (ap2, char *)) != NULL)
+    {
+      strcpy (cp, next);
+      cp = strchr (cp, '\0');
+    }
+  *cp = 0;
+  va_end (ap2);
+
+  return bufs[n];
+}
+
+/* Try to mount SRC onto DEST.  */
+static void
+trymount (const char *src, const char *dest)
+{
+  if (mount (src, dest, "", MS_BIND, NULL) < 0)
+    FAIL_UNSUPPORTED ("can't mount %s onto %s\n", src, dest);
+}
+
+/* Special case of above for devices like /dev/zero where we have to
+   mount a device over a device, not a directory over a directory.  */
+static void
+devmount (const char *new_root_path, const char *which)
+{
+  int fd;
+  fd = open (concat (new_root_path, "/dev/", which, NULL),
+	     O_CREAT | O_TRUNC | O_RDWR, 0777);
+  xclose (fd);
+
+  trymount (concat ("/dev/", which, NULL),
+	    concat (new_root_path, "/dev/", which, NULL));
+}
+
+/* Returns true if the string "looks like" an environement variable
+   being set.  */
+static int
+is_env_setting (const char *a)
+{
+  int count_name = 0;
+
+  while (*a)
+    {
+      if (isalnum (*a) || *a == '_')
+	++count_name;
+      else if (*a == '=' && count_name > 0)
+	return 1;
+      else
+	return 0;
+      ++a;
+    }
+  return 0;
+}
+
+/* Break the_line into words and store in the_words.  Max nwords,
+   returns actual count.  */
+static int
+tokenize (char *the_line, char **the_words, int nwords)
+{
+  int rv = 0;
+
+  while (nwords > 0)
+    {
+      /* Skip leading whitespace, if any.  */
+      while (*the_line && isspace (*the_line))
+	++the_line;
+
+      /* End of line?  */
+      if (*the_line == 0)
+	return rv;
+
+      /* THE_LINE points to a non-whitespace character, so we have a
+	 word.  */
+      *the_words = the_line;
+      ++the_words;
+      nwords--;
+      ++rv;
+
+      /* Skip leading whitespace, if any.  */
+      while (*the_line && ! isspace (*the_line))
+	++the_line;
+
+      /* We now point at the trailing NUL *or* some whitespace.  */
+      if (*the_line == 0)
+	return rv;
+
+      /* It was whitespace, skip and keep tokenizing.  */
+      *the_line++ = 0;
+    }
+
+  /* We get here if we filled the words buffer.  */
+  return rv;
+}
+
+/* Mini-RSYNC implementation.  Optimize later.      */
+
+/* A few routines for an "rsync buffer" which stores the paths we're
+   working on.  We continuously grow and shrink the paths in each
+   buffer so there's lot of re-use.  */
+
+/* We rely on "initialized to zero" to set these up.  */
+typedef struct
+{
+  char *buf;
+  size_t len;
+  size_t size;
+} path_buf;
+
+static path_buf spath, dpath;
+
+static void
+r_setup (char *path, path_buf * pb)
+{
+  size_t len = strlen (path);
+  if (pb->buf == NULL || pb->size < len + 1)
+    {
+      /* Round up */
+      size_t sz = ALIGN_UP (len + 1, 512);
+      if (pb->buf == NULL)
+	pb->buf = (char *) xmalloc (sz);
+      else
+	pb->buf = (char *) xrealloc (pb->buf, sz);
+      if (pb->buf == NULL)
+	FAIL_UNSUPPORTED ("Out of memory while rsyncing\n");
+
+      pb->size = sz;
+    }
+  strcpy (pb->buf, path);
+  pb->len = len;
+}
+
+static void
+r_append (const char *path, path_buf * pb)
+{
+  size_t len = strlen (path) + pb->len;
+  if (pb->size < len + 1)
+    {
+      /* Round up */
+      size_t sz = ALIGN_UP (len + 1, 512);
+      pb->buf = (char *) xrealloc (pb->buf, sz);
+      if (pb->buf == NULL)
+	FAIL_UNSUPPORTED ("Out of memory while rsyncing\n");
+
+      pb->size = sz;
+    }
+  strcpy (pb->buf + pb->len, path);
+  pb->len = len;
+}
+
+static int
+file_exists (char *path)
+{
+  struct stat st;
+  if (lstat (path, &st) == 0)
+    return 1;
+  return 0;
+}
+
+static void
+recursive_remove (char *path)
+{
+  pid_t child;
+  int status;
+
+  child = fork ();
+
+  switch (child) {
+  case -1:
+    FAIL_UNSUPPORTED ("Unable to fork");
+  case 0:
+    /* Child.  */
+    execlp ("rm", "rm", "-rf", path, NULL);
+  default:
+    /* Parent.  */
+    waitpid (child, &status, 0);
+    /* "rm" would have already printed a suitable error message.  */
+    if (! WIFEXITED (status)
+	|| WEXITSTATUS (status) != 0)
+      exit (1);
+
+    break;
+  }
+}
+
+/* Used for both rsync and the mytest.script "cp" command.  */
+static void
+copy_one_file (const char *sname, const char *dname)
+{
+  int sfd, dfd;
+  struct stat st;
+  struct utimbuf times;
+
+  sfd = open (sname, O_RDONLY);
+  if (sfd < 0)
+    FAIL_EXIT1 ("unable to open %s for reading\n", sname);
+
+  if (fstat (sfd, &st) < 0)
+    FAIL_EXIT1 ("unable to fstat %s\n", sname);
+
+  dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+  if (dfd < 0)
+    FAIL_EXIT1 ("unable to open %s for writing\n", dname);
+
+  if (copy_file_range (sfd, 0, dfd, 0, st.st_size, 0) != st.st_size)
+    FAIL_EXIT1 ("cannot copy file %s to %s\n", sname, dname);
+
+  xclose (sfd);
+  xclose (dfd);
+
+  if (chmod (dname, st.st_mode & 0777) < 0)
+    FAIL_EXIT1 ("chmod %s: %s\n", dname, strerror (errno));
+
+  times.actime = st.st_atime;
+  times.modtime = st.st_mtime;
+  if (utime (dname, &times) < 0)
+    FAIL_EXIT1 ("utime %s: %s\n", dname, strerror (errno));
+}
+
+/* We don't check *everything* about the two files to see if a copy is
+   needed, just the minimum to make sure we get the latest copy.  */
+static int
+need_sync (char *ap, char *bp, struct stat *a, struct stat *b)
+{
+  if ((a->st_mode & S_IFMT) != (b->st_mode & S_IFMT))
+    return 1;
+
+  if (S_ISLNK (a->st_mode))
+    {
+      int rv;
+      char *al, *bl;
+
+      if (a->st_size != b->st_size)
+	return 1;
+
+      al = xreadlink (ap);
+      bl = xreadlink (bp);
+      rv = strcmp (al, bl);
+      free (al);
+      free (bl);
+      if (rv == 0)
+	return 0; /* links are same */
+      return 1; /* links differ */
+    }
+
+  if (verbose)
+    {
+      if (a->st_size != b->st_size)
+	printf ("SIZE\n");
+      if ((a->st_mode & 0777) != (b->st_mode & 0777))
+	printf ("MODE\n");
+      if (a->st_mtime != b->st_mtime)
+	printf ("TIME\n");
+    }
+
+  if (a->st_size == b->st_size
+      && ((a->st_mode & 0777) == (b->st_mode & 0777))
+      && a->st_mtime == b->st_mtime)
+    return 0;
+
+  return 1;
+}
+
+static void
+rsync_1 (path_buf * src, path_buf * dest, int and_delete)
+{
+  DIR *dir;
+  struct dirent *de;
+  struct stat s, d;
+
+  r_append ("/", src);
+  r_append ("/", dest);
+
+  if (verbose)
+    printf ("sync %s to %s %s\n", src->buf, dest->buf,
+	    and_delete ? "and delete" : "");
+
+  size_t staillen = src->len;
+
+  size_t dtaillen = dest->len;
+
+  dir = opendir (src->buf);
+
+  while ((de = readdir (dir)) != NULL)
+    {
+      if (strcmp (de->d_name, ".") == 0
+	  || strcmp (de->d_name, "..") == 0)
+	continue;
+
+      src->len = staillen;
+      r_append (de->d_name, src);
+      dest->len = dtaillen;
+      r_append (de->d_name, dest);
+
+      s.st_mode = ~0;
+      d.st_mode = ~0;
+
+      if (lstat (src->buf, &s) != 0)
+	FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", src->buf);
+
+      /* It's OK if this one fails, since we know the file might be missing.  */
+      lstat (dest->buf, &d);
+
+      if (! need_sync (src->buf, dest->buf, &s, &d))
+	{
+	  if (S_ISDIR (s.st_mode))
+	    rsync_1 (src, dest, and_delete);
+	  continue;
+	}
+
+      if (d.st_mode != ~0)
+	switch (d.st_mode & S_IFMT)
+	  {
+	  case S_IFDIR:
+	    if (!S_ISDIR (s.st_mode))
+	      {
+		if (verbose)
+		  printf ("-D %s\n", dest->buf);
+		recursive_remove (dest->buf);
+	      }
+	    break;
+
+	  default:
+	    if (verbose)
+	      printf ("-F %s\n", dest->buf);
+	    maybe_xunlink (dest->buf);
+	    break;
+	  }
+
+      switch (s.st_mode & S_IFMT)
+	{
+	case S_IFREG:
+	  if (verbose)
+	    printf ("+F %s\n", dest->buf);
+	  copy_one_file (src->buf, dest->buf);
+	  break;
+
+	case S_IFDIR:
+	  if (verbose)
+	    printf ("+D %s\n", dest->buf);
+	  maybe_xmkdir (dest->buf, (s.st_mode & 0777) | 0700);
+	  rsync_1 (src, dest, and_delete);
+	  break;
+
+	case S_IFLNK:
+	  {
+	    char *lp;
+	    if (verbose)
+	      printf ("+L %s\n", dest->buf);
+	    lp = xreadlink (src->buf);
+	    xsymlink (lp, dest->buf);
+	    free (lp);
+	    break;
+	  }
+
+	default:
+	  break;
+	}
+    }
+
+  closedir (dir);
+  src->len = staillen;
+  src->buf[staillen] = 0;
+  dest->len = dtaillen;
+  dest->buf[dtaillen] = 0;
+
+  if (!and_delete)
+    return;
+
+  /* The rest of this function removes any files/directories in DEST
+     that do not exist in SRC.  This is triggered as part of a
+     preclean or postsclean step.  */
+
+  dir = opendir (dest->buf);
+
+  while ((de = readdir (dir)) != NULL)
+    {
+      if (strcmp (de->d_name, ".") == 0
+	  || strcmp (de->d_name, "..") == 0)
+	continue;
+
+      src->len = staillen;
+      r_append (de->d_name, src);
+      dest->len = dtaillen;
+      r_append (de->d_name, dest);
+
+      s.st_mode = ~0;
+      d.st_mode = ~0;
+
+      lstat (src->buf, &s);
+      
+      if (lstat (dest->buf, &d) != 0)
+	FAIL_EXIT1 ("%s obtained by readdir, but stat failed.\n", dest->buf);
+
+      if (s.st_mode == ~0)
+	{
+	  /* dest exists and src doesn't, clean it.  */
+	  switch (d.st_mode & S_IFMT)
+	    {
+	    case S_IFDIR:
+	      if (!S_ISDIR (s.st_mode))
+		{
+		  if (verbose)
+		    printf ("-D %s\n", dest->buf);
+		  recursive_remove (dest->buf);
+		}
+	      break;
+
+	    default:
+	      if (verbose)
+		printf ("-F %s\n", dest->buf);
+	      maybe_xunlink (dest->buf);
+	      break;
+	    }
+	}
+    }
+
+  closedir (dir);
+}
+
+static void
+rsync (char *src, char *dest, int and_delete)
+{
+  r_setup (src, &spath);
+  r_setup (dest, &dpath);
+
+  rsync_1 (&spath, &dpath, and_delete);
+}
+
+int
+main (int argc, char **argv)
+{
+  pid_t child;
+  char *pristine_root_path;
+  char *new_root_path;
+  char *new_cwd_path;
+  char *new_objdir_path;
+  char *new_srcdir_path;
+  char **new_child_proc;
+  char *command_root;
+  char *command_base;
+  char *command_basename;
+  char *so_base;
+  int do_postclean = 0;
+
+  uid_t original_uid;
+  gid_t original_gid;
+  int UMAP;
+  int GMAP;
+  /* Used for "%lld %lld 1" so need not be large.  */
+  char tmp[100];
+  struct stat st;
+  int lock_fd;
+
+  setbuf (stdout, NULL);
+
+  /* The command line we're expecting looks like this:
+     env <set some vars> ld.so <library path> test-binary
+
+     We need to peel off any "env" or "ld.so" portion of the command
+     line, and keep track of which env vars we should preserve and
+     which we drop.  */
+
+  if (argc < 2)
+    {
+      fprintf (stderr, "Usage: containerize <program to run> <args...>\n");
+      exit (1);
+    }
+
+  if (strcmp (argv[1], "-v") == 0)
+    {
+      verbose = 1;
+      ++argv;
+      --argc;
+    }
+  verbose = 1;
+
+  if (strcmp (argv[1], "env") == 0)
+    {
+      ++argv;
+      --argc;
+      while (is_env_setting (argv[1]))
+	{
+	  /* List variables we do NOT want to propogate. */
+#if 0
+	  /* until we discover why locale/iconv tests don't
+	     work against an installed tree...  */
+	  if (memcmp (argv[1], "GCONV_PATH=", 11)
+	      && memcmp (argv[1], "LOCPATH=", 8))
+#endif
+	    {
+	      /* Need to keep these.  Note that putenv stores a
+	         pointer to our argv. */
+	      putenv (argv[1]);
+	    }
+	  ++argv;
+	  --argc;
+	}
+    }
+
+  if (strncmp (argv[1], concat (support_objdir_root, "/elf/ld-linux-", NULL),
+	       strlen (support_objdir_root) + 14) == 0)
+    {
+      ++argv;
+      --argc;
+      while (argv[1][0] == '-')
+	{
+	  if (strcmp (argv[1], "--library-path") == 0)
+	    {
+	      ++argv;
+	      --argc;
+	    }
+	  ++argv;
+	  --argc;
+	}
+    }
+
+  pristine_root_path = strdup (concat (support_objdir_root, "/testroot.pristine", NULL));
+  new_root_path = strdup (concat (support_objdir_root, "/testroot.root", NULL));
+  new_cwd_path = get_current_dir_name ();
+  new_child_proc = argv + 1;
+
+  lock_fd = open (concat (pristine_root_path, "/lock.fd", NULL),
+		 O_CREAT | O_TRUNC | O_RDWR, 0666);
+  if (lock_fd < 0)
+    FAIL_UNSUPPORTED ("Cannot create testroot lock.\n");
+
+  while (flock (lock_fd, LOCK_EX) != 0)
+    {
+      if (errno != EINTR)
+	FAIL_UNSUPPORTED ("Cannot lock testroot.\n");
+    }
+
+  xmkdirp (new_root_path, 0755);
+
+  /* We look for extra setup info in a subdir in the same spot as the
+     test, with the same name but a ".root" extension.  This is that
+     directory.  We try to look in the source tree if the path we're
+     given refers to the build tree, but we rely on the path to be
+     absolute.  This is what the glibc makefiles do.  */
+  command_root = concat (argv[1], ".root", NULL);
+  if (strncmp (command_root, support_objdir_root, strlen (support_objdir_root)) == 0
+      && command_root[strlen (support_objdir_root)] == '/')
+    command_root = concat (support_srcdir_root, argv[1] + strlen (support_objdir_root), ".root", NULL);
+  command_root = strdup (command_root);
+
+  /* This cuts off the ".root" we appended above.  */
+  command_base = strdup (command_root);
+  command_base[strlen (command_base) - 5] = 0;
+
+  /* This is the basename of the test we're running.  */
+  command_basename = strrchr (command_base, '/');
+  if (command_basename == NULL)
+    command_basename = command_base;
+  else
+    ++command_basename;
+
+  /* Shared object base directory.  */
+  so_base = strdup (argv[1]);
+  if (strrchr (so_base, '/') != NULL)
+    strrchr (so_base, '/')[1] = 0;
+
+  if (file_exists (concat (command_root, "/postclean.req", NULL)))
+    do_postclean = 1;
+
+  rsync (pristine_root_path, new_root_path,
+	 file_exists (concat (command_root, "/preclean.req", NULL)));
+
+  if (stat (command_root, &st) >= 0
+      && S_ISDIR (st.st_mode))
+    rsync (command_root, new_root_path, 0);
+
+  new_objdir_path = strdup (concat (new_root_path, support_objdir_root, NULL));
+  new_srcdir_path = strdup (concat (new_root_path, support_srcdir_root, NULL));
+
+  /* new_cwd_path starts with '/' so no "/" needed between the two.  */
+  xmkdirp (concat (new_root_path, new_cwd_path, NULL), 0755);
+  xmkdirp (new_srcdir_path, 0755);
+  xmkdirp (new_objdir_path, 0755);
+
+  original_uid = getuid ();
+  original_gid = getgid ();
+
+  /* Handle the cp/mv/rm "script" here.  */
+  {
+    char *the_line = NULL;
+    size_t line_len = 0;
+    char *fname = concat (command_root, "/", command_basename, ".script", NULL);
+    char *the_words[3];
+    FILE *f = fopen (fname, "r");
+
+    if (verbose && f)
+      fprintf (stderr, "running %s\n", fname);
+
+    if (f == NULL)
+      {
+	/* Try foo.script instead of foo.root/foo.script, as a shortcut.  */
+	fname = concat (command_base, ".script", NULL);
+	f = fopen (fname, "r");
+	if (verbose && f)
+	  fprintf (stderr, "running %s\n", fname);
+      }
+
+    /* Note that we do NOT look for a Makefile-generated foo.script in
+       the build directory.  If that is ever needed, this is the place
+       to add it.  */
+
+    /* This is where we "interpret" the mini-script which is <test>.script.  */
+    if (f != NULL)
+      {
+	while (getline (&the_line, &line_len, f) > 0)
+	  {
+	    int nt = tokenize (the_line, the_words, 3);
+	    int i;
+
+	    for (i = 1; i < nt; ++i)
+	      {
+		if (memcmp (the_words[i], "$B/", 3) == 0)
+		  the_words[i] = concat (support_objdir_root, the_words[i] + 2, NULL);
+		else if (memcmp (the_words[i], "$S/", 3) == 0)
+		  the_words[i] = concat (support_srcdir_root, the_words[i] + 2, NULL);
+		else if (memcmp (the_words[i], "$I/", 3) == 0)
+		  the_words[i] = concat (new_root_path, support_install_prefix, the_words[i] + 2, NULL);
+		else if (memcmp (the_words[i], "$L/", 3) == 0)
+		  the_words[i] = concat (new_root_path, support_libdir_prefix, the_words[i] + 2, NULL);
+		else if (the_words[i][0] == '/')
+		  the_words[i] = concat (new_root_path, the_words[i], NULL);
+	      }
+
+	    if (nt == 3 && the_words[2][strlen (the_words[2]) - 1] == '/')
+	      {
+		char *r = strrchr (the_words[1], '/');
+		if (r)
+		  the_words[2] = concat (the_words[2], r + 1, NULL);
+		else
+		  the_words[2] = concat (the_words[2], the_words[1], NULL);
+	      }
+
+	    if (nt == 2 && strcmp (the_words[0], "so") == 0)
+	      {
+		the_words[2] = concat (new_root_path, support_libdir_prefix, "/", the_words[1], NULL);
+		the_words[1] = concat (so_base, the_words[1], NULL);
+		copy_one_file (the_words[1], the_words[2]);
+	      }
+	    else if (nt == 3 && strcmp (the_words[0], "cp") == 0)
+	      {
+		copy_one_file (the_words[1], the_words[2]);
+	      }
+	    else if (nt == 3 && strcmp (the_words[0], "mv") == 0)
+	      {
+		if (rename (the_words[1], the_words[2]) < 0)
+		  FAIL_EXIT1 ("rename %s -> %s: %s", the_words[1], the_words[2], strerror (errno));
+	      }
+	    else if (nt == 3 && strcmp (the_words[0], "chmod") == 0)
+	      {
+		long int m;
+		m = strtol (the_words[1], NULL, 0);
+		if (chmod (the_words[2], m) < 0)
+		    FAIL_EXIT1 ("chmod %s: %s\n", the_words[2], strerror (errno));
+
+	      }
+	    else if (nt == 2 && strcmp (the_words[0], "rm") == 0)
+	      {
+		maybe_xunlink (the_words[1]);
+	      }
+	    else if (nt > 0 && the_words[0][0] != '#')
+	      {
+		printf ("\033[31minvalid [%s]\033[0m\n", the_words[0]);
+	      }
+	  }
+	fclose (f);
+      }
+  }
+
+  /* The unshare here gives us our own spaces and capabilities.  */
+  if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
+    FAIL_UNSUPPORTED ("unable to unshare user/fs, ");
+
+  /* Some systems, by default, all mounts leak out of the namespace.  */
+  if (mount ("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0)
+    FAIL_UNSUPPORTED ("could not create a private mount namespace\n");
+
+  trymount (support_srcdir_root, new_srcdir_path);
+  trymount (support_objdir_root, new_objdir_path);
+
+  xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
+  devmount (new_root_path, "null");
+  devmount (new_root_path, "zero");
+  devmount (new_root_path, "urandom");
+
+  /* We're done with the "old" root, switch to the new one.  */
+  if (chroot (new_root_path) < 0)
+    FAIL_UNSUPPORTED ("Can't chroot to %s - ", new_root_path);
+
+  if (chdir (new_cwd_path) < 0)
+    FAIL_UNSUPPORTED ("Can't cd to new %s - ", new_cwd_path);
+
+  /* To complete the containerization, we need to fork () at least
+     once.  We can't exec, nor can we somehow link the new child to
+     our parent.  So we run the child and propogate it's exit status
+     up.  */
+  child = fork ();
+  if (child < 0)
+    FAIL_UNSUPPORTED ("Unable to fork");
+  else if (child > 0)
+    {
+      /* Parent.  */
+      int status;
+      waitpid (child, &status, 0);
+
+      /* There's a bit of magic here, since the buildroot is mounted
+	 in our space, the paths are still valid, and since the mounts
+	 aren't recursive, it sees *only* the built root, not anything
+	 we would normally se if we rsync'd to "/" like mounted /dev
+	 files.  */
+      if (do_postclean)
+	  rsync (pristine_root_path, new_root_path, 1);
+
+      if (WIFEXITED (status))
+	exit (WEXITSTATUS (status));
+
+      if (WIFSIGNALED (status))
+	{
+	  printf ("%%SIGNALLED%%\n");
+	  exit (77);
+	}
+
+      printf ("%%EXITERROR%%\n");
+      exit (78);
+    }
+
+  /* The rest is the child process, which is now PID 1 and "in" the
+     new root.  */
+
+  maybe_xmkdir ("/tmp", 0755);
+
+  /* Now that we're pid 1 (effectively "root") we can mount /proc  */
+  maybe_xmkdir ("/proc", 0777);
+  if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
+    FAIL_UNSUPPORTED ("Unable to mount /proc: ");
+
+  /* We map our original UID to the same UID in the container so we
+     can own our own files normally.  */
+  UMAP = open ("/proc/self/uid_map", O_WRONLY);
+  if (UMAP < 0)
+    FAIL_UNSUPPORTED ("can't write to /proc/self/uid_map\n");
+
+  sprintf (tmp, "%lld %lld 1\n", (long long) original_uid, (long long) original_uid);
+  write (UMAP, tmp, strlen (tmp));
+  xclose (UMAP);
+
+  /* We must disable setgroups () before we can map our groups, else we
+     get EPERM.  */
+  GMAP = open ("/proc/self/setgroups", O_WRONLY);
+  if (GMAP >= 0)
+    {
+      /* We support kernels old enough to not have this.  */
+      write (GMAP, "deny\n", 5);
+      xclose (GMAP);
+    }
+
+  /* We map our original GID to the same GID in the container so we
+     can own our own files normally.  */
+  GMAP = open ("/proc/self/gid_map", O_WRONLY);
+  if (GMAP < 0)
+    FAIL_UNSUPPORTED ("can't write to /proc/self/gid_map\n");
+
+  sprintf (tmp, "%lld %lld 1\n", (long long) original_gid, (long long) original_gid);
+  write (GMAP, tmp, strlen (tmp));
+  xclose (GMAP);
+
+  /* Now run the child.  */
+  execvp (new_child_proc[0], new_child_proc);
+
+  /* Or don't run the child?  */
+  FAIL_EXIT1 ("Unable to exec %s\n", new_child_proc[0]);
+
+  /* Because gcc won't know error () never returns...  */
+  exit (EXIT_UNSUPPORTED);
+}
diff --git a/support/true-container.c b/support/true-container.c
new file mode 100644
index 0000000000..2e37061255
--- /dev/null
+++ b/support/true-container.c
@@ -0,0 +1,26 @@
+/* Minimal /bin/true for in-container use.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Implements the in-container /bin/true, which always returns true
+   (0).  */
+
+int
+main(void)
+{
+  return 0;
+}
diff --git a/support/xmkdirp.c b/support/xmkdirp.c
new file mode 100644
index 0000000000..422a03e2f9
--- /dev/null
+++ b/support/xmkdirp.c
@@ -0,0 +1,67 @@
+/* Error-checking replacement for "mkdir -p".
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/support.h>
+#include <support/check.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/* Equivalent of "mkdir -p".  Any failures cause FAIL_EXIT1 so no
+   return code is needed.  */
+
+void
+xmkdirp (const char *path, mode_t mode)
+{
+  struct stat s;
+  const char *slash_p;
+  int rv;
+
+  if (path[0] == 0)
+    return;
+
+  if (stat (path, &s) == 0)
+    {
+      if (S_ISDIR (s.st_mode))
+	return;
+      errno = EEXIST;
+      FAIL_EXIT1 ("mkdir_p (\"%s\", 0%o): %m", path, mode);
+    }
+
+  slash_p = strrchr (path, '/');
+  if (slash_p != NULL)
+    {
+      while (slash_p > path && slash_p[-1] == '/')
+	--slash_p;
+      if (slash_p > path)
+	{
+	  char *parent = xstrndup (path, slash_p - path);
+	  xmkdirp (parent, mode);
+	  free (parent);
+	}
+    }
+
+  rv = mkdir (path, mode);
+  if (rv != 0)
+    FAIL_EXIT1 ("mkdir_p (\"%s\", 0%o): %m", path, mode);
+
+  return;
+}
diff --git a/support/xsymlink.c b/support/xsymlink.c
new file mode 100644
index 0000000000..c3fdd642f1
--- /dev/null
+++ b/support/xsymlink.c
@@ -0,0 +1,29 @@
+/* Error-checking replacement for "symlink".
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/support.h>
+#include <support/check.h>
+
+#include <unistd.h>
+
+void
+xsymlink(const char *target, const char *linkpath)
+{
+  if (symlink (target, linkpath) < 0)
+    FAIL_EXIT1 ("symlink (\"%s\", \"%s\")", target, linkpath);
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 5fe5dae818..a912209827 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -43,6 +43,7 @@ void xunlink (const char *path);
 long xsysconf (int name);
 long long xlseek (int fd, long long offset, int whence);
 void xftruncate (int fd, long long length);
+void xsymlink(const char *target, const char *linkpath);
 
 /* Read the link at PATH.  The caller should free the returned string
    with free.  */

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: V7 test-in-container patch
  2018-08-15  5:21 V7 test-in-container patch DJ Delorie
@ 2018-08-15 14:24 ` Florian Weimer
  2018-08-15 14:29   ` Carlos O'Donell
  2018-08-15 23:57   ` DJ Delorie
  0 siblings, 2 replies; 6+ messages in thread
From: Florian Weimer @ 2018-08-15 14:24 UTC (permalink / raw)
  To: DJ Delorie, libc-alpha; +Cc: Joseph Myers, Carlos O'Donell

On 08/15/2018 07:21 AM, DJ Delorie wrote:
> Includes changes as per https://www.sourceware.org/ml/libc-alpha/2018-08/msg00293.html

There are sill a bunch of style issues, visible in:

   git show | grep '^+.*[^ ($]('

There are also a few lines longer than 79 characters.  And two instances 
of this:

   git show | grep '\. \*/'

+#define __USE_LARGEFILE64

That should be:

#define _FILE_OFFSET_BITS 64

(Twice, at least).

+/* Equivalent of "mkdir -p".  */
+void xmkdirp (const char *, mode_t);

This should probably go into <support/xunistd.h>, not <support/support.h>.

Hurd doesn't have unshare, so you need to avoid building the container 
framework there.

The Red Hat Enterprise Linux 7 kernel doesn't seem to like this 
combination of flags, even as root:

unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID) = -1 EINVAL (Invalid 
argument)

Is CLONE_NEWPID really required?

My concern about the over-use of FAIL_UNSUPPORTED and the UNSUPPORTED 
test status in the container framework remains.  Sure, there are some 
things that can fail due to missing host support, but e.g. a fork file 
shouldn't lead to UNSUPPORTED, but FAIL.

Thanks,
Florian

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: V7 test-in-container patch
  2018-08-15 14:24 ` Florian Weimer
@ 2018-08-15 14:29   ` Carlos O'Donell
  2018-08-15 23:57   ` DJ Delorie
  1 sibling, 0 replies; 6+ messages in thread
From: Carlos O'Donell @ 2018-08-15 14:29 UTC (permalink / raw)
  To: Florian Weimer, DJ Delorie, libc-alpha; +Cc: Joseph Myers

On 08/15/2018 10:24 AM, Florian Weimer wrote:
> On 08/15/2018 07:21 AM, DJ Delorie wrote:
>> Includes changes as per https://www.sourceware.org/ml/libc-alpha/2018-08/msg00293.html
> 
> There are sill a bunch of style issues, visible in:
> 
>   git show | grep '^+.*[^ ($]('
> 
> There are also a few lines longer than 79 characters.  And two instances of this:
> 
>   git show | grep '\. \*/'
> 
> +#define __USE_LARGEFILE64
> 
> That should be:
> 
> #define _FILE_OFFSET_BITS 64
> 
> (Twice, at least).
> 
> +/* Equivalent of "mkdir -p".  */
> +void xmkdirp (const char *, mode_t);
> 
> This should probably go into <support/xunistd.h>, not <support/support.h>.
> 
> Hurd doesn't have unshare, so you need to avoid building the container framework there.
> 
> The Red Hat Enterprise Linux 7 kernel doesn't seem to like this combination of flags, even as root:
> 
> unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID) = -1 EINVAL (Invalid argument)
> 
> Is CLONE_NEWPID really required?
> 
> My concern about the over-use of FAIL_UNSUPPORTED and the UNSUPPORTED test status in the container framework remains.  Sure, there are some things that can fail due to missing host support, but e.g. a fork file shouldn't lead to UNSUPPORTED, but FAIL.

Thanks for the review.

I expect DJ to prepare a v8 incorporating this feedback.

When v8 is ready I'll review that again in detail.

-- 
Cheers,
Carlos.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: V7 test-in-container patch
  2018-08-15 14:24 ` Florian Weimer
  2018-08-15 14:29   ` Carlos O'Donell
@ 2018-08-15 23:57   ` DJ Delorie
  2018-08-16  7:59     ` Florian Weimer
  1 sibling, 1 reply; 6+ messages in thread
From: DJ Delorie @ 2018-08-15 23:57 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha, joseph, carlos

Florian Weimer <fweimer@redhat.com> writes:

> There are sill a bunch of style issues, visible in:
>
>    git show | grep '^+.*[^ ($]('

Fixed.

> There are also a few lines longer than 79 characters.

Fixed.  And annoying.  It encourages use of short variable names instead
of more self-documenting longer ones.  Is anyone still coding on a VT52
terminal these days?

> And two instances 
> of this:
>
>    git show | grep '\. \*/'

Fixed.

> +#define __USE_LARGEFILE64
>
> That should be:
>
> #define _FILE_OFFSET_BITS 64

Fixed.

> +/* Equivalent of "mkdir -p".  */
> +void xmkdirp (const char *, mode_t);
>
> This should probably go into <support/xunistd.h>, not <support/support.h>.

Makes sense.  Fixed.

> Hurd doesn't have unshare, so you need to avoid building the container 
> framework there.

Is it OK to return UNSUPPORTED for those?  I don't think it makes sense
to try to work around the whole containerized-test setup if containers
are missing.

> The Red Hat Enterprise Linux 7 kernel doesn't seem to like this 
> combination of flags, even as root:
>
> unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID) = -1 EINVAL (Invalid 
> argument)
>
> Is CLONE_NEWPID really required?

Yes.  You can't mount /proc without it.  Don't ask me why ;-)

Another case for UNSUPPORTED then?

> My concern about the over-use of FAIL_UNSUPPORTED and the UNSUPPORTED 
> test status in the container framework remains.  Sure, there are some 
> things that can fail due to missing host support, but e.g. a fork file 
> shouldn't lead to UNSUPPORTED, but FAIL.

I replaced them all with FAILs.  I was trying to keep "failure of the
test" separate from "failure of the test harness" but I'm OK with it
either way.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: V7 test-in-container patch
  2018-08-15 23:57   ` DJ Delorie
@ 2018-08-16  7:59     ` Florian Weimer
  2018-08-16 12:19       ` Joseph Myers
  0 siblings, 1 reply; 6+ messages in thread
From: Florian Weimer @ 2018-08-16  7:59 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha, joseph, carlos

On 08/16/2018 01:57 AM, DJ Delorie wrote:

>> Hurd doesn't have unshare, so you need to avoid building the container
>> framework there.
> 
> Is it OK to return UNSUPPORTED for those?  I don't think it makes sense
> to try to work around the whole containerized-test setup if containers
> are missing.

UNSUPPORTED would work there.  If the Hurd maintainers do not like it, 
we could probably filter out test-containers tests there.

>> The Red Hat Enterprise Linux 7 kernel doesn't seem to like this
>> combination of flags, even as root:
>>
>> unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID) = -1 EINVAL (Invalid
>> argument)
>>
>> Is CLONE_NEWPID really required?
> 
> Yes.  You can't mount /proc without it.  Don't ask me why ;-)

Can you perform a bind mount of the existing /proc instead?  Maybe you 
can drop the CLONE_NEWPID this way.

If the bind mount doesn't work, I suggest to ask internally what our 
kernel needs to get this working.

>> My concern about the over-use of FAIL_UNSUPPORTED and the UNSUPPORTED
>> test status in the container framework remains.  Sure, there are some
>> things that can fail due to missing host support, but e.g. a fork file
>> shouldn't lead to UNSUPPORTED, but FAIL.
> 
> I replaced them all with FAILs.  I was trying to keep "failure of the
> test" separate from "failure of the test harness" but I'm OK with it
> either way.

It's hard to draw a line.  Fork failures in particular tend to leak 
between tests because of kernel bugs (still unfixed upstream, 
unfortunately).

Thanks,
Florian

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: V7 test-in-container patch
  2018-08-16  7:59     ` Florian Weimer
@ 2018-08-16 12:19       ` Joseph Myers
  0 siblings, 0 replies; 6+ messages in thread
From: Joseph Myers @ 2018-08-16 12:19 UTC (permalink / raw)
  To: Florian Weimer; +Cc: DJ Delorie, libc-alpha, carlos, samuel.thibault, thomas

On Thu, 16 Aug 2018, Florian Weimer wrote:

> On 08/16/2018 01:57 AM, DJ Delorie wrote:
> 
> > > Hurd doesn't have unshare, so you need to avoid building the container
> > > framework there.
> > 
> > Is it OK to return UNSUPPORTED for those?  I don't think it makes sense
> > to try to work around the whole containerized-test setup if containers
> > are missing.
> 
> UNSUPPORTED would work there.  If the Hurd maintainers do not like it, we
> could probably filter out test-containers tests there.

Or the Hurd maintainers could add appropriate Hurd-specific support for 
setting up suitable containers for these tests.  Surely the Hurd 
translator mechanism must be suitably expressive to be able to implement 
an equivalent container framework with enough functionality for these 
tests.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-08-16 12:19 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-08-15  5:21 V7 test-in-container patch DJ Delorie
2018-08-15 14:24 ` Florian Weimer
2018-08-15 14:29   ` Carlos O'Donell
2018-08-15 23:57   ` DJ Delorie
2018-08-16  7:59     ` Florian Weimer
2018-08-16 12:19       ` Joseph Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).