Blame support/shell-container.c

Packit Service ada5e0
/* Minimal /bin/sh for in-container use.
Packit Service ada5e0
   Copyright (C) 2018 Free Software Foundation, Inc.
Packit Service ada5e0
   This file is part of the GNU C Library.
Packit Service ada5e0
Packit Service ada5e0
   The GNU C Library is free software; you can redistribute it and/or
Packit Service ada5e0
   modify it under the terms of the GNU Lesser General Public
Packit Service ada5e0
   License as published by the Free Software Foundation; either
Packit Service ada5e0
   version 2.1 of the License, or (at your option) any later version.
Packit Service ada5e0
Packit Service ada5e0
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service ada5e0
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service ada5e0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service ada5e0
   Lesser General Public License for more details.
Packit Service ada5e0
Packit Service ada5e0
   You should have received a copy of the GNU Lesser General Public
Packit Service ada5e0
   License along with the GNU C Library; if not, see
Packit Service ada5e0
   <http://www.gnu.org/licenses/>.  */
Packit Service ada5e0
Packit Service ada5e0
#define _FILE_OFFSET_BITS 64
Packit Service ada5e0
Packit Service ada5e0
#include <stdio.h>
Packit Service ada5e0
#include <stdlib.h>
Packit Service ada5e0
#include <string.h>
Packit Service ada5e0
#include <sched.h>
Packit Service ada5e0
#include <sys/syscall.h>
Packit Service ada5e0
#include <unistd.h>
Packit Service ada5e0
#include <sys/types.h>
Packit Service ada5e0
#include <dirent.h>
Packit Service ada5e0
#include <string.h>
Packit Service ada5e0
#include <sys/stat.h>
Packit Service ada5e0
#include <sys/fcntl.h>
Packit Service ada5e0
#include <sys/file.h>
Packit Service ada5e0
#include <sys/wait.h>
Packit Service ada5e0
#include <stdarg.h>
Packit Service ada5e0
#include <sys/sysmacros.h>
Packit Service ada5e0
#include <ctype.h>
Packit Service ada5e0
#include <utime.h>
Packit Service ada5e0
#include <errno.h>
Packit Service ada5e0
#include <error.h>
Packit Service ada5e0
Packit Service ada5e0
#include <support/support.h>
Packit Service ada5e0
Packit Service ada5e0
/* Design considerations
Packit Service ada5e0
Packit Service ada5e0
 General rule: optimize for developer time, not run time.
Packit Service ada5e0
Packit Service ada5e0
 Specifically:
Packit Service ada5e0
Packit Service ada5e0
 * Don't worry about slow algorithms
Packit Service ada5e0
 * Don't worry about free'ing memory
Packit Service ada5e0
 * Don't implement anything the testsuite doesn't need.
Packit Service ada5e0
 * Line and argument counts are limited, see below.
Packit Service ada5e0
Packit Service ada5e0
*/
Packit Service ada5e0
Packit Service ada5e0
#define MAX_ARG_COUNT 100
Packit Service ada5e0
#define MAX_LINE_LENGTH 1000
Packit Service ada5e0
Packit Service ada5e0
/* Debugging is enabled via --debug, which must be the first argument.  */
Packit Service ada5e0
static int debug_mode = 0;
Packit Service ada5e0
#define dprintf if (debug_mode) fprintf
Packit Service ada5e0
Packit Service ada5e0
/* Emulate the "/bin/true" command.  Arguments are ignored.  */
Packit Service ada5e0
static int
Packit Service ada5e0
true_func (char **argv)
Packit Service ada5e0
{
Packit Service ada5e0
  return 0;
Packit Service ada5e0
}
Packit Service ada5e0
Packit Service ada5e0
/* Emulate the "/bin/echo" command.  Options are ignored, arguments
Packit Service ada5e0
   are printed to stdout.  */
Packit Service ada5e0
static int
Packit Service ada5e0
echo_func (char **argv)
Packit Service ada5e0
{
Packit Service ada5e0
  int i;
Packit Service ada5e0
Packit Service ada5e0
  for (i = 0; argv[i]; i++)
Packit Service ada5e0
    {
Packit Service ada5e0
      if (i > 0)
Packit Service ada5e0
	putchar (' ');
Packit Service ada5e0
      fputs (argv[i], stdout);
Packit Service ada5e0
    }
Packit Service ada5e0
  putchar ('\n');
Packit Service ada5e0
Packit Service ada5e0
  return 0;
Packit Service ada5e0
}
Packit Service ada5e0
Packit Service ada5e0
/* Emulate the "/bin/cp" command.  Options are ignored.  Only copies
Packit Service ada5e0
   one source file to one destination file.  Directory destinations
Packit Service ada5e0
   are not supported.  */
Packit Service ada5e0
static int
Packit Service ada5e0
copy_func (char **argv)
Packit Service ada5e0
{
Packit Service ada5e0
  char *sname = argv[0];
Packit Service ada5e0
  char *dname = argv[1];
Packit Service ada5e0
  int sfd, dfd;
Packit Service ada5e0
  struct stat st;
Packit Service ada5e0
Packit Service ada5e0
  sfd = open (sname, O_RDONLY);
Packit Service ada5e0
  if (sfd < 0)
Packit Service ada5e0
    {
Packit Service ada5e0
      fprintf (stderr, "cp: unable to open %s for reading: %s\n",
Packit Service ada5e0
	       sname, strerror (errno));
Packit Service ada5e0
      return 1;
Packit Service ada5e0
    }
Packit Service ada5e0
Packit Service ada5e0
  if (fstat (sfd, &st) < 0)
Packit Service ada5e0
    {
Packit Service ada5e0
      fprintf (stderr, "cp: unable to fstat %s: %s\n",
Packit Service ada5e0
	       sname, strerror (errno));
Packit Service ada5e0
      return 1;
Packit Service ada5e0
    }
Packit Service ada5e0
Packit Service ada5e0
  dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
Packit Service ada5e0
  if (dfd < 0)
Packit Service ada5e0
    {
Packit Service ada5e0
      fprintf (stderr, "cp: unable to open %s for writing: %s\n",
Packit Service ada5e0
	       dname, strerror (errno));
Packit Service ada5e0
      return 1;
Packit Service ada5e0
    }
Packit Service ada5e0
Packit Service ada5e0
  if (support_copy_file_range (sfd, 0, dfd, 0, st.st_size, 0) != st.st_size)
Packit Service ada5e0
    {
Packit Service ada5e0
      fprintf (stderr, "cp: cannot copy file %s to %s: %s\n",
Packit Service ada5e0
	       sname, dname, strerror (errno));
Packit Service ada5e0
      return 1;
Packit Service ada5e0
    }
Packit Service ada5e0
Packit Service ada5e0
  close (sfd);
Packit Service ada5e0
  close (dfd);
Packit Service ada5e0
Packit Service ada5e0
  chmod (dname, st.st_mode & 0777);
Packit Service ada5e0
Packit Service ada5e0
  return 0;
Packit Service ada5e0
Packit Service ada5e0
}
Packit Service ada5e0
Packit Service ada5e0
/* This is a list of all the built-in commands we understand.  */
Packit Service ada5e0
static struct {
Packit Service ada5e0
  const char *name;
Packit Service ada5e0
  int (*func) (char **argv);
Packit Service ada5e0
} builtin_funcs[] = {
Packit Service ada5e0
  { "true", true_func },
Packit Service ada5e0
  { "echo", echo_func },
Packit Service ada5e0
  { "cp", copy_func },
Packit Service ada5e0
  { NULL, NULL }
Packit Service ada5e0
};
Packit Service ada5e0
Packit Service ada5e0
/* Run one tokenized command.  argv[0] is the command.  argv is
Packit Service ada5e0
   NULL-terminated.  */
Packit Service ada5e0
static void
Packit Service ada5e0
run_command_array (char **argv)
Packit Service ada5e0
{
Packit Service ada5e0
  int i, j;
Packit Service ada5e0
  pid_t pid;
Packit Service ada5e0
  int status;
Packit Service ada5e0
  int (*builtin_func) (char **args);
Packit Service ada5e0
Packit Service ada5e0
  if (argv[0] == NULL)
Packit Service ada5e0
    return;
Packit Service ada5e0
Packit Service ada5e0
  builtin_func = NULL;
Packit Service ada5e0
Packit Service ada5e0
  int new_stdin = 0;
Packit Service ada5e0
  int new_stdout = 1;
Packit Service ada5e0
  int new_stderr = 2;
Packit Service ada5e0
Packit Service ada5e0
  dprintf (stderr, "run_command_array starting\n");
Packit Service ada5e0
  for (i = 0; argv[i]; i++)
Packit Service ada5e0
    dprintf (stderr, "   argv [%d] `%s'\n", i, argv[i]);
Packit Service ada5e0
Packit Service ada5e0
  for (j = i = 0; argv[i]; i++)
Packit Service ada5e0
    {
Packit Service ada5e0
      if (strcmp (argv[i], "<") == 0 && argv[i + 1])
Packit Service ada5e0
	{
Packit Service ada5e0
	  new_stdin = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
Packit Service ada5e0
	  ++i;
Packit Service ada5e0
	  continue;
Packit Service ada5e0
	}
Packit Service ada5e0
      if (strcmp (argv[i], ">") == 0 && argv[i + 1])
Packit Service ada5e0
	{
Packit Service ada5e0
	  new_stdout = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
Packit Service ada5e0
	  ++i;
Packit Service ada5e0
	  continue;
Packit Service ada5e0
	}
Packit Service ada5e0
      if (strcmp (argv[i], ">>") == 0 && argv[i + 1])
Packit Service ada5e0
	{
Packit Service ada5e0
	  new_stdout = open (argv[i + 1], O_WRONLY|O_CREAT|O_APPEND, 0777);
Packit Service ada5e0
	  ++i;
Packit Service ada5e0
	  continue;
Packit Service ada5e0
	}
Packit Service ada5e0
      if (strcmp (argv[i], "2>") == 0 && argv[i + 1])
Packit Service ada5e0
	{
Packit Service ada5e0
	  new_stderr = open (argv[i + 1], O_WRONLY|O_CREAT|O_TRUNC, 0777);
Packit Service ada5e0
	  ++i;
Packit Service ada5e0
	  continue;
Packit Service ada5e0
	}
Packit Service ada5e0
      argv[j++] = argv[i];
Packit Service ada5e0
    }
Packit Service ada5e0
  argv[j] = NULL;
Packit Service ada5e0
Packit Service ada5e0
Packit Service ada5e0
  for (i = 0; builtin_funcs[i].name != NULL; i++)
Packit Service ada5e0
    if (strcmp (argv[0], builtin_funcs[i].name) == 0)
Packit Service ada5e0
       builtin_func = builtin_funcs[i].func;
Packit Service ada5e0
Packit Service ada5e0
  dprintf (stderr, "builtin %p argv0 `%s'\n", builtin_func, argv[0]);
Packit Service ada5e0
Packit Service ada5e0
  pid = fork ();
Packit Service ada5e0
  if (pid < 0)
Packit Service ada5e0
    {
Packit Service ada5e0
      fprintf (stderr, "sh: fork failed\n");
Packit Service ada5e0
      exit (1);
Packit Service ada5e0
    }
Packit Service ada5e0
Packit Service ada5e0
  if (pid == 0)
Packit Service ada5e0
    {
Packit Service ada5e0
      if (new_stdin != 0)
Packit Service ada5e0
	{
Packit Service ada5e0
	  dup2 (new_stdin, 0);
Packit Service ada5e0
	  close (new_stdin);
Packit Service ada5e0
	}
Packit Service ada5e0
      if (new_stdout != 1)
Packit Service ada5e0
	{
Packit Service ada5e0
	  dup2 (new_stdout, 1);
Packit Service ada5e0
	  close (new_stdout);
Packit Service ada5e0
	}
Packit Service ada5e0
      if (new_stderr != 2)
Packit Service ada5e0
	{
Packit Service ada5e0
	  dup2 (new_stderr, 2);
Packit Service ada5e0
	  close (new_stdout);
Packit Service ada5e0
	}
Packit Service ada5e0
Packit Service ada5e0
      if (builtin_func != NULL)
Packit Service ada5e0
	exit (builtin_func (argv + 1));
Packit Service ada5e0
Packit Service ada5e0
      execvp (argv[0], argv);
Packit Service ada5e0
Packit Service ada5e0
      fprintf (stderr, "sh: execing %s failed: %s",
Packit Service ada5e0
	       argv[0], strerror (errno));
Packit Service ada5e0
      exit (1);
Packit Service ada5e0
    }
Packit Service ada5e0
Packit Service ada5e0
  waitpid (pid, &status, 0);
Packit Service ada5e0
Packit Service ada5e0
  dprintf (stderr, "exiting run_command_array\n");
Packit Service ada5e0
Packit Service ada5e0
  if (WIFEXITED (status))
Packit Service ada5e0
    {
Packit Service ada5e0
      int rv = WEXITSTATUS (status);
Packit Service ada5e0
      if (rv)
Packit Service ada5e0
	exit (rv);
Packit Service ada5e0
    }
Packit Service ada5e0
  else
Packit Service ada5e0
    exit (1);
Packit Service ada5e0
}
Packit Service ada5e0
Packit Service ada5e0
/* Run one command-as-a-string, by tokenizing it.  Limited to
Packit Service ada5e0
   MAX_ARG_COUNT arguments.  Simple substitution is done of $1 to $9
Packit Service ada5e0
   (as whole separate tokens) from iargs[].  Quoted strings work if
Packit Service ada5e0
   the quotes wrap whole tokens; i.e. "foo bar" but not foo" bar".  */
Packit Service ada5e0
static void
Packit Service ada5e0
run_command_string (const char *cmdline, const char **iargs)
Packit Service ada5e0
{
Packit Service ada5e0
  char *args[MAX_ARG_COUNT+1];
Packit Service ada5e0
  int ap = 0;
Packit Service ada5e0
  const char *start, *end;
Packit Service ada5e0
  int nargs;
Packit Service ada5e0
Packit Service ada5e0
  for (nargs = 0; iargs[nargs] != NULL; ++nargs)
Packit Service ada5e0
    ;
Packit Service ada5e0
Packit Service ada5e0
  dprintf (stderr, "run_command_string starting: '%s'\n", cmdline);
Packit Service ada5e0
Packit Service ada5e0
  while (ap < MAX_ARG_COUNT)
Packit Service ada5e0
    {
Packit Service ada5e0
      /* If the argument is quoted, this is the quote character, else NUL.  */
Packit Service ada5e0
      int in_quote = 0;
Packit Service ada5e0
Packit Service ada5e0
      /* Skip whitespace up to the next token.  */
Packit Service ada5e0
      while (*cmdline && isspace (*cmdline))
Packit Service ada5e0
	cmdline ++;
Packit Service ada5e0
      if (*cmdline == 0)
Packit Service ada5e0
	break;
Packit Service ada5e0
Packit Service ada5e0
      start = cmdline;
Packit Service ada5e0
      /* Check for quoted argument.  */
Packit Service ada5e0
      in_quote = (*cmdline == '\'' || *cmdline == '"') ? *cmdline : 0;
Packit Service ada5e0
Packit Service ada5e0
      /* Skip to end of token; either by whitespace or matching quote.  */
Packit Service ada5e0
      dprintf (stderr, "in_quote %d\n", in_quote);
Packit Service ada5e0
      while (*cmdline
Packit Service ada5e0
	     && (!isspace (*cmdline) || in_quote))
Packit Service ada5e0
	{
Packit Service ada5e0
	  if (*cmdline == in_quote
Packit Service ada5e0
	      && cmdline != start)
Packit Service ada5e0
	    in_quote = 0;
Packit Service ada5e0
	  dprintf (stderr, "[%c]%d ", *cmdline, in_quote);
Packit Service ada5e0
	  cmdline ++;
Packit Service ada5e0
	}
Packit Service ada5e0
      dprintf (stderr, "\n");
Packit Service ada5e0
Packit Service ada5e0
      /* Allocate space for this token and store it in args[].  */
Packit Service ada5e0
      end = cmdline;
Packit Service ada5e0
      dprintf (stderr, "start<%s> end<%s>\n", start, end);
Packit Service ada5e0
      args[ap] = (char *) xmalloc (end - start + 1);
Packit Service ada5e0
      memcpy (args[ap], start, end - start);
Packit Service ada5e0
      args[ap][end - start] = 0;
Packit Service ada5e0
Packit Service ada5e0
      /* Strip off quotes, if found.  */
Packit Service ada5e0
      dprintf (stderr, "args[%d] = <%s>\n", ap, args[ap]);
Packit Service ada5e0
      if (args[ap][0] == '\''
Packit Service ada5e0
	  && args[ap][strlen (args[ap])-1] == '\'')
Packit Service ada5e0
	{
Packit Service ada5e0
	  args[ap][strlen (args[ap])-1] = 0;
Packit Service ada5e0
	  args[ap] ++;
Packit Service ada5e0
	}
Packit Service ada5e0
Packit Service ada5e0
      else if (args[ap][0] == '"'
Packit Service ada5e0
	  && args[ap][strlen (args[ap])-1] == '"')
Packit Service ada5e0
	{
Packit Service ada5e0
	  args[ap][strlen (args[ap])-1] = 0;
Packit Service ada5e0
	  args[ap] ++;
Packit Service ada5e0
	}
Packit Service ada5e0
Packit Service ada5e0
      /* Replace positional parameters like $4.  */
Packit Service ada5e0
      else if (args[ap][0] == '$'
Packit Service ada5e0
	       && isdigit (args[ap][1])
Packit Service ada5e0
	       && args[ap][2] == 0)
Packit Service ada5e0
	{
Packit Service ada5e0
	  int a = args[ap][1] - '1';
Packit Service ada5e0
	  if (0 <= a && a < nargs)
Packit Service ada5e0
	    args[ap] = strdup (iargs[a]);
Packit Service ada5e0
	}
Packit Service ada5e0
Packit Service ada5e0
      ap ++;
Packit Service ada5e0
Packit Service ada5e0
      if (*cmdline == 0)
Packit Service ada5e0
	break;
Packit Service ada5e0
    }
Packit Service ada5e0
Packit Service ada5e0
  /* Lastly, NULL terminate the array and run it.  */
Packit Service ada5e0
  args[ap] = NULL;
Packit Service ada5e0
  run_command_array (args);
Packit Service ada5e0
}
Packit Service ada5e0
Packit Service ada5e0
/* Run a script by reading lines and passing them to the above
Packit Service ada5e0
   function.  */
Packit Service ada5e0
static void
Packit Service ada5e0
run_script (const char *filename, const char **args)
Packit Service ada5e0
{
Packit Service ada5e0
  char line[MAX_LINE_LENGTH + 1];
Packit Service ada5e0
  dprintf (stderr, "run_script starting: '%s'\n", filename);
Packit Service ada5e0
  FILE *f = fopen (filename, "r");
Packit Service ada5e0
  if (f == NULL)
Packit Service ada5e0
    {
Packit Service ada5e0
      fprintf (stderr, "sh: %s: %s\n", filename, strerror (errno));
Packit Service ada5e0
      exit (1);
Packit Service ada5e0
    }
Packit Service ada5e0
  while (fgets (line, sizeof (line), f) != NULL)
Packit Service ada5e0
    {
Packit Service ada5e0
      if (line[0] == '#')
Packit Service ada5e0
	{
Packit Service ada5e0
	  dprintf (stderr, "comment: %s\n", line);
Packit Service ada5e0
	  continue;
Packit Service ada5e0
	}
Packit Service ada5e0
      run_command_string (line, args);
Packit Service ada5e0
    }
Packit Service ada5e0
  fclose (f);
Packit Service ada5e0
}
Packit Service ada5e0
Packit Service ada5e0
int
Packit Service ada5e0
main (int argc, const char **argv)
Packit Service ada5e0
{
Packit Service ada5e0
  int i;
Packit Service ada5e0
Packit Service ada5e0
  if (strcmp (argv[1], "--debug") == 0)
Packit Service ada5e0
    {
Packit Service ada5e0
      debug_mode = 1;
Packit Service ada5e0
      --argc;
Packit Service ada5e0
      ++argv;
Packit Service ada5e0
    }
Packit Service ada5e0
Packit Service ada5e0
  dprintf (stderr, "container-sh starting:\n");
Packit Service ada5e0
  for (i = 0; i < argc; i++)
Packit Service ada5e0
    dprintf (stderr, "  argv[%d] is `%s'\n", i, argv[i]);
Packit Service ada5e0
Packit Service ada5e0
  if (strcmp (argv[1], "-c") == 0)
Packit Service ada5e0
    run_command_string (argv[2], argv+3);
Packit Service ada5e0
  else
Packit Service ada5e0
    run_script (argv[1], argv+2);
Packit Service ada5e0
Packit Service ada5e0
  dprintf (stderr, "normal exit 0\n");
Packit Service ada5e0
  return 0;
Packit Service ada5e0
}