Blame sysdeps/posix/spawni.c

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