aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDJ Delorie <dj@redhat.com>2018-07-06 01:10:41 -0400
committerDJ Delorie <dj@redhat.com>2018-08-22 21:20:37 -0400
commit561b0bec4448f0302cb4915bf67c919bde4a1c57 (patch)
treea05c836bfe23e523ec7d357203e7b6599b287f98 /support/shell-container.c
parentregex: port Gnulib code to z/OS POSIX environment (diff)
downloadglibc-561b0bec4448f0302cb4915bf67c919bde4a1c57.tar.gz
glibc-561b0bec4448f0302cb4915bf67c919bde4a1c57.tar.bz2
glibc-561b0bec4448f0302cb4915bf67c919bde4a1c57.zip
Add test-in-container infrastructure.
* Makefile (testroot.pristine): New rules to initialize the test-in-container "testroot". * Makerules (all-testsuite): Add tests-container. * Rules (tests-expected): 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 support paths prototypes. * support/xunistd.h: Add xmkdirp () and xsymlink (). * nss/tst-nss-test3.c: Convert to test-in-container. * nss/tst-nss-test3.root/: New.
Diffstat (limited to 'support/shell-container.c')
-rw-r--r--support/shell-container.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/support/shell-container.c b/support/shell-container.c
new file mode 100644
index 0000000000..d303131daf
--- /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 _FILE_OFFSET_BITS 64
+
+#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/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;
+}