1 /* Utilities to execute a program in a subprocess (possibly linked by pipes
2    with other subprocesses), and wait for it.  Generic Win32 specialization.
3    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005
4    Free Software Foundation, Inc.
5 
6 This file is part of the libiberty library.
7 Libiberty is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11 
12 Libiberty is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Library General Public License for more details.
16 
17 You should have received a copy of the GNU Library General Public
18 License along with libiberty; see the file COPYING.LIB.  If not,
19 write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 Boston, MA 02110-1301, USA.  */
21 
22 #include "pex-common.h"
23 
24 #include <windows.h>
25 
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 #ifdef HAVE_SYS_WAIT_H
36 #include <sys/wait.h>
37 #endif
38 
39 #include <process.h>
40 #include <io.h>
41 #include <fcntl.h>
42 #include <signal.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 
46 /* mingw32 headers may not define the following.  */
47 
48 #ifndef _P_WAIT
49 #  define _P_WAIT	0
50 #  define _P_NOWAIT	1
51 #  define _P_OVERLAY	2
52 #  define _P_NOWAITO	3
53 #  define _P_DETACH	4
54 
55 #  define WAIT_CHILD		0
56 #  define WAIT_GRANDCHILD	1
57 #endif
58 
59 #define MINGW_NAME "Minimalist GNU for Windows"
60 #define MINGW_NAME_LEN (sizeof(MINGW_NAME) - 1)
61 
62 /* Ensure that the executable pathname uses Win32 backslashes. This
63    is not necessary on NT, but on W9x, forward slashes causes
64    failure of spawn* and exec* functions (and probably any function
65    that calls CreateProcess) *iff* the executable pathname (argv[0])
66    is a quoted string.  And quoting is necessary in case a pathname
67    contains embedded white space.  You can't win.  */
68 static void
backslashify(char * s)69 backslashify (char *s)
70 {
71   while ((s = strchr (s, '/')) != NULL)
72     *s = '\\';
73   return;
74 }
75 
76 static int pex_win32_open_read (struct pex_obj *, const char *, int);
77 static int pex_win32_open_write (struct pex_obj *, const char *, int);
78 static long pex_win32_exec_child (struct pex_obj *, int, const char *,
79 				  char * const *, int, int, int,
80 				  const char **, int *);
81 static int pex_win32_close (struct pex_obj *, int);
82 static int pex_win32_wait (struct pex_obj *, long, int *,
83 			   struct pex_time *, int, const char **, int *);
84 static int pex_win32_pipe (struct pex_obj *, int *, int);
85 static FILE *pex_win32_fdopenr (struct pex_obj *, int, int);
86 static FILE *pex_win32_fdopenw (struct pex_obj *, int, int);
87 
88 /* The list of functions we pass to the common routines.  */
89 
90 const struct pex_funcs funcs =
91 {
92   pex_win32_open_read,
93   pex_win32_open_write,
94   pex_win32_exec_child,
95   pex_win32_close,
96   pex_win32_wait,
97   pex_win32_pipe,
98   pex_win32_fdopenr,
99   pex_win32_fdopenw,
100   NULL /* cleanup */
101 };
102 
103 /* Return a newly initialized pex_obj structure.  */
104 
105 struct pex_obj *
pex_init(int flags,const char * pname,const char * tempbase)106 pex_init (int flags, const char *pname, const char *tempbase)
107 {
108   return pex_init_common (flags, pname, tempbase, &funcs);
109 }
110 
111 /* Open a file for reading.  */
112 
113 static int
pex_win32_open_read(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary)114 pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
115 		     int binary)
116 {
117   return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT));
118 }
119 
120 /* Open a file for writing.  */
121 
122 static int
pex_win32_open_write(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary)123 pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
124 		      int binary)
125 {
126   /* Note that we can't use O_EXCL here because gcc may have already
127      created the temporary file via make_temp_file.  */
128   return _open (name,
129 		(_O_WRONLY | _O_CREAT | _O_TRUNC
130 		 | (binary ? _O_BINARY : _O_TEXT)),
131 		_S_IREAD | _S_IWRITE);
132 }
133 
134 /* Close a file.  */
135 
136 static int
pex_win32_close(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd)137 pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
138 {
139   return _close (fd);
140 }
141 
142 #ifdef USE_MINGW_MSYS
143 static const char *mingw_keys[] = {"SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Uninstall", NULL};
144 
145 /* Tack the executable on the end of a (possibly slash terminated) buffer
146    and convert everything to \. */
147 static const char *
tack_on_executable(char * buf,const char * executable)148 tack_on_executable (char *buf, const char *executable)
149 {
150   char *p = strchr (buf, '\0');
151   if (p > buf && (p[-1] == '\\' || p[-1] == '/'))
152     p[-1] = '\0';
153   backslashify (strcat (buf, executable));
154   return buf;
155 }
156 
157 /* Walk down a registry hierarchy until the end.  Return the key. */
158 static HKEY
openkey(HKEY hStart,const char * keys[])159 openkey (HKEY hStart, const char *keys[])
160 {
161   HKEY hKey, hTmp;
162   for (hKey = hStart; *keys; keys++)
163     {
164       LONG res;
165       hTmp = hKey;
166       res = RegOpenKey (hTmp, *keys, &hKey);
167 
168       if (hTmp != HKEY_LOCAL_MACHINE)
169 	RegCloseKey (hTmp);
170 
171       if (res != ERROR_SUCCESS)
172 	return NULL;
173     }
174   return hKey;
175 }
176 
177 /* Return the "mingw root" as derived from the mingw uninstall information. */
178 static const char *
mingw_rootify(const char * executable)179 mingw_rootify (const char *executable)
180 {
181   HKEY hKey, hTmp;
182   DWORD maxlen;
183   char *namebuf, *foundbuf;
184   DWORD i;
185   LONG res;
186 
187   /* Open the uninstall "directory". */
188   hKey = openkey (HKEY_LOCAL_MACHINE, mingw_keys);
189 
190   /* Not found. */
191   if (!hKey)
192     return executable;
193 
194   /* Need to enumerate all of the keys here looking for one the most recent
195      one for MinGW. */
196   if (RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, &maxlen, NULL, NULL,
197 		       NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
198     {
199       RegCloseKey (hKey);
200       return executable;
201     }
202   namebuf = XNEWVEC (char, ++maxlen);
203   foundbuf = XNEWVEC (char, maxlen);
204   foundbuf[0] = '\0';
205   if (!namebuf || !foundbuf)
206     {
207       RegCloseKey (hKey);
208       if (namebuf)
209 	free (namebuf);
210       if (foundbuf)
211 	free (foundbuf);
212       return executable;
213     }
214 
215   /* Look through all of the keys for one that begins with Minimal GNU...
216      Try to get the latest version by doing a string compare although that
217      string never really works with version number sorting. */
218   for (i = 0; RegEnumKey (hKey, i, namebuf, maxlen) == ERROR_SUCCESS; i++)
219     {
220       int match = strcasecmp (namebuf, MINGW_NAME);
221       if (match < 0)
222 	continue;
223       if (match > 0 && strncasecmp (namebuf, MINGW_NAME, MINGW_NAME_LEN) > 0)
224 	continue;
225       if (strcasecmp (namebuf, foundbuf) > 0)
226 	strcpy (foundbuf, namebuf);
227     }
228   free (namebuf);
229 
230   /* If foundbuf is empty, we didn't find anything.  Punt. */
231   if (!foundbuf[0])
232     {
233       free (foundbuf);
234       RegCloseKey (hKey);
235       return executable;
236     }
237 
238   /* Open the key that we wanted */
239   res = RegOpenKey (hKey, foundbuf, &hTmp);
240   RegCloseKey (hKey);
241   free (foundbuf);
242 
243   /* Don't know why this would fail, but you gotta check */
244   if (res != ERROR_SUCCESS)
245     return executable;
246 
247   maxlen = 0;
248   /* Get the length of the value pointed to by InstallLocation */
249   if (RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, NULL,
250 		       &maxlen) != ERROR_SUCCESS || maxlen == 0)
251     {
252       RegCloseKey (hTmp);
253       return executable;
254     }
255 
256   /* Allocate space for the install location */
257   foundbuf = XNEWVEC (char, maxlen + strlen (executable));
258   if (!foundbuf)
259     {
260       free (foundbuf);
261       RegCloseKey (hTmp);
262     }
263 
264   /* Read the install location into the buffer */
265   res = RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, (LPBYTE) foundbuf,
266 			 &maxlen);
267   RegCloseKey (hTmp);
268   if (res != ERROR_SUCCESS)
269     {
270       free (foundbuf);
271       return executable;
272     }
273 
274   /* Concatenate the install location and the executable, turn all slashes
275      to backslashes, and return that. */
276   return tack_on_executable (foundbuf, executable);
277 }
278 
279 /* Read the install location of msys from it's installation file and
280    rootify the executable based on that. */
281 static const char *
msys_rootify(const char * executable)282 msys_rootify (const char *executable)
283 {
284   size_t bufsize = 64;
285   size_t execlen = strlen (executable) + 1;
286   char *buf;
287   DWORD res = 0;
288   for (;;)
289     {
290       buf = XNEWVEC (char, bufsize + execlen);
291       if (!buf)
292 	break;
293       res = GetPrivateProfileString ("InstallSettings", "InstallPath", NULL,
294 				     buf, bufsize, "msys.ini");
295       if (!res)
296 	break;
297       if (strlen (buf) < bufsize)
298 	break;
299       res = 0;
300       free (buf);
301       bufsize *= 2;
302       if (bufsize > 65536)
303 	{
304 	  buf = NULL;
305 	  break;
306 	}
307     }
308 
309   if (res)
310     return tack_on_executable (buf, executable);
311 
312   /* failed */
313   if (buf)
314     free (buf);
315   return executable;
316 }
317 #endif
318 
319 /* Return a Windows command-line from ARGV.  It is the caller's
320    responsibility to free the string returned.  */
321 
322 static char *
argv_to_cmdline(char * const * argv)323 argv_to_cmdline (char *const *argv)
324 {
325   char *cmdline;
326   char *p;
327   size_t cmdline_len;
328   int i, j, k;
329 
330   cmdline_len = 0;
331   for (i = 0; argv[i]; i++)
332     {
333       /* We quote every last argument.  This simplifies the problem;
334 	 we need only escape embedded double-quotes and immediately
335 	 preceeding backslash characters.  A sequence of backslach characters
336 	 that is not follwed by a double quote character will not be
337 	 escaped.  */
338       for (j = 0; argv[i][j]; j++)
339 	{
340 	  if (argv[i][j] == '"')
341 	    {
342 	      /* Escape preceeding backslashes.  */
343 	      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
344 		cmdline_len++;
345 	      /* Escape the qote character.  */
346 	      cmdline_len++;
347 	    }
348 	}
349       /* Trailing backslashes also need to be escaped because they will be
350          followed by the terminating quote.  */
351       for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
352 	cmdline_len++;
353       cmdline_len += j;
354       cmdline_len += 3;  /* for leading and trailing quotes and space */
355     }
356   cmdline = xmalloc (cmdline_len);
357   p = cmdline;
358   for (i = 0; argv[i]; i++)
359     {
360       *p++ = '"';
361       for (j = 0; argv[i][j]; j++)
362 	{
363 	  if (argv[i][j] == '"')
364 	    {
365 	      for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
366 		*p++ = '\\';
367 	      *p++ = '\\';
368 	    }
369 	  *p++ = argv[i][j];
370 	}
371       for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
372 	*p++ = '\\';
373       *p++ = '"';
374       *p++ = ' ';
375     }
376   p[-1] = '\0';
377   return cmdline;
378 }
379 
380 static const char *const
381 std_suffixes[] = {
382   ".com",
383   ".exe",
384   ".bat",
385   ".cmd",
386   0
387 };
388 static const char *const
389 no_suffixes[] = {
390   "",
391   0
392 };
393 
394 /* Returns the full path to PROGRAM.  If SEARCH is true, look for
395    PROGRAM in each directory in PATH.  */
396 
397 static char *
find_executable(const char * program,BOOL search)398 find_executable (const char *program, BOOL search)
399 {
400   char *full_executable;
401   char *e;
402   size_t fe_len;
403   const char *path = 0;
404   const char *const *ext;
405   const char *p, *q;
406   size_t proglen = strlen (program);
407   int has_extension = !!strchr (program, '.');
408   int has_slash = (strchr (program, '/') || strchr (program, '\\'));
409   HANDLE h;
410 
411   if (has_slash)
412     search = FALSE;
413 
414   if (search)
415     path = getenv ("PATH");
416   if (!path)
417     path = "";
418 
419   fe_len = 0;
420   for (p = path; *p; p = q)
421     {
422       q = p;
423       while (*q != ';' && *q != '\0')
424 	q++;
425       if ((size_t)(q - p) > fe_len)
426 	fe_len = q - p;
427       if (*q == ';')
428 	q++;
429     }
430   fe_len = fe_len + 1 + proglen + (has_extension ? 1 : 5);
431   full_executable = xmalloc (fe_len);
432 
433   p = path;
434   do
435     {
436       q = p;
437       while (*q != ';' && *q != '\0')
438 	q++;
439 
440       e = full_executable;
441       memcpy (e, p, q - p);
442       e += (q - p);
443       if (q - p)
444 	*e++ = '\\';
445       strcpy (e, program);
446 
447       if (*q == ';')
448 	q++;
449 
450       for (e = full_executable; *e; e++)
451 	if (*e == '/')
452 	  *e = '\\';
453 
454       /* At this point, e points to the terminating NUL character for
455          full_executable.  */
456       for (ext = has_extension ? no_suffixes : std_suffixes; *ext; ext++)
457 	{
458 	  /* Remove any current extension.  */
459 	  *e = '\0';
460 	  /* Add the new one.  */
461 	  strcat (full_executable, *ext);
462 
463 	  /* Attempt to open this file.  */
464 	  h = CreateFile (full_executable, GENERIC_READ,
465 			  FILE_SHARE_READ | FILE_SHARE_WRITE,
466 			  0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
467 	  if (h != INVALID_HANDLE_VALUE)
468 	    goto found;
469 	}
470       p = q;
471     }
472   while (*p);
473   free (full_executable);
474   return 0;
475 
476  found:
477   CloseHandle (h);
478   return full_executable;
479 }
480 
481 /* Low-level process creation function.  */
482 
483 static long
win32_spawn(const char * executable,BOOL search,char * const * argv,DWORD dwCreationFlags,LPSTARTUPINFO si,LPPROCESS_INFORMATION pi)484 win32_spawn (const char *executable,
485 	     BOOL search,
486 	     char *const *argv,
487 	     DWORD dwCreationFlags,
488 	     LPSTARTUPINFO si,
489 	     LPPROCESS_INFORMATION pi)
490 {
491   char *full_executable;
492   char *cmdline;
493 
494   full_executable = NULL;
495   cmdline = NULL;
496 
497   full_executable = find_executable (executable, search);
498   if (!full_executable)
499     goto error;
500   cmdline = argv_to_cmdline (argv);
501   if (!cmdline)
502     goto error;
503 
504   /* Create the child process.  */
505   if (!CreateProcess (full_executable, cmdline,
506 		      /*lpProcessAttributes=*/NULL,
507 		      /*lpThreadAttributes=*/NULL,
508 		      /*bInheritHandles=*/TRUE,
509 		      dwCreationFlags,
510 		      /*lpEnvironment=*/NULL,
511 		      /*lpCurrentDirectory=*/NULL,
512 		      si,
513 		      pi))
514     {
515       free (full_executable);
516       return -1;
517     }
518 
519   /* Clean up.  */
520   CloseHandle (pi->hThread);
521   free (full_executable);
522 
523   return (long) pi->hProcess;
524 
525  error:
526   if (cmdline)
527     free (cmdline);
528   if (full_executable)
529     free (full_executable);
530   return -1;
531 }
532 
533 static long
spawn_script(const char * executable,char * const * argv,DWORD dwCreationFlags,LPSTARTUPINFO si,LPPROCESS_INFORMATION pi)534 spawn_script (const char *executable, char *const *argv,
535 	      DWORD dwCreationFlags,
536 	      LPSTARTUPINFO si,
537 	      LPPROCESS_INFORMATION pi)
538 {
539   int pid = -1;
540   int save_errno = errno;
541   int fd = _open (executable, _O_RDONLY);
542 
543   if (fd >= 0)
544     {
545       char buf[MAX_PATH + 5];
546       int len = _read (fd, buf, sizeof (buf) - 1);
547       _close (fd);
548       if (len > 3)
549 	{
550 	  char *eol;
551 	  buf[len] = '\0';
552 	  eol = strchr (buf, '\n');
553 	  if (eol && strncmp (buf, "#!", 2) == 0)
554 	    {
555 	      char *executable1;
556 	      const char ** avhere = (const char **) --argv;
557 	      do
558 		*eol = '\0';
559 	      while (*--eol == '\r' || *eol == ' ' || *eol == '\t');
560 	      for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++)
561 		continue;
562 
563 	      backslashify (executable1);
564 	      *avhere = executable1;
565 #ifndef USE_MINGW_MSYS
566 	      executable = strrchr (executable1, '\\') + 1;
567 	      if (!executable)
568 		executable = executable1;
569 	      pid = win32_spawn (executable, TRUE, argv,
570 				 dwCreationFlags, si, pi);
571 #else
572 	      if (strchr (executable1, '\\') == NULL)
573 		pid = win32_spawn (executable1, TRUE, argv,
574 				   dwCreationFlags, si, pi);
575 	      else if (executable1[0] != '\\')
576 		pid = win32_spawn (executable1, FALSE, argv,
577 				   dwCreationFlags, si, pi);
578 	      else
579 		{
580 		  const char *newex = mingw_rootify (executable1);
581 		  *avhere = newex;
582 		  pid = win32_spawn (newex, FALSE, argv,
583 				     dwCreationFlags, si, pi);
584 		  if (executable1 != newex)
585 		    free ((char *) newex);
586 		  if (pid < 0)
587 		    {
588 		      newex = msys_rootify (executable1);
589 		      if (newex != executable1)
590 			{
591 			  *avhere = newex;
592 			  pid = win32_spawn (newex, FALSE, argv,
593 					     dwCreationFlags, si, pi);
594 			  free ((char *) newex);
595 			}
596 		    }
597 		}
598 #endif
599 	    }
600 	}
601     }
602   if (pid < 0)
603     errno = save_errno;
604   return pid;
605 }
606 
607 /* Execute a child.  */
608 
609 static long
pex_win32_exec_child(struct pex_obj * obj ATTRIBUTE_UNUSED,int flags,const char * executable,char * const * argv,int in,int out,int errdes,const char ** errmsg,int * err)610 pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
611 		      const char *executable, char * const * argv,
612 		      int in, int out, int errdes, const char **errmsg,
613 		      int *err)
614 {
615   long pid;
616   HANDLE stdin_handle;
617   HANDLE stdout_handle;
618   HANDLE stderr_handle;
619   DWORD dwCreationFlags;
620   OSVERSIONINFO version_info;
621   STARTUPINFO si;
622   PROCESS_INFORMATION pi;
623 
624   stdin_handle = INVALID_HANDLE_VALUE;
625   stdout_handle = INVALID_HANDLE_VALUE;
626   stderr_handle = INVALID_HANDLE_VALUE;
627 
628   stdin_handle = (HANDLE) _get_osfhandle (in);
629   stdout_handle = (HANDLE) _get_osfhandle (out);
630   if (!(flags & PEX_STDERR_TO_STDOUT))
631     stderr_handle = (HANDLE) _get_osfhandle (errdes);
632   else
633     stderr_handle = stdout_handle;
634 
635   /* Determine the version of Windows we are running on.  */
636   version_info.dwOSVersionInfoSize = sizeof (version_info);
637   GetVersionEx (&version_info);
638   if (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
639     /* On Windows 95/98/ME the CREATE_NO_WINDOW flag is not
640        supported, so we cannot avoid creating a console window.  */
641     dwCreationFlags = 0;
642   else
643     {
644       HANDLE conout_handle;
645 
646       /* Determine whether or not we have an associated console.  */
647       conout_handle = CreateFile("CONOUT$",
648 				 GENERIC_WRITE,
649 				 FILE_SHARE_WRITE,
650 				 /*lpSecurityAttributes=*/NULL,
651 				 OPEN_EXISTING,
652 				 FILE_ATTRIBUTE_NORMAL,
653 				 /*hTemplateFile=*/NULL);
654       if (conout_handle == INVALID_HANDLE_VALUE)
655 	/* There is no console associated with this process.  Since
656 	   the child is a console process, the OS would normally
657 	   create a new console Window for the child.  Since we'll be
658 	   redirecting the child's standard streams, we do not need
659 	   the console window.  */
660 	dwCreationFlags = CREATE_NO_WINDOW;
661       else
662 	{
663 	  /* There is a console associated with the process, so the OS
664 	     will not create a new console.  And, if we use
665 	     CREATE_NO_WINDOW in this situation, the child will have
666 	     no associated console.  Therefore, if the child's
667 	     standard streams are connected to the console, the output
668 	     will be discarded.  */
669 	  CloseHandle(conout_handle);
670 	  dwCreationFlags = 0;
671 	}
672     }
673 
674   /* Since the child will be a console process, it will, by default,
675      connect standard input/output to its console.  However, we want
676      the child to use the handles specifically designated above.  In
677      addition, if there is no console (such as when we are running in
678      a Cygwin X window), then we must redirect the child's
679      input/output, as there is no console for the child to use.  */
680   memset (&si, 0, sizeof (si));
681   si.cb = sizeof (si);
682   si.dwFlags = STARTF_USESTDHANDLES;
683   si.hStdInput = stdin_handle;
684   si.hStdOutput = stdout_handle;
685   si.hStdError = stderr_handle;
686 
687   /* Create the child process.  */
688   pid = win32_spawn (executable, (flags & PEX_SEARCH) != 0,
689 		     argv, dwCreationFlags, &si, &pi);
690   if (pid == -1)
691     pid = spawn_script (executable, argv, dwCreationFlags, &si, &pi);
692   if (pid == -1)
693     {
694       *err = ENOENT;
695       *errmsg = "CreateProcess";
696     }
697 
698   /* Close the standard output and standard error handles in the
699      parent.  */
700   if (out != STDOUT_FILENO)
701     obj->funcs->close (obj, out);
702   if (errdes != STDERR_FILENO)
703     obj->funcs->close (obj, errdes);
704 
705   return pid;
706 }
707 
708 /* Wait for a child process to complete.  MS CRTDLL doesn't return
709    enough information in status to decide if the child exited due to a
710    signal or not, rather it simply returns an integer with the exit
711    code of the child; eg., if the child exited with an abort() call
712    and didn't have a handler for SIGABRT, it simply returns with
713    status == 3.  We fix the status code to conform to the usual WIF*
714    macros.  Note that WIFSIGNALED will never be true under CRTDLL. */
715 
716 static int
pex_win32_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,long pid,int * status,struct pex_time * time,int done ATTRIBUTE_UNUSED,const char ** errmsg,int * err)717 pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, long pid,
718 		int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED,
719 		const char **errmsg, int *err)
720 {
721   DWORD termstat;
722   HANDLE h;
723 
724   if (time != NULL)
725     memset (time, 0, sizeof *time);
726 
727   h = (HANDLE) pid;
728 
729   /* FIXME: If done is non-zero, we should probably try to kill the
730      process.  */
731   if (WaitForSingleObject (h, INFINITE) != WAIT_OBJECT_0)
732     {
733       CloseHandle (h);
734       *err = ECHILD;
735       *errmsg = "WaitForSingleObject";
736       return -1;
737     }
738 
739   GetExitCodeProcess (h, &termstat);
740   CloseHandle (h);
741 
742   /* A value of 3 indicates that the child caught a signal, but not
743      which one.  Since only SIGABRT, SIGFPE and SIGINT do anything, we
744      report SIGABRT.  */
745   if (termstat == 3)
746     *status = SIGABRT;
747   else
748     *status = (termstat & 0xff) << 8;
749 
750   return 0;
751 }
752 
753 /* Create a pipe.  */
754 
755 static int
pex_win32_pipe(struct pex_obj * obj ATTRIBUTE_UNUSED,int * p,int binary)756 pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
757 		int binary)
758 {
759   return _pipe (p, 256, binary ? _O_BINARY : _O_TEXT);
760 }
761 
762 /* Get a FILE pointer to read from a file descriptor.  */
763 
764 static FILE *
pex_win32_fdopenr(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary)765 pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
766 		   int binary)
767 {
768   return fdopen (fd, binary ? "rb" : "r");
769 }
770 
771 static FILE *
pex_win32_fdopenw(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary)772 pex_win32_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
773 		   int binary)
774 {
775   HANDLE h = (HANDLE) _get_osfhandle (fd);
776   if (h == INVALID_HANDLE_VALUE)
777     return NULL;
778   if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0))
779     return NULL;
780   return fdopen (fd, binary ? "wb" : "w");
781 }
782 
783 #ifdef MAIN
784 #include <stdio.h>
785 
786 int
main(int argc ATTRIBUTE_UNUSED,char ** argv)787 main (int argc ATTRIBUTE_UNUSED, char **argv)
788 {
789   char const *errmsg;
790   int err;
791   argv++;
792   printf ("%ld\n", pex_win32_exec_child (NULL, PEX_SEARCH, argv[0], argv, 0, 1, 2, &errmsg, &err));
793   exit (0);
794 }
795 #endif
796