198b9484cSchristos /* Utilities to execute a program in a subprocess (possibly linked by pipes 298b9484cSchristos with other subprocesses), and wait for it. Generic Win32 specialization. 3*7e120ff0Schristos Copyright (C) 1996-2024 Free Software Foundation, Inc. 498b9484cSchristos 598b9484cSchristos This file is part of the libiberty library. 698b9484cSchristos Libiberty is free software; you can redistribute it and/or 798b9484cSchristos modify it under the terms of the GNU Library General Public 898b9484cSchristos License as published by the Free Software Foundation; either 998b9484cSchristos version 2 of the License, or (at your option) any later version. 1098b9484cSchristos 1198b9484cSchristos Libiberty is distributed in the hope that it will be useful, 1298b9484cSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of 1398b9484cSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1498b9484cSchristos Library General Public License for more details. 1598b9484cSchristos 1698b9484cSchristos You should have received a copy of the GNU Library General Public 1798b9484cSchristos License along with libiberty; see the file COPYING.LIB. If not, 1898b9484cSchristos write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, 1998b9484cSchristos Boston, MA 02110-1301, USA. */ 2098b9484cSchristos 2198b9484cSchristos #include "pex-common.h" 2298b9484cSchristos 23*7e120ff0Schristos #define WIN32_LEAN_AND_MEAN 2498b9484cSchristos #include <windows.h> 2598b9484cSchristos 2698b9484cSchristos #ifdef HAVE_STDLIB_H 2798b9484cSchristos #include <stdlib.h> 2898b9484cSchristos #endif 2998b9484cSchristos #ifdef HAVE_STRING_H 3098b9484cSchristos #include <string.h> 3198b9484cSchristos #endif 3298b9484cSchristos #ifdef HAVE_UNISTD_H 3398b9484cSchristos #include <unistd.h> 3498b9484cSchristos #endif 3598b9484cSchristos #ifdef HAVE_SYS_WAIT_H 3698b9484cSchristos #include <sys/wait.h> 3798b9484cSchristos #endif 3898b9484cSchristos 3998b9484cSchristos #include <assert.h> 4098b9484cSchristos #include <process.h> 4198b9484cSchristos #include <io.h> 4298b9484cSchristos #include <fcntl.h> 4398b9484cSchristos #include <signal.h> 4498b9484cSchristos #include <sys/stat.h> 4598b9484cSchristos #include <errno.h> 4698b9484cSchristos #include <ctype.h> 4798b9484cSchristos 4898b9484cSchristos /* mingw32 headers may not define the following. */ 4998b9484cSchristos 5098b9484cSchristos #ifndef _P_WAIT 5198b9484cSchristos # define _P_WAIT 0 5298b9484cSchristos # define _P_NOWAIT 1 5398b9484cSchristos # define _P_OVERLAY 2 5498b9484cSchristos # define _P_NOWAITO 3 5598b9484cSchristos # define _P_DETACH 4 5698b9484cSchristos 5798b9484cSchristos # define WAIT_CHILD 0 5898b9484cSchristos # define WAIT_GRANDCHILD 1 5998b9484cSchristos #endif 6098b9484cSchristos 6198b9484cSchristos #define MINGW_NAME "Minimalist GNU for Windows" 6298b9484cSchristos #define MINGW_NAME_LEN (sizeof(MINGW_NAME) - 1) 6398b9484cSchristos 6498b9484cSchristos extern char *stpcpy (char *dst, const char *src); 6598b9484cSchristos 6698b9484cSchristos /* Ensure that the executable pathname uses Win32 backslashes. This 6798b9484cSchristos is not necessary on NT, but on W9x, forward slashes causes 6898b9484cSchristos failure of spawn* and exec* functions (and probably any function 6998b9484cSchristos that calls CreateProcess) *iff* the executable pathname (argv[0]) 7098b9484cSchristos is a quoted string. And quoting is necessary in case a pathname 7198b9484cSchristos contains embedded white space. You can't win. */ 7298b9484cSchristos static void 7398b9484cSchristos backslashify (char *s) 7498b9484cSchristos { 7598b9484cSchristos while ((s = strchr (s, '/')) != NULL) 7698b9484cSchristos *s = '\\'; 7798b9484cSchristos return; 7898b9484cSchristos } 7998b9484cSchristos 8098b9484cSchristos static int pex_win32_open_read (struct pex_obj *, const char *, int); 81837edd6bSchristos static int pex_win32_open_write (struct pex_obj *, const char *, int, int); 8298b9484cSchristos static pid_t pex_win32_exec_child (struct pex_obj *, int, const char *, 8398b9484cSchristos char * const *, char * const *, 8498b9484cSchristos int, int, int, int, 8598b9484cSchristos const char **, int *); 8698b9484cSchristos static int pex_win32_close (struct pex_obj *, int); 8798b9484cSchristos static pid_t pex_win32_wait (struct pex_obj *, pid_t, int *, 8898b9484cSchristos struct pex_time *, int, const char **, int *); 8998b9484cSchristos static int pex_win32_pipe (struct pex_obj *, int *, int); 9098b9484cSchristos static FILE *pex_win32_fdopenr (struct pex_obj *, int, int); 9198b9484cSchristos static FILE *pex_win32_fdopenw (struct pex_obj *, int, int); 9298b9484cSchristos 9398b9484cSchristos /* The list of functions we pass to the common routines. */ 9498b9484cSchristos 9598b9484cSchristos const struct pex_funcs funcs = 9698b9484cSchristos { 9798b9484cSchristos pex_win32_open_read, 9898b9484cSchristos pex_win32_open_write, 9998b9484cSchristos pex_win32_exec_child, 10098b9484cSchristos pex_win32_close, 10198b9484cSchristos pex_win32_wait, 10298b9484cSchristos pex_win32_pipe, 10398b9484cSchristos pex_win32_fdopenr, 10498b9484cSchristos pex_win32_fdopenw, 10598b9484cSchristos NULL /* cleanup */ 10698b9484cSchristos }; 10798b9484cSchristos 10898b9484cSchristos /* Return a newly initialized pex_obj structure. */ 10998b9484cSchristos 11098b9484cSchristos struct pex_obj * 11198b9484cSchristos pex_init (int flags, const char *pname, const char *tempbase) 11298b9484cSchristos { 11398b9484cSchristos return pex_init_common (flags, pname, tempbase, &funcs); 11498b9484cSchristos } 11598b9484cSchristos 11698b9484cSchristos /* Open a file for reading. */ 11798b9484cSchristos 11898b9484cSchristos static int 11998b9484cSchristos pex_win32_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name, 12098b9484cSchristos int binary) 12198b9484cSchristos { 12298b9484cSchristos return _open (name, _O_RDONLY | (binary ? _O_BINARY : _O_TEXT)); 12398b9484cSchristos } 12498b9484cSchristos 12598b9484cSchristos /* Open a file for writing. */ 12698b9484cSchristos 12798b9484cSchristos static int 12898b9484cSchristos pex_win32_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name, 129837edd6bSchristos int binary, int append) 13098b9484cSchristos { 13198b9484cSchristos /* Note that we can't use O_EXCL here because gcc may have already 13298b9484cSchristos created the temporary file via make_temp_file. */ 133837edd6bSchristos if (append) 134837edd6bSchristos return -1; 13598b9484cSchristos return _open (name, 13698b9484cSchristos (_O_WRONLY | _O_CREAT | _O_TRUNC 13798b9484cSchristos | (binary ? _O_BINARY : _O_TEXT)), 13898b9484cSchristos _S_IREAD | _S_IWRITE); 13998b9484cSchristos } 14098b9484cSchristos 14198b9484cSchristos /* Close a file. */ 14298b9484cSchristos 14398b9484cSchristos static int 14498b9484cSchristos pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd) 14598b9484cSchristos { 14698b9484cSchristos return _close (fd); 14798b9484cSchristos } 14898b9484cSchristos 14998b9484cSchristos #ifdef USE_MINGW_MSYS 15098b9484cSchristos static const char *mingw_keys[] = {"SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Uninstall", NULL}; 15198b9484cSchristos 15298b9484cSchristos /* Tack the executable on the end of a (possibly slash terminated) buffer 15398b9484cSchristos and convert everything to \. */ 15498b9484cSchristos static const char * 15598b9484cSchristos tack_on_executable (char *buf, const char *executable) 15698b9484cSchristos { 15798b9484cSchristos char *p = strchr (buf, '\0'); 15898b9484cSchristos if (p > buf && (p[-1] == '\\' || p[-1] == '/')) 15998b9484cSchristos p[-1] = '\0'; 16098b9484cSchristos backslashify (strcat (buf, executable)); 16198b9484cSchristos return buf; 16298b9484cSchristos } 16398b9484cSchristos 16498b9484cSchristos /* Walk down a registry hierarchy until the end. Return the key. */ 16598b9484cSchristos static HKEY 16698b9484cSchristos openkey (HKEY hStart, const char *keys[]) 16798b9484cSchristos { 16898b9484cSchristos HKEY hKey, hTmp; 16998b9484cSchristos for (hKey = hStart; *keys; keys++) 17098b9484cSchristos { 17198b9484cSchristos LONG res; 17298b9484cSchristos hTmp = hKey; 17398b9484cSchristos res = RegOpenKey (hTmp, *keys, &hKey); 17498b9484cSchristos 17598b9484cSchristos if (hTmp != HKEY_LOCAL_MACHINE) 17698b9484cSchristos RegCloseKey (hTmp); 17798b9484cSchristos 17898b9484cSchristos if (res != ERROR_SUCCESS) 17998b9484cSchristos return NULL; 18098b9484cSchristos } 18198b9484cSchristos return hKey; 18298b9484cSchristos } 18398b9484cSchristos 18498b9484cSchristos /* Return the "mingw root" as derived from the mingw uninstall information. */ 18598b9484cSchristos static const char * 18698b9484cSchristos mingw_rootify (const char *executable) 18798b9484cSchristos { 18898b9484cSchristos HKEY hKey, hTmp; 18998b9484cSchristos DWORD maxlen; 19098b9484cSchristos char *namebuf, *foundbuf; 19198b9484cSchristos DWORD i; 19298b9484cSchristos LONG res; 19398b9484cSchristos 19498b9484cSchristos /* Open the uninstall "directory". */ 19598b9484cSchristos hKey = openkey (HKEY_LOCAL_MACHINE, mingw_keys); 19698b9484cSchristos 19798b9484cSchristos /* Not found. */ 19898b9484cSchristos if (!hKey) 19998b9484cSchristos return executable; 20098b9484cSchristos 20198b9484cSchristos /* Need to enumerate all of the keys here looking for one the most recent 20298b9484cSchristos one for MinGW. */ 20398b9484cSchristos if (RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, &maxlen, NULL, NULL, 20498b9484cSchristos NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 20598b9484cSchristos { 20698b9484cSchristos RegCloseKey (hKey); 20798b9484cSchristos return executable; 20898b9484cSchristos } 20998b9484cSchristos namebuf = XNEWVEC (char, ++maxlen); 21098b9484cSchristos foundbuf = XNEWVEC (char, maxlen); 21198b9484cSchristos foundbuf[0] = '\0'; 21298b9484cSchristos if (!namebuf || !foundbuf) 21398b9484cSchristos { 21498b9484cSchristos RegCloseKey (hKey); 21598b9484cSchristos free (namebuf); 21698b9484cSchristos free (foundbuf); 21798b9484cSchristos return executable; 21898b9484cSchristos } 21998b9484cSchristos 22098b9484cSchristos /* Look through all of the keys for one that begins with Minimal GNU... 22198b9484cSchristos Try to get the latest version by doing a string compare although that 22298b9484cSchristos string never really works with version number sorting. */ 22398b9484cSchristos for (i = 0; RegEnumKey (hKey, i, namebuf, maxlen) == ERROR_SUCCESS; i++) 22498b9484cSchristos { 22598b9484cSchristos int match = strcasecmp (namebuf, MINGW_NAME); 22698b9484cSchristos if (match < 0) 22798b9484cSchristos continue; 22898b9484cSchristos if (match > 0 && strncasecmp (namebuf, MINGW_NAME, MINGW_NAME_LEN) > 0) 22998b9484cSchristos continue; 23098b9484cSchristos if (strcasecmp (namebuf, foundbuf) > 0) 23198b9484cSchristos strcpy (foundbuf, namebuf); 23298b9484cSchristos } 23398b9484cSchristos free (namebuf); 23498b9484cSchristos 23598b9484cSchristos /* If foundbuf is empty, we didn't find anything. Punt. */ 23698b9484cSchristos if (!foundbuf[0]) 23798b9484cSchristos { 23898b9484cSchristos free (foundbuf); 23998b9484cSchristos RegCloseKey (hKey); 24098b9484cSchristos return executable; 24198b9484cSchristos } 24298b9484cSchristos 24398b9484cSchristos /* Open the key that we wanted */ 24498b9484cSchristos res = RegOpenKey (hKey, foundbuf, &hTmp); 24598b9484cSchristos RegCloseKey (hKey); 24698b9484cSchristos free (foundbuf); 24798b9484cSchristos 24898b9484cSchristos /* Don't know why this would fail, but you gotta check */ 24998b9484cSchristos if (res != ERROR_SUCCESS) 25098b9484cSchristos return executable; 25198b9484cSchristos 25298b9484cSchristos maxlen = 0; 25398b9484cSchristos /* Get the length of the value pointed to by InstallLocation */ 25498b9484cSchristos if (RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, NULL, 25598b9484cSchristos &maxlen) != ERROR_SUCCESS || maxlen == 0) 25698b9484cSchristos { 25798b9484cSchristos RegCloseKey (hTmp); 25898b9484cSchristos return executable; 25998b9484cSchristos } 26098b9484cSchristos 26198b9484cSchristos /* Allocate space for the install location */ 26298b9484cSchristos foundbuf = XNEWVEC (char, maxlen + strlen (executable)); 26398b9484cSchristos if (!foundbuf) 26498b9484cSchristos { 26598b9484cSchristos free (foundbuf); 26698b9484cSchristos RegCloseKey (hTmp); 26798b9484cSchristos } 26898b9484cSchristos 26998b9484cSchristos /* Read the install location into the buffer */ 27098b9484cSchristos res = RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, (LPBYTE) foundbuf, 27198b9484cSchristos &maxlen); 27298b9484cSchristos RegCloseKey (hTmp); 27398b9484cSchristos if (res != ERROR_SUCCESS) 27498b9484cSchristos { 27598b9484cSchristos free (foundbuf); 27698b9484cSchristos return executable; 27798b9484cSchristos } 27898b9484cSchristos 27998b9484cSchristos /* Concatenate the install location and the executable, turn all slashes 28098b9484cSchristos to backslashes, and return that. */ 28198b9484cSchristos return tack_on_executable (foundbuf, executable); 28298b9484cSchristos } 28398b9484cSchristos 28498b9484cSchristos /* Read the install location of msys from it's installation file and 28598b9484cSchristos rootify the executable based on that. */ 28698b9484cSchristos static const char * 28798b9484cSchristos msys_rootify (const char *executable) 28898b9484cSchristos { 28998b9484cSchristos size_t bufsize = 64; 29098b9484cSchristos size_t execlen = strlen (executable) + 1; 29198b9484cSchristos char *buf; 29298b9484cSchristos DWORD res = 0; 29398b9484cSchristos for (;;) 29498b9484cSchristos { 29598b9484cSchristos buf = XNEWVEC (char, bufsize + execlen); 29698b9484cSchristos if (!buf) 29798b9484cSchristos break; 29898b9484cSchristos res = GetPrivateProfileString ("InstallSettings", "InstallPath", NULL, 29998b9484cSchristos buf, bufsize, "msys.ini"); 30098b9484cSchristos if (!res) 30198b9484cSchristos break; 30298b9484cSchristos if (strlen (buf) < bufsize) 30398b9484cSchristos break; 30498b9484cSchristos res = 0; 30598b9484cSchristos free (buf); 30698b9484cSchristos bufsize *= 2; 30798b9484cSchristos if (bufsize > 65536) 30898b9484cSchristos { 30998b9484cSchristos buf = NULL; 31098b9484cSchristos break; 31198b9484cSchristos } 31298b9484cSchristos } 31398b9484cSchristos 31498b9484cSchristos if (res) 31598b9484cSchristos return tack_on_executable (buf, executable); 31698b9484cSchristos 31798b9484cSchristos /* failed */ 31898b9484cSchristos free (buf); 31998b9484cSchristos return executable; 32098b9484cSchristos } 32198b9484cSchristos #endif 32298b9484cSchristos 32398b9484cSchristos /* Return the number of arguments in an argv array, not including the null 32498b9484cSchristos terminating argument. */ 32598b9484cSchristos 32698b9484cSchristos static int 32798b9484cSchristos argv_to_argc (char *const *argv) 32898b9484cSchristos { 32998b9484cSchristos char *const *i = argv; 33098b9484cSchristos while (*i) 33198b9484cSchristos i++; 33298b9484cSchristos return i - argv; 33398b9484cSchristos } 33498b9484cSchristos 33598b9484cSchristos /* Return a Windows command-line from ARGV. It is the caller's 33698b9484cSchristos responsibility to free the string returned. */ 33798b9484cSchristos 33898b9484cSchristos static char * 33998b9484cSchristos argv_to_cmdline (char *const *argv) 34098b9484cSchristos { 34198b9484cSchristos char *cmdline; 34298b9484cSchristos char *p; 34398b9484cSchristos size_t cmdline_len; 34498b9484cSchristos int i, j, k; 345837edd6bSchristos int needs_quotes; 34698b9484cSchristos 34798b9484cSchristos cmdline_len = 0; 34898b9484cSchristos for (i = 0; argv[i]; i++) 34998b9484cSchristos { 350837edd6bSchristos /* We only quote arguments that contain spaces, \t or " characters to 351837edd6bSchristos prevent wasting 2 chars per argument of the CreateProcess 32k char 352837edd6bSchristos limit. We need only escape embedded double-quotes and immediately 35398b9484cSchristos preceeding backslash characters. A sequence of backslach characters 354*7e120ff0Schristos that is not followed by a double quote character will not be 35598b9484cSchristos escaped. */ 356837edd6bSchristos needs_quotes = 0; 35798b9484cSchristos for (j = 0; argv[i][j]; j++) 35898b9484cSchristos { 359837edd6bSchristos if (argv[i][j] == ' ' || argv[i][j] == '\t' || argv[i][j] == '"') 360837edd6bSchristos { 361837edd6bSchristos needs_quotes = 1; 362837edd6bSchristos } 363837edd6bSchristos 36498b9484cSchristos if (argv[i][j] == '"') 36598b9484cSchristos { 36698b9484cSchristos /* Escape preceeding backslashes. */ 36798b9484cSchristos for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--) 36898b9484cSchristos cmdline_len++; 369*7e120ff0Schristos /* Escape the quote character. */ 37098b9484cSchristos cmdline_len++; 37198b9484cSchristos } 37298b9484cSchristos } 373796c32c9Schristos if (j == 0) 374796c32c9Schristos needs_quotes = 1; 37598b9484cSchristos /* Trailing backslashes also need to be escaped because they will be 37698b9484cSchristos followed by the terminating quote. */ 377837edd6bSchristos if (needs_quotes) 378837edd6bSchristos { 37998b9484cSchristos for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--) 38098b9484cSchristos cmdline_len++; 381837edd6bSchristos } 38298b9484cSchristos cmdline_len += j; 383837edd6bSchristos /* for leading and trailing quotes and space */ 384837edd6bSchristos cmdline_len += needs_quotes * 2 + 1; 38598b9484cSchristos } 38698b9484cSchristos cmdline = XNEWVEC (char, cmdline_len); 38798b9484cSchristos p = cmdline; 38898b9484cSchristos for (i = 0; argv[i]; i++) 38998b9484cSchristos { 390837edd6bSchristos needs_quotes = 0; 391837edd6bSchristos for (j = 0; argv[i][j]; j++) 392837edd6bSchristos { 393837edd6bSchristos if (argv[i][j] == ' ' || argv[i][j] == '\t' || argv[i][j] == '"') 394837edd6bSchristos { 395837edd6bSchristos needs_quotes = 1; 396837edd6bSchristos break; 397837edd6bSchristos } 398837edd6bSchristos } 399796c32c9Schristos if (j == 0) 400796c32c9Schristos needs_quotes = 1; 401837edd6bSchristos 402837edd6bSchristos if (needs_quotes) 403837edd6bSchristos { 40498b9484cSchristos *p++ = '"'; 405837edd6bSchristos } 40698b9484cSchristos for (j = 0; argv[i][j]; j++) 40798b9484cSchristos { 40898b9484cSchristos if (argv[i][j] == '"') 40998b9484cSchristos { 41098b9484cSchristos for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--) 41198b9484cSchristos *p++ = '\\'; 41298b9484cSchristos *p++ = '\\'; 41398b9484cSchristos } 41498b9484cSchristos *p++ = argv[i][j]; 41598b9484cSchristos } 416837edd6bSchristos if (needs_quotes) 417837edd6bSchristos { 41898b9484cSchristos for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--) 41998b9484cSchristos *p++ = '\\'; 42098b9484cSchristos *p++ = '"'; 421837edd6bSchristos } 42298b9484cSchristos *p++ = ' '; 42398b9484cSchristos } 42498b9484cSchristos p[-1] = '\0'; 42598b9484cSchristos return cmdline; 42698b9484cSchristos } 42798b9484cSchristos 42898b9484cSchristos /* We'll try the passed filename with all the known standard 42998b9484cSchristos extensions, and then without extension. We try no extension 43098b9484cSchristos last so that we don't try to run some random extension-less 43198b9484cSchristos file that might be hanging around. We try both extension 43298b9484cSchristos and no extension so that we don't need any fancy logic 43398b9484cSchristos to determine if a file has extension. */ 43498b9484cSchristos static const char *const 43598b9484cSchristos std_suffixes[] = { 43698b9484cSchristos ".com", 43798b9484cSchristos ".exe", 43898b9484cSchristos ".bat", 43998b9484cSchristos ".cmd", 44098b9484cSchristos "", 44198b9484cSchristos 0 44298b9484cSchristos }; 44398b9484cSchristos 44498b9484cSchristos /* Returns the full path to PROGRAM. If SEARCH is true, look for 44598b9484cSchristos PROGRAM in each directory in PATH. */ 44698b9484cSchristos 44798b9484cSchristos static char * 44898b9484cSchristos find_executable (const char *program, BOOL search) 44998b9484cSchristos { 45098b9484cSchristos char *full_executable; 45198b9484cSchristos char *e; 45298b9484cSchristos size_t fe_len; 45398b9484cSchristos const char *path = 0; 45498b9484cSchristos const char *const *ext; 45598b9484cSchristos const char *p, *q; 45698b9484cSchristos size_t proglen = strlen (program); 45798b9484cSchristos int has_slash = (strchr (program, '/') || strchr (program, '\\')); 45898b9484cSchristos HANDLE h; 45998b9484cSchristos 46098b9484cSchristos if (has_slash) 46198b9484cSchristos search = FALSE; 46298b9484cSchristos 46398b9484cSchristos if (search) 46498b9484cSchristos path = getenv ("PATH"); 46598b9484cSchristos if (!path) 46698b9484cSchristos path = ""; 46798b9484cSchristos 46898b9484cSchristos fe_len = 0; 46998b9484cSchristos for (p = path; *p; p = q) 47098b9484cSchristos { 47198b9484cSchristos q = p; 47298b9484cSchristos while (*q != ';' && *q != '\0') 47398b9484cSchristos q++; 47498b9484cSchristos if ((size_t)(q - p) > fe_len) 47598b9484cSchristos fe_len = q - p; 47698b9484cSchristos if (*q == ';') 47798b9484cSchristos q++; 47898b9484cSchristos } 47998b9484cSchristos fe_len = fe_len + 1 + proglen + 5 /* space for extension */; 48098b9484cSchristos full_executable = XNEWVEC (char, fe_len); 48198b9484cSchristos 48298b9484cSchristos p = path; 48398b9484cSchristos do 48498b9484cSchristos { 48598b9484cSchristos q = p; 48698b9484cSchristos while (*q != ';' && *q != '\0') 48798b9484cSchristos q++; 48898b9484cSchristos 48998b9484cSchristos e = full_executable; 49098b9484cSchristos memcpy (e, p, q - p); 49198b9484cSchristos e += (q - p); 49298b9484cSchristos if (q - p) 49398b9484cSchristos *e++ = '\\'; 49498b9484cSchristos strcpy (e, program); 49598b9484cSchristos 49698b9484cSchristos if (*q == ';') 49798b9484cSchristos q++; 49898b9484cSchristos 49998b9484cSchristos for (e = full_executable; *e; e++) 50098b9484cSchristos if (*e == '/') 50198b9484cSchristos *e = '\\'; 50298b9484cSchristos 50398b9484cSchristos /* At this point, e points to the terminating NUL character for 50498b9484cSchristos full_executable. */ 50598b9484cSchristos for (ext = std_suffixes; *ext; ext++) 50698b9484cSchristos { 50798b9484cSchristos /* Remove any current extension. */ 50898b9484cSchristos *e = '\0'; 50998b9484cSchristos /* Add the new one. */ 51098b9484cSchristos strcat (full_executable, *ext); 51198b9484cSchristos 51298b9484cSchristos /* Attempt to open this file. */ 51398b9484cSchristos h = CreateFile (full_executable, GENERIC_READ, 51498b9484cSchristos FILE_SHARE_READ | FILE_SHARE_WRITE, 51598b9484cSchristos 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 51698b9484cSchristos if (h != INVALID_HANDLE_VALUE) 51798b9484cSchristos goto found; 51898b9484cSchristos } 51998b9484cSchristos p = q; 52098b9484cSchristos } 52198b9484cSchristos while (*p); 52298b9484cSchristos free (full_executable); 52398b9484cSchristos return 0; 52498b9484cSchristos 52598b9484cSchristos found: 52698b9484cSchristos CloseHandle (h); 52798b9484cSchristos return full_executable; 52898b9484cSchristos } 52998b9484cSchristos 53098b9484cSchristos /* Low-level process creation function and helper. */ 53198b9484cSchristos 53298b9484cSchristos static int 53398b9484cSchristos env_compare (const void *a_ptr, const void *b_ptr) 53498b9484cSchristos { 53598b9484cSchristos const char *a; 53698b9484cSchristos const char *b; 53798b9484cSchristos unsigned char c1; 53898b9484cSchristos unsigned char c2; 53998b9484cSchristos 54098b9484cSchristos a = *(const char **) a_ptr; 54198b9484cSchristos b = *(const char **) b_ptr; 54298b9484cSchristos 54398b9484cSchristos /* a and b will be of the form: VAR=VALUE 54498b9484cSchristos We compare only the variable name part here using a case-insensitive 54598b9484cSchristos comparison algorithm. It might appear that in fact strcasecmp () can 54698b9484cSchristos take the place of this whole function, and indeed it could, save for 54798b9484cSchristos the fact that it would fail in cases such as comparing A1=foo and 54898b9484cSchristos A=bar (because 1 is less than = in the ASCII character set). 54998b9484cSchristos (Environment variables containing no numbers would work in such a 55098b9484cSchristos scenario.) */ 55198b9484cSchristos 55298b9484cSchristos do 55398b9484cSchristos { 55498b9484cSchristos c1 = (unsigned char) tolower (*a++); 55598b9484cSchristos c2 = (unsigned char) tolower (*b++); 55698b9484cSchristos 55798b9484cSchristos if (c1 == '=') 55898b9484cSchristos c1 = '\0'; 55998b9484cSchristos 56098b9484cSchristos if (c2 == '=') 56198b9484cSchristos c2 = '\0'; 56298b9484cSchristos } 56398b9484cSchristos while (c1 == c2 && c1 != '\0'); 56498b9484cSchristos 56598b9484cSchristos return c1 - c2; 56698b9484cSchristos } 56798b9484cSchristos 56898b9484cSchristos /* Execute a Windows executable as a child process. This will fail if the 56998b9484cSchristos * target is not actually an executable, such as if it is a shell script. */ 57098b9484cSchristos 57198b9484cSchristos static pid_t 572*7e120ff0Schristos win32_spawn (struct pex_obj *obj, 573*7e120ff0Schristos const char *executable, 57498b9484cSchristos BOOL search, 57598b9484cSchristos char *const *argv, 57698b9484cSchristos char *const *env, /* array of strings of the form: VAR=VALUE */ 57798b9484cSchristos DWORD dwCreationFlags, 57898b9484cSchristos LPSTARTUPINFO si, 57998b9484cSchristos LPPROCESS_INFORMATION pi) 58098b9484cSchristos { 581*7e120ff0Schristos char *full_executable = NULL; 582*7e120ff0Schristos char *cmdline = NULL; 583*7e120ff0Schristos pid_t pid = (pid_t) -1; 58498b9484cSchristos char **env_copy; 58598b9484cSchristos char *env_block = NULL; 58698b9484cSchristos 58798b9484cSchristos if (env) 58898b9484cSchristos { 58998b9484cSchristos int env_size; 59098b9484cSchristos 59198b9484cSchristos /* Count the number of environment bindings supplied. */ 59298b9484cSchristos for (env_size = 0; env[env_size]; env_size++) 59398b9484cSchristos continue; 59498b9484cSchristos 59598b9484cSchristos /* Assemble an environment block, if required. This consists of 59698b9484cSchristos VAR=VALUE strings juxtaposed (with one null character between each 59798b9484cSchristos pair) and an additional null at the end. */ 59898b9484cSchristos if (env_size > 0) 59998b9484cSchristos { 60098b9484cSchristos int var; 60198b9484cSchristos int total_size = 1; /* 1 is for the final null. */ 60298b9484cSchristos char *bufptr; 60398b9484cSchristos 60498b9484cSchristos /* Windows needs the members of the block to be sorted by variable 60598b9484cSchristos name. */ 60698b9484cSchristos env_copy = (char **) alloca (sizeof (char *) * env_size); 60798b9484cSchristos memcpy (env_copy, env, sizeof (char *) * env_size); 60898b9484cSchristos qsort (env_copy, env_size, sizeof (char *), env_compare); 60998b9484cSchristos 61098b9484cSchristos for (var = 0; var < env_size; var++) 61198b9484cSchristos total_size += strlen (env[var]) + 1; 61298b9484cSchristos 61398b9484cSchristos env_block = XNEWVEC (char, total_size); 61498b9484cSchristos bufptr = env_block; 61598b9484cSchristos for (var = 0; var < env_size; var++) 61698b9484cSchristos bufptr = stpcpy (bufptr, env_copy[var]) + 1; 61798b9484cSchristos 61898b9484cSchristos *bufptr = '\0'; 61998b9484cSchristos } 62098b9484cSchristos } 62198b9484cSchristos 62298b9484cSchristos full_executable = find_executable (executable, search); 62398b9484cSchristos if (!full_executable) 624*7e120ff0Schristos goto exit; 62598b9484cSchristos cmdline = argv_to_cmdline (argv); 62698b9484cSchristos if (!cmdline) 627*7e120ff0Schristos goto exit; 628*7e120ff0Schristos /* If cmdline is too large, CreateProcess will fail with a bad 629*7e120ff0Schristos 'No such file or directory' error. Try passing it through a 630*7e120ff0Schristos temporary response file instead. */ 631*7e120ff0Schristos if (strlen (cmdline) > 32767) 632*7e120ff0Schristos { 633*7e120ff0Schristos char *response_file = make_temp_file (""); 634*7e120ff0Schristos /* Register the file for deletion by pex_free. */ 635*7e120ff0Schristos ++obj->remove_count; 636*7e120ff0Schristos obj->remove = XRESIZEVEC (char *, obj->remove, obj->remove_count); 637*7e120ff0Schristos obj->remove[obj->remove_count - 1] = response_file; 638*7e120ff0Schristos int fd = pex_win32_open_write (obj, response_file, 0, 0); 639*7e120ff0Schristos if (fd == -1) 640*7e120ff0Schristos goto exit; 641*7e120ff0Schristos FILE *f = pex_win32_fdopenw (obj, fd, 0); 642*7e120ff0Schristos /* Don't write argv[0] (program name) to the response file. */ 643*7e120ff0Schristos if (writeargv (&argv[1], f)) 644*7e120ff0Schristos { 645*7e120ff0Schristos fclose (f); 646*7e120ff0Schristos goto exit; 647*7e120ff0Schristos } 648*7e120ff0Schristos fclose (f); /* Also closes fd and the underlying OS handle. */ 649*7e120ff0Schristos char *response_arg = concat ("@", response_file, NULL); 650*7e120ff0Schristos char *response_argv[3] = {argv[0], response_arg, NULL}; 651*7e120ff0Schristos free (cmdline); 652*7e120ff0Schristos cmdline = argv_to_cmdline (response_argv); 653*7e120ff0Schristos free (response_arg); 654*7e120ff0Schristos if (!cmdline) 655*7e120ff0Schristos goto exit; 656*7e120ff0Schristos } 65798b9484cSchristos 65898b9484cSchristos /* Create the child process. */ 659*7e120ff0Schristos if (CreateProcess (full_executable, cmdline, 66098b9484cSchristos /*lpProcessAttributes=*/NULL, 66198b9484cSchristos /*lpThreadAttributes=*/NULL, 66298b9484cSchristos /*bInheritHandles=*/TRUE, 66398b9484cSchristos dwCreationFlags, 66498b9484cSchristos (LPVOID) env_block, 66598b9484cSchristos /*lpCurrentDirectory=*/NULL, 66698b9484cSchristos si, 66798b9484cSchristos pi)) 66898b9484cSchristos { 669*7e120ff0Schristos CloseHandle (pi->hThread); 670*7e120ff0Schristos pid = (pid_t) pi->hProcess; 67198b9484cSchristos } 67298b9484cSchristos 673*7e120ff0Schristos exit: 67498b9484cSchristos /* Clean up. */ 67598b9484cSchristos free (env_block); 67698b9484cSchristos free (cmdline); 67798b9484cSchristos free (full_executable); 67898b9484cSchristos 679*7e120ff0Schristos return pid; 68098b9484cSchristos } 68198b9484cSchristos 68298b9484cSchristos /* Spawn a script. This simulates the Unix script execution mechanism. 68398b9484cSchristos This function is called as a fallback if win32_spawn fails. */ 68498b9484cSchristos 68598b9484cSchristos static pid_t 686*7e120ff0Schristos spawn_script (struct pex_obj *obj, 687*7e120ff0Schristos const char *executable, char *const *argv, 68898b9484cSchristos char* const *env, 68998b9484cSchristos DWORD dwCreationFlags, 69098b9484cSchristos LPSTARTUPINFO si, 69198b9484cSchristos LPPROCESS_INFORMATION pi) 69298b9484cSchristos { 69398b9484cSchristos pid_t pid = (pid_t) -1; 69498b9484cSchristos int save_errno = errno; 69598b9484cSchristos int fd = _open (executable, _O_RDONLY); 69698b9484cSchristos 69798b9484cSchristos /* Try to open script, check header format, extract interpreter path, 69898b9484cSchristos and spawn script using that interpretter. */ 69998b9484cSchristos if (fd >= 0) 70098b9484cSchristos { 70198b9484cSchristos char buf[MAX_PATH + 5]; 70298b9484cSchristos int len = _read (fd, buf, sizeof (buf) - 1); 70398b9484cSchristos _close (fd); 70498b9484cSchristos if (len > 3) 70598b9484cSchristos { 70698b9484cSchristos char *eol; 70798b9484cSchristos buf[len] = '\0'; 70898b9484cSchristos eol = strchr (buf, '\n'); 70998b9484cSchristos if (eol && strncmp (buf, "#!", 2) == 0) 71098b9484cSchristos { 71198b9484cSchristos 71298b9484cSchristos /* Header format is OK. */ 71398b9484cSchristos char *executable1; 71498b9484cSchristos int new_argc; 71598b9484cSchristos const char **avhere; 71698b9484cSchristos 71798b9484cSchristos /* Extract interpreter path. */ 71898b9484cSchristos do 71998b9484cSchristos *eol = '\0'; 72098b9484cSchristos while (*--eol == '\r' || *eol == ' ' || *eol == '\t'); 72198b9484cSchristos for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++) 72298b9484cSchristos continue; 72398b9484cSchristos backslashify (executable1); 72498b9484cSchristos 72598b9484cSchristos /* Duplicate argv, prepending the interpreter path. */ 72698b9484cSchristos new_argc = argv_to_argc (argv) + 1; 72798b9484cSchristos avhere = XNEWVEC (const char *, new_argc + 1); 72898b9484cSchristos *avhere = executable1; 72998b9484cSchristos memcpy (avhere + 1, argv, new_argc * sizeof(*argv)); 73098b9484cSchristos argv = (char *const *)avhere; 73198b9484cSchristos 73298b9484cSchristos /* Spawn the child. */ 73398b9484cSchristos #ifndef USE_MINGW_MSYS 73498b9484cSchristos executable = strrchr (executable1, '\\') + 1; 73598b9484cSchristos if (!executable) 73698b9484cSchristos executable = executable1; 737*7e120ff0Schristos pid = win32_spawn (obj, executable, TRUE, argv, env, 73898b9484cSchristos dwCreationFlags, si, pi); 73998b9484cSchristos #else 74098b9484cSchristos if (strchr (executable1, '\\') == NULL) 741*7e120ff0Schristos pid = win32_spawn (obj, executable1, TRUE, argv, env, 74298b9484cSchristos dwCreationFlags, si, pi); 74398b9484cSchristos else if (executable1[0] != '\\') 744*7e120ff0Schristos pid = win32_spawn (obj, executable1, FALSE, argv, env, 74598b9484cSchristos dwCreationFlags, si, pi); 74698b9484cSchristos else 74798b9484cSchristos { 74898b9484cSchristos const char *newex = mingw_rootify (executable1); 74998b9484cSchristos *avhere = newex; 750*7e120ff0Schristos pid = win32_spawn (obj, newex, FALSE, argv, env, 75198b9484cSchristos dwCreationFlags, si, pi); 75298b9484cSchristos if (executable1 != newex) 75398b9484cSchristos free ((char *) newex); 75498b9484cSchristos if (pid == (pid_t) -1) 75598b9484cSchristos { 75698b9484cSchristos newex = msys_rootify (executable1); 75798b9484cSchristos if (newex != executable1) 75898b9484cSchristos { 75998b9484cSchristos *avhere = newex; 760*7e120ff0Schristos pid = win32_spawn (obj, newex, FALSE, argv, env, 76198b9484cSchristos dwCreationFlags, si, pi); 76298b9484cSchristos free ((char *) newex); 76398b9484cSchristos } 76498b9484cSchristos } 76598b9484cSchristos } 76698b9484cSchristos #endif 76798b9484cSchristos free (avhere); 76898b9484cSchristos } 76998b9484cSchristos } 77098b9484cSchristos } 77198b9484cSchristos if (pid == (pid_t) -1) 77298b9484cSchristos errno = save_errno; 77398b9484cSchristos return pid; 77498b9484cSchristos } 77598b9484cSchristos 77698b9484cSchristos /* Execute a child. */ 77798b9484cSchristos 77898b9484cSchristos static pid_t 779*7e120ff0Schristos pex_win32_exec_child (struct pex_obj *obj, int flags, 78098b9484cSchristos const char *executable, char * const * argv, 78198b9484cSchristos char* const* env, 78298b9484cSchristos int in, int out, int errdes, 78398b9484cSchristos int toclose ATTRIBUTE_UNUSED, 78498b9484cSchristos const char **errmsg, 78598b9484cSchristos int *err) 78698b9484cSchristos { 78798b9484cSchristos pid_t pid; 78898b9484cSchristos HANDLE stdin_handle; 78998b9484cSchristos HANDLE stdout_handle; 79098b9484cSchristos HANDLE stderr_handle; 79198b9484cSchristos DWORD dwCreationFlags; 79298b9484cSchristos OSVERSIONINFO version_info; 79398b9484cSchristos STARTUPINFO si; 79498b9484cSchristos PROCESS_INFORMATION pi; 7954b169a6bSchristos int orig_out, orig_in, orig_err = 0; 79698b9484cSchristos BOOL separate_stderr = !(flags & PEX_STDERR_TO_STDOUT); 79798b9484cSchristos 798a2e2270fSchristos /* Ensure we have inheritable descriptors to pass to the child. */ 79998b9484cSchristos orig_in = in; 80098b9484cSchristos in = _dup (orig_in); 80198b9484cSchristos 80298b9484cSchristos orig_out = out; 80398b9484cSchristos out = _dup (orig_out); 80498b9484cSchristos 80598b9484cSchristos if (separate_stderr) 80698b9484cSchristos { 80798b9484cSchristos orig_err = errdes; 80898b9484cSchristos errdes = _dup (orig_err); 80998b9484cSchristos } 81098b9484cSchristos 81198b9484cSchristos stdin_handle = INVALID_HANDLE_VALUE; 81298b9484cSchristos stdout_handle = INVALID_HANDLE_VALUE; 81398b9484cSchristos stderr_handle = INVALID_HANDLE_VALUE; 81498b9484cSchristos 81598b9484cSchristos stdin_handle = (HANDLE) _get_osfhandle (in); 81698b9484cSchristos stdout_handle = (HANDLE) _get_osfhandle (out); 81798b9484cSchristos if (separate_stderr) 81898b9484cSchristos stderr_handle = (HANDLE) _get_osfhandle (errdes); 81998b9484cSchristos else 82098b9484cSchristos stderr_handle = stdout_handle; 82198b9484cSchristos 82298b9484cSchristos /* Determine the version of Windows we are running on. */ 82398b9484cSchristos version_info.dwOSVersionInfoSize = sizeof (version_info); 82498b9484cSchristos GetVersionEx (&version_info); 82598b9484cSchristos if (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) 82698b9484cSchristos /* On Windows 95/98/ME the CREATE_NO_WINDOW flag is not 82798b9484cSchristos supported, so we cannot avoid creating a console window. */ 82898b9484cSchristos dwCreationFlags = 0; 82998b9484cSchristos else 83098b9484cSchristos { 83198b9484cSchristos HANDLE conout_handle; 83298b9484cSchristos 83398b9484cSchristos /* Determine whether or not we have an associated console. */ 83498b9484cSchristos conout_handle = CreateFile("CONOUT$", 83598b9484cSchristos GENERIC_WRITE, 83698b9484cSchristos FILE_SHARE_WRITE, 83798b9484cSchristos /*lpSecurityAttributes=*/NULL, 83898b9484cSchristos OPEN_EXISTING, 83998b9484cSchristos FILE_ATTRIBUTE_NORMAL, 84098b9484cSchristos /*hTemplateFile=*/NULL); 84198b9484cSchristos if (conout_handle == INVALID_HANDLE_VALUE) 84298b9484cSchristos /* There is no console associated with this process. Since 84398b9484cSchristos the child is a console process, the OS would normally 84498b9484cSchristos create a new console Window for the child. Since we'll be 84598b9484cSchristos redirecting the child's standard streams, we do not need 84698b9484cSchristos the console window. */ 84798b9484cSchristos dwCreationFlags = CREATE_NO_WINDOW; 84898b9484cSchristos else 84998b9484cSchristos { 85098b9484cSchristos /* There is a console associated with the process, so the OS 85198b9484cSchristos will not create a new console. And, if we use 85298b9484cSchristos CREATE_NO_WINDOW in this situation, the child will have 85398b9484cSchristos no associated console. Therefore, if the child's 85498b9484cSchristos standard streams are connected to the console, the output 85598b9484cSchristos will be discarded. */ 85698b9484cSchristos CloseHandle(conout_handle); 85798b9484cSchristos dwCreationFlags = 0; 85898b9484cSchristos } 85998b9484cSchristos } 86098b9484cSchristos 86198b9484cSchristos /* Since the child will be a console process, it will, by default, 86298b9484cSchristos connect standard input/output to its console. However, we want 86398b9484cSchristos the child to use the handles specifically designated above. In 86498b9484cSchristos addition, if there is no console (such as when we are running in 86598b9484cSchristos a Cygwin X window), then we must redirect the child's 86698b9484cSchristos input/output, as there is no console for the child to use. */ 86798b9484cSchristos memset (&si, 0, sizeof (si)); 86898b9484cSchristos si.cb = sizeof (si); 86998b9484cSchristos si.dwFlags = STARTF_USESTDHANDLES; 87098b9484cSchristos si.hStdInput = stdin_handle; 87198b9484cSchristos si.hStdOutput = stdout_handle; 87298b9484cSchristos si.hStdError = stderr_handle; 87398b9484cSchristos 87498b9484cSchristos /* Create the child process. */ 875*7e120ff0Schristos pid = win32_spawn (obj, executable, (flags & PEX_SEARCH) != 0, 87698b9484cSchristos argv, env, dwCreationFlags, &si, &pi); 87798b9484cSchristos if (pid == (pid_t) -1) 878*7e120ff0Schristos pid = spawn_script (obj, executable, argv, env, dwCreationFlags, 87998b9484cSchristos &si, &pi); 88098b9484cSchristos if (pid == (pid_t) -1) 88198b9484cSchristos { 88298b9484cSchristos *err = ENOENT; 88398b9484cSchristos *errmsg = "CreateProcess"; 88498b9484cSchristos } 88598b9484cSchristos 886a2e2270fSchristos /* If the child was created successfully, close the original file 887a2e2270fSchristos descriptors. If the process creation fails, these are closed by 888a2e2270fSchristos pex_run_in_environment instead. We must not close them twice as 889a2e2270fSchristos that seems to cause a Windows exception. */ 890a2e2270fSchristos 891a2e2270fSchristos if (pid != (pid_t) -1) 892a2e2270fSchristos { 893a2e2270fSchristos if (orig_in != STDIN_FILENO) 894a2e2270fSchristos _close (orig_in); 895a2e2270fSchristos if (orig_out != STDOUT_FILENO) 896a2e2270fSchristos _close (orig_out); 897a2e2270fSchristos if (separate_stderr 898a2e2270fSchristos && orig_err != STDERR_FILENO) 899a2e2270fSchristos _close (orig_err); 900a2e2270fSchristos } 901a2e2270fSchristos 90298b9484cSchristos /* Close the standard input, standard output and standard error handles 90398b9484cSchristos in the parent. */ 90498b9484cSchristos 90598b9484cSchristos _close (in); 90698b9484cSchristos _close (out); 90798b9484cSchristos if (separate_stderr) 90898b9484cSchristos _close (errdes); 90998b9484cSchristos 91098b9484cSchristos return pid; 91198b9484cSchristos } 91298b9484cSchristos 91398b9484cSchristos /* Wait for a child process to complete. MS CRTDLL doesn't return 91498b9484cSchristos enough information in status to decide if the child exited due to a 91598b9484cSchristos signal or not, rather it simply returns an integer with the exit 91698b9484cSchristos code of the child; eg., if the child exited with an abort() call 91798b9484cSchristos and didn't have a handler for SIGABRT, it simply returns with 91898b9484cSchristos status == 3. We fix the status code to conform to the usual WIF* 91998b9484cSchristos macros. Note that WIFSIGNALED will never be true under CRTDLL. */ 92098b9484cSchristos 92198b9484cSchristos static pid_t 92298b9484cSchristos pex_win32_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, 92398b9484cSchristos int *status, struct pex_time *time, int done ATTRIBUTE_UNUSED, 92498b9484cSchristos const char **errmsg, int *err) 92598b9484cSchristos { 92698b9484cSchristos DWORD termstat; 92798b9484cSchristos HANDLE h; 92898b9484cSchristos 92998b9484cSchristos if (time != NULL) 93098b9484cSchristos memset (time, 0, sizeof *time); 93198b9484cSchristos 93298b9484cSchristos h = (HANDLE) pid; 93398b9484cSchristos 93498b9484cSchristos /* FIXME: If done is non-zero, we should probably try to kill the 93598b9484cSchristos process. */ 93698b9484cSchristos if (WaitForSingleObject (h, INFINITE) != WAIT_OBJECT_0) 93798b9484cSchristos { 93898b9484cSchristos CloseHandle (h); 93998b9484cSchristos *err = ECHILD; 94098b9484cSchristos *errmsg = "WaitForSingleObject"; 94198b9484cSchristos return -1; 94298b9484cSchristos } 94398b9484cSchristos 94498b9484cSchristos GetExitCodeProcess (h, &termstat); 94598b9484cSchristos CloseHandle (h); 94698b9484cSchristos 94798b9484cSchristos /* A value of 3 indicates that the child caught a signal, but not 94898b9484cSchristos which one. Since only SIGABRT, SIGFPE and SIGINT do anything, we 94998b9484cSchristos report SIGABRT. */ 95098b9484cSchristos if (termstat == 3) 95198b9484cSchristos *status = SIGABRT; 95298b9484cSchristos else 95398b9484cSchristos *status = (termstat & 0xff) << 8; 95498b9484cSchristos 95598b9484cSchristos return 0; 95698b9484cSchristos } 95798b9484cSchristos 95898b9484cSchristos /* Create a pipe. */ 95998b9484cSchristos 96098b9484cSchristos static int 96198b9484cSchristos pex_win32_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p, 96298b9484cSchristos int binary) 96398b9484cSchristos { 96498b9484cSchristos return _pipe (p, 256, (binary ? _O_BINARY : _O_TEXT) | _O_NOINHERIT); 96598b9484cSchristos } 96698b9484cSchristos 96798b9484cSchristos /* Get a FILE pointer to read from a file descriptor. */ 96898b9484cSchristos 96998b9484cSchristos static FILE * 97098b9484cSchristos pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd, 97198b9484cSchristos int binary) 97298b9484cSchristos { 97398b9484cSchristos HANDLE h = (HANDLE) _get_osfhandle (fd); 97498b9484cSchristos if (h == INVALID_HANDLE_VALUE) 97598b9484cSchristos return NULL; 97698b9484cSchristos if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0)) 97798b9484cSchristos return NULL; 97898b9484cSchristos return fdopen (fd, binary ? "rb" : "r"); 97998b9484cSchristos } 98098b9484cSchristos 98198b9484cSchristos static FILE * 98298b9484cSchristos pex_win32_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd, 98398b9484cSchristos int binary) 98498b9484cSchristos { 98598b9484cSchristos HANDLE h = (HANDLE) _get_osfhandle (fd); 98698b9484cSchristos if (h == INVALID_HANDLE_VALUE) 98798b9484cSchristos return NULL; 98898b9484cSchristos if (! SetHandleInformation (h, HANDLE_FLAG_INHERIT, 0)) 98998b9484cSchristos return NULL; 99098b9484cSchristos return fdopen (fd, binary ? "wb" : "w"); 99198b9484cSchristos } 99298b9484cSchristos 99398b9484cSchristos #ifdef MAIN 99498b9484cSchristos #include <stdio.h> 99598b9484cSchristos 99698b9484cSchristos int 99798b9484cSchristos main (int argc ATTRIBUTE_UNUSED, char **argv) 99898b9484cSchristos { 99998b9484cSchristos char const *errmsg; 100098b9484cSchristos int err; 100198b9484cSchristos argv++; 100298b9484cSchristos printf ("%ld\n", (long) pex_win32_exec_child (NULL, PEX_SEARCH, argv[0], argv, NULL, 0, 0, 1, 2, &errmsg, &err)); 100398b9484cSchristos exit (0); 100498b9484cSchristos } 100598b9484cSchristos #endif 1006