OSDN Git Service

be35a0b7da039aefca54f37e477741dd1b163166
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / spawn.cc
1 /* spawn.cc
2
3    Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <unistd.h>
15 #include <process.h>
16 #include <sys/wait.h>
17 #include <errno.h>
18 #include <limits.h>
19 #include <wingdi.h>
20 #include <winuser.h>
21 #include <ctype.h>
22 #include "paths.h"
23
24 extern BOOL allow_ntsec;
25
26 #define LINE_BUF_CHUNK (MAX_PATH * 2)
27
28 suffix_info std_suffixes[] =
29 {
30   suffix_info (".exe", 1), suffix_info ("", 1),
31   suffix_info (".com"), suffix_info (".cmd"),
32   suffix_info (".bat"), suffix_info (".dll"),
33   suffix_info (NULL)
34 };
35
36 /* Add .exe to PROG if not already present and see if that exists.
37    If not, return PROG (converted from posix to win32 rules if necessary).
38    The result is always BUF.
39
40    Returns (possibly NULL) suffix */
41
42 static const char *
43 perhaps_suffix (const char *prog, path_conv &buf)
44 {
45   char *ext;
46
47   debug_printf ("prog '%s'", prog);
48   buf.check (prog, PC_SYM_FOLLOW | PC_FULL, std_suffixes);
49
50   if (buf.file_attributes () & FILE_ATTRIBUTE_DIRECTORY)
51     ext = NULL;
52   else if (buf.known_suffix)
53     ext = buf + (buf.known_suffix - buf.get_win32 ());
54   else
55     ext = strchr (buf, '\0');
56
57   debug_printf ("buf %s, suffix found '%s'", (char *) buf, ext);
58   return ext;
59 }
60
61 /* Find an executable name, possibly by appending known executable
62    suffixes to it.  The win32-translated name is placed in 'buf'.
63    Any found suffix is returned in known_suffix.
64
65    If the file is not found and !null_if_not_found then the win32 version
66    of name is placed in buf and returned.  Otherwise the contents of buf
67    is undefined and NULL is returned.  */
68
69 const char * __stdcall
70 find_exec (const char *name, path_conv& buf, const char *mywinenv,
71            int null_if_notfound, const char **known_suffix)
72 {
73   const char *suffix = "";
74   debug_printf ("find_exec (%s)", name);
75   char *retval = buf;
76
77   /* Check to see if file can be opened as is first.
78      Win32 systems always check . first, but PATH may not be set up to
79      do this. */
80   if ((suffix = perhaps_suffix (name, buf)) != NULL)
81     goto out;
82
83   win_env *winpath;
84   const char *path;
85   char tmp[MAX_PATH];
86
87   /* Return the error condition if this is an absolute path or if there
88      is no PATH to search. */
89   if (strchr (name, '/') || strchr (name, '\\') ||
90       isdrive (name) ||
91       !(winpath = getwinenv (mywinenv)) ||
92       !(path = winpath->get_native ()) ||
93       *path == '\0')
94     goto errout;
95
96   debug_printf ("%s%s", mywinenv, path);
97
98   /* Iterate over the specified path, looking for the file with and
99      without executable extensions. */
100   do
101     {
102       char *eotmp = strccpy (tmp, &path, ';');
103       /* An empty path or '.' means the current directory, but we've
104          already tried that.  */
105       if (tmp[0] == '\0' || (tmp[0] == '.' && tmp[1] == '\0'))
106         continue;
107
108       *eotmp++ = '\\';
109       strcpy (eotmp, name);
110
111       debug_printf ("trying %s", tmp);
112
113       if ((suffix = perhaps_suffix (tmp, buf)) != NULL)
114         goto out;
115     }
116   while (*path && *++path);
117
118 errout:
119   /* Couldn't find anything in the given path.
120      Take the appropriate action based on null_if_not_found. */
121   if (null_if_notfound)
122     retval = NULL;
123   else
124     buf.check (name);
125
126 out:
127   debug_printf ("%s = find_exec (%s)", (char *) buf, name);
128   if (known_suffix)
129     *known_suffix = suffix ?: strchr (buf, '\0');
130   return retval;
131 }
132
133 /* Utility for spawn_guts.  */
134
135 static HANDLE
136 handle (int n, int direction)
137 {
138   fhandler_base *fh = dtable[n];
139
140   if (!fh)
141     return INVALID_HANDLE_VALUE;
142   if (fh->get_close_on_exec ())
143     return INVALID_HANDLE_VALUE;
144   if (direction == 0)
145     return fh->get_handle ();
146   return fh->get_output_handle ();
147 }
148
149 /* Cover function for CreateProcess.
150
151    This function is used by both the routines that search $PATH and those
152    that do not.  This should work out ok as according to the documentation,
153    CreateProcess only searches $PATH if PROG has no directory elements.
154
155    Spawning doesn't fit well with Posix's fork/exec (one can argue the merits
156    of either but that's beside the point).  If we're exec'ing we want to
157    record the child pid for fork.  If we're spawn'ing we don't want to do
158    this.  It is up to the caller to handle both cases.
159
160    The result is the process id.  The handle of the created process is
161    stored in H.
162 */
163
164 HANDLE NO_COPY hExeced = NULL;
165 DWORD NO_COPY exec_exit = 0;
166
167 int
168 iscmd (const char *argv0, const char *what)
169 {
170   int n;
171   n = strlen (argv0) - strlen (what);
172   if (n >= 2 && argv0[1] != ':')
173     return 0;
174   return n >= 0 && strcasematch (argv0 + n, what) &&
175          (n == 0 || isdirsep (argv0[n - 1]));
176 }
177
178 class linebuf
179 {
180 public:
181   size_t ix;
182   char *buf;
183   size_t alloced;
184   linebuf () : ix (0), buf (NULL), alloced (0)
185   {
186   }
187   ~linebuf () {/* if (buf) free (buf);*/}
188   void add (const char *what, int len);
189   void add (const char *what) {add (what, strlen (what));}
190   void prepend (const char *what, int len);
191 };
192
193 void
194 linebuf::add (const char *what, int len)
195 {
196   size_t newix;
197   if ((newix = ix + len) >= alloced || !buf)
198     {
199       alloced += LINE_BUF_CHUNK + newix;
200       buf = (char *) realloc (buf, alloced + 1);
201     }
202   memcpy (buf + ix, what, len);
203   ix = newix;
204   buf[ix] = '\0';
205 }
206
207 void
208 linebuf::prepend (const char *what, int len)
209 {
210   int buflen;
211   size_t newix;
212   if ((newix = ix + len) >= alloced)
213     {
214       alloced += LINE_BUF_CHUNK + newix;
215       buf = (char *) realloc (buf, alloced + 1);
216       buf[ix] = '\0';
217     }
218   if ((buflen = strlen (buf)))
219       memmove (buf + len, buf, buflen + 1);
220   else
221       buf[newix] = '\0';
222   memcpy (buf, what, len);
223   ix = newix;
224 }
225
226 static HANDLE hexec_proc = NULL;
227
228 void __stdcall
229 exec_fixup_after_fork ()
230 {
231   if (hexec_proc)
232     CloseHandle (hexec_proc);
233   hexec_proc = NULL;
234 }
235
236 static int __stdcall
237 spawn_guts (HANDLE hToken, const char * prog_arg, const char *const *argv,
238             const char *const envp[], int mode)
239 {
240   int i;
241   BOOL rc;
242   int argc;
243   pid_t cygpid;
244
245   hExeced = NULL;
246
247   MALLOC_CHECK;
248
249   if (prog_arg == NULL)
250     {
251       syscall_printf ("prog_arg is NULL");
252       set_errno(EINVAL);
253       return -1;
254     }
255
256   syscall_printf ("spawn_guts (%.132s)", prog_arg);
257
258   if (argv == NULL)
259     {
260       syscall_printf ("argv is NULL");
261       set_errno(EINVAL);
262       return (-1);
263     }
264
265   /* CreateProcess takes one long string that is the command line (sigh).
266      We need to quote any argument that has whitespace or embedded "'s.  */
267
268   for (argc = 0; argv[argc]; argc++)
269     /* nothing */;
270
271   char *real_path;
272   path_conv real_path_buf;
273
274   linebuf one_line;
275
276   if (argc == 3 && argv[1][0] == '/' && argv[1][1] == 'c' &&
277       (iscmd (argv[0], "command.com") || iscmd (argv[0], "cmd.exe")))
278     {
279       one_line.add (argv[0]);
280       one_line.add (" ");
281       one_line.add (argv[1]);
282       one_line.add (" ");
283       real_path = NULL;
284       one_line.add (argv[2]);
285       strcpy (real_path_buf, argv[0]);
286       goto skip_arg_parsing;
287     }
288
289   real_path = real_path_buf;
290
291   const char *saved_prog_arg;
292   const char *newargv0, **firstarg;
293   const char *ext;
294
295   if ((ext = perhaps_suffix (prog_arg, real_path_buf)) == NULL)
296     {
297       set_errno (ENOENT);
298       return -1;
299     }
300
301   MALLOC_CHECK;
302   saved_prog_arg = prog_arg;
303   newargv0 = argv[0];
304   firstarg = &newargv0;
305
306   /* If the file name ends in either .exe, .com, .bat, or .cmd we assume
307      that it is NOT a script file */
308   while (*ext == '\0')
309     {
310       HANDLE hnd = CreateFileA (real_path,
311                                 GENERIC_READ,
312                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
313                                 &sec_none_nih,
314                                 OPEN_EXISTING,
315                                 FILE_ATTRIBUTE_NORMAL,
316                                 0);
317       if (hnd == INVALID_HANDLE_VALUE)
318         {
319           __seterrno ();
320           return -1;
321         }
322
323       DWORD done;
324
325       char buf[2 * MAX_PATH + 1];
326       buf[0] = buf[1] = buf[2] = buf[sizeof(buf) - 1] = '\0';
327       if (! ReadFile (hnd, buf, sizeof (buf) - 1, &done, 0))
328         {
329           CloseHandle (hnd);
330           __seterrno ();
331           return -1;
332         }
333
334       CloseHandle (hnd);
335
336       if (buf[0] == 'M' && buf[1] == 'Z')
337         break;
338
339       debug_printf ("%s is a script", prog_arg);
340
341       char *ptr, *pgm, *arg1;
342
343       if (buf[0] != '#' || buf[1] != '!')
344         {
345           strcpy (buf, "sh");         /* shell script without magic */
346           pgm = buf;
347           ptr = buf + 2;
348           arg1 = NULL;
349         }
350       else
351         {
352           pgm = buf + 2;
353           pgm += strspn (pgm, " \t");
354           for (ptr = pgm, arg1 = NULL;
355                *ptr && *ptr != '\r' && *ptr != '\n';
356                ptr++)
357             if (!arg1 && (*ptr == ' ' || *ptr == '\t'))
358               {
359                 /* Null terminate the initial command and step over
360                    any additional white space.  If we've hit the
361                    end of the line, exit the loop.  Otherwise, position
362                    we've found the first argument. Position the current
363                    pointer on the last known white space. */
364                 *ptr = '\0';
365                 char *newptr = ptr + 1;
366                 newptr += strspn (newptr, " \t");
367                 if (!*newptr || *newptr == '\r' || *newptr == '\n')
368                   break;
369                 arg1 = newptr;
370                 ptr = newptr - 1;
371               }
372
373
374           *ptr = '\0';
375         }
376
377       char buf2[MAX_PATH + 1];
378
379       /* pointers:
380        * pgm    interpreter name
381        * arg1   optional string
382        * ptr    end of string
383        */
384
385       if (!arg1)
386         one_line.prepend (" ", 1);
387       else
388         {
389           one_line.prepend ("\" ", 2);
390           one_line.prepend (arg1, strlen (arg1));
391           one_line.prepend (" \"", 2);
392         }
393
394       find_exec (pgm, real_path_buf, "PATH=", 0, &ext);
395       cygwin_conv_to_posix_path (real_path, buf2);
396       one_line.prepend (buf2, strlen (buf2));
397
398       /* If script had absolute path, add it to script name now!
399        * This is necessary if script has been found via PATH.
400        * For example, /usr/local/bin/tkman started as "tkman":
401        * #!/usr/local/bin/wish -f
402        * ...
403        * We should run /usr/local/bin/wish -f /usr/local/bin/tkman,
404        * but not /usr/local/bin/wish -f tkman!
405        * We don't modify anything, if script has qulified path.
406        */
407       if (firstarg)
408         *firstarg = saved_prog_arg;
409
410       debug_printf ("prog_arg '%s', copy '%s'", prog_arg, one_line.buf);
411       firstarg = NULL;
412     }
413
414   for (; *argv; argv++)
415     {
416       char *p = NULL;
417       const char *a = newargv0 ?: *argv;
418
419       MALLOC_CHECK;
420
421       newargv0 = NULL;
422       int len = strlen (a);
423       if (len != 0 && !strpbrk (a, " \t\n\r\""))
424         one_line.add (a, len);
425       else
426         {
427           one_line.add ("\"", 1);
428           for (; (p = strpbrk (a, "\"\\")); a = ++p)
429             {
430               one_line.add (a, p - a);
431               if (*p == '\\' || *p == '"')
432                 one_line.add ("\\", 1);
433               one_line.add (p, 1);
434             }
435           if (*a)
436             one_line.add (a);
437           one_line.add ("\"", 1);
438         }
439       MALLOC_CHECK;
440       one_line.add (" ", 1);
441       MALLOC_CHECK;
442     }
443
444   MALLOC_CHECK;
445   if (one_line.ix)
446     one_line.buf[one_line.ix - 1] = '\0';
447   else
448     one_line.add ("", 1);
449   MALLOC_CHECK;
450
451 skip_arg_parsing:
452   PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
453   STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
454   si.lpReserved = NULL;
455   si.lpDesktop = NULL;
456   si.dwFlags = STARTF_USESTDHANDLES;
457   si.hStdInput = handle (0, 0); /* Get input handle */
458   si.hStdOutput = handle (1, 1); /* Get output handle */
459   si.hStdError = handle (2, 1); /* Get output handle */
460   si.cb = sizeof (si);
461
462   /* Pass fd table to a child */
463
464   MALLOC_CHECK;
465   int len = dtable.linearize_fd_array (0, 0);
466   MALLOC_CHECK;
467   if (len == -1)
468     {
469       system_printf ("FATAL error in linearize_fd_array");
470       return -1;
471     }
472   int titlelen = 1 + (old_title && mode == _P_OVERLAY ? strlen (old_title) : 0);
473   si.cbReserved2 = len + titlelen + sizeof(child_info);
474   si.lpReserved2 = (LPBYTE) alloca (si.cbReserved2);
475
476 # define ciresrv ((child_info *)si.lpReserved2)
477   HANDLE spr = NULL;
478   DWORD chtype;
479   if (mode != _P_OVERLAY)
480     chtype = PROC_SPAWN;
481   else
482     {
483       spr = CreateEvent(&sec_all, TRUE, FALSE, NULL);
484       ProtectHandle (spr);
485       chtype = PROC_EXEC;
486     }
487
488   init_child_info (chtype, ciresrv, (mode == _P_OVERLAY) ? myself->pid : 1, spr);
489
490   LPBYTE resrv = si.lpReserved2 + sizeof *ciresrv;
491 # undef ciresrv
492
493   if (dtable.linearize_fd_array (resrv, len) < 0)
494     {
495       system_printf ("FATAL error in second linearize_fd_array");
496       return -1;
497     }
498
499   if (titlelen > 1)
500     strcpy ((char *) resrv + len, old_title);
501   else
502     resrv[len] = '\0';
503
504   /* We print the translated program and arguments here so the user can see
505      what was done to it.  */
506   syscall_printf ("spawn_guts (%s, %.132s)", real_path, one_line.buf);
507
508   int flags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED |
509               GetPriorityClass (hMainProc);
510
511   if (mode == _P_DETACH || !set_console_state_for_spawn ())
512     flags |= DETACHED_PROCESS;
513
514   /* Build windows style environment list */
515   char *envblock = winenv (envp, 0);
516
517   /* Preallocated buffer for `sec_user' call */
518   char sa_buf[1024];
519
520   if (!hToken && myself->token != INVALID_HANDLE_VALUE)
521     hToken = myself->token;
522
523   if (mode == _P_OVERLAY && !hexec_proc &&
524       !DuplicateHandle (hMainProc, hMainProc, hMainProc, &hexec_proc, 0,
525                         TRUE, DUPLICATE_SAME_ACCESS))
526     system_printf ("couldn't save current process handle %p, %E", hMainProc);
527
528   if (hToken)
529     {
530       /* allow the child to interact with our window station/desktop */
531       HANDLE hwst, hdsk;
532       SECURITY_INFORMATION dsi = DACL_SECURITY_INFORMATION;
533       DWORD n;
534       char wstname[1024];
535       char dskname[1024];
536
537       hwst = GetProcessWindowStation();
538       SetUserObjectSecurity(hwst, &dsi, get_null_sd ());
539       GetUserObjectInformation(hwst, UOI_NAME, wstname, 1024, &n);
540       hdsk = GetThreadDesktop(GetCurrentThreadId());
541       SetUserObjectSecurity(hdsk, &dsi, get_null_sd ());
542       GetUserObjectInformation(hdsk, UOI_NAME, dskname, 1024, &n);
543       strcat (wstname, "\\");
544       strcat (wstname, dskname);
545       si.lpDesktop = wstname;
546
547       char tu[1024];
548       PSID sid = NULL;
549       DWORD ret_len;
550       if (GetTokenInformation (hToken, TokenUser,
551                                (LPVOID) &tu, sizeof tu,
552                                &ret_len))
553         sid = ((TOKEN_USER *) &tu)->User.Sid;
554       else
555         system_printf ("GetTokenInformation: %E");
556
557       /* Retrieve security attributes before setting psid to NULL
558          since it's value is needed by `sec_user'. */
559       PSECURITY_ATTRIBUTES sec_attribs = allow_ntsec && sid
560                                          ? sec_user (sa_buf, sid)
561                                          : &sec_all_nih;
562
563       /* Remove impersonation */
564       uid_t uid = geteuid();
565       if (myself->impersonated && myself->token != INVALID_HANDLE_VALUE)
566         seteuid (myself->orig_uid);
567
568       /* Load users registry hive. */
569       load_registry_hive (sid);
570
571       rc = CreateProcessAsUser (hToken,
572                        real_path,       /* image name - with full path */
573                        one_line.buf,    /* what was passed to exec */
574                        sec_attribs,     /* process security attrs */
575                        sec_attribs,     /* thread security attrs */
576                        TRUE,    /* inherit handles from parent */
577                        flags,
578                        envblock,/* environment */
579                        0,       /* use current drive/directory */
580                        &si,
581                        &pi);
582       /* Restore impersonation. In case of _P_OVERLAY this isn't
583          allowed since it would overwrite child data. */
584       if (mode != _P_OVERLAY
585           && myself->impersonated && myself->token != INVALID_HANDLE_VALUE)
586         seteuid (uid);
587     }
588   else
589     rc = CreateProcessA (real_path,     /* image name - with full path */
590                        one_line.buf,    /* what was passed to exec */
591                                         /* process security attrs */
592                        allow_ntsec ? sec_user (sa_buf) : &sec_all_nih,
593                                         /* thread security attrs */
594                        allow_ntsec ? sec_user (sa_buf) : &sec_all_nih,
595                        TRUE,    /* inherit handles from parent */
596                        flags,
597                        envblock,/* environment */
598                        0,       /* use current drive/directory */
599                        &si,
600                        &pi);
601
602   MALLOC_CHECK;
603   free (envblock);
604   MALLOC_CHECK;
605
606   /* Set errno now so that debugging messages from it appear before our
607      final debugging message [this is a general rule for debugging
608      messages].  */
609   if (!rc)
610
611   if (!rc)
612     {
613       if (spr)
614         ForceCloseHandle (spr);
615       __seterrno ();
616       syscall_printf ("CreateProcess failed, %E");
617       return -1;
618     }
619
620   if (mode == _P_OVERLAY)
621     cygpid = myself->pid;
622   else
623     cygpid = cygwin_pid (pi.dwProcessId);
624
625   /* We print the original program name here so the user can see that too.  */
626   syscall_printf ("%d = spawn_guts (%s, %.132s)",
627                   rc ? cygpid : (unsigned int) -1,
628                   prog_arg, one_line.buf);
629
630   MALLOC_CHECK;
631   /* Name the handle similarly to proc_subproc. */
632   ProtectHandle1 (pi.hProcess, childhProc);
633   ProtectHandle (pi.hThread);
634   MALLOC_CHECK;
635
636   if (mode == _P_OVERLAY)
637     {
638       close_all_files ();
639       strcpy (myself->progname, real_path_buf);
640       proc_terminate ();
641       hExeced = pi.hProcess;
642
643     /* Set up child's signal handlers */
644     /* CGF FIXME - consolidate with signal stuff below */
645     for (i = 0; i < NSIG; i++)
646       {
647         myself->getsig(i).sa_mask = 0;
648         if (myself->getsig(i).sa_handler != SIG_IGN || (mode != _P_OVERLAY))
649           myself->getsig(i).sa_handler = SIG_DFL;
650       }
651     }
652   else
653     {
654       pinfo child (cygpid, 1);
655       if (!child)
656         {
657           set_errno (EAGAIN);
658           syscall_printf ("-1 = spawnve (), process table full");
659           return -1;
660         }
661       child->username[0] = '\0';
662       child->progname[0] = '\0';
663       // CGF FIXME -- need to do this? strcpy (child->progname, path);
664       // CGF FIXME -- need to do this? memcpy (child->username, myself->username, MAX_USER_NAME);
665       child->ppid = myself->pid;
666       child->uid = myself->uid;
667       child->gid = myself->gid;
668       child->pgid = myself->pgid;
669       child->sid = myself->sid;
670       child->ctty = myself->ctty;
671       child->umask = myself->umask;
672       child->process_state |= PID_INITIALIZING;
673       memcpy (child->sidbuf, myself->sidbuf, MAX_SID_LEN);
674       if (myself->psid)
675         child->psid = child->sidbuf;
676       memcpy (child->logsrv, myself->logsrv, MAX_HOST_NAME);
677       memcpy (child->domain, myself->domain, MAX_COMPUTERNAME_LENGTH+1);
678       memcpy (child->root, myself->root, MAX_PATH+1);
679       child->rootlen = myself->rootlen;
680       child->dwProcessId = pi.dwProcessId;
681       child->hProcess = pi.hProcess;
682       child->process_state |= PID_INITIALIZING;
683       for (i = 0; i < NSIG; i++)
684         {
685           child->getsig(i).sa_mask = 0;
686           if (child->getsig(i).sa_handler != SIG_IGN || (mode != _P_OVERLAY))
687             child->getsig(i).sa_handler = SIG_DFL;
688         }
689       if (hToken)
690         {
691           /* Set child->uid to USHRT_MAX to force calling internal_getlogin()
692              from child process. Clear username and psid to play it safe. */
693           child->uid = USHRT_MAX;
694           child->psid = NULL;
695         }
696       child.remember ();
697     }
698
699   sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
700   /* Start the child running */
701   ResumeThread (pi.hThread);
702   ForceCloseHandle (pi.hThread);
703
704   if (hToken && hToken != myself->token)
705     CloseHandle (hToken);
706
707   DWORD res;
708
709   if (mode == _P_OVERLAY)
710     {
711       BOOL exited;
712
713       HANDLE waitbuf[3] = {pi.hProcess, signal_arrived, spr};
714       int nwait = 3;
715
716       SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
717       res = 0;
718       DWORD timeout = INFINITE;
719       exec_exit = 1;
720       exited = FALSE;
721       MALLOC_CHECK;
722       for (int i = 0; i < 100; i++)
723         {
724           switch (WaitForMultipleObjects (nwait, waitbuf, FALSE, timeout))
725             {
726             case WAIT_TIMEOUT:
727               syscall_printf ("WFMO timed out after signal");
728               if (WaitForSingleObject (pi.hProcess, 0) != WAIT_OBJECT_0)
729                 {
730                   sigproc_printf ("subprocess still alive after signal");
731                   res = exec_exit;
732                 }
733               else
734                 {
735                   sigproc_printf ("subprocess exited after signal");
736             case WAIT_OBJECT_0:
737                   sigproc_printf ("subprocess exited");
738                   if (!GetExitCodeProcess (pi.hProcess, &res))
739                     res = exec_exit;
740                   exited = TRUE;
741                  }
742               if (nwait > 2)
743                 if (WaitForSingleObject (spr, 1) == WAIT_OBJECT_0)
744                   res |= EXIT_REPARENTING;
745                 else if (!(res & EXIT_REPARENTING))
746                   {
747                     MALLOC_CHECK;
748                     close_all_files ();
749                     MALLOC_CHECK;
750                   }
751               break;
752             case WAIT_OBJECT_0 + 1:
753               sigproc_printf ("signal arrived");
754               ResetEvent (signal_arrived);
755               continue;
756             case WAIT_OBJECT_0 + 2:
757               res = EXIT_REPARENTING;
758               MALLOC_CHECK;
759               ForceCloseHandle (spr);
760               MALLOC_CHECK;
761               if (!parent_alive)
762                 {
763                   nwait = 1;
764                   sigproc_terminate ();
765                   continue;
766                 }
767               break;
768             case WAIT_FAILED:
769               DWORD r;
770               system_printf ("wait failed: nwait %d, pid %d, winpid %d, %E",
771                              nwait, myself->pid, myself->dwProcessId);
772               system_printf ("waitbuf[0] %p %d", waitbuf[0],
773                              GetHandleInformation (waitbuf[0], &r));
774               system_printf ("waitbuf[1] %p = %d", waitbuf[1],
775                              GetHandleInformation (waitbuf[1], &r));
776               set_errno (ECHILD);
777               return -1;
778             }
779           break;
780         }
781
782       if (nwait > 2)
783         ForceCloseHandle (spr);
784
785       sigproc_printf ("res = %x", res);
786
787       if (res & EXIT_REPARENTING)
788         {
789           /* Try to reparent child process.
790            * Make handles to child available to parent process and exit with
791            * EXIT_REPARENTING status. Wait() syscall in parent will then wait
792            * for newly created child.
793            */
794           pinfo parent (myself->ppid);
795           if (!parent)
796             /* nothing */;
797           else
798             {
799               HANDLE hP = OpenProcess (PROCESS_ALL_ACCESS, FALSE,
800                                        parent->dwProcessId);
801               sigproc_printf ("parent handle %p, pid %d", hP, parent->dwProcessId);
802               if (hP == NULL && GetLastError () == ERROR_INVALID_PARAMETER)
803                 res = 1;
804               else if (hP)
805                 {
806                   ProtectHandle (hP);
807                   res = DuplicateHandle (hMainProc, pi.hProcess, hP,
808                                          &myself->hProcess, 0, FALSE,
809                                          DUPLICATE_SAME_ACCESS);
810                   sigproc_printf ("Dup hP %d", res);
811                   ForceCloseHandle (hP);
812                 }
813               if (!res)
814                 {
815                   system_printf ("Reparent failed, parent handle %p, %E", hP);
816                   system_printf ("my dwProcessId %d, myself->dwProcessId %d",
817                                  GetCurrentProcessId(), myself->dwProcessId);
818                   system_printf ("myself->process_state %x",
819                                  myself->process_state);
820                   system_printf ("myself->hProcess %x", myself->hProcess);
821                 }
822             }
823           res = EXIT_REPARENTING;
824           ForceCloseHandle1 (hExeced, childhProc);
825           hExeced = INVALID_HANDLE_VALUE;
826         }
827       else if (exited)
828         {
829           ForceCloseHandle1 (hExeced, childhProc);
830           hExeced = INVALID_HANDLE_VALUE; // stop do_exit from attempting to terminate child
831         }
832
833       MALLOC_CHECK;
834       do_exit (res | EXIT_NOCLOSEALL);
835     }
836
837   if (mode == _P_WAIT)
838     waitpid (cygpid, (int *) &res, 0);
839   else if (mode == _P_DETACH)
840     res = 0;    /* Lose all memory of this child. */
841   else if ((mode == _P_NOWAIT) || (mode == _P_NOWAITO))
842     res = cygpid;
843
844   return (int) res;
845 }
846
847 extern "C"
848 int
849 cwait (int *result, int pid, int)
850 {
851   return waitpid (pid, result, 0);
852 }
853
854 /*
855  * Helper function for spawn runtime calls.
856  * Doesn't search the path.
857  */
858
859 extern "C" int
860 _spawnve (HANDLE hToken, int mode, const char *path, const char *const *argv,
861           const char *const *envp)
862 {
863   int ret;
864   vfork_save *vf = vfork_storage.val ();
865
866   if (vf != NULL && (vf->pid < 0) && mode == _P_OVERLAY)
867     mode = _P_NOWAIT;
868   else
869     vf = NULL;
870
871   syscall_printf ("_spawnve (%s, %s, %x)", path, argv[0], envp);
872
873   switch (mode)
874     {
875       case _P_OVERLAY:
876         /* We do not pass _P_SEARCH_PATH here. execve doesn't search PATH.*/
877         /* Just act as an exec if _P_OVERLAY set. */
878         spawn_guts (hToken, path, argv, envp, mode);
879         /* Errno should be set by spawn_guts.  */
880         ret = -1;
881         break;
882       case _P_NOWAIT:
883       case _P_NOWAITO:
884       case _P_WAIT:
885       case _P_DETACH:
886         subproc_init ();
887         ret = spawn_guts (hToken, path, argv, envp, mode);
888         if (vf && ret > 0)
889           {
890             vf->pid = ret;
891             longjmp (vf->j, 1);
892           }
893         break;
894       default:
895         set_errno (EINVAL);
896         ret = -1;
897         break;
898     }
899   return ret;
900 }
901
902 /*
903  * spawn functions as implemented in the MS runtime library.
904  * Most of these based on (and copied from) newlib/libc/posix/execXX.c
905  */
906
907 extern "C"
908 int
909 spawnl (int mode, const char *path, const char *arg0, ...)
910 {
911   int i;
912   va_list args;
913   const char *argv[256];
914
915   va_start (args, arg0);
916   argv[0] = arg0;
917   i = 1;
918
919   do
920       argv[i] = va_arg (args, const char *);
921   while (argv[i++] != NULL);
922
923   va_end (args);
924
925   return _spawnve (NULL, mode, path, (char * const  *) argv, cur_environ ());
926 }
927
928 extern "C"
929 int
930 spawnle (int mode, const char *path, const char *arg0, ...)
931 {
932   int i;
933   va_list args;
934   const char * const *envp;
935   const char *argv[256];
936
937   va_start (args, arg0);
938   argv[0] = arg0;
939   i = 1;
940
941   do
942     argv[i] = va_arg (args, const char *);
943   while (argv[i++] != NULL);
944
945   envp = va_arg (args, const char * const *);
946   va_end (args);
947
948   return _spawnve (NULL, mode, path, (char * const *) argv,
949                    (char * const *) envp);
950 }
951
952 extern "C"
953 int
954 spawnlp (int mode, const char *path, const char *arg0, ...)
955 {
956   int i;
957   va_list args;
958   const char *argv[256];
959
960   va_start (args, arg0);
961   argv[0] = arg0;
962   i = 1;
963
964   do
965       argv[i] = va_arg (args, const char *);
966   while (argv[i++] != NULL);
967
968   va_end (args);
969
970   return spawnvpe (mode, path, (char * const *) argv, cur_environ ());
971 }
972
973 extern "C"
974 int
975 spawnlpe (int mode, const char *path, const char *arg0, ...)
976 {
977   int i;
978   va_list args;
979   const char * const *envp;
980   const char *argv[256];
981
982   va_start (args, arg0);
983   argv[0] = arg0;
984   i = 1;
985
986   do
987     argv[i] = va_arg (args, const char *);
988   while (argv[i++] != NULL);
989
990   envp = va_arg (args, const char * const *);
991   va_end (args);
992
993   return spawnvpe (mode, path, (char * const *) argv, envp);
994 }
995
996 extern "C"
997 int
998 spawnv (int mode, const char *path, const char * const *argv)
999 {
1000   return _spawnve (NULL, mode, path, argv, cur_environ ());
1001 }
1002
1003 extern "C"
1004 int
1005 spawnve (int mode, const char *path, char * const *argv,
1006                                              const char * const *envp)
1007 {
1008   return _spawnve (NULL, mode, path, argv, envp);
1009 }
1010
1011 extern "C"
1012 int
1013 spawnvp (int mode, const char *path, const char * const *argv)
1014 {
1015   return spawnvpe (mode, path, argv, cur_environ ());
1016 }
1017
1018 extern "C"
1019 int
1020 spawnvpe (int mode, const char *file, const char * const *argv,
1021                                              const char * const *envp)
1022 {
1023   path_conv buf;
1024   return _spawnve (NULL, mode, find_exec (file, buf), argv, envp);
1025 }