1214 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1214 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
/* spawn.cc
 | 
						|
 | 
						|
   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
 | 
						|
   2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Red Hat, Inc.
 | 
						|
 | 
						|
This file is part of Cygwin.
 | 
						|
 | 
						|
This software is a copyrighted work licensed under the terms of the
 | 
						|
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | 
						|
details. */
 | 
						|
 | 
						|
#include "winsup.h"
 | 
						|
#include <stdlib.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <process.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
#include <wchar.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <sys/cygwin.h>
 | 
						|
#include "cygerrno.h"
 | 
						|
#include "security.h"
 | 
						|
#include "sigproc.h"
 | 
						|
#include "pinfo.h"
 | 
						|
#include "path.h"
 | 
						|
#include "fhandler.h"
 | 
						|
#include "dtable.h"
 | 
						|
#include "cygheap.h"
 | 
						|
#include "child_info.h"
 | 
						|
#include "environ.h"
 | 
						|
#include "cygtls.h"
 | 
						|
#include "tls_pbuf.h"
 | 
						|
#include "winf.h"
 | 
						|
#include "ntdll.h"
 | 
						|
 | 
						|
static const suffix_info exe_suffixes[] =
 | 
						|
{
 | 
						|
  suffix_info ("", 1),
 | 
						|
  suffix_info (".exe", 1),
 | 
						|
  suffix_info (".com"),
 | 
						|
  suffix_info (NULL)
 | 
						|
};
 | 
						|
 | 
						|
/* Add .exe to PROG if not already present and see if that exists.
 | 
						|
   If not, return PROG (converted from posix to win32 rules if necessary).
 | 
						|
   The result is always BUF.
 | 
						|
 | 
						|
   Returns (possibly NULL) suffix */
 | 
						|
 | 
						|
static const char *
 | 
						|
perhaps_suffix (const char *prog, path_conv& buf, int& err, unsigned opt)
 | 
						|
{
 | 
						|
  const char *ext;
 | 
						|
 | 
						|
  err = 0;
 | 
						|
  debug_printf ("prog '%s'", prog);
 | 
						|
  buf.check (prog, PC_SYM_FOLLOW | PC_NULLEMPTY | PC_POSIX,
 | 
						|
	     (opt & FE_DLL) ? stat_suffixes : exe_suffixes);
 | 
						|
 | 
						|
  if (buf.isdir ())
 | 
						|
    {
 | 
						|
      err = EACCES;
 | 
						|
      ext = NULL;
 | 
						|
    }
 | 
						|
  else if (!buf.exists ())
 | 
						|
    {
 | 
						|
      err = ENOENT;
 | 
						|
      ext = NULL;
 | 
						|
    }
 | 
						|
  else if (buf.known_suffix)
 | 
						|
    ext = buf.get_win32 () + (buf.known_suffix - buf.get_win32 ());
 | 
						|
  else
 | 
						|
    ext = strchr (buf.get_win32 (), '\0');
 | 
						|
 | 
						|
  debug_printf ("buf %s, suffix found '%s'", (char *) buf.get_win32 (), ext);
 | 
						|
  return ext;
 | 
						|
}
 | 
						|
 | 
						|
/* Find an executable name, possibly by appending known executable suffixes
 | 
						|
   to it.  The path_conv struct 'buf' is filled and contains both, win32 and
 | 
						|
   posix path of the file..  Any found suffix is returned in known_suffix.
 | 
						|
 | 
						|
   If the file is not found and !FE_NNF then the win32 version of name is
 | 
						|
   placed in buf and returned.  Otherwise the contents of buf is undefined
 | 
						|
   and NULL is returned.  */
 | 
						|
const char * __reg3
 | 
						|
find_exec (const char *name, path_conv& buf, const char *search,
 | 
						|
	   unsigned opt, const char **known_suffix)
 | 
						|
{
 | 
						|
  const char *suffix = "";
 | 
						|
  const char *retval = NULL;
 | 
						|
  tmp_pathbuf tp;
 | 
						|
  char *tmp_path;
 | 
						|
  char *tmp = tp.c_get ();
 | 
						|
  bool has_slash = !!strpbrk (name, "/\\");
 | 
						|
  int err = 0;
 | 
						|
 | 
						|
  debug_printf ("find_exec (%s)", name);
 | 
						|
 | 
						|
  /* Check to see if file can be opened as is first. */
 | 
						|
  if ((has_slash || opt & FE_CWD)
 | 
						|
      && (suffix = perhaps_suffix (name, buf, err, opt)) != NULL)
 | 
						|
    {
 | 
						|
      retval = buf.get_win32 ();
 | 
						|
      goto out;
 | 
						|
    }
 | 
						|
 | 
						|
  const char *path;
 | 
						|
  /* If it starts with a slash, it's a PATH-like pathlist.  Otherwise it's
 | 
						|
     the name of an environment variable. */
 | 
						|
  if (strchr (search, '/'))
 | 
						|
    *stpncpy (tmp, search, NT_MAX_PATH - 1) = '\0';
 | 
						|
  else if (has_slash || isdrive (name) || !(path = getenv (search)) || !*path)
 | 
						|
    goto errout;
 | 
						|
  else
 | 
						|
    *stpncpy (tmp, path, NT_MAX_PATH - 1) = '\0';
 | 
						|
 | 
						|
  path = tmp;
 | 
						|
  debug_printf ("searchpath %s", path);
 | 
						|
 | 
						|
  tmp_path = tp.c_get ();
 | 
						|
  do
 | 
						|
    {
 | 
						|
      char *eotmp = strccpy (tmp_path, &path, ':');
 | 
						|
      /* An empty path or '.' means the current directory, but we've
 | 
						|
	 already tried that.  */
 | 
						|
      if ((opt & FE_CWD) && (tmp_path[0] == '\0'
 | 
						|
			     || (tmp_path[0] == '.' && tmp_path[1] == '\0')))
 | 
						|
	continue;
 | 
						|
 | 
						|
      *eotmp++ = '/';
 | 
						|
      strcpy (eotmp, name);
 | 
						|
 | 
						|
      debug_printf ("trying %s", tmp_path);
 | 
						|
 | 
						|
      int err1;
 | 
						|
 | 
						|
      if ((suffix = perhaps_suffix (tmp_path, buf, err1, opt)) != NULL)
 | 
						|
	{
 | 
						|
	  if (buf.has_acls () && check_file_access (buf, X_OK, true))
 | 
						|
	    continue;
 | 
						|
	  retval = buf.get_win32 ();
 | 
						|
	  goto out;
 | 
						|
	}
 | 
						|
 | 
						|
    }
 | 
						|
  while (*path && *++path);
 | 
						|
 | 
						|
 errout:
 | 
						|
  /* Couldn't find anything in the given path.
 | 
						|
     Take the appropriate action based on FE_NNF. */
 | 
						|
  if (!(opt & FE_NNF))
 | 
						|
    {
 | 
						|
      buf.check (name, PC_SYM_FOLLOW | PC_POSIX);
 | 
						|
      retval = buf.get_win32 ();
 | 
						|
    }
 | 
						|
 | 
						|
 out:
 | 
						|
  debug_printf ("%s = find_exec (%s)", (char *) buf.get_win32 (), name);
 | 
						|
  if (known_suffix)
 | 
						|
    *known_suffix = suffix ?: strchr (buf.get_win32 (), '\0');
 | 
						|
  if (!retval && err)
 | 
						|
    set_errno (err);
 | 
						|
  return retval;
 | 
						|
}
 | 
						|
 | 
						|
/* Utility for child_info_spawn::worker.  */
 | 
						|
 | 
						|
static HANDLE
 | 
						|
handle (int fd, bool writing)
 | 
						|
{
 | 
						|
  HANDLE h;
 | 
						|
  cygheap_fdget cfd (fd);
 | 
						|
 | 
						|
  if (cfd < 0)
 | 
						|
    h = INVALID_HANDLE_VALUE;
 | 
						|
  else if (cfd->close_on_exec ())
 | 
						|
    h = INVALID_HANDLE_VALUE;
 | 
						|
  else if (!writing)
 | 
						|
    h = cfd->get_handle ();
 | 
						|
  else
 | 
						|
    h = cfd->get_output_handle ();
 | 
						|
 | 
						|
  return h;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
iscmd (const char *argv0, const char *what)
 | 
						|
{
 | 
						|
  int n;
 | 
						|
  n = strlen (argv0) - strlen (what);
 | 
						|
  if (n >= 2 && argv0[1] != ':')
 | 
						|
    return 0;
 | 
						|
  return n >= 0 && strcasematch (argv0 + n, what) &&
 | 
						|
	 (n == 0 || isdirsep (argv0[n - 1]));
 | 
						|
}
 | 
						|
 | 
						|
#define ILLEGAL_SIG_FUNC_PTR ((_sig_func_ptr) (-2))
 | 
						|
struct system_call_handle
 | 
						|
{
 | 
						|
  _sig_func_ptr oldint;
 | 
						|
  _sig_func_ptr oldquit;
 | 
						|
  sigset_t oldmask;
 | 
						|
  bool is_system_call ()
 | 
						|
  {
 | 
						|
    return oldint != ILLEGAL_SIG_FUNC_PTR;
 | 
						|
  }
 | 
						|
  system_call_handle (bool issystem)
 | 
						|
  {
 | 
						|
    if (!issystem)
 | 
						|
      oldint = ILLEGAL_SIG_FUNC_PTR;
 | 
						|
    else
 | 
						|
      {
 | 
						|
	sig_send (NULL, __SIGHOLD);
 | 
						|
	oldint = NULL;
 | 
						|
      }
 | 
						|
  }
 | 
						|
  void arm()
 | 
						|
  {
 | 
						|
    if (is_system_call ())
 | 
						|
      {
 | 
						|
	sigset_t child_block;
 | 
						|
	oldint = signal (SIGINT,  SIG_IGN);
 | 
						|
	oldquit = signal (SIGQUIT, SIG_IGN);
 | 
						|
	sigemptyset (&child_block);
 | 
						|
	sigaddset (&child_block, SIGCHLD);
 | 
						|
	sigprocmask (SIG_BLOCK, &child_block, &oldmask);
 | 
						|
	sig_send (NULL, __SIGNOHOLD);
 | 
						|
      }
 | 
						|
  }
 | 
						|
  ~system_call_handle ()
 | 
						|
  {
 | 
						|
    if (is_system_call ())
 | 
						|
      {
 | 
						|
	signal (SIGINT, oldint);
 | 
						|
	signal (SIGQUIT, oldquit);
 | 
						|
	sigprocmask (SIG_SETMASK, &oldmask, NULL);
 | 
						|
      }
 | 
						|
  }
 | 
						|
# undef cleanup
 | 
						|
};
 | 
						|
 | 
						|
child_info_spawn NO_COPY ch_spawn;
 | 
						|
 | 
						|
int
 | 
						|
child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 | 
						|
			  const char *const envp[], int mode,
 | 
						|
			  int in__stdin, int in__stdout)
 | 
						|
{
 | 
						|
  bool rc;
 | 
						|
  pid_t cygpid;
 | 
						|
  int res = -1;
 | 
						|
 | 
						|
  /* Check if we have been called from exec{lv}p or spawn{lv}p and mask
 | 
						|
     mode to keep only the spawn mode. */
 | 
						|
  bool p_type_exec = !!(mode & _P_PATH_TYPE_EXEC);
 | 
						|
  mode = _P_MODE (mode);
 | 
						|
 | 
						|
  if (prog_arg == NULL)
 | 
						|
    {
 | 
						|
      syscall_printf ("prog_arg is NULL");
 | 
						|
      set_errno (EFAULT);	/* As on Linux. */
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
  if (!prog_arg[0])
 | 
						|
    {
 | 
						|
      syscall_printf ("prog_arg is empty");
 | 
						|
      set_errno (ENOENT);	/* Per POSIX */
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  syscall_printf ("mode = %d, prog_arg = %.9500s", mode, prog_arg);
 | 
						|
 | 
						|
  /* FIXME: This is no error condition on Linux. */
 | 
						|
  if (argv == NULL)
 | 
						|
    {
 | 
						|
      syscall_printf ("argv is NULL");
 | 
						|
      set_errno (EINVAL);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  av newargv;
 | 
						|
  linebuf cmd;
 | 
						|
  PWCHAR envblock = NULL;
 | 
						|
  path_conv real_path;
 | 
						|
  bool reset_sendsig = false;
 | 
						|
 | 
						|
  tmp_pathbuf tp;
 | 
						|
  PWCHAR runpath = tp.w_get ();
 | 
						|
  int c_flags;
 | 
						|
 | 
						|
  bool null_app_name = false;
 | 
						|
  STARTUPINFOW si = {};
 | 
						|
  int looped = 0;
 | 
						|
 | 
						|
  system_call_handle system_call (mode == _P_SYSTEM);
 | 
						|
 | 
						|
  __try
 | 
						|
    {
 | 
						|
      child_info_types chtype;
 | 
						|
      if (mode == _P_OVERLAY)
 | 
						|
	chtype = _CH_EXEC;
 | 
						|
      else
 | 
						|
	chtype = _CH_SPAWN;
 | 
						|
 | 
						|
      moreinfo = cygheap_exec_info::alloc ();
 | 
						|
 | 
						|
      /* CreateProcess takes one long string that is the command line (sigh).
 | 
						|
	 We need to quote any argument that has whitespace or embedded "'s.  */
 | 
						|
 | 
						|
      int ac;
 | 
						|
      for (ac = 0; argv[ac]; ac++)
 | 
						|
	/* nothing */;
 | 
						|
 | 
						|
      int err;
 | 
						|
      const char *ext;
 | 
						|
      if ((ext = perhaps_suffix (prog_arg, real_path, err, FE_NADA)) == NULL)
 | 
						|
	{
 | 
						|
	  set_errno (err);
 | 
						|
	  res = -1;
 | 
						|
	  __leave;
 | 
						|
	}
 | 
						|
 | 
						|
      res = newargv.setup (prog_arg, real_path, ext, ac, argv, p_type_exec);
 | 
						|
 | 
						|
      if (res)
 | 
						|
	__leave;
 | 
						|
 | 
						|
      if (!real_path.iscygexec () && ::cygheap->cwd.get_error ())
 | 
						|
	{
 | 
						|
	  small_printf ("Error: Current working directory %s.\n"
 | 
						|
			"Can't start native Windows application from here.\n\n",
 | 
						|
			::cygheap->cwd.get_error_desc ());
 | 
						|
	  set_errno (::cygheap->cwd.get_error ());
 | 
						|
	  res = -1;
 | 
						|
	  __leave;
 | 
						|
	}
 | 
						|
 | 
						|
      if (ac == 3 && argv[1][0] == '/' && tolower (argv[1][1]) == 'c' &&
 | 
						|
	  (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe")))
 | 
						|
	{
 | 
						|
	  real_path.check (prog_arg);
 | 
						|
	  cmd.add ("\"");
 | 
						|
	  if (!real_path.error)
 | 
						|
	    cmd.add (real_path.get_win32 ());
 | 
						|
	  else
 | 
						|
	    cmd.add (argv[0]);
 | 
						|
	  cmd.add ("\"");
 | 
						|
	  cmd.add (" ");
 | 
						|
	  cmd.add (argv[1]);
 | 
						|
	  cmd.add (" ");
 | 
						|
	  cmd.add (argv[2]);
 | 
						|
	  real_path.set_path (argv[0]);
 | 
						|
	  null_app_name = true;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if (real_path.iscygexec ())
 | 
						|
	    {
 | 
						|
	      moreinfo->argc = newargv.argc;
 | 
						|
	      moreinfo->argv = newargv;
 | 
						|
	    }
 | 
						|
	  if ((wincmdln || !real_path.iscygexec ())
 | 
						|
	       && !cmd.fromargv (newargv, real_path.get_win32 (),
 | 
						|
				 real_path.iscygexec ()))
 | 
						|
	    {
 | 
						|
	      res = -1;
 | 
						|
	      __leave;
 | 
						|
	    }
 | 
						|
 | 
						|
 | 
						|
	  if (mode != _P_OVERLAY || !real_path.iscygexec ()
 | 
						|
	      || !DuplicateHandle (GetCurrentProcess (), myself.shared_handle (),
 | 
						|
				   GetCurrentProcess (), &moreinfo->myself_pinfo,
 | 
						|
				   0, TRUE, DUPLICATE_SAME_ACCESS))
 | 
						|
	    moreinfo->myself_pinfo = NULL;
 | 
						|
	  else
 | 
						|
	    VerifyHandle (moreinfo->myself_pinfo);
 | 
						|
	}
 | 
						|
 | 
						|
      PROCESS_INFORMATION pi;
 | 
						|
      pi.hProcess = pi.hThread = NULL;
 | 
						|
      pi.dwProcessId = pi.dwThreadId = 0;
 | 
						|
 | 
						|
      /* Set up needed handles for stdio */
 | 
						|
      si.dwFlags = STARTF_USESTDHANDLES;
 | 
						|
      si.hStdInput = handle ((in__stdin < 0 ? 0 : in__stdin), false);
 | 
						|
      si.hStdOutput = handle ((in__stdout < 0 ? 1 : in__stdout), true);
 | 
						|
      si.hStdError = handle (2, true);
 | 
						|
 | 
						|
      si.cb = sizeof (si);
 | 
						|
 | 
						|
      c_flags = GetPriorityClass (GetCurrentProcess ());
 | 
						|
      sigproc_printf ("priority class %d", c_flags);
 | 
						|
 | 
						|
      c_flags |= CREATE_SEPARATE_WOW_VDM | CREATE_UNICODE_ENVIRONMENT;
 | 
						|
 | 
						|
      if (wincap.has_program_compatibility_assistant ())
 | 
						|
	{
 | 
						|
	  /* We're adding the CREATE_BREAKAWAY_FROM_JOB flag here to workaround
 | 
						|
	     issues with the "Program Compatibility Assistant (PCA) Service"
 | 
						|
	     starting with Windows Vista.  For some reason, when starting long
 | 
						|
	     running sessions from mintty(*), the affected svchost.exe process
 | 
						|
	     takes more and more memory and at one point takes over the CPU.  At
 | 
						|
	     this point the machine becomes unresponsive.  The only way to get
 | 
						|
	     back to normal is to stop the entire mintty session, or to stop the
 | 
						|
	     PCA service.  However, a process which is controlled by PCA is part
 | 
						|
	     of a compatibility job, which allows child processes to break away
 | 
						|
	     from the job.  This helps to avoid this issue.
 | 
						|
 | 
						|
	     First we call IsProcessInJob.  It fetches the information whether or
 | 
						|
	     not we're part of a job 20 times faster than QueryInformationJobObject.
 | 
						|
 | 
						|
	     (*) Note that this is not mintty's fault.  It has just been observed
 | 
						|
	     with mintty in the first place.  See the archives for more info:
 | 
						|
	     http://cygwin.com/ml/cygwin-developers/2012-02/msg00018.html */
 | 
						|
 | 
						|
	  JOBOBJECT_BASIC_LIMIT_INFORMATION jobinfo;
 | 
						|
	  BOOL is_in_job;
 | 
						|
 | 
						|
	  if (IsProcessInJob (GetCurrentProcess (), NULL, &is_in_job)
 | 
						|
	      && is_in_job
 | 
						|
	      && QueryInformationJobObject (NULL, JobObjectBasicLimitInformation,
 | 
						|
					 &jobinfo, sizeof jobinfo, NULL)
 | 
						|
	      && (jobinfo.LimitFlags & (JOB_OBJECT_LIMIT_BREAKAWAY_OK
 | 
						|
					| JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)))
 | 
						|
	    {
 | 
						|
	      debug_printf ("Add CREATE_BREAKAWAY_FROM_JOB");
 | 
						|
	      c_flags |= CREATE_BREAKAWAY_FROM_JOB;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      if (mode == _P_DETACH)
 | 
						|
	c_flags |= DETACHED_PROCESS;
 | 
						|
      else
 | 
						|
	fhandler_console::need_invisible ();
 | 
						|
 | 
						|
      if (mode != _P_OVERLAY)
 | 
						|
	myself->exec_sendsig = NULL;
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Reset sendsig so that any process which wants to send a signal
 | 
						|
	     to this pid will wait for the new process to become active.
 | 
						|
	     Save the old value in case the exec fails.  */
 | 
						|
	  if (!myself->exec_sendsig)
 | 
						|
	    {
 | 
						|
	      myself->exec_sendsig = myself->sendsig;
 | 
						|
	      myself->exec_dwProcessId = myself->dwProcessId;
 | 
						|
	      myself->sendsig = NULL;
 | 
						|
	      reset_sendsig = true;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      if (null_app_name)
 | 
						|
	runpath = NULL;
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  USHORT len = real_path.get_nt_native_path ()->Length / sizeof (WCHAR);
 | 
						|
	  if (RtlEqualUnicodePathPrefix (real_path.get_nt_native_path (),
 | 
						|
					 &ro_u_natp, FALSE))
 | 
						|
	    {
 | 
						|
	      runpath = real_path.get_wide_win32_path (runpath);
 | 
						|
	      /* If the executable path length is < MAX_PATH, make sure the long
 | 
						|
		 path win32 prefix is removed from the path to make subsequent
 | 
						|
		 not long path aware native Win32 child processes happy. */
 | 
						|
	      if (len < MAX_PATH + 4)
 | 
						|
		{
 | 
						|
		  if (runpath[5] == ':')
 | 
						|
		    runpath += 4;
 | 
						|
		  else if (len < MAX_PATH + 6)
 | 
						|
		    *(runpath += 6) = L'\\';
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  else if (len < NT_MAX_PATH - ro_u_globalroot.Length / sizeof (WCHAR))
 | 
						|
	    {
 | 
						|
	      UNICODE_STRING rpath;
 | 
						|
 | 
						|
	      RtlInitEmptyUnicodeString (&rpath, runpath,
 | 
						|
					 (NT_MAX_PATH - 1) * sizeof (WCHAR));
 | 
						|
	      RtlCopyUnicodeString (&rpath, &ro_u_globalroot);
 | 
						|
	      RtlAppendUnicodeStringToString (&rpath,
 | 
						|
					      real_path.get_nt_native_path ());
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      set_errno (ENAMETOOLONG);
 | 
						|
	      res = -1;
 | 
						|
	      __leave;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      cygbench ("spawn-worker");
 | 
						|
 | 
						|
      if (!real_path.iscygexec())
 | 
						|
	::cygheap->fdtab.set_file_pointers_for_exec ();
 | 
						|
 | 
						|
      /* If we switch the user, merge the user's Windows environment. */
 | 
						|
      bool switch_user = ::cygheap->user.issetuid ()
 | 
						|
			 && (::cygheap->user.saved_uid
 | 
						|
			     != ::cygheap->user.real_uid);
 | 
						|
      moreinfo->envp = build_env (envp, envblock, moreinfo->envc,
 | 
						|
				  real_path.iscygexec (),
 | 
						|
				  switch_user ? ::cygheap->user.primary_token ()
 | 
						|
					      : NULL);
 | 
						|
      if (!moreinfo->envp || !envblock)
 | 
						|
	{
 | 
						|
	  set_errno (E2BIG);
 | 
						|
	  res = -1;
 | 
						|
	  __leave;
 | 
						|
	}
 | 
						|
      set (chtype, real_path.iscygexec ());
 | 
						|
      __stdin = in__stdin;
 | 
						|
      __stdout = in__stdout;
 | 
						|
      record_children ();
 | 
						|
 | 
						|
      si.lpReserved2 = (LPBYTE) this;
 | 
						|
      si.cbReserved2 = sizeof (*this);
 | 
						|
 | 
						|
      /* Depends on set call above.
 | 
						|
	 Some file types might need extra effort in the parent after CreateProcess
 | 
						|
	 and before copying the datastructures to the child.  So we have to start
 | 
						|
	 the child in suspend state, unfortunately, to avoid a race condition. */
 | 
						|
      if (!newargv.win16_exe
 | 
						|
	  && (!iscygwin () || mode != _P_OVERLAY
 | 
						|
	      || ::cygheap->fdtab.need_fixup_before ()))
 | 
						|
	c_flags |= CREATE_SUSPENDED;
 | 
						|
      /* If a native application should be spawned, we test here if the spawning
 | 
						|
	 process is running in a console and, if so, if it's a foreground or
 | 
						|
	 background process.  If it's a background process, we start the native
 | 
						|
	 process with the CREATE_NEW_PROCESS_GROUP flag set.  This lets the native
 | 
						|
	 process ignore Ctrl-C by default.  If we don't do that, pressing Ctrl-C
 | 
						|
	 in a console will break native processes running in the background,
 | 
						|
	 because the Ctrl-C event is sent to all processes in the console, unless
 | 
						|
	 they ignore it explicitely.  CREATE_NEW_PROCESS_GROUP does that for us. */
 | 
						|
      if (!iscygwin () && fhandler_console::exists ()
 | 
						|
	  && fhandler_console::tc_getpgid () != myself->pgid)
 | 
						|
	c_flags |= CREATE_NEW_PROCESS_GROUP;
 | 
						|
      refresh_cygheap ();
 | 
						|
 | 
						|
      if (mode == _P_DETACH)
 | 
						|
	/* all set */;
 | 
						|
      else if (mode != _P_OVERLAY || !my_wr_proc_pipe)
 | 
						|
	prefork ();
 | 
						|
      else
 | 
						|
	wr_proc_pipe = my_wr_proc_pipe;
 | 
						|
 | 
						|
      /* Don't allow child to inherit these handles if it's not a Cygwin program.
 | 
						|
	 wr_proc_pipe will be injected later.  parent won't be used by the child
 | 
						|
	 so there is no reason for the child to have it open as it can confuse
 | 
						|
	 ps into thinking that children of windows processes are all part of
 | 
						|
	 the same "execed" process.
 | 
						|
	 FIXME: Someday, make it so that parent is never created when starting
 | 
						|
	 non-Cygwin processes. */
 | 
						|
      if (!iscygwin ())
 | 
						|
	{
 | 
						|
	  SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT, 0);
 | 
						|
	  SetHandleInformation (parent, HANDLE_FLAG_INHERIT, 0);
 | 
						|
	}
 | 
						|
      /* FIXME: racy */
 | 
						|
      if (mode != _P_OVERLAY)
 | 
						|
	SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT, 0);
 | 
						|
      parent_winpid = GetCurrentProcessId ();
 | 
						|
 | 
						|
    loop:
 | 
						|
      /* When ruid != euid we create the new process under the current original
 | 
						|
	 account and impersonate in child, this way maintaining the different
 | 
						|
	 effective vs. real ids.
 | 
						|
	 FIXME: If ruid != euid and ruid != saved_uid we currently give
 | 
						|
	 up on ruid. The new process will have ruid == euid. */
 | 
						|
      ::cygheap->user.deimpersonate ();
 | 
						|
 | 
						|
      if (!real_path.iscygexec () && mode == _P_OVERLAY)
 | 
						|
	myself->process_state |= PID_NOTCYGWIN;
 | 
						|
 | 
						|
      wchar_t wcmd[(size_t) cmd];
 | 
						|
      if (!::cygheap->user.issetuid ()
 | 
						|
	  || (::cygheap->user.saved_uid == ::cygheap->user.real_uid
 | 
						|
	      && ::cygheap->user.saved_gid == ::cygheap->user.real_gid
 | 
						|
	      && !::cygheap->user.groups.issetgroups ()
 | 
						|
	      && !::cygheap->user.setuid_to_restricted))
 | 
						|
	{
 | 
						|
	  rc = CreateProcessW (runpath,	  /* image name - with full path */
 | 
						|
			       cmd.wcs (wcmd),/* what was passed to exec */
 | 
						|
			       &sec_none_nih, /* process security attrs */
 | 
						|
			       &sec_none_nih, /* thread security attrs */
 | 
						|
			       TRUE,	  /* inherit handles from parent */
 | 
						|
			       c_flags,
 | 
						|
			       envblock,	  /* environment */
 | 
						|
			       NULL,
 | 
						|
			       &si,
 | 
						|
			       &pi);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Give access to myself */
 | 
						|
	  if (mode == _P_OVERLAY)
 | 
						|
	    myself.set_acl();
 | 
						|
 | 
						|
	  WCHAR wstname[1024] = { L'\0' };
 | 
						|
	  HWINSTA hwst_orig = NULL, hwst = NULL;
 | 
						|
	  HDESK hdsk_orig = NULL, hdsk = NULL;
 | 
						|
	  PSECURITY_ATTRIBUTES sa;
 | 
						|
	  DWORD n;
 | 
						|
 | 
						|
	  hwst_orig = GetProcessWindowStation ();
 | 
						|
	  hdsk_orig = GetThreadDesktop (GetCurrentThreadId ());
 | 
						|
	  GetUserObjectInformationW (hwst_orig, UOI_NAME, wstname, 1024, &n);
 | 
						|
	  /* Prior to Vista it was possible to start a service with the
 | 
						|
	     "Interact with desktop" flag.  This started the service in the
 | 
						|
	     interactive window station of the console.  A big security
 | 
						|
	     risk, but we don't want to disable this behaviour for older
 | 
						|
	     OSes because it's still heavily used by some users.  They have
 | 
						|
	     been warned. */
 | 
						|
	  if (!::cygheap->user.setuid_to_restricted
 | 
						|
	      && wcscasecmp (wstname, L"WinSta0") != 0)
 | 
						|
	    {
 | 
						|
	      WCHAR sid[128];
 | 
						|
 | 
						|
	      sa = sec_user ((PSECURITY_ATTRIBUTES) alloca (1024),
 | 
						|
			     ::cygheap->user.sid ());
 | 
						|
	      /* We're creating a window station per user, not per logon session.
 | 
						|
		 First of all we might not have a valid logon session for
 | 
						|
		 the user (logon by create_token), and second, it doesn't
 | 
						|
		 make sense in terms of security to create a new window
 | 
						|
		 station for every logon of the same user.  It just fills up
 | 
						|
		 the system with window stations for no good reason. */
 | 
						|
	      hwst = CreateWindowStationW (::cygheap->user.get_windows_id (sid),
 | 
						|
					   0, GENERIC_READ | GENERIC_WRITE, sa);
 | 
						|
	      if (!hwst)
 | 
						|
		system_printf ("CreateWindowStation failed, %E");
 | 
						|
	      else if (!SetProcessWindowStation (hwst))
 | 
						|
		system_printf ("SetProcessWindowStation failed, %E");
 | 
						|
	      else if (!(hdsk = CreateDesktopW (L"Default", NULL, NULL, 0,
 | 
						|
						GENERIC_ALL, sa)))
 | 
						|
		system_printf ("CreateDesktop failed, %E");
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  wcpcpy (wcpcpy (wstname, sid), L"\\Default");
 | 
						|
		  si.lpDesktop = wstname;
 | 
						|
		  debug_printf ("Desktop: %W", si.lpDesktop);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
 | 
						|
	  rc = CreateProcessAsUserW (::cygheap->user.primary_token (),
 | 
						|
			       runpath,	  /* image name - with full path */
 | 
						|
			       cmd.wcs (wcmd),/* what was passed to exec */
 | 
						|
			       &sec_none_nih, /* process security attrs */
 | 
						|
			       &sec_none_nih, /* thread security attrs */
 | 
						|
			       TRUE,	  /* inherit handles from parent */
 | 
						|
			       c_flags,
 | 
						|
			       envblock,	  /* environment */
 | 
						|
			       NULL,
 | 
						|
			       &si,
 | 
						|
			       &pi);
 | 
						|
	  if (hwst)
 | 
						|
	    {
 | 
						|
	      SetProcessWindowStation (hwst_orig);
 | 
						|
	      CloseWindowStation (hwst);
 | 
						|
	    }
 | 
						|
	  if (hdsk)
 | 
						|
	    {
 | 
						|
	      SetThreadDesktop (hdsk_orig);
 | 
						|
	      CloseDesktop (hdsk);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      if (mode != _P_OVERLAY)
 | 
						|
	SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT,
 | 
						|
			      HANDLE_FLAG_INHERIT);
 | 
						|
 | 
						|
      /* Set errno now so that debugging messages from it appear before our
 | 
						|
	 final debugging message [this is a general rule for debugging
 | 
						|
	 messages].  */
 | 
						|
      if (!rc)
 | 
						|
	{
 | 
						|
	  __seterrno ();
 | 
						|
	  syscall_printf ("CreateProcess failed, %E");
 | 
						|
	  /* If this was a failed exec, restore the saved sendsig. */
 | 
						|
	  if (reset_sendsig)
 | 
						|
	    {
 | 
						|
	      myself->sendsig = myself->exec_sendsig;
 | 
						|
	      myself->exec_sendsig = NULL;
 | 
						|
	    }
 | 
						|
	  myself->process_state &= ~PID_NOTCYGWIN;
 | 
						|
	  /* Reset handle inheritance to default when the execution of a non-Cygwin
 | 
						|
	     process fails.  Only need to do this for _P_OVERLAY since the handle will
 | 
						|
	     be closed otherwise.  Don't need to do this for 'parent' since it will
 | 
						|
	     be closed in every case.  See FIXME above. */
 | 
						|
	  if (!iscygwin () && mode == _P_OVERLAY)
 | 
						|
	    SetHandleInformation (wr_proc_pipe, HANDLE_FLAG_INHERIT,
 | 
						|
				  HANDLE_FLAG_INHERIT);
 | 
						|
	  if (wr_proc_pipe == my_wr_proc_pipe)
 | 
						|
	    wr_proc_pipe = NULL;	/* We still own it: don't nuke in destructor */
 | 
						|
 | 
						|
	  /* Restore impersonation. In case of _P_OVERLAY this isn't
 | 
						|
	     allowed since it would overwrite child data. */
 | 
						|
	  if (mode != _P_OVERLAY)
 | 
						|
	    ::cygheap->user.reimpersonate ();
 | 
						|
 | 
						|
	  res = -1;
 | 
						|
	  __leave;
 | 
						|
	}
 | 
						|
 | 
						|
      /* The CREATE_SUSPENDED case is handled below */
 | 
						|
      if (iscygwin () && !(c_flags & CREATE_SUSPENDED))
 | 
						|
	strace.write_childpid (pi.dwProcessId);
 | 
						|
 | 
						|
      /* Fixup the parent data structures if needed and resume the child's
 | 
						|
	 main thread. */
 | 
						|
      if (::cygheap->fdtab.need_fixup_before ())
 | 
						|
	::cygheap->fdtab.fixup_before_exec (pi.dwProcessId);
 | 
						|
 | 
						|
      if (mode != _P_OVERLAY)
 | 
						|
	cygpid = cygwin_pid (pi.dwProcessId);
 | 
						|
      else
 | 
						|
	cygpid = myself->pid;
 | 
						|
 | 
						|
      /* We print the original program name here so the user can see that too.  */
 | 
						|
      syscall_printf ("pid %d, prog_arg %s, cmd line %.9500s)",
 | 
						|
		      rc ? cygpid : (unsigned int) -1, prog_arg, (const char *) cmd);
 | 
						|
 | 
						|
      /* Name the handle similarly to proc_subproc. */
 | 
						|
      ProtectHandle1 (pi.hProcess, childhProc);
 | 
						|
 | 
						|
      if (mode == _P_OVERLAY)
 | 
						|
	{
 | 
						|
	  myself->dwProcessId = pi.dwProcessId;
 | 
						|
	  strace.execing = 1;
 | 
						|
	  myself.hProcess = hExeced = pi.hProcess;
 | 
						|
	  real_path.get_wide_win32_path (myself->progname); // FIXME: race?
 | 
						|
	  sigproc_printf ("new process name %W", myself->progname);
 | 
						|
	  if (!iscygwin ())
 | 
						|
	    close_all_files ();
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  myself->set_has_pgid_children ();
 | 
						|
	  ProtectHandle (pi.hThread);
 | 
						|
	  pinfo child (cygpid,
 | 
						|
		       PID_IN_USE | (real_path.iscygexec () ? 0 : PID_NOTCYGWIN));
 | 
						|
	  if (!child)
 | 
						|
	    {
 | 
						|
	      syscall_printf ("pinfo failed");
 | 
						|
	      if (get_errno () != ENOMEM)
 | 
						|
		set_errno (EAGAIN);
 | 
						|
	      res = -1;
 | 
						|
	      __leave;
 | 
						|
	    }
 | 
						|
	  child->dwProcessId = pi.dwProcessId;
 | 
						|
	  child.hProcess = pi.hProcess;
 | 
						|
 | 
						|
	  real_path.get_wide_win32_path (child->progname);
 | 
						|
	  /* FIXME: This introduces an unreferenced, open handle into the child.
 | 
						|
	     The purpose is to keep the pid shared memory open so that all of
 | 
						|
	     the fields filled out by child.remember do not disappear and so there
 | 
						|
	     is not a brief period during which the pid is not available.
 | 
						|
	     However, we should try to find another way to do this eventually. */
 | 
						|
	  DuplicateHandle (GetCurrentProcess (), child.shared_handle (),
 | 
						|
			   pi.hProcess, NULL, 0, 0, DUPLICATE_SAME_ACCESS);
 | 
						|
	  child->start_time = time (NULL); /* Register child's starting time. */
 | 
						|
	  child->nice = myself->nice;
 | 
						|
	  postfork (child);
 | 
						|
	  if (!child.remember (mode == _P_DETACH))
 | 
						|
	    {
 | 
						|
	      /* FIXME: Child in strange state now */
 | 
						|
	      CloseHandle (pi.hProcess);
 | 
						|
	      ForceCloseHandle (pi.hThread);
 | 
						|
	      res = -1;
 | 
						|
	      __leave;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      /* Start the child running */
 | 
						|
      if (c_flags & CREATE_SUSPENDED)
 | 
						|
	{
 | 
						|
	  /* Inject a non-inheritable wr_proc_pipe handle into child so that we
 | 
						|
	     can accurately track when the child exits without keeping this
 | 
						|
	     process waiting around for it to exit.  */
 | 
						|
	  if (!iscygwin ())
 | 
						|
	    DuplicateHandle (GetCurrentProcess (), wr_proc_pipe, pi.hProcess, NULL,
 | 
						|
			     0, false, DUPLICATE_SAME_ACCESS);
 | 
						|
	  ResumeThread (pi.hThread);
 | 
						|
	  if (iscygwin ())
 | 
						|
	    strace.write_childpid (pi.dwProcessId);
 | 
						|
	}
 | 
						|
      ForceCloseHandle (pi.hThread);
 | 
						|
 | 
						|
      sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
 | 
						|
 | 
						|
      bool synced;
 | 
						|
      if ((mode == _P_DETACH || mode == _P_NOWAIT) && !iscygwin ())
 | 
						|
	synced = false;
 | 
						|
      else
 | 
						|
	/* Just mark a non-cygwin process as 'synced'.  We will still eventually
 | 
						|
	   wait for it to exit in maybe_set_exit_code_from_windows(). */
 | 
						|
	synced = iscygwin () ? sync (pi.dwProcessId, pi.hProcess, INFINITE) : true;
 | 
						|
 | 
						|
      switch (mode)
 | 
						|
	{
 | 
						|
	case _P_OVERLAY:
 | 
						|
	  myself.hProcess = pi.hProcess;
 | 
						|
	  if (!synced)
 | 
						|
	    {
 | 
						|
	      if (!proc_retry (pi.hProcess))
 | 
						|
		{
 | 
						|
		  looped++;
 | 
						|
		  goto loop;
 | 
						|
		}
 | 
						|
	      close_all_files (true);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      if (iscygwin ())
 | 
						|
		close_all_files (true);
 | 
						|
	      if (!my_wr_proc_pipe
 | 
						|
		  && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT)
 | 
						|
		wait_for_myself ();
 | 
						|
	    }
 | 
						|
	  myself.exit (EXITCODE_NOSET);
 | 
						|
	  break;
 | 
						|
	case _P_WAIT:
 | 
						|
	case _P_SYSTEM:
 | 
						|
	  system_call.arm ();
 | 
						|
	  if (waitpid (cygpid, &res, 0) != cygpid)
 | 
						|
	    res = -1;
 | 
						|
	  break;
 | 
						|
	case _P_DETACH:
 | 
						|
	  res = 0;	/* Lost all memory of this child. */
 | 
						|
	  break;
 | 
						|
	case _P_NOWAIT:
 | 
						|
	case _P_NOWAITO:
 | 
						|
	case _P_VFORK:
 | 
						|
	  res = cygpid;
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  __except (NO_ERROR)
 | 
						|
    {
 | 
						|
      if (get_errno () == ENOMEM)
 | 
						|
	set_errno (E2BIG);
 | 
						|
      else
 | 
						|
	set_errno (EFAULT);
 | 
						|
      res = -1;
 | 
						|
    }
 | 
						|
  __endtry
 | 
						|
  this->cleanup ();
 | 
						|
  if (envblock)
 | 
						|
    free (envblock);
 | 
						|
  return (int) res;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
cwait (int *result, int pid, int)
 | 
						|
{
 | 
						|
  return waitpid (pid, result, 0);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
* Helper function for spawn runtime calls.
 | 
						|
* Doesn't search the path.
 | 
						|
*/
 | 
						|
 | 
						|
extern "C" int
 | 
						|
spawnve (int mode, const char *path, const char *const *argv,
 | 
						|
       const char *const *envp)
 | 
						|
{
 | 
						|
  static char *const empty_env[] = { NULL };
 | 
						|
 | 
						|
  int ret;
 | 
						|
#ifdef NEWVFORK
 | 
						|
  vfork_save *vf = vfork_storage.val ();
 | 
						|
 | 
						|
  if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY)
 | 
						|
    mode = _P_NOWAIT;
 | 
						|
  else
 | 
						|
    vf = NULL;
 | 
						|
#endif
 | 
						|
 | 
						|
  syscall_printf ("spawnve (%s, %s, %p)", path, argv[0], envp);
 | 
						|
 | 
						|
  if (!envp)
 | 
						|
    envp = empty_env;
 | 
						|
 | 
						|
  switch (_P_MODE (mode))
 | 
						|
    {
 | 
						|
    case _P_OVERLAY:
 | 
						|
      ch_spawn.worker (path, argv, envp, mode);
 | 
						|
      /* Errno should be set by worker.  */
 | 
						|
      ret = -1;
 | 
						|
      break;
 | 
						|
    case _P_VFORK:
 | 
						|
    case _P_NOWAIT:
 | 
						|
    case _P_NOWAITO:
 | 
						|
    case _P_WAIT:
 | 
						|
    case _P_DETACH:
 | 
						|
    case _P_SYSTEM:
 | 
						|
      ret = ch_spawn.worker (path, argv, envp, mode);
 | 
						|
#ifdef NEWVFORK
 | 
						|
      if (vf)
 | 
						|
	{
 | 
						|
	  if (ret > 0)
 | 
						|
	    {
 | 
						|
	      debug_printf ("longjmping due to vfork");
 | 
						|
	      vf->restore_pid (ret);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
#endif
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      set_errno (EINVAL);
 | 
						|
      ret = -1;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
* spawn functions as implemented in the MS runtime library.
 | 
						|
* Most of these based on (and copied from) newlib/libc/posix/execXX.c
 | 
						|
*/
 | 
						|
 | 
						|
extern "C" int
 | 
						|
spawnl (int mode, const char *path, const char *arg0, ...)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  va_list args;
 | 
						|
  const char *argv[256];
 | 
						|
 | 
						|
  va_start (args, arg0);
 | 
						|
  argv[0] = arg0;
 | 
						|
  i = 1;
 | 
						|
 | 
						|
  do
 | 
						|
      argv[i] = va_arg (args, const char *);
 | 
						|
  while (argv[i++] != NULL);
 | 
						|
 | 
						|
  va_end (args);
 | 
						|
 | 
						|
  return spawnve (mode, path, (char * const  *) argv, cur_environ ());
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
spawnle (int mode, const char *path, const char *arg0, ...)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  va_list args;
 | 
						|
  const char * const *envp;
 | 
						|
  const char *argv[256];
 | 
						|
 | 
						|
  va_start (args, arg0);
 | 
						|
  argv[0] = arg0;
 | 
						|
  i = 1;
 | 
						|
 | 
						|
  do
 | 
						|
    argv[i] = va_arg (args, const char *);
 | 
						|
  while (argv[i++] != NULL);
 | 
						|
 | 
						|
  envp = va_arg (args, const char * const *);
 | 
						|
  va_end (args);
 | 
						|
 | 
						|
  return spawnve (mode, path, (char * const *) argv, (char * const *) envp);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
spawnlp (int mode, const char *file, const char *arg0, ...)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  va_list args;
 | 
						|
  const char *argv[256];
 | 
						|
  path_conv buf;
 | 
						|
 | 
						|
  va_start (args, arg0);
 | 
						|
  argv[0] = arg0;
 | 
						|
  i = 1;
 | 
						|
 | 
						|
  do
 | 
						|
      argv[i] = va_arg (args, const char *);
 | 
						|
  while (argv[i++] != NULL);
 | 
						|
 | 
						|
  va_end (args);
 | 
						|
 | 
						|
  return spawnve (mode | _P_PATH_TYPE_EXEC, find_exec (file, buf),
 | 
						|
		  (char * const *) argv, cur_environ ());
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
spawnlpe (int mode, const char *file, const char *arg0, ...)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  va_list args;
 | 
						|
  const char * const *envp;
 | 
						|
  const char *argv[256];
 | 
						|
  path_conv buf;
 | 
						|
 | 
						|
  va_start (args, arg0);
 | 
						|
  argv[0] = arg0;
 | 
						|
  i = 1;
 | 
						|
 | 
						|
  do
 | 
						|
    argv[i] = va_arg (args, const char *);
 | 
						|
  while (argv[i++] != NULL);
 | 
						|
 | 
						|
  envp = va_arg (args, const char * const *);
 | 
						|
  va_end (args);
 | 
						|
 | 
						|
  return spawnve (mode | _P_PATH_TYPE_EXEC, find_exec (file, buf),
 | 
						|
		  (char * const *) argv, envp);
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
spawnv (int mode, const char *path, const char * const *argv)
 | 
						|
{
 | 
						|
  return spawnve (mode, path, argv, cur_environ ());
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
spawnvp (int mode, const char *file, const char * const *argv)
 | 
						|
{
 | 
						|
  path_conv buf;
 | 
						|
  return spawnve (mode | _P_PATH_TYPE_EXEC, find_exec (file, buf), argv,
 | 
						|
		  cur_environ ());
 | 
						|
}
 | 
						|
 | 
						|
extern "C" int
 | 
						|
spawnvpe (int mode, const char *file, const char * const *argv,
 | 
						|
	  const char * const *envp)
 | 
						|
{
 | 
						|
  path_conv buf;
 | 
						|
  return spawnve (mode | _P_PATH_TYPE_EXEC, find_exec (file, buf), argv, envp);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
av::setup (const char *prog_arg, path_conv& real_path, const char *ext,
 | 
						|
	   int argc, const char *const *argv, bool p_type_exec)
 | 
						|
{
 | 
						|
  const char *p;
 | 
						|
  bool exeext = ascii_strcasematch (ext, ".exe");
 | 
						|
  new (this) av (argc, argv);
 | 
						|
  if ((exeext && real_path.iscygexec ()) || ascii_strcasematch (ext, ".bat")
 | 
						|
      || (!*ext && ((p = ext - 4) > real_path.get_win32 ())
 | 
						|
	  && (ascii_strcasematch (p, ".bat") || ascii_strcasematch (p, ".cmd")
 | 
						|
	      || ascii_strcasematch (p, ".btm"))))
 | 
						|
    /* no extra checks needed */;
 | 
						|
  else
 | 
						|
    while (1)
 | 
						|
      {
 | 
						|
	char *pgm = NULL;
 | 
						|
	char *arg1 = NULL;
 | 
						|
	char *ptr, *buf;
 | 
						|
	OBJECT_ATTRIBUTES attr;
 | 
						|
	IO_STATUS_BLOCK io;
 | 
						|
	HANDLE h;
 | 
						|
	NTSTATUS status;
 | 
						|
	LARGE_INTEGER size;
 | 
						|
 | 
						|
	status = NtOpenFile (&h, SYNCHRONIZE | GENERIC_READ,
 | 
						|
			     real_path.get_object_attr (attr, sec_none_nih),
 | 
						|
			     &io, FILE_SHARE_VALID_FLAGS,
 | 
						|
			     FILE_SYNCHRONOUS_IO_NONALERT
 | 
						|
			     | FILE_OPEN_FOR_BACKUP_INTENT
 | 
						|
			     | FILE_NON_DIRECTORY_FILE);
 | 
						|
	if (!NT_SUCCESS (status))
 | 
						|
	  {
 | 
						|
	    /* File is not readable?  Doesn't mean it's not executable.
 | 
						|
	       Test for executability and if so, just assume the file is
 | 
						|
	       a cygwin executable and go ahead. */
 | 
						|
	    if (status == STATUS_ACCESS_DENIED && real_path.has_acls ()
 | 
						|
		&& check_file_access (real_path, X_OK, true) == 0)
 | 
						|
	      {
 | 
						|
		real_path.set_cygexec (true);
 | 
						|
		break;
 | 
						|
	      }
 | 
						|
	    goto err;
 | 
						|
	  }
 | 
						|
	if (!GetFileSizeEx (h, &size))
 | 
						|
	  {
 | 
						|
	    NtClose (h);
 | 
						|
	    goto err;
 | 
						|
	  }
 | 
						|
	if (size.QuadPart > (LONGLONG) wincap.allocation_granularity ())
 | 
						|
	  size.LowPart = wincap.allocation_granularity ();
 | 
						|
 | 
						|
	HANDLE hm = CreateFileMapping (h, &sec_none_nih, PAGE_READONLY,
 | 
						|
				       0, 0, NULL);
 | 
						|
	NtClose (h);
 | 
						|
	if (!hm)
 | 
						|
	  {
 | 
						|
	    /* ERROR_FILE_INVALID indicates very likely an empty file. */
 | 
						|
	    if (GetLastError () == ERROR_FILE_INVALID)
 | 
						|
	      {
 | 
						|
		debug_printf ("zero length file, treat as script.");
 | 
						|
		goto just_shell;
 | 
						|
	      }
 | 
						|
	    goto err;
 | 
						|
	  }
 | 
						|
	/* Try to map the first 64K of the image.  That's enough for the local
 | 
						|
	   tests, and it's enough for hook_or_detect_cygwin to compute the IAT
 | 
						|
	   address. */
 | 
						|
	buf = (char *) MapViewOfFile (hm, FILE_MAP_READ, 0, 0, size.LowPart);
 | 
						|
	if (!buf)
 | 
						|
	  {
 | 
						|
	    CloseHandle (hm);
 | 
						|
	    goto err;
 | 
						|
	  }
 | 
						|
 | 
						|
	{
 | 
						|
	  __try
 | 
						|
	    {
 | 
						|
	      if (buf[0] == 'M' && buf[1] == 'Z')
 | 
						|
		{
 | 
						|
		  WORD subsys;
 | 
						|
		  unsigned off = (unsigned char) buf[0x18] | (((unsigned char) buf[0x19]) << 8);
 | 
						|
		  win16_exe = off < sizeof (IMAGE_DOS_HEADER);
 | 
						|
		  if (!win16_exe)
 | 
						|
		    real_path.set_cygexec (hook_or_detect_cygwin (buf, NULL,
 | 
						|
								  subsys, hm));
 | 
						|
		  else
 | 
						|
		    real_path.set_cygexec (false);
 | 
						|
		  UnmapViewOfFile (buf);
 | 
						|
		  CloseHandle (hm);
 | 
						|
		  break;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  __except (NO_ERROR)
 | 
						|
	    {
 | 
						|
	      UnmapViewOfFile (buf);
 | 
						|
	      CloseHandle (hm);
 | 
						|
	      real_path.set_cygexec (false);
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	  __endtry
 | 
						|
	}
 | 
						|
	CloseHandle (hm);
 | 
						|
 | 
						|
	debug_printf ("%s is possibly a script", real_path.get_win32 ());
 | 
						|
 | 
						|
	ptr = buf;
 | 
						|
	if (*ptr++ == '#' && *ptr++ == '!')
 | 
						|
	  {
 | 
						|
	    ptr += strspn (ptr, " \t");
 | 
						|
	    size_t len = strcspn (ptr, "\r\n");
 | 
						|
	    while (ptr[len - 1] == ' ' || ptr[len - 1] == '\t')
 | 
						|
	      len--;
 | 
						|
	    if (len)
 | 
						|
	      {
 | 
						|
		char *namebuf = (char *) alloca (len + 1);
 | 
						|
		memcpy (namebuf, ptr, len);
 | 
						|
		namebuf[len] = '\0';
 | 
						|
		for (ptr = pgm = namebuf; *ptr; ptr++)
 | 
						|
		  if (!arg1 && (*ptr == ' ' || *ptr == '\t'))
 | 
						|
		    {
 | 
						|
		      /* Null terminate the initial command and step over any
 | 
						|
			 additional white space.  If we've hit the end of the
 | 
						|
			 line, exit the loop.  Otherwise, we've found the first
 | 
						|
			 argument. Position the current pointer on the last known
 | 
						|
			 white space. */
 | 
						|
		      *ptr = '\0';
 | 
						|
		      char *newptr = ptr + 1;
 | 
						|
		      newptr += strspn (newptr, " \t");
 | 
						|
		      if (!*newptr)
 | 
						|
			break;
 | 
						|
		      arg1 = newptr;
 | 
						|
		      ptr = newptr - 1;
 | 
						|
		    }
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	UnmapViewOfFile (buf);
 | 
						|
  just_shell:
 | 
						|
	if (!pgm)
 | 
						|
	  {
 | 
						|
	    if (!p_type_exec)
 | 
						|
	      {
 | 
						|
		/* Not called from exec[lv]p.  Don't try to treat as script. */
 | 
						|
		debug_printf ("%s is not a valid executable",
 | 
						|
			      real_path.get_win32 ());
 | 
						|
		set_errno (ENOEXEC);
 | 
						|
		return -1;
 | 
						|
	      }
 | 
						|
	    if (ascii_strcasematch (ext, ".com"))
 | 
						|
	      break;
 | 
						|
	    pgm = (char *) "/bin/sh";
 | 
						|
	    arg1 = NULL;
 | 
						|
	  }
 | 
						|
 | 
						|
	/* Check if script is executable.  Otherwise we start non-executable
 | 
						|
	   scripts successfully, which is incorrect behaviour. */
 | 
						|
	if (real_path.has_acls ()
 | 
						|
	    && check_file_access (real_path, X_OK, true) < 0)
 | 
						|
	  return -1;	/* errno is already set. */
 | 
						|
 | 
						|
	/* Replace argv[0] with the full path to the script if this is the
 | 
						|
	   first time through the loop. */
 | 
						|
	replace0_maybe (prog_arg);
 | 
						|
 | 
						|
	/* pointers:
 | 
						|
	 * pgm	interpreter name
 | 
						|
	 * arg1	optional string
 | 
						|
	 */
 | 
						|
	if (arg1)
 | 
						|
	  unshift (arg1);
 | 
						|
 | 
						|
	find_exec (pgm, real_path, "PATH", FE_NADA, &ext);
 | 
						|
	unshift (real_path.normalized_path);
 | 
						|
      }
 | 
						|
  if (real_path.iscygexec ())
 | 
						|
    dup_all ();
 | 
						|
  return 0;
 | 
						|
 | 
						|
err:
 | 
						|
  __seterrno ();
 | 
						|
  return -1;
 | 
						|
}
 |