diff options
Diffstat (limited to 'winsup/cygwin/spawn.cc')
-rw-r--r-- | winsup/cygwin/spawn.cc | 1119 |
1 files changed, 1119 insertions, 0 deletions
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc new file mode 100644 index 00000000000..653fdd5e1ae --- /dev/null +++ b/winsup/cygwin/spawn.cc @@ -0,0 +1,1119 @@ +/* spawn.cc + + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 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 <stdarg.h> +#include <unistd.h> +#include <process.h> +#include <sys/wait.h> +#include <limits.h> +#include <wingdi.h> +#include <winuser.h> +#include <ctype.h> +#include "cygerrno.h" +#include <sys/cygwin.h> +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "sigproc.h" +#include "cygheap.h" +#include "child_info.h" +#include "shared_info.h" +#include "pinfo.h" +#define NEED_VFORK +#include "perthread.h" +#include "registry.h" +#include "environ.h" +#include "cygthread.h" + +#define LINE_BUF_CHUNK (CYG_MAX_PATH * 2) + +static 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) +}; + +HANDLE hExeced; +DWORD dwExeced; + +/* 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) +{ + char *ext; + + debug_printf ("prog '%s'", prog); + buf.check (prog, PC_SYM_FOLLOW | PC_FULL, std_suffixes); + + if (!buf.exists () || buf.isdir ()) + ext = NULL; + else if (buf.known_suffix) + ext = (char *) buf + (buf.known_suffix - buf.get_win32 ()); + else + ext = strchr (buf, '\0'); + + debug_printf ("buf %s, suffix found '%s'", (char *) 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, path_conv& buf, const char *mywinenv, + unsigned opt, const char **known_suffix) +{ + const char *suffix = ""; + debug_printf ("find_exec (%s)", name); + const char *retval = buf; + char tmp[CYG_MAX_PATH]; + const char *posix = (opt & FE_NATIVE) ? NULL : name; + bool has_slash = strchr (name, '/'); + + /* 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)) != NULL) + { + if (posix && !has_slash) + { + tmp[0] = '.'; + tmp[1] = '/'; + strcpy (tmp + 2, name); + posix = tmp; + } + goto out; + } + + win_env *winpath; + const char *path; + const char *posix_path; + + /* Return the error condition if this is an absolute path or if there + is no PATH to search. */ + if (strchr (name, '/') || strchr (name, '\\') || + isdrive (name) || + !(winpath = getwinenv (mywinenv)) || + !(path = winpath->get_native ()) || + *path == '\0') + goto errout; + + debug_printf ("%s%s", mywinenv, path); + + posix = (opt & FE_NATIVE) ? NULL : tmp; + posix_path = winpath->get_posix () - 1; + /* 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); + + if ((suffix = perhaps_suffix (tmp, buf)) != NULL) + { + if (posix == tmp) + { + eotmp = strccpy (tmp, &posix_path, ':'); + if (eotmp == tmp) + *eotmp++ = '.'; + *eotmp++ = '/'; + strcpy (eotmp, name); + } + 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) + buf.check (name); + else + retval = name; + + out: + if (posix) + buf.set_path (posix); + debug_printf ("%s = find_exec (%s)", (char *) 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 = cygheap->fdtab[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 (); +} + +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])); +} + +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; +} + +class av +{ + char **argv; + int calloced; + public: + int error; + int argc; + av (int ac, const char * const *av) : calloced (0), error (false), argc (ac) + { + argv = (char **) cmalloc (HEAP_1_ARGV, (argc + 5) * sizeof (char *)); + memcpy (argv, av, (argc + 1) * sizeof (char *)); + } + ~av () + { + if (argv) + { + for (int i = 0; i < calloced; i++) + if (argv[i]) + cfree (argv[i]); + cfree (argv); + } + } + int unshift (const char *what, int conv = 0); + operator char **() {return argv;} + void all_calloced () {calloced = argc;} + void replace0_maybe (const char *arg0) + { + /* Note: Assumes that argv array has not yet been "unshifted" */ + if (!calloced + && (argv[0] = cstrdup1 (arg0))) + calloced = true; + else + error = errno; + } + void dup_maybe (int i) + { + if (i >= calloced + && !(argv[i] = cstrdup1 (argv[i]))) + error = errno; + } + void dup_all () + { + for (int i = calloced; i < argc; i++) + if (!(argv[i] = cstrdup1 (argv[i]))) + error = errno; + } +}; + +int +av::unshift (const char *what, int conv) +{ + char **av; + av = (char **) crealloc (argv, (argc + 2) * sizeof (char *)); + if (!av) + return 0; + + argv = av; + memmove (argv + 1, argv, (argc + 1) * sizeof (char *)); + char buf[CYG_MAX_PATH + 1]; + if (conv) + { + cygwin_conv_to_posix_path (what, buf); + char *p = strchr (buf, '\0') - 4; + if (p > buf && strcasematch (p, ".exe")) + *p = '\0'; + what = buf; + } + if (!(*argv = cstrdup1 (what))) + error = errno; + argc++; + calloced++; + return 1; +} + +struct pthread_cleanup +{ + _sig_func_ptr oldint; + _sig_func_ptr oldquit; + sigset_t oldmask; + pthread_cleanup (): oldint (NULL), oldquit (NULL), oldmask (0) {} +}; + +static void +do_cleanup (void *args) +{ +# define cleanup ((pthread_cleanup *) args) + if (cleanup->oldint) + signal (SIGINT, cleanup->oldint); + if (cleanup->oldquit) + signal (SIGQUIT, cleanup->oldquit); + if (cleanup->oldmask) + sigprocmask (SIG_SETMASK, &(cleanup->oldmask), NULL); +# undef cleanup +} + + +static int __stdcall +spawn_guts (const char * prog_arg, const char *const *argv, + const char *const envp[], int mode) +{ + bool rc; + pid_t cygpid; + + MALLOC_CHECK; + + if (prog_arg == NULL) + { + syscall_printf ("prog_arg is NULL"); + set_errno (EINVAL); + return -1; + } + + syscall_printf ("spawn_guts (%d, %.9500s)", mode, prog_arg); + + if (argv == NULL) + { + syscall_printf ("argv is NULL"); + set_errno (EINVAL); + return -1; + } + + path_conv real_path; + + linebuf one_line; + + STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}; + + child_info_spawn ciresrv; + si.lpReserved2 = (LPBYTE) &ciresrv; + si.cbReserved2 = sizeof (ciresrv); + + DWORD chtype; + if (mode != _P_OVERLAY) + chtype = PROC_SPAWN; + else + chtype = PROC_EXEC; + + HANDLE subproc_ready; + if (chtype != PROC_EXEC) + subproc_ready = NULL; + else + { + subproc_ready = CreateEvent (&sec_all, TRUE, FALSE, NULL); + ProtectHandleINH (subproc_ready); + } + + init_child_info (chtype, &ciresrv, (mode == _P_OVERLAY) ? myself->pid : 1, + subproc_ready); + if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &ciresrv.parent, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + system_printf ("couldn't create handle to myself for child, %E"); + return -1; + } + + VerifyHandle (ciresrv.parent); + ciresrv.moreinfo = (cygheap_exec_info *) ccalloc (HEAP_1_EXEC, 1, sizeof (cygheap_exec_info)); + ciresrv.moreinfo->old_title = NULL; + + /* 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 */; + + av newargv (ac, argv); + + int null_app_name = 0; + if (ac == 3 && argv[1][0] == '/' && 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); + 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]); + strcpy (real_path, argv[0]); + null_app_name = 1; + goto skip_arg_parsing; + } + + const char *ext; + if ((ext = perhaps_suffix (prog_arg, real_path)) == NULL) + { + set_errno (ENOENT); + return -1; + } + + MALLOC_CHECK; + + /* 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 = CreateFile (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 * CYG_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", (char *) real_path); + + char *pgm, *arg1; + + if (buf[0] != '#' || buf[1] != '!') + { + pgm = (char *) "/bin/sh"; + arg1 = NULL; + } + else + { + char *ptr; + 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, 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'; + } + + /* Replace argv[0] with the full path to the script if this is the + first time through the loop. */ + newargv.replace0_maybe (prog_arg); + + /* pointers: + * pgm interpreter name + * arg1 optional string + */ + if (arg1) + newargv.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); + newargv.unshift (real_path, 1); + } + + if (real_path.iscygexec ()) + newargv.dup_all (); + else + { + for (int i = 0; i < newargv.argc; i++) + { + char *p = NULL; + const char *a; + + newargv.dup_maybe (i); + a = i ? newargv[i] : (char *) real_path; + int len = strlen (a); + if (len != 0 && !strpbrk (a, " \t\n\r\"")) + one_line.add (a, len); + else + { + one_line.add ("\"", 1); + /* Handle embedded special characters " and \. + A " is always preceded by a \. + A \ is not special unless it precedes a ". If it does, + then all preceding \'s must be doubled to avoid having + the Windows command line parser interpret the \ as quoting + the ". This rule applies to a string of \'s before the end + of the string, since cygwin/windows uses a " to delimit the + argument. */ + for (; (p = strpbrk (a, "\"\\")); a = ++p) + { + one_line.add (a, p - a); + /* Find length of string of backslashes */ + int n = strspn (p, "\\"); + if (!n) + one_line.add ("\\\"", 2); /* No backslashes, so it must be a ". + The " has to be protected with a backslash. */ + else + { + one_line.add (p, n); /* Add the run of backslashes */ + /* Need to double up all of the preceding + backslashes if they precede a quote or EOS. */ + if (!p[n] || p[n] == '"') + one_line.add (p, n); + p += n - 1; /* Point to last backslash */ + } + } + 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; + + if (one_line.ix > 32767) + { + debug_printf ("Command line too long (>32K), return E2BIG"); + set_errno (E2BIG); + return -1; + } + } + + char *envblock; + newargv.all_calloced (); + if (newargv.error) + { + set_errno (newargv.error); + return -1; + } + + ciresrv.moreinfo->argc = newargv.argc; + ciresrv.moreinfo->argv = newargv; + ciresrv.hexec_proc = hexec_proc; + + if (mode != _P_OVERLAY || + !DuplicateHandle (hMainProc, myself.shared_handle (), hMainProc, + &ciresrv.moreinfo->myself_pinfo, 0, + TRUE, DUPLICATE_SAME_ACCESS)) + ciresrv.moreinfo->myself_pinfo = NULL; + else + VerifyHandle (ciresrv.moreinfo->myself_pinfo); + + skip_arg_parsing: + PROCESS_INFORMATION pi = {NULL, 0, 0, 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); + + int flags = CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (hMainProc); + + if (mode == _P_DETACH || !set_console_state_for_spawn ()) + flags |= DETACHED_PROCESS; + if (mode != _P_OVERLAY) + flags |= CREATE_SUSPENDED; + + /* Some file types (currently only sockets) 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 (cygheap->fdtab.need_fixup_before ()) + flags |= CREATE_SUSPENDED; + + + const char *runpath = null_app_name ? NULL : (const char *) real_path; + + syscall_printf ("null_app_name %d (%s, %.9500s)", null_app_name, runpath, one_line.buf); + + void *newheap; + /* Preallocated buffer for `sec_user' call */ + char sa_buf[1024]; + + cygbench ("spawn-guts"); + + cygheap->fdtab.set_file_pointers_for_exec (); + cygheap->user.deimpersonate (); + /* 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. */ + if (!cygheap->user.issetuid () + || (cygheap->user.saved_uid == cygheap->user.real_uid + && cygheap->user.saved_gid == cygheap->user.real_gid + && !cygheap->user.groups.issetgroups ())) + { + PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf); + ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc, + real_path.iscygexec ()); + newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ()); + rc = CreateProcess (runpath, /* image name - with full path */ + one_line.buf, /* what was passed to exec */ + sec_attribs, /* process security attrs */ + sec_attribs, /* thread security attrs */ + TRUE, /* inherit handles from parent */ + flags, + envblock, /* environment */ + 0, /* use current drive/directory */ + &si, + &pi); + } + else + { + PSID sid = cygheap->user.sid (); + /* Give access to myself */ + if (mode == _P_OVERLAY) + myself.set_acl(); + + /* Set security attributes with sid */ + PSECURITY_ATTRIBUTES sec_attribs = sec_user_nih (sa_buf, sid); + + /* 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; + + ciresrv.moreinfo->envp = build_env (envp, envblock, ciresrv.moreinfo->envc, + real_path.iscygexec ()); + newheap = cygheap_setup_for_child (&ciresrv, cygheap->fdtab.need_fixup_before ()); + rc = CreateProcessAsUser (cygheap->user.token (), + runpath, /* image name - with full path */ + one_line.buf, /* what was passed to exec */ + sec_attribs, /* process security attrs */ + sec_attribs, /* thread security attrs */ + TRUE, /* inherit handles from parent */ + flags, + envblock, /* environment */ + 0, /* use current drive/directory */ + &si, + &pi); + } + + /* 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 (); + + MALLOC_CHECK; + if (envblock) + 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 (); + syscall_printf ("CreateProcess failed, %E"); + if (subproc_ready) + ForceCloseHandle (subproc_ready); + cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0); + return -1; + } + + /* FIXME: There is a small race here */ + + int res; + pthread_cleanup cleanup; + pthread_cleanup_push (do_cleanup, (void *) &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); + (void) sigprocmask (SIG_BLOCK, &child_block, &cleanup.oldmask); + } + + /* Fixup the parent datastructure if needed and resume the child's + main thread. */ + if (!cygheap->fdtab.need_fixup_before ()) + cygheap_setup_for_child_cleanup (newheap, &ciresrv, 0); + else + { + cygheap->fdtab.fixup_before_exec (pi.dwProcessId); + cygheap_setup_for_child_cleanup (newheap, &ciresrv, 1); + if (mode == _P_OVERLAY) + { + ResumeThread (pi.hThread); + cygthread::terminate (); + } + } + + 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 ("%d = spawn_guts (%s, %.9500s)", + rc ? cygpid : (unsigned int) -1, prog_arg, one_line.buf); + + /* Name the handle similarly to proc_subproc. */ + ProtectHandle1 (pi.hProcess, childhProc); + + if (mode == _P_OVERLAY) + { + /* These are both duplicated in the child code. We do this here, + primarily for strace. */ + strace.execing = 1; + hExeced = pi.hProcess; + dwExeced = pi.dwProcessId; + strcpy (myself->progname, real_path); + close_all_files (); + } + else + { + myself->set_has_pgid_children (); + ProtectHandle (pi.hThread); + pinfo child (cygpid, PID_IN_USE); + 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; + if (!child.remember ()) + { + syscall_printf ("process table full"); + set_errno (EAGAIN); + res = -1; + goto out; + } + + strcpy (child->progname, real_path); + /* 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. */ + (void) DuplicateHandle (hMainProc, child.shared_handle (), pi.hProcess, + NULL, 0, 0, DUPLICATE_SAME_ACCESS); + /* Start the child running */ + ResumeThread (pi.hThread); + } + + ForceCloseHandle (pi.hThread); + + sigproc_printf ("spawned windows pid %d", pi.dwProcessId); + + bool exited; + + res = 0; + exited = false; + MALLOC_CHECK; + if (mode == _P_OVERLAY) + { + int nwait = 3; + HANDLE waitbuf[3] = {pi.hProcess, signal_arrived, subproc_ready}; + for (int i = 0; i < 100; i++) + { + switch (WaitForMultipleObjects (nwait, waitbuf, FALSE, INFINITE)) + { + case WAIT_OBJECT_0: + sigproc_printf ("subprocess exited"); + DWORD exitcode; + if (!GetExitCodeProcess (pi.hProcess, &exitcode)) + exitcode = 1; + res |= exitcode; + exited = true; + break; + case WAIT_OBJECT_0 + 1: + sigproc_printf ("signal arrived"); + reset_signal_arrived (); + continue; + case WAIT_OBJECT_0 + 2: + if (my_parent_is_alive ()) + res |= EXIT_REPARENTING; + else if (!myself->ppid_handle) + { + nwait = 2; + sigproc_terminate (); + continue; + } + break; + case WAIT_FAILED: + system_printf ("wait failed: nwait %d, pid %d, winpid %d, %E", + nwait, myself->pid, myself->dwProcessId); + system_printf ("waitbuf[0] %p %d", waitbuf[0], + WaitForSingleObject (waitbuf[0], 0)); + system_printf ("waitbuf[1] %p = %d", waitbuf[1], + WaitForSingleObject (waitbuf[1], 0)); + system_printf ("waitbuf[w] %p = %d", waitbuf[2], + WaitForSingleObject (waitbuf[2], 0)); + set_errno (ECHILD); + try_to_debug (); + return -1; + } + break; + } + + ForceCloseHandle (subproc_ready); + + 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. + */ + HANDLE oldh = myself->hProcess; + HANDLE h = myself->ppid_handle; + sigproc_printf ("parent handle %p", h); + int rc = DuplicateHandle (hMainProc, pi.hProcess, h, &myself->hProcess, + 0, FALSE, DUPLICATE_SAME_ACCESS); + sigproc_printf ("%d = DuplicateHandle, oldh %p, newh %p", + rc, oldh, myself->hProcess); + VerifyHandle (myself->hProcess); + if (!rc && my_parent_is_alive ()) + { + system_printf ("Reparent failed, parent handle %p, %E", h); + system_printf ("my dwProcessId %d, myself->dwProcessId %d", + GetCurrentProcessId (), myself->dwProcessId); + system_printf ("old hProcess %p, hProcess %p", oldh, myself->hProcess); + } + } + + } + + MALLOC_CHECK; + + switch (mode) + { + case _P_OVERLAY: + ForceCloseHandle1 (pi.hProcess, childhProc); + proc_terminate (); + myself->exit (res, 1); + break; + case _P_WAIT: + case _P_SYSTEM: + waitpid (cygpid, (int *) &res, 0); + break; + case _P_DETACH: + res = 0; /* Lose all memory of this child. */ + break; + case _P_NOWAIT: + case _P_NOWAITO: + case _P_VFORK: + res = cygpid; + break; + default: + break; + } + +out: + 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) +{ + 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); + + 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 (path, argv, envp, mode); + /* Errno should be set by spawn_guts. */ + ret = -1; + break; + case _P_VFORK: + case _P_NOWAIT: + case _P_NOWAITO: + case _P_WAIT: + case _P_DETACH: + case _P_SYSTEM: + subproc_init (); + ret = spawn_guts (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 *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, cur_environ ()); +} + +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 (mode, path, argv, cur_environ ()); +} + +extern "C" int +spawnvp (int mode, const char *path, const char * const *argv) +{ + return spawnvpe (mode, path, 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, find_exec (file, buf), argv, envp); +} |