Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/spawn.cc')
-rw-r--r--winsup/cygwin/spawn.cc980
1 files changed, 980 insertions, 0 deletions
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
new file mode 100644
index 000000000..510031911
--- /dev/null
+++ b/winsup/cygwin/spawn.cc
@@ -0,0 +1,980 @@
+/* spawn.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
+
+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 <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <process.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <limits.h>
+#include "winsup.h"
+#include <ctype.h>
+#include "paths.h"
+
+extern BOOL allow_ntsec;
+
+#define LINE_BUF_CHUNK (MAX_PATH * 2)
+
+suffix_info std_suffixes[] =
+{
+ suffix_info (".exe", 1), suffix_info ("", 1),
+ suffix_info (".com"), suffix_info (".cmd"),
+ suffix_info (".bat"), suffix_info (".dll"),
+ 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, char *buf)
+{
+ char *ext;
+
+ debug_printf ("prog '%s'", prog);
+ path_conv temp (prog, SYMLINK_FOLLOW, 1, std_suffixes);
+ strcpy (buf, temp.get_win32 ());
+
+ if (temp.file_attributes () & FILE_ATTRIBUTE_DIRECTORY)
+ ext = NULL;
+ else if (temp.known_suffix)
+ ext = buf + (temp.known_suffix - temp.get_win32 ());
+ else
+ ext = strchr (buf, '\0');
+
+ debug_printf ("buf %s, suffix found '%s'", buf, 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, char *buf, const char *mywinenv,
+ int null_if_notfound, const char **known_suffix)
+{
+ const char *suffix = "";
+ debug_printf ("find_exec (%s)", name);
+ char *retval = buf;
+
+ /* 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 ((suffix = perhaps_suffix (name, buf)) != NULL)
+ goto out;
+
+ win_env *winpath;
+ const char *path;
+ char tmp[MAX_PATH];
+
+ /* Return the error condition if this is an absolute path or if there
+ is no PATH to search. */
+ if (strchr (name, '/') || strchr (name, '\\') ||
+ isalpha (name[0]) && name[1] == ':' ||
+ !(winpath = getwinenv (mywinenv)) ||
+ !(path = winpath->get_native ()) ||
+ *path == '\0')
+ goto errout;
+
+ debug_printf ("%s%s", mywinenv, path);
+
+ /* Iterate over the specified path, looking for the file with and
+ without executable extensions. */
+ do
+ {
+ char *eotmp = strccpy (tmp, &path, ';');
+ /* An empty path or '.' means the current directory, but we've
+ already tried that. */
+ if (tmp[0] == '\0' || (tmp[0] == '.' && tmp[1] == '\0'))
+ continue;
+
+ *eotmp++ = '\\';
+ strcpy (eotmp, name);
+
+ debug_printf ("trying %s", tmp);
+
+ if ((suffix = perhaps_suffix (tmp, buf)) != NULL)
+ goto out;
+ }
+ while (*path && *++path);
+
+errout:
+ /* Couldn't find anything in the given path.
+ Take the appropriate action based on null_if_not_found. */
+ if (null_if_notfound)
+ retval = NULL;
+ else
+ strcpy (buf, path_conv (name).get_win32 ());
+
+out:
+ debug_printf ("%s = find_exec (%s)", buf, name);
+ if (known_suffix)
+ *known_suffix = suffix ?: strchr (buf, '\0');
+ return retval;
+}
+
+/* Utility for spawn_guts. */
+
+static HANDLE
+handle (int n, int direction)
+{
+ fhandler_base *fh = dtable[n];
+
+ if (!fh)
+ return INVALID_HANDLE_VALUE;
+ if (fh->get_close_on_exec ())
+ return INVALID_HANDLE_VALUE;
+ if (direction == 0)
+ return fh->get_handle ();
+ return fh->get_output_handle ();
+}
+
+/* Cover function for CreateProcess.
+
+ This function is used by both the routines that search $PATH and those
+ that do not. This should work out ok as according to the documentation,
+ CreateProcess only searches $PATH if PROG has no directory elements.
+
+ Spawning doesn't fit well with Posix's fork/exec (one can argue the merits
+ of either but that's beside the point). If we're exec'ing we want to
+ record the child pid for fork. If we're spawn'ing we don't want to do
+ this. It is up to the caller to handle both cases.
+
+ The result is the process id. The handle of the created process is
+ stored in H.
+*/
+
+HANDLE NO_COPY hExeced = NULL;
+DWORD NO_COPY exec_exit = 0;
+
+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 && strcasecmp (argv0 + n, what) == 0 &&
+ (n == 0 || isdirsep (argv0[n - 1]));
+}
+
+class linebuf
+{
+public:
+ size_t ix;
+ char *buf;
+ size_t alloced;
+ linebuf () : ix (0), buf (NULL), alloced (0)
+ {
+ }
+ ~linebuf () {/* if (buf) free (buf);*/}
+ void add (const char *what, int len);
+ void add (const char *what) {add (what, strlen (what));}
+ void prepend (const char *what, int len);
+};
+
+void
+linebuf::add (const char *what, int len)
+{
+ size_t newix;
+ if ((newix = ix + len) >= alloced || !buf)
+ {
+ alloced += LINE_BUF_CHUNK + newix;
+ buf = (char *) realloc (buf, alloced + 1);
+ }
+ memcpy (buf + ix, what, len);
+ ix = newix;
+ buf[ix] = '\0';
+}
+
+void
+linebuf::prepend (const char *what, int len)
+{
+ int buflen;
+ size_t newix;
+ if ((newix = ix + len) >= alloced)
+ {
+ alloced += LINE_BUF_CHUNK + newix;
+ buf = (char *) realloc (buf, alloced + 1);
+ buf[ix] = '\0';
+ }
+ if ((buflen = strlen (buf)))
+ memmove (buf + len, buf, buflen + 1);
+ else
+ buf[newix] = '\0';
+ memcpy (buf, what, len);
+ ix = newix;
+}
+
+int __stdcall
+spawn_guts (HANDLE hToken, const char * prog_arg, const char *const *argv,
+ const char *const envp[], pinfo *child, int mode)
+{
+ int i;
+ BOOL rc;
+ int argc;
+
+ hExeced = NULL;
+
+ MALLOC_CHECK;
+
+ if (prog_arg == NULL)
+ {
+ syscall_printf ("prog_arg is NULL");
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ syscall_printf ("spawn_guts (%.132s)", prog_arg);
+
+ if (argv == NULL)
+ {
+ syscall_printf ("argv is NULL");
+ set_errno(EINVAL);
+ return (-1);
+ }
+
+ /* CreateProcess takes one long string that is the command line (sigh).
+ We need to quote any argument that has whitespace or embedded "'s. */
+
+ for (argc = 0; argv[argc]; argc++)
+ /* nothing */;
+
+ char *real_path;
+ char real_path_buf[MAX_PATH];
+
+ linebuf one_line;
+
+ if (argc == 3 && argv[1][0] == '/' && argv[1][1] == 'c' &&
+ (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe")))
+ {
+ one_line.add (argv[0]);
+ one_line.add (" ");
+ one_line.add (argv[1]);
+ one_line.add (" ");
+ real_path = NULL;
+ one_line.add (argv[2]);
+ strcpy (real_path_buf, argv[0]);
+ goto skip_arg_parsing;
+ }
+
+ MALLOC_CHECK;
+
+ real_path = real_path_buf;
+
+ const char *saved_prog_arg;
+ const char *newargv0, **firstarg;
+ const char *ext;
+
+ if ((ext = perhaps_suffix (prog_arg, real_path)) == NULL)
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ MALLOC_CHECK;
+ saved_prog_arg = prog_arg;
+ newargv0 = argv[0];
+ firstarg = &newargv0;
+
+ /* If the file name ends in either .exe, .com, .bat, or .cmd we assume
+ that it is NOT a script file */
+ while (*ext == '\0')
+ {
+ HANDLE hnd = CreateFileA (real_path,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sec_none_nih,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+ if (hnd == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ DWORD done;
+
+ char buf[2 * MAX_PATH + 1];
+ buf[0] = buf[1] = buf[2] = buf[sizeof(buf) - 1] = '\0';
+ if (! ReadFile (hnd, buf, sizeof (buf) - 1, &done, 0))
+ {
+ CloseHandle (hnd);
+ __seterrno ();
+ return -1;
+ }
+
+ CloseHandle (hnd);
+
+ if (buf[0] == 'M' && buf[1] == 'Z')
+ break;
+
+ debug_printf ("%s is a script", prog_arg);
+
+ char *ptr, *pgm, *arg1;
+
+ if (buf[0] != '#' || buf[1] != '!')
+ {
+ strcpy (buf, "sh"); /* shell script without magic */
+ pgm = buf;
+ ptr = buf + 2;
+ arg1 = NULL;
+ }
+ else
+ {
+ pgm = buf + 2;
+ pgm += strspn (pgm, " \t");
+ for (ptr = pgm, arg1 = NULL;
+ *ptr && *ptr != '\r' && *ptr != '\n';
+ 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, position
+ 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 || *newptr == '\r' || *newptr == '\n')
+ break;
+ arg1 = newptr;
+ ptr = newptr - 1;
+ }
+
+
+ *ptr = '\0';
+ }
+
+ char buf2[MAX_PATH + 1];
+
+ /* pointers:
+ * pgm interpreter name
+ * arg1 optional string
+ * ptr end of string
+ */
+
+ if (!arg1)
+ one_line.prepend (" ", 1);
+ else
+ {
+ one_line.prepend ("\" ", 2);
+ one_line.prepend (arg1, strlen (arg1));
+ one_line.prepend (" \"", 2);
+ }
+
+ find_exec (pgm, real_path, "PATH=", 0, &ext);
+ cygwin_conv_to_posix_path (real_path, buf2);
+ one_line.prepend (buf2, strlen (buf2));
+
+ /* If script had absolute path, add it to script name now!
+ * This is necessary if script has been found via PATH.
+ * For example, /usr/local/bin/tkman started as "tkman":
+ * #!/usr/local/bin/wish -f
+ * ...
+ * We should run /usr/local/bin/wish -f /usr/local/bin/tkman,
+ * but not /usr/local/bin/wish -f tkman!
+ * We don't modify anything, if script has qulified path.
+ */
+ if (firstarg)
+ *firstarg = saved_prog_arg;
+
+ debug_printf ("prog_arg '%s', copy '%s'", prog_arg, one_line.buf);
+ firstarg = NULL;
+ }
+
+ for (; *argv; argv++)
+ {
+ char *p = NULL;
+ const char *a = newargv0 ?: *argv;
+
+ MALLOC_CHECK;
+
+ newargv0 = NULL;
+ int len = strlen (a);
+ if (len != 0 && !(p = strpbrk (a, " \t\n\r\"")))
+ one_line.add (a, len);
+ else
+ {
+ one_line.add ("\"", 1);
+ for (; p; a = p, p = strchr (p, '"'))
+ {
+ one_line.add (a, ++p - a);
+ if (p[-1] == '"')
+ one_line.add ("\"", 1);
+ }
+ if (*a)
+ one_line.add (a);
+ one_line.add ("\"", 1);
+ }
+ MALLOC_CHECK;
+ one_line.add (" ", 1);
+ MALLOC_CHECK;
+ }
+
+ MALLOC_CHECK;
+ if (one_line.ix)
+ one_line.buf[one_line.ix - 1] = '\0';
+ else
+ one_line.add ("", 1);
+ MALLOC_CHECK;
+
+skip_arg_parsing:
+ PROCESS_INFORMATION pi = {0};
+
+ STARTUPINFO si = {0};
+ si.lpReserved = NULL;
+ si.lpDesktop = NULL;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = handle (0, 0); /* Get input handle */
+ si.hStdOutput = handle (1, 1); /* Get output handle */
+ si.hStdError = handle (2, 1); /* Get output handle */
+ si.cb = sizeof (si);
+
+ /* Pass fd table to a child */
+
+ MALLOC_CHECK;
+ int len = dtable.linearize_fd_array (0, 0);
+ MALLOC_CHECK;
+ if (len == -1)
+ {
+ system_printf ("FATAL error in linearize_fd_array");
+ return -1;
+ }
+ int titlelen = 1 + (old_title && mode == _P_OVERLAY ? strlen (old_title) : 0);
+ si.cbReserved2 = len + titlelen + sizeof(child_info);
+ si.lpReserved2 = (LPBYTE) alloca (si.cbReserved2);
+
+# define ciresrv ((child_info *)si.lpReserved2)
+ HANDLE spr = NULL;
+ DWORD chtype;
+ if (mode != _P_OVERLAY)
+ chtype = PROC_SPAWN;
+ else
+ {
+ spr = CreateEvent(&sec_all, TRUE, FALSE, NULL);
+ ProtectHandle (spr);
+ chtype = PROC_EXEC;
+ }
+
+ init_child_info (chtype, ciresrv, child->pid, spr);
+
+ LPBYTE resrv = si.lpReserved2 + sizeof *ciresrv;
+# undef ciresrv
+
+ if (dtable.linearize_fd_array (resrv, len) < 0)
+ {
+ system_printf ("FATAL error in second linearize_fd_array");
+ return -1;
+ }
+
+ if (titlelen > 1)
+ strcpy ((char *) resrv + len, old_title);
+ else
+ resrv[len] = '\0';
+
+ /* We print the translated program and arguments here so the user can see
+ what was done to it. */
+ syscall_printf ("spawn_guts (%s, %.132s)", real_path, one_line.buf);
+
+ int flags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED |
+ GetPriorityClass (hMainProc);
+
+ if (mode == _P_DETACH || !set_console_state_for_spawn ())
+ flags |= DETACHED_PROCESS;
+
+ MALLOC_CHECK;
+ /* Build windows style environment list */
+ char *envblock = winenv (envp);
+ MALLOC_CHECK;
+
+ /* Preallocated buffer for `sec_user' call */
+ char sa_buf[1024];
+
+ if (hToken)
+ {
+ /* allow the child to interact with our window station/desktop */
+ HANDLE hwst, hdsk;
+ SECURITY_INFORMATION dsi = DACL_SECURITY_INFORMATION;
+ DWORD n;
+ char wstname[1024];
+ char dskname[1024];
+
+ hwst = GetProcessWindowStation();
+ SetUserObjectSecurity(hwst, &dsi, get_null_sd ());
+ GetUserObjectInformation(hwst, UOI_NAME, wstname, 1024, &n);
+ hdsk = GetThreadDesktop(GetCurrentThreadId());
+ SetUserObjectSecurity(hdsk, &dsi, get_null_sd ());
+ GetUserObjectInformation(hdsk, UOI_NAME, dskname, 1024, &n);
+ strcat (wstname, "\\");
+ strcat (wstname, dskname);
+ si.lpDesktop = wstname;
+ /* force the new process to reread /etc/passwd and /etc/group */
+ child->uid = USHRT_MAX;
+ child->username[0] = '\0';
+
+ char tu[1024];
+ PSID sid = NULL;
+ DWORD ret_len;
+ if (GetTokenInformation (hToken, TokenUser,
+ (LPVOID) &tu, sizeof tu,
+ &ret_len))
+ sid = ((TOKEN_USER *) &tu)->User.Sid;
+ else
+ system_printf ("GetTokenInformation: %E");
+
+ rc = CreateProcessAsUser (hToken,
+ real_path, /* image name - with full path */
+ one_line.buf, /* what was passed to exec */
+ /* process security attrs */
+ allow_ntsec && sid ? sec_user (sa_buf, sid)
+ : &sec_all_nih,
+ /* thread security attrs */
+ allow_ntsec && sid ? sec_user (sa_buf, sid)
+ : &sec_all_nih,
+ TRUE, /* inherit handles from parent */
+ flags,
+ envblock,/* environment */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+ }
+ else
+ rc = CreateProcessA (real_path, /* image name - with full path */
+ one_line.buf, /* what was passed to exec */
+ /* process security attrs */
+ allow_ntsec ? sec_user (sa_buf) : &sec_all_nih,
+ /* thread security attrs */
+ allow_ntsec ? sec_user (sa_buf) : &sec_all_nih,
+ TRUE, /* inherit handles from parent */
+ flags,
+ envblock,/* environment */
+ 0, /* use current drive/directory */
+ &si,
+ &pi);
+
+ MALLOC_CHECK;
+ free (envblock);
+ MALLOC_CHECK;
+
+ /* 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 ();
+
+ MALLOC_CHECK;
+ /* Name the handle similarly to proc_subproc. */
+ ProtectHandle1 (pi.hProcess, childhProc);
+ ProtectHandle (pi.hThread);
+ MALLOC_CHECK;
+
+ /* We print the original program name here so the user can see that too. */
+ syscall_printf ("%d = spawn_guts (%s, %.132s)",
+ rc ? pi.dwProcessId : (unsigned int) -1,
+ prog_arg, one_line.buf);
+
+ if (!rc)
+ {
+ if (spr)
+ ForceCloseHandle (spr);
+ return -1;
+ }
+
+ /* Set up child's signal handlers */
+ for (i = 0; i < NSIG; i++)
+ {
+ child->getsig(i).sa_mask = 0;
+ if (myself->getsig(i).sa_handler != SIG_IGN || (mode != _P_OVERLAY))
+ child->getsig(i).sa_handler = SIG_DFL;
+ }
+
+ if (mode == _P_OVERLAY)
+ {
+ close_all_files ();
+ strcpy (child->progname, real_path_buf);
+ proc_terminate ();
+ hExeced = pi.hProcess;
+ }
+ else
+ {
+ child->dwProcessId = pi.dwProcessId;
+ child->hProcess = pi.hProcess;
+ child->process_state |= PID_INITIALIZING;
+ proc_register (child);
+ }
+
+ sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
+ /* Start the child running */
+ ResumeThread (pi.hThread);
+ ForceCloseHandle (pi.hThread);
+
+ if (hToken)
+ CloseHandle (hToken);
+
+ DWORD res;
+
+ if (mode == _P_OVERLAY)
+ {
+ BOOL exited;
+
+ HANDLE waitbuf[3] = {pi.hProcess, signal_arrived, spr};
+ int nwait = 3;
+
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
+ res = 0;
+ DWORD timeout = INFINITE;
+ exec_exit = 1;
+ exited = FALSE;
+ MALLOC_CHECK;
+ waitfor:
+ switch (WaitForMultipleObjects (nwait, waitbuf, FALSE, timeout))
+ {
+ case WAIT_TIMEOUT:
+ syscall_printf ("WFMO timed out after signal");
+ if (WaitForSingleObject (pi.hProcess, 0) != WAIT_OBJECT_0)
+ {
+ sigproc_printf ("subprocess still alive after signal");
+ res = exec_exit;
+ }
+ else
+ {
+ sigproc_printf ("subprocess exited after signal");
+ case WAIT_OBJECT_0:
+ sigproc_printf ("subprocess exited");
+ if (!GetExitCodeProcess (pi.hProcess, &res))
+ res = exec_exit;
+ exited = TRUE;
+ }
+ if (nwait > 2)
+ if (WaitForSingleObject (spr, 1) == WAIT_OBJECT_0)
+ res |= EXIT_REPARENTING;
+ else if (!(res & EXIT_REPARENTING))
+ {
+ MALLOC_CHECK;
+ close_all_files ();
+ MALLOC_CHECK;
+ }
+ break;
+ case WAIT_OBJECT_0 + 1:
+ sigproc_printf ("signal arrived");
+ timeout = 10;
+ goto waitfor;
+ case WAIT_OBJECT_0 + 2:
+ res = EXIT_REPARENTING;
+ MALLOC_CHECK;
+ ForceCloseHandle (spr);
+ MALLOC_CHECK;
+ if (!parent_alive)
+ {
+ nwait = 1;
+ sigproc_terminate ();
+ goto waitfor;
+ }
+ break;
+ case WAIT_FAILED:
+ DWORD r;
+ system_printf ("wait failed: nwait %d, pid %d, winpid %d, %E",
+ nwait, myself->pid, myself->dwProcessId);
+ system_printf ("waitbuf[0] %p %d", waitbuf[0],
+ GetHandleInformation (waitbuf[0], &r));
+ system_printf ("waitbuf[1] %p = %d", waitbuf[1],
+ GetHandleInformation (waitbuf[1], &r));
+ set_errno (ECHILD);
+ return -1;
+ }
+
+ if (nwait > 2)
+ ForceCloseHandle (spr);
+
+ sigproc_printf ("res = %x", res);
+
+ if (res & EXIT_REPARENTING)
+ {
+ /* Try to reparent child process.
+ * Make handles to child available to parent process and exit with
+ * EXIT_REPARENTING status. Wait() syscall in parent will then wait
+ * for newly created child.
+ */
+ if (my_parent_is_alive ())
+ {
+ pinfo *parent = procinfo (myself->ppid);
+ sigproc_printf ("parent = %p", parent);
+ HANDLE hP = OpenProcess (PROCESS_ALL_ACCESS, FALSE,
+ parent->dwProcessId);
+ sigproc_printf ("parent's handle = %d", hP);
+ if (hP == NULL && GetLastError () == ERROR_INVALID_PARAMETER)
+ res = 1;
+ else if (hP)
+ {
+ ProtectHandle (hP);
+ res = DuplicateHandle (hMainProc, pi.hProcess, hP,
+ &myself->hProcess, 0, FALSE,
+ DUPLICATE_SAME_ACCESS);
+ sigproc_printf ("Dup hP %d", res);
+ ForceCloseHandle (hP);
+ }
+ if (!res)
+ {
+ system_printf ("Reparent failed, parent handle %p, %E", hP);
+ system_printf ("my dwProcessId %d, myself->dwProcessId %d",
+ GetCurrentProcessId(), myself->dwProcessId);
+ system_printf ("myself->process_state %x",
+ myself->process_state);
+ system_printf ("myself->hProcess %x", myself->hProcess);
+ }
+ }
+ res = EXIT_REPARENTING;
+ ForceCloseHandle1 (hExeced, childhProc);
+ hExeced = INVALID_HANDLE_VALUE;
+ }
+ else if (exited)
+ {
+ ForceCloseHandle1 (hExeced, childhProc);
+ hExeced = INVALID_HANDLE_VALUE; // stop do_exit from attempting to terminate child
+ }
+
+ MALLOC_CHECK;
+ do_exit (res | EXIT_NOCLOSEALL);
+ }
+
+ if (mode == _P_WAIT)
+ {
+ waitpid (child->pid, (int *) &res, 0);
+ }
+ else if (mode == _P_DETACH)
+ {
+ /* Lose all memory of this child. */
+ res = 0;
+ }
+ else if ((mode == _P_NOWAIT) || (mode == _P_NOWAITO))
+ {
+ res = child->pid;
+ }
+
+ 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 (HANDLE hToken, int mode, const char *path, const char *const *argv,
+ const char *const *envp)
+{
+ pinfo *child;
+ int ret;
+ vfork_save *vf = vfork_storage.val ();
+
+ if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY)
+ mode = _P_NOWAIT;
+ else
+ vf = NULL;
+
+ syscall_printf ("_spawnve (%s, %s, %x)", path, argv[0], envp);
+
+ switch (mode)
+ {
+ case _P_OVERLAY:
+ /* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/
+ /* Just act as an exec if _P_OVERLAY set. */
+ spawn_guts (hToken, path, argv, envp, myself, mode);
+ /* Errno should be set by spawn_guts. */
+ ret = -1;
+ break;
+ case _P_NOWAIT:
+ case _P_NOWAITO:
+ case _P_WAIT:
+ case _P_DETACH:
+ child = cygwin_shared->p.allocate_pid ();
+ if (!child)
+ {
+ set_errno (EAGAIN);
+ syscall_printf ("-1 = spawnve (), process table full");
+ return -1;
+ }
+ strcpy (child->progname, path);
+ child->ppid = myself->pid;
+ child->uid = myself->uid;
+ child->gid = myself->gid;
+ child->pgid = myself->pgid;
+ child->sid = myself->sid;
+ child->ctty = myself->ctty;
+ child->umask = myself->umask;
+ child->process_state |= PID_INITIALIZING;
+ memcpy (child->username, myself->username, MAX_USER_NAME);
+ child->psid = myself->psid;
+ memcpy (child->sidbuf, myself->sidbuf, 40);
+ memcpy (child->logsrv, myself->logsrv, 256);
+ memcpy (child->domain, myself->domain, MAX_COMPUTERNAME_LENGTH+1);
+ subproc_init ();
+ ret = spawn_guts (hToken, path, argv, envp, child, mode);
+ if (ret == -1)
+ child->process_state = PID_NOT_IN_USE;
+
+ if (vf)
+ {
+ vf->pid = child->pid;
+ longjmp (vf->j, 1);
+ }
+ 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 (NULL, mode, path, (char * const *) argv,
+ *user_data->envptr);
+}
+
+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 (NULL, mode, path, (char * const *) argv,
+ (char * const *) envp);
+}
+
+extern "C"
+int
+spawnlp (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 spawnvpe (mode, path, (char * const *) argv, *user_data->envptr);
+}
+
+extern "C"
+int
+spawnlpe (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 spawnvpe (mode, path, (char * const *) argv, envp);
+}
+
+extern "C"
+int
+spawnv (int mode, const char *path, const char * const *argv)
+{
+ return _spawnve (NULL, mode, path, argv, *user_data->envptr);
+}
+
+extern "C"
+int
+spawnve (int mode, const char *path, char * const *argv,
+ const char * const *envp)
+{
+ return _spawnve (NULL, mode, path, argv, envp);
+}
+
+extern "C"
+int
+spawnvp (int mode, const char *path, const char * const *argv)
+{
+ return spawnvpe (mode, path, argv, *user_data->envptr);
+}
+
+extern "C"
+int
+spawnvpe (int mode, const char *file, const char * const *argv,
+ const char * const *envp)
+{
+ char buf[MAXNAMLEN];
+ return _spawnve (NULL, mode, find_exec (file, buf), argv, envp);
+}