summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/spawn.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/spawn.cc')
-rw-r--r--winsup/cygwin/spawn.cc1257
1 files changed, 0 insertions, 1257 deletions
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
deleted file mode 100644
index 586bd9781..000000000
--- a/winsup/cygwin/spawn.cc
+++ /dev/null
@@ -1,1257 +0,0 @@
-/* spawn.cc
-
- Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 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 <wingdi.h>
-#include <winuser.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 suffix_info NO_COPY exe_suffixes[] =
-{
- suffix_info ("", 1),
- suffix_info (".exe", 1),
- suffix_info (".com"),
- suffix_info (NULL)
-};
-
-#if 0
-/* CV, 2009-11-05: Used to be used when searching for DLLs in calls to
- dlopen(). However, dlopen() on other platforms never adds a suffix by
- its own. Therefore we use stat_suffixes now, which only adds a .exe
- suffix for symmetry. */
-static suffix_info dll_suffixes[] =
-{
- suffix_info (".dll"),
- suffix_info ("", 1),
- suffix_info (".exe", 1),
- suffix_info (NULL)
-};
-#endif
-
-/* 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,
- (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 win32-translated name is placed in 'buf'.
- Any found suffix is returned in known_suffix.
-
- If the file is not found and !null_if_not_found 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 * __stdcall
-find_exec (const char *name, path_conv& buf, const char *mywinenv,
- unsigned opt, const char **known_suffix)
-{
- const char *suffix = "";
- debug_printf ("find_exec (%s)", name);
- const char *retval;
- tmp_pathbuf tp;
- char *tmp = tp.c_get ();
- const char *posix = (opt & FE_NATIVE) ? NULL : name;
- bool has_slash = !!strpbrk (name, "/\\");
- int err;
-
- /* Check to see if file can be opened as is first.
- Win32 systems always check . first, but PATH may not be set up to
- do this. */
- if ((has_slash || opt & FE_CWD)
- && (suffix = perhaps_suffix (name, buf, err, opt)) != NULL)
- {
- if (posix && !has_slash)
- {
- tmp[0] = '.';
- tmp[1] = '/';
- strcpy (tmp + 2, name);
- posix = tmp;
- }
- retval = buf.get_win32 ();
- goto out;
- }
-
- win_env *winpath;
- const char *path;
- const char *posix_path;
-
- posix = (opt & FE_NATIVE) ? NULL : tmp;
-
- if (strchr (mywinenv, '/'))
- {
- /* it's not really an environment variable at all */
- int n = cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, mywinenv, NULL, 0);
- char *s = (char *) alloca (n);
- if (cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, mywinenv, s, n))
- goto errout;
- path = s;
- posix_path = mywinenv - 1;
- }
- else if (has_slash || strchr (name, '\\') || isdrive (name)
- || !(winpath = getwinenv (mywinenv))
- || !(path = winpath->get_native ()) || *path == '\0')
- /* Return the error condition if this is an absolute path or if there
- is no PATH to search. */
- goto errout;
- else
- posix_path = winpath->get_posix () - 1;
-
- debug_printf ("%s%s", mywinenv, path);
- /* Iterate over the specified path, looking for the file with and without
- executable extensions. */
- do
- {
- posix_path++;
- char *eotmp = strccpy (tmp, &path, ';');
- /* An empty path or '.' means the current directory, but we've
- already tried that. */
- if (opt & FE_CWD && (tmp[0] == '\0' || (tmp[0] == '.' && tmp[1] == '\0')))
- continue;
-
- *eotmp++ = '\\';
- strcpy (eotmp, name);
-
- debug_printf ("trying %s", tmp);
-
- int err1;
-
- if ((suffix = perhaps_suffix (tmp, buf, err1, opt)) != NULL)
- {
- if (buf.has_acls () && check_file_access (buf, X_OK, true))
- continue;
-
- if (posix == tmp)
- {
- eotmp = strccpy (tmp, &posix_path, ':');
- if (eotmp == tmp)
- *eotmp++ = '.';
- *eotmp++ = '/';
- strcpy (eotmp, name);
- }
- retval = buf.get_win32 ();
- goto out;
- }
- }
- while (*path && *++path && (posix_path = strchr (posix_path, ':')));
-
- errout:
- posix = NULL;
- /* Couldn't find anything in the given path.
- Take the appropriate action based on null_if_not_found. */
- if (opt & FE_NNF)
- retval = NULL;
- else if (!(opt & FE_NATIVE))
- retval = name;
- else
- {
- buf.check (name);
- retval = buf.get_win32 ();
- }
-
- out:
- if (posix)
- retval = buf.set_path (posix);
- 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]));
-}
-
-struct pthread_cleanup
-{
- _sig_func_ptr oldint;
- _sig_func_ptr oldquit;
- sigset_t oldmask;
- pthread_cleanup (): oldint (NULL), oldquit (NULL), oldmask ((sigset_t) -1) {}
-};
-
-static void
-do_cleanup (void *args)
-{
-# define cleanup ((pthread_cleanup *) args)
- if (cleanup->oldmask != (sigset_t) -1)
- {
- signal (SIGINT, cleanup->oldint);
- signal (SIGQUIT, cleanup->oldquit);
- sigprocmask (SIG_SETMASK, &(cleanup->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;
- }
-
- /* FIXME: There is a small race here and FIXME: not thread safe! */
- pthread_cleanup cleanup;
- if (mode == _P_SYSTEM)
- {
- sigset_t child_block;
- cleanup.oldint = signal (SIGINT, SIG_IGN);
- cleanup.oldquit = signal (SIGQUIT, SIG_IGN);
- sigemptyset (&child_block);
- sigaddset (&child_block, SIGCHLD);
- sigprocmask (SIG_BLOCK, &child_block, &cleanup.oldmask);
- }
- pthread_cleanup_push (do_cleanup, (void *) &cleanup);
- av newargv;
- linebuf one_line;
- PWCHAR envblock = NULL;
- path_conv real_path;
- bool reset_sendsig = false;
-
- tmp_pathbuf tp;
- PWCHAR runpath = tp.w_get ();
- int c_flags;
- bool wascygexec;
-
- bool null_app_name = false;
- STARTUPINFOW si = {};
- int looped = 0;
-
- myfault efault;
- if (efault.faulted ())
- {
- if (get_errno () == ENOMEM)
- set_errno (E2BIG);
- else
- set_errno (EFAULT);
- res = -1;
- goto out;
- }
-
- 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 */;
-
- newargv.set (ac, argv);
-
- int err;
- const char *ext;
- if ((ext = perhaps_suffix (prog_arg, real_path, err, FE_NADA)) == NULL)
- {
- set_errno (err);
- res = -1;
- goto out;
- }
-
-
- wascygexec = real_path.iscygexec ();
- res = newargv.fixup (prog_arg, real_path, ext, p_type_exec);
-
- if (res)
- goto out;
-
- 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;
- goto out;
- }
-
- 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);
- one_line.add ("\"");
- if (!real_path.error)
- one_line.add (real_path.get_win32 ());
- else
- one_line.add (argv[0]);
- one_line.add ("\"");
- one_line.add (" ");
- one_line.add (argv[1]);
- one_line.add (" ");
- one_line.add (argv[2]);
- real_path.set_path (argv[0]);
- null_app_name = true;
- }
- else
- {
- if (wascygexec)
- newargv.dup_all ();
- else if (!one_line.fromargv (newargv, real_path.get_win32 (),
- real_path.iscygexec ()))
- {
- res = -1;
- goto out;
- }
-
-
- newargv.all_calloced ();
- moreinfo->argc = newargv.argc;
- moreinfo->argv = newargv;
-
- 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);
- }
- WCHAR wone_line[one_line.ix + 1];
- if (one_line.ix)
- sys_mbstowcs (wone_line, one_line.ix + 1, one_line.buf);
- else
- wone_line[0] = L'\0';
-
- 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.
-
- (*) 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;
-
- /* Calling QueryInformationJobObject costs time. Starting with
- Windows XP there's a function IsProcessInJob, which fetches the
- information whether or not we're part of a job 20 times faster than
- the call to QueryInformationJobObject. But we're still
- supporting Windows 2000, so we can't just link to that function.
- On the other hand, loading the function pointer at runtime is a
- time comsuming operation, too. So, what we do here is to emulate
- the IsProcessInJob function when called for the own process and with
- a NULL job handle. In this case it just returns the value of the
- lowest bit from PEB->EnvironmentUpdateCount (observed with WinDbg).
- The name of this PEB member is the same in all (inofficial)
- documentations of the PEB. Apparently it's a bit misleading.
- As a result, we only call QueryInformationJobObject if we're on
- Vista or later *and* if the PEB indicates we're running in a job.
- Tested on Vista/32, Vista/64, W7/32, W7/64, W8/64. */
- if ((NtCurrentTeb ()->Peb->EnvironmentUpdateCount & 1) != 0
- && 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;
- goto out;
- }
- }
- syscall_printf ("null_app_name %d (%W, %.9500W)", null_app_name,
- runpath, wone_line);
-
- cygbench ("spawn-worker");
-
- if (!real_path.iscygexec())
- ::cygheap->fdtab.set_file_pointers_for_exec ();
-
- moreinfo->envp = build_env (envp, envblock, moreinfo->envc,
- real_path.iscygexec ());
- if (!moreinfo->envp || !envblock)
- {
- set_errno (E2BIG);
- res = -1;
- goto out;
- }
- 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);
- }
- parent_winpid = GetCurrentProcessId ();
-
- /* 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. */
-loop:
- ::cygheap->user.deimpersonate ();
-
- if (!real_path.iscygexec () && mode == _P_OVERLAY)
- myself->process_state |= PID_NOTCYGWIN;
-
- 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 */
- wone_line, /* 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 */
- wone_line, /* 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);
- }
- }
-
- /* Restore impersonation. In case of _P_OVERLAY this isn't
- allowed since it would overwrite child data. */
- if (mode != _P_OVERLAY || !rc)
- ::cygheap->user.reimpersonate ();
-
- /* 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 */
- res = -1;
- goto out;
- }
-
- /* 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, one_line.buf);
-
- /* Name the handle similarly to proc_subproc. */
- ProtectHandle1 (pi.hProcess, childhProc);
-
- pid_t pid;
- 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);
- pid = myself->pid;
- 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;
- goto out;
- }
- 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;
- goto out;
- }
- pid = child->pid;
- }
-
- /* 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:
- 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;
- }
-
-out:
- this->cleanup ();
- if (envblock)
- free (envblock);
- pthread_cleanup_pop (1);
- 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, %x)", 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::fixup (const char *prog_arg, path_conv& real_path, const char *ext,
- bool p_type_exec)
-{
- const char *p;
- bool exeext = ascii_strcasematch (ext, ".exe");
- if ((exeext && real_path.iscygexec ()) || ascii_strcasematch (ext, ".bat"))
- return 0;
- if (!*ext && ((p = ext - 4) > real_path.get_win32 ())
- && (ascii_strcasematch (p, ".bat") || ascii_strcasematch (p, ".cmd")
- || ascii_strcasematch (p, ".btm")))
- return 0;
- 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 > 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;
- }
-
- {
- myfault efault;
- if (efault.faulted ())
- {
- UnmapViewOfFile (buf);
- CloseHandle (hm);
- real_path.set_cygexec (false);
- break;
- }
- 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;
- }
- }
- 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");
- 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);
-
- /* FIXME: This should not be using FE_NATIVE. It should be putting
- the posix path on the argv list. */
- find_exec (pgm, real_path, "PATH=", FE_NATIVE, &ext);
- unshift (real_path.get_win32 (), 1);
- }
- return 0;
-
-err:
- __seterrno ();
- return -1;
-}