Blame sysdeps/posix/spawni.c

Packit 6c4009
/* Guts of POSIX spawn interface.  Generic POSIX.1 version.
Packit 6c4009
   Copyright (C) 2000-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <spawn.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <paths.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <sys/resource.h>
Packit 6c4009
#include <sys/wait.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <not-cancel.h>
Packit 6c4009
#include <local-setxid.h>
Packit 6c4009
#include <shlib-compat.h>
Packit 6c4009
#include <nptl/pthreadP.h>
Packit 6c4009
#include <dl-sysdep.h>
Packit 6c4009
#include <libc-pointer-arith.h>
Packit 6c4009
#include <ldsodefs.h>
Packit 6c4009
#include "spawn_int.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The Unix standard contains a long explanation of the way to signal
Packit 6c4009
   an error after the fork() was successful.  Since no new wait status
Packit 6c4009
   was wanted there is no way to signal an error using one of the
Packit 6c4009
   available methods.  The committee chose to signal an error by a
Packit 6c4009
   normal program exit with the exit code 127.  */
Packit 6c4009
#define SPAWN_ERROR	127
Packit 6c4009
Packit 6c4009
struct posix_spawn_args
Packit 6c4009
{
Packit 6c4009
  sigset_t oldmask;
Packit 6c4009
  const char *file;
Packit 6c4009
  int (*exec) (const char *, char *const *, char *const *);
Packit 6c4009
  const posix_spawn_file_actions_t *fa;
Packit 6c4009
  const posix_spawnattr_t *restrict attr;
Packit 6c4009
  char *const *argv;
Packit 6c4009
  ptrdiff_t argc;
Packit 6c4009
  char *const *envp;
Packit 6c4009
  int xflags;
Packit 6c4009
  int pipe[2];
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Older version requires that shell script without shebang definition
Packit 6c4009
   to be called explicitly using /bin/sh (_PATH_BSHELL).  */
Packit 6c4009
static void
Packit 6c4009
maybe_script_execute (struct posix_spawn_args *args)
Packit 6c4009
{
Packit 6c4009
  if (SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_15)
Packit 6c4009
      && (args->xflags & SPAWN_XFLAGS_TRY_SHELL) && errno == ENOEXEC)
Packit 6c4009
    {
Packit 6c4009
      char *const *argv = args->argv;
Packit 6c4009
      ptrdiff_t argc = args->argc;
Packit 6c4009
Packit 6c4009
      /* Construct an argument list for the shell.  */
Packit 6c4009
      char *new_argv[argc + 1];
Packit 6c4009
      new_argv[0] = (char *) _PATH_BSHELL;
Packit 6c4009
      new_argv[1] = (char *) args->file;
Packit 6c4009
      if (argc > 1)
Packit 6c4009
	memcpy (new_argv + 2, argv + 1, argc * sizeof(char *));
Packit 6c4009
      else
Packit 6c4009
	new_argv[2] = NULL;
Packit 6c4009
Packit 6c4009
      /* Execute the shell.  */
Packit 6c4009
      args->exec (new_argv[0], new_argv, args->envp);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Function used in the clone call to setup the signals mask, posix_spawn
Packit 6c4009
   attributes, and file actions.  */
Packit 6c4009
static int
Packit 6c4009
__spawni_child (void *arguments)
Packit 6c4009
{
Packit 6c4009
  struct posix_spawn_args *args = arguments;
Packit 6c4009
  const posix_spawnattr_t *restrict attr = args->attr;
Packit 6c4009
  const posix_spawn_file_actions_t *file_actions = args->fa;
Packit 6c4009
  int ret;
Packit 6c4009
Packit 6c4009
  __close (args->pipe[0]);
Packit 6c4009
Packit 6c4009
  /* Set signal default action.  */
Packit 6c4009
  if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) != 0)
Packit 6c4009
    {
Packit 6c4009
      /* We have to iterate over all signals.  This could possibly be
Packit 6c4009
	 done better but it requires system specific solutions since
Packit 6c4009
	 the sigset_t data type can be very different on different
Packit 6c4009
	 architectures.  */
Packit 6c4009
      int sig;
Packit 6c4009
      struct sigaction sa;
Packit 6c4009
Packit 6c4009
      memset (&sa, '\0', sizeof (sa));
Packit 6c4009
      sa.sa_handler = SIG_DFL;
Packit 6c4009
Packit 6c4009
      for (sig = 1; sig <= _NSIG; ++sig)
Packit 6c4009
	if (__sigismember (&attr->__sd, sig) != 0
Packit 6c4009
	    && __sigaction (sig, &sa, NULL) != 0)
Packit 6c4009
	  goto fail;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
#ifdef _POSIX_PRIORITY_SCHEDULING
Packit 6c4009
  /* Set the scheduling algorithm and parameters.  */
Packit 6c4009
  if ((attr->__flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
Packit 6c4009
      == POSIX_SPAWN_SETSCHEDPARAM)
Packit 6c4009
    {
Packit 6c4009
      if (__sched_setparam (0, &attr->__sp) == -1)
Packit 6c4009
	goto fail;
Packit 6c4009
    }
Packit 6c4009
  else if ((attr->__flags & POSIX_SPAWN_SETSCHEDULER) != 0)
Packit 6c4009
    {
Packit 6c4009
      if (__sched_setscheduler (0, attr->__policy, &attr->__sp) == -1)
Packit 6c4009
	goto fail;
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Set the process session ID.  */
Packit 6c4009
  if ((attr->__flags & POSIX_SPAWN_SETSID) != 0
Packit 6c4009
      && __setsid () < 0)
Packit 6c4009
    goto fail;
Packit 6c4009
Packit 6c4009
  /* Set the process group ID.  */
Packit 6c4009
  if ((attr->__flags & POSIX_SPAWN_SETPGROUP) != 0
Packit 6c4009
      && __setpgid (0, attr->__pgrp) != 0)
Packit 6c4009
    goto fail;
Packit 6c4009
Packit 6c4009
  /* Set the effective user and group IDs.  */
Packit 6c4009
  if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0
Packit 6c4009
      && (local_seteuid (__getuid ()) != 0
Packit 6c4009
	  || local_setegid (__getgid ())) != 0)
Packit 6c4009
    goto fail;
Packit 6c4009
Packit 6c4009
  /* Execute the file actions.  */
Packit 6c4009
  if (file_actions != NULL)
Packit 6c4009
    {
Packit 6c4009
      int cnt;
Packit 6c4009
      struct rlimit64 fdlimit;
Packit 6c4009
      bool have_fdlimit = false;
Packit 6c4009
Packit 6c4009
      for (cnt = 0; cnt < file_actions->__used; ++cnt)
Packit 6c4009
	{
Packit 6c4009
	  struct __spawn_action *action = &file_actions->__actions[cnt];
Packit 6c4009
Packit 6c4009
	  switch (action->tag)
Packit 6c4009
	    {
Packit 6c4009
	    case spawn_do_close:
Packit 6c4009
	      if (__close_nocancel (action->action.close_action.fd) != 0)
Packit 6c4009
		{
Packit 6c4009
		  if (have_fdlimit == 0)
Packit 6c4009
		    {
Packit 6c4009
		      __getrlimit64 (RLIMIT_NOFILE, &fdlimit);
Packit 6c4009
		      have_fdlimit = true;
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  /* Only signal errors for file descriptors out of range.  */
Packit 6c4009
		  if (action->action.close_action.fd < 0
Packit 6c4009
		      || action->action.close_action.fd >= fdlimit.rlim_cur)
Packit 6c4009
		    goto fail;
Packit 6c4009
		}
Packit 6c4009
	      break;
Packit 6c4009
Packit 6c4009
	    case spawn_do_open:
Packit 6c4009
	      {
Packit 6c4009
		/* POSIX states that if fildes was already an open file descriptor,
Packit 6c4009
		   it shall be closed before the new file is opened.  This avoid
Packit 6c4009
		   pontential issues when posix_spawn plus addopen action is called
Packit 6c4009
		   with the process already at maximum number of file descriptor
Packit 6c4009
		   opened and also for multiple actions on single-open special
Packit 6c4009
		   paths (like /dev/watchdog).  */
Packit 6c4009
		__close_nocancel (action->action.open_action.fd);
Packit 6c4009
Packit 6c4009
		int new_fd = __open_nocancel (action->action.open_action.path,
Packit 6c4009
					      action->action.open_action.oflag
Packit 6c4009
					      | O_LARGEFILE,
Packit 6c4009
					      action->action.open_action.mode);
Packit 6c4009
Packit 6c4009
		if (new_fd == -1)
Packit 6c4009
		  goto fail;
Packit 6c4009
Packit 6c4009
		/* Make sure the desired file descriptor is used.  */
Packit 6c4009
		if (new_fd != action->action.open_action.fd)
Packit 6c4009
		  {
Packit 6c4009
		    if (__dup2 (new_fd, action->action.open_action.fd)
Packit 6c4009
			!= action->action.open_action.fd)
Packit 6c4009
		      goto fail;
Packit 6c4009
Packit 6c4009
		    if (__close_nocancel (new_fd) != 0)
Packit 6c4009
		      goto fail;
Packit 6c4009
		  }
Packit 6c4009
	      }
Packit 6c4009
	      break;
Packit 6c4009
Packit 6c4009
	    case spawn_do_dup2:
Packit 6c4009
	      if (__dup2 (action->action.dup2_action.fd,
Packit 6c4009
			  action->action.dup2_action.newfd)
Packit 6c4009
		  != action->action.dup2_action.newfd)
Packit 6c4009
		goto fail;
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Set the initial signal mask of the child if POSIX_SPAWN_SETSIGMASK
Packit 6c4009
     is set, otherwise restore the previous one.  */
Packit 6c4009
  __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK)
Packit 6c4009
		 ? &attr->__ss : &args->oldmask, 0);
Packit 6c4009
Packit 6c4009
  args->exec (args->file, args->argv, args->envp);
Packit 6c4009
Packit 6c4009
  /* This is compatibility function required to enable posix_spawn run
Packit 6c4009
     script without shebang definition for older posix_spawn versions
Packit 6c4009
     (2.15).  */
Packit 6c4009
  maybe_script_execute (args);
Packit 6c4009
Packit 6c4009
fail:
Packit 6c4009
  /* errno should have an appropriate non-zero value; otherwise,
Packit 6c4009
     there's a bug in glibc or the kernel.  For lack of an error code
Packit 6c4009
     (EINTERNALBUG) describing that, use ECHILD.  Another option would
Packit 6c4009
     be to set args->err to some negative sentinel and have the parent
Packit 6c4009
     abort(), but that seems needlessly harsh.  */
Packit 6c4009
  ret = errno ? : ECHILD;
Packit 6c4009
  if (ret)
Packit 6c4009
    /* Since sizeof errno < PIPE_BUF, the write is atomic. */
Packit 6c4009
    while (__write_nocancel (args->pipe[1], &ret, sizeof (ret)) < 0);
Packit 6c4009
Packit 6c4009
  _exit (SPAWN_ERROR);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Spawn a new process executing PATH with the attributes describes in *ATTRP.
Packit 6c4009
   Before running the process perform the actions described in FILE-ACTIONS. */
Packit 6c4009
int
Packit 6c4009
__spawnix (pid_t *pid, const char *file,
Packit 6c4009
	   const posix_spawn_file_actions_t *file_actions,
Packit 6c4009
	   const posix_spawnattr_t *attrp, char *const argv[],
Packit 6c4009
	   char *const envp[], int xflags,
Packit 6c4009
	   int (*exec) (const char *, char *const *, char *const *))
Packit 6c4009
{
Packit 6c4009
  struct posix_spawn_args args;
Packit 6c4009
  int ec;
Packit 6c4009
Packit 6c4009
  if (__pipe2 (args.pipe, O_CLOEXEC))
Packit 6c4009
    return errno;
Packit 6c4009
Packit 6c4009
  /* Disable asynchronous cancellation.  */
Packit 6c4009
  int state;
Packit 6c4009
  __libc_ptf_call (__pthread_setcancelstate,
Packit 6c4009
                   (PTHREAD_CANCEL_DISABLE, &state), 0);
Packit 6c4009
Packit 6c4009
  ptrdiff_t argc = 0;
Packit 6c4009
  ptrdiff_t limit = INT_MAX - 1;
Packit 6c4009
  while (argv[argc++] != NULL)
Packit 6c4009
    if (argc == limit)
Packit 6c4009
      {
Packit 6c4009
	errno = E2BIG;
Packit 6c4009
	return errno;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  args.file = file;
Packit 6c4009
  args.exec = exec;
Packit 6c4009
  args.fa = file_actions;
Packit 6c4009
  args.attr = attrp ? attrp : &(const posix_spawnattr_t) { 0 };
Packit 6c4009
  args.argv = argv;
Packit 6c4009
  args.argc = argc;
Packit 6c4009
  args.envp = envp;
Packit 6c4009
  args.xflags = xflags;
Packit 6c4009
Packit 6c4009
  /* Generate the new process.  */
Packit 6c4009
  pid_t new_pid = __fork ();
Packit 6c4009
Packit 6c4009
  if (new_pid == 0)
Packit 6c4009
    __spawni_child (&args);
Packit 6c4009
  else if (new_pid > 0)
Packit 6c4009
    {
Packit 6c4009
      __close (args.pipe[1]);
Packit 6c4009
Packit 6c4009
      if (__read (args.pipe[0], &ec, sizeof ec) != sizeof ec)
Packit 6c4009
	ec = 0;
Packit 6c4009
      else
Packit 6c4009
	__waitpid (new_pid, &(int) { 0 }, 0);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    ec = errno;
Packit 6c4009
Packit 6c4009
  __close (args.pipe[0]);
Packit 6c4009
Packit 6c4009
  if ((ec == 0) && (pid != NULL))
Packit 6c4009
    *pid = new_pid;
Packit 6c4009
Packit 6c4009
  __libc_ptf_call (__pthread_setcancelstate, (state, NULL), 0);
Packit 6c4009
Packit 6c4009
  return ec;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__spawni (pid_t * pid, const char *file,
Packit 6c4009
	  const posix_spawn_file_actions_t * acts,
Packit 6c4009
	  const posix_spawnattr_t * attrp, char *const argv[],
Packit 6c4009
	  char *const envp[], int xflags)
Packit 6c4009
{
Packit 6c4009
  /* It uses __execvpex to avoid run ENOEXEC in non compatibility mode (it
Packit 6c4009
     will be handled by maybe_script_execute).  */
Packit 6c4009
  return __spawnix (pid, file, acts, attrp, argv, envp, xflags,
Packit 6c4009
		    xflags & SPAWN_XFLAGS_USE_PATH ? __execvpex : __execve);
Packit 6c4009
}