xref: /dflybsd-src/contrib/binutils-2.34/libiberty/pex-unix.c (revision b52ef7118d1621abed722c5bbbd542210290ecef)
1*fae548d3Szrj /* Utilities to execute a program in a subprocess (possibly linked by pipes
2*fae548d3Szrj    with other subprocesses), and wait for it.  Generic Unix version
3*fae548d3Szrj    (also used for UWIN and VMS).
4*fae548d3Szrj    Copyright (C) 1996-2020 Free Software Foundation, Inc.
5*fae548d3Szrj 
6*fae548d3Szrj This file is part of the libiberty library.
7*fae548d3Szrj Libiberty is free software; you can redistribute it and/or
8*fae548d3Szrj modify it under the terms of the GNU Library General Public
9*fae548d3Szrj License as published by the Free Software Foundation; either
10*fae548d3Szrj version 2 of the License, or (at your option) any later version.
11*fae548d3Szrj 
12*fae548d3Szrj Libiberty is distributed in the hope that it will be useful,
13*fae548d3Szrj but WITHOUT ANY WARRANTY; without even the implied warranty of
14*fae548d3Szrj MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15*fae548d3Szrj Library General Public License for more details.
16*fae548d3Szrj 
17*fae548d3Szrj You should have received a copy of the GNU Library General Public
18*fae548d3Szrj License along with libiberty; see the file COPYING.LIB.  If not,
19*fae548d3Szrj write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20*fae548d3Szrj Boston, MA 02110-1301, USA.  */
21*fae548d3Szrj 
22*fae548d3Szrj #include "config.h"
23*fae548d3Szrj #include "libiberty.h"
24*fae548d3Szrj #include "pex-common.h"
25*fae548d3Szrj #include "environ.h"
26*fae548d3Szrj 
27*fae548d3Szrj #include <stdio.h>
28*fae548d3Szrj #include <signal.h>
29*fae548d3Szrj #include <errno.h>
30*fae548d3Szrj #ifdef NEED_DECLARATION_ERRNO
31*fae548d3Szrj extern int errno;
32*fae548d3Szrj #endif
33*fae548d3Szrj #ifdef HAVE_STDLIB_H
34*fae548d3Szrj #include <stdlib.h>
35*fae548d3Szrj #endif
36*fae548d3Szrj #ifdef HAVE_STRING_H
37*fae548d3Szrj #include <string.h>
38*fae548d3Szrj #endif
39*fae548d3Szrj #ifdef HAVE_UNISTD_H
40*fae548d3Szrj #include <unistd.h>
41*fae548d3Szrj #endif
42*fae548d3Szrj 
43*fae548d3Szrj #include <sys/types.h>
44*fae548d3Szrj 
45*fae548d3Szrj #ifdef HAVE_FCNTL_H
46*fae548d3Szrj #include <fcntl.h>
47*fae548d3Szrj #endif
48*fae548d3Szrj #ifdef HAVE_SYS_WAIT_H
49*fae548d3Szrj #include <sys/wait.h>
50*fae548d3Szrj #endif
51*fae548d3Szrj #ifdef HAVE_GETRUSAGE
52*fae548d3Szrj #include <sys/time.h>
53*fae548d3Szrj #include <sys/resource.h>
54*fae548d3Szrj #endif
55*fae548d3Szrj #ifdef HAVE_SYS_STAT_H
56*fae548d3Szrj #include <sys/stat.h>
57*fae548d3Szrj #endif
58*fae548d3Szrj #ifdef HAVE_PROCESS_H
59*fae548d3Szrj #include <process.h>
60*fae548d3Szrj #endif
61*fae548d3Szrj 
62*fae548d3Szrj #ifdef vfork /* Autoconf may define this to fork for us. */
63*fae548d3Szrj # define VFORK_STRING "fork"
64*fae548d3Szrj #else
65*fae548d3Szrj # define VFORK_STRING "vfork"
66*fae548d3Szrj #endif
67*fae548d3Szrj #ifdef HAVE_VFORK_H
68*fae548d3Szrj #include <vfork.h>
69*fae548d3Szrj #endif
70*fae548d3Szrj #if defined(VMS) && defined (__LONG_POINTERS)
71*fae548d3Szrj #ifndef __CHAR_PTR32
72*fae548d3Szrj typedef char * __char_ptr32
73*fae548d3Szrj __attribute__ ((mode (SI)));
74*fae548d3Szrj #endif
75*fae548d3Szrj 
76*fae548d3Szrj typedef __char_ptr32 *__char_ptr_char_ptr32
77*fae548d3Szrj __attribute__ ((mode (SI)));
78*fae548d3Szrj 
79*fae548d3Szrj /* Return a 32 bit pointer to an array of 32 bit pointers
80*fae548d3Szrj    given a 64 bit pointer to an array of 64 bit pointers.  */
81*fae548d3Szrj 
82*fae548d3Szrj static __char_ptr_char_ptr32
to_ptr32(char ** ptr64)83*fae548d3Szrj to_ptr32 (char **ptr64)
84*fae548d3Szrj {
85*fae548d3Szrj   int argc;
86*fae548d3Szrj   __char_ptr_char_ptr32 short_argv;
87*fae548d3Szrj 
88*fae548d3Szrj   /* Count number of arguments.  */
89*fae548d3Szrj   for (argc = 0; ptr64[argc] != NULL; argc++)
90*fae548d3Szrj     ;
91*fae548d3Szrj 
92*fae548d3Szrj   /* Reallocate argv with 32 bit pointers.  */
93*fae548d3Szrj   short_argv = (__char_ptr_char_ptr32) decc$malloc
94*fae548d3Szrj     (sizeof (__char_ptr32) * (argc + 1));
95*fae548d3Szrj 
96*fae548d3Szrj   for (argc = 0; ptr64[argc] != NULL; argc++)
97*fae548d3Szrj     short_argv[argc] = (__char_ptr32) decc$strdup (ptr64[argc]);
98*fae548d3Szrj 
99*fae548d3Szrj   short_argv[argc] = (__char_ptr32) 0;
100*fae548d3Szrj   return short_argv;
101*fae548d3Szrj 
102*fae548d3Szrj }
103*fae548d3Szrj #else
104*fae548d3Szrj #define to_ptr32(argv) argv
105*fae548d3Szrj #endif
106*fae548d3Szrj 
107*fae548d3Szrj /* File mode to use for private and world-readable files.  */
108*fae548d3Szrj 
109*fae548d3Szrj #if defined (S_IRUSR) && defined (S_IWUSR) && defined (S_IRGRP) && defined (S_IWGRP) && defined (S_IROTH) && defined (S_IWOTH)
110*fae548d3Szrj #define PUBLIC_MODE  \
111*fae548d3Szrj     (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
112*fae548d3Szrj #else
113*fae548d3Szrj #define PUBLIC_MODE 0666
114*fae548d3Szrj #endif
115*fae548d3Szrj 
116*fae548d3Szrj /* Get the exit status of a particular process, and optionally get the
117*fae548d3Szrj    time that it took.  This is simple if we have wait4, slightly
118*fae548d3Szrj    harder if we have waitpid, and is a pain if we only have wait.  */
119*fae548d3Szrj 
120*fae548d3Szrj static pid_t pex_wait (struct pex_obj *, pid_t, int *, struct pex_time *);
121*fae548d3Szrj 
122*fae548d3Szrj #ifdef HAVE_WAIT4
123*fae548d3Szrj 
124*fae548d3Szrj static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)125*fae548d3Szrj pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
126*fae548d3Szrj 	  struct pex_time *time)
127*fae548d3Szrj {
128*fae548d3Szrj   pid_t ret;
129*fae548d3Szrj   struct rusage r;
130*fae548d3Szrj 
131*fae548d3Szrj #ifdef HAVE_WAITPID
132*fae548d3Szrj   if (time == NULL)
133*fae548d3Szrj     return waitpid (pid, status, 0);
134*fae548d3Szrj #endif
135*fae548d3Szrj 
136*fae548d3Szrj   ret = wait4 (pid, status, 0, &r);
137*fae548d3Szrj 
138*fae548d3Szrj   if (time != NULL)
139*fae548d3Szrj     {
140*fae548d3Szrj       time->user_seconds = r.ru_utime.tv_sec;
141*fae548d3Szrj       time->user_microseconds= r.ru_utime.tv_usec;
142*fae548d3Szrj       time->system_seconds = r.ru_stime.tv_sec;
143*fae548d3Szrj       time->system_microseconds= r.ru_stime.tv_usec;
144*fae548d3Szrj     }
145*fae548d3Szrj 
146*fae548d3Szrj   return ret;
147*fae548d3Szrj }
148*fae548d3Szrj 
149*fae548d3Szrj #else /* ! defined (HAVE_WAIT4) */
150*fae548d3Szrj 
151*fae548d3Szrj #ifdef HAVE_WAITPID
152*fae548d3Szrj 
153*fae548d3Szrj #ifndef HAVE_GETRUSAGE
154*fae548d3Szrj 
155*fae548d3Szrj static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)156*fae548d3Szrj pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
157*fae548d3Szrj 	  struct pex_time *time)
158*fae548d3Szrj {
159*fae548d3Szrj   if (time != NULL)
160*fae548d3Szrj     memset (time, 0, sizeof (struct pex_time));
161*fae548d3Szrj   return waitpid (pid, status, 0);
162*fae548d3Szrj }
163*fae548d3Szrj 
164*fae548d3Szrj #else /* defined (HAVE_GETRUSAGE) */
165*fae548d3Szrj 
166*fae548d3Szrj static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)167*fae548d3Szrj pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
168*fae548d3Szrj 	  struct pex_time *time)
169*fae548d3Szrj {
170*fae548d3Szrj   struct rusage r1, r2;
171*fae548d3Szrj   pid_t ret;
172*fae548d3Szrj 
173*fae548d3Szrj   if (time == NULL)
174*fae548d3Szrj     return waitpid (pid, status, 0);
175*fae548d3Szrj 
176*fae548d3Szrj   getrusage (RUSAGE_CHILDREN, &r1);
177*fae548d3Szrj 
178*fae548d3Szrj   ret = waitpid (pid, status, 0);
179*fae548d3Szrj   if (ret < 0)
180*fae548d3Szrj     return ret;
181*fae548d3Szrj 
182*fae548d3Szrj   getrusage (RUSAGE_CHILDREN, &r2);
183*fae548d3Szrj 
184*fae548d3Szrj   time->user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
185*fae548d3Szrj   time->user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
186*fae548d3Szrj   if (r2.ru_utime.tv_usec < r1.ru_utime.tv_usec)
187*fae548d3Szrj     {
188*fae548d3Szrj       --time->user_seconds;
189*fae548d3Szrj       time->user_microseconds += 1000000;
190*fae548d3Szrj     }
191*fae548d3Szrj 
192*fae548d3Szrj   time->system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
193*fae548d3Szrj   time->system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
194*fae548d3Szrj   if (r2.ru_stime.tv_usec < r1.ru_stime.tv_usec)
195*fae548d3Szrj     {
196*fae548d3Szrj       --time->system_seconds;
197*fae548d3Szrj       time->system_microseconds += 1000000;
198*fae548d3Szrj     }
199*fae548d3Szrj 
200*fae548d3Szrj   return ret;
201*fae548d3Szrj }
202*fae548d3Szrj 
203*fae548d3Szrj #endif /* defined (HAVE_GETRUSAGE) */
204*fae548d3Szrj 
205*fae548d3Szrj #else /* ! defined (HAVE_WAITPID) */
206*fae548d3Szrj 
207*fae548d3Szrj struct status_list
208*fae548d3Szrj {
209*fae548d3Szrj   struct status_list *next;
210*fae548d3Szrj   pid_t pid;
211*fae548d3Szrj   int status;
212*fae548d3Szrj   struct pex_time time;
213*fae548d3Szrj };
214*fae548d3Szrj 
215*fae548d3Szrj static pid_t
pex_wait(struct pex_obj * obj,pid_t pid,int * status,struct pex_time * time)216*fae548d3Szrj pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time)
217*fae548d3Szrj {
218*fae548d3Szrj   struct status_list **pp;
219*fae548d3Szrj 
220*fae548d3Szrj   for (pp = (struct status_list **) &obj->sysdep;
221*fae548d3Szrj        *pp != NULL;
222*fae548d3Szrj        pp = &(*pp)->next)
223*fae548d3Szrj     {
224*fae548d3Szrj       if ((*pp)->pid == pid)
225*fae548d3Szrj 	{
226*fae548d3Szrj 	  struct status_list *p;
227*fae548d3Szrj 
228*fae548d3Szrj 	  p = *pp;
229*fae548d3Szrj 	  *status = p->status;
230*fae548d3Szrj 	  if (time != NULL)
231*fae548d3Szrj 	    *time = p->time;
232*fae548d3Szrj 	  *pp = p->next;
233*fae548d3Szrj 	  free (p);
234*fae548d3Szrj 	  return pid;
235*fae548d3Szrj 	}
236*fae548d3Szrj     }
237*fae548d3Szrj 
238*fae548d3Szrj   while (1)
239*fae548d3Szrj     {
240*fae548d3Szrj       pid_t cpid;
241*fae548d3Szrj       struct status_list *psl;
242*fae548d3Szrj       struct pex_time pt;
243*fae548d3Szrj #ifdef HAVE_GETRUSAGE
244*fae548d3Szrj       struct rusage r1, r2;
245*fae548d3Szrj #endif
246*fae548d3Szrj 
247*fae548d3Szrj       if (time != NULL)
248*fae548d3Szrj 	{
249*fae548d3Szrj #ifdef HAVE_GETRUSAGE
250*fae548d3Szrj 	  getrusage (RUSAGE_CHILDREN, &r1);
251*fae548d3Szrj #else
252*fae548d3Szrj 	  memset (&pt, 0, sizeof (struct pex_time));
253*fae548d3Szrj #endif
254*fae548d3Szrj 	}
255*fae548d3Szrj 
256*fae548d3Szrj       cpid = wait (status);
257*fae548d3Szrj 
258*fae548d3Szrj #ifdef HAVE_GETRUSAGE
259*fae548d3Szrj       if (time != NULL && cpid >= 0)
260*fae548d3Szrj 	{
261*fae548d3Szrj 	  getrusage (RUSAGE_CHILDREN, &r2);
262*fae548d3Szrj 
263*fae548d3Szrj 	  pt.user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
264*fae548d3Szrj 	  pt.user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
265*fae548d3Szrj 	  if (pt.user_microseconds < 0)
266*fae548d3Szrj 	    {
267*fae548d3Szrj 	      --pt.user_seconds;
268*fae548d3Szrj 	      pt.user_microseconds += 1000000;
269*fae548d3Szrj 	    }
270*fae548d3Szrj 
271*fae548d3Szrj 	  pt.system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
272*fae548d3Szrj 	  pt.system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
273*fae548d3Szrj 	  if (pt.system_microseconds < 0)
274*fae548d3Szrj 	    {
275*fae548d3Szrj 	      --pt.system_seconds;
276*fae548d3Szrj 	      pt.system_microseconds += 1000000;
277*fae548d3Szrj 	    }
278*fae548d3Szrj 	}
279*fae548d3Szrj #endif
280*fae548d3Szrj 
281*fae548d3Szrj       if (cpid < 0 || cpid == pid)
282*fae548d3Szrj 	{
283*fae548d3Szrj 	  if (time != NULL)
284*fae548d3Szrj 	    *time = pt;
285*fae548d3Szrj 	  return cpid;
286*fae548d3Szrj 	}
287*fae548d3Szrj 
288*fae548d3Szrj       psl = XNEW (struct status_list);
289*fae548d3Szrj       psl->pid = cpid;
290*fae548d3Szrj       psl->status = *status;
291*fae548d3Szrj       if (time != NULL)
292*fae548d3Szrj 	psl->time = pt;
293*fae548d3Szrj       psl->next = (struct status_list *) obj->sysdep;
294*fae548d3Szrj       obj->sysdep = (void *) psl;
295*fae548d3Szrj     }
296*fae548d3Szrj }
297*fae548d3Szrj 
298*fae548d3Szrj #endif /* ! defined (HAVE_WAITPID) */
299*fae548d3Szrj #endif /* ! defined (HAVE_WAIT4) */
300*fae548d3Szrj 
301*fae548d3Szrj static int pex_unix_open_read (struct pex_obj *, const char *, int);
302*fae548d3Szrj static int pex_unix_open_write (struct pex_obj *, const char *, int, int);
303*fae548d3Szrj static pid_t pex_unix_exec_child (struct pex_obj *, int, const char *,
304*fae548d3Szrj 				 char * const *, char * const *,
305*fae548d3Szrj 				 int, int, int, int,
306*fae548d3Szrj 				 const char **, int *);
307*fae548d3Szrj static int pex_unix_close (struct pex_obj *, int);
308*fae548d3Szrj static int pex_unix_wait (struct pex_obj *, pid_t, int *, struct pex_time *,
309*fae548d3Szrj 			  int, const char **, int *);
310*fae548d3Szrj static int pex_unix_pipe (struct pex_obj *, int *, int);
311*fae548d3Szrj static FILE *pex_unix_fdopenr (struct pex_obj *, int, int);
312*fae548d3Szrj static FILE *pex_unix_fdopenw (struct pex_obj *, int, int);
313*fae548d3Szrj static void pex_unix_cleanup (struct pex_obj *);
314*fae548d3Szrj 
315*fae548d3Szrj /* The list of functions we pass to the common routines.  */
316*fae548d3Szrj 
317*fae548d3Szrj const struct pex_funcs funcs =
318*fae548d3Szrj {
319*fae548d3Szrj   pex_unix_open_read,
320*fae548d3Szrj   pex_unix_open_write,
321*fae548d3Szrj   pex_unix_exec_child,
322*fae548d3Szrj   pex_unix_close,
323*fae548d3Szrj   pex_unix_wait,
324*fae548d3Szrj   pex_unix_pipe,
325*fae548d3Szrj   pex_unix_fdopenr,
326*fae548d3Szrj   pex_unix_fdopenw,
327*fae548d3Szrj   pex_unix_cleanup
328*fae548d3Szrj };
329*fae548d3Szrj 
330*fae548d3Szrj /* Return a newly initialized pex_obj structure.  */
331*fae548d3Szrj 
332*fae548d3Szrj struct pex_obj *
pex_init(int flags,const char * pname,const char * tempbase)333*fae548d3Szrj pex_init (int flags, const char *pname, const char *tempbase)
334*fae548d3Szrj {
335*fae548d3Szrj   return pex_init_common (flags, pname, tempbase, &funcs);
336*fae548d3Szrj }
337*fae548d3Szrj 
338*fae548d3Szrj /* Open a file for reading.  */
339*fae548d3Szrj 
340*fae548d3Szrj static int
pex_unix_open_read(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary ATTRIBUTE_UNUSED)341*fae548d3Szrj pex_unix_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
342*fae548d3Szrj 		    int binary ATTRIBUTE_UNUSED)
343*fae548d3Szrj {
344*fae548d3Szrj   return open (name, O_RDONLY);
345*fae548d3Szrj }
346*fae548d3Szrj 
347*fae548d3Szrj /* Open a file for writing.  */
348*fae548d3Szrj 
349*fae548d3Szrj static int
pex_unix_open_write(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary ATTRIBUTE_UNUSED,int append)350*fae548d3Szrj pex_unix_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
351*fae548d3Szrj 		     int binary ATTRIBUTE_UNUSED, int append)
352*fae548d3Szrj {
353*fae548d3Szrj   /* Note that we can't use O_EXCL here because gcc may have already
354*fae548d3Szrj      created the temporary file via make_temp_file.  */
355*fae548d3Szrj   return open (name, O_WRONLY | O_CREAT
356*fae548d3Szrj 		     | (append ? O_APPEND : O_TRUNC), PUBLIC_MODE);
357*fae548d3Szrj }
358*fae548d3Szrj 
359*fae548d3Szrj /* Close a file.  */
360*fae548d3Szrj 
361*fae548d3Szrj static int
pex_unix_close(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd)362*fae548d3Szrj pex_unix_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
363*fae548d3Szrj {
364*fae548d3Szrj   return close (fd);
365*fae548d3Szrj }
366*fae548d3Szrj 
367*fae548d3Szrj /* Execute a child.  */
368*fae548d3Szrj 
369*fae548d3Szrj #if defined(HAVE_SPAWNVE) && defined(HAVE_SPAWNVPE)
370*fae548d3Szrj /* Implementation of pex->exec_child using the Cygwin spawn operation.  */
371*fae548d3Szrj 
372*fae548d3Szrj /* Subroutine of pex_unix_exec_child.  Move OLD_FD to a new file descriptor
373*fae548d3Szrj    to be stored in *PNEW_FD, save the flags in *PFLAGS, and arrange for the
374*fae548d3Szrj    saved copy to be close-on-exec.  Move CHILD_FD into OLD_FD.  If CHILD_FD
375*fae548d3Szrj    is -1, OLD_FD is to be closed.  Return -1 on error.  */
376*fae548d3Szrj 
377*fae548d3Szrj static int
save_and_install_fd(int * pnew_fd,int * pflags,int old_fd,int child_fd)378*fae548d3Szrj save_and_install_fd(int *pnew_fd, int *pflags, int old_fd, int child_fd)
379*fae548d3Szrj {
380*fae548d3Szrj   int new_fd, flags;
381*fae548d3Szrj 
382*fae548d3Szrj   flags = fcntl (old_fd, F_GETFD);
383*fae548d3Szrj 
384*fae548d3Szrj   /* If we could not retrieve the flags, then OLD_FD was not open.  */
385*fae548d3Szrj   if (flags < 0)
386*fae548d3Szrj     {
387*fae548d3Szrj       new_fd = -1, flags = 0;
388*fae548d3Szrj       if (child_fd >= 0 && dup2 (child_fd, old_fd) < 0)
389*fae548d3Szrj 	return -1;
390*fae548d3Szrj     }
391*fae548d3Szrj   /* If we wish to close OLD_FD, just mark it CLOEXEC.  */
392*fae548d3Szrj   else if (child_fd == -1)
393*fae548d3Szrj     {
394*fae548d3Szrj       new_fd = old_fd;
395*fae548d3Szrj       if ((flags & FD_CLOEXEC) == 0 && fcntl (old_fd, F_SETFD, FD_CLOEXEC) < 0)
396*fae548d3Szrj 	return -1;
397*fae548d3Szrj     }
398*fae548d3Szrj   /* Otherwise we need to save a copy of OLD_FD before installing CHILD_FD.  */
399*fae548d3Szrj   else
400*fae548d3Szrj     {
401*fae548d3Szrj #ifdef F_DUPFD_CLOEXEC
402*fae548d3Szrj       new_fd = fcntl (old_fd, F_DUPFD_CLOEXEC, 3);
403*fae548d3Szrj       if (new_fd < 0)
404*fae548d3Szrj 	return -1;
405*fae548d3Szrj #else
406*fae548d3Szrj       /* Prefer F_DUPFD over dup in order to avoid getting a new fd
407*fae548d3Szrj 	 in the range 0-2, right where a new stderr fd might get put.  */
408*fae548d3Szrj       new_fd = fcntl (old_fd, F_DUPFD, 3);
409*fae548d3Szrj       if (new_fd < 0)
410*fae548d3Szrj 	return -1;
411*fae548d3Szrj       if (fcntl (new_fd, F_SETFD, FD_CLOEXEC) < 0)
412*fae548d3Szrj 	return -1;
413*fae548d3Szrj #endif
414*fae548d3Szrj       if (dup2 (child_fd, old_fd) < 0)
415*fae548d3Szrj 	return -1;
416*fae548d3Szrj     }
417*fae548d3Szrj 
418*fae548d3Szrj   *pflags = flags;
419*fae548d3Szrj   if (pnew_fd)
420*fae548d3Szrj     *pnew_fd = new_fd;
421*fae548d3Szrj   else if (new_fd != old_fd)
422*fae548d3Szrj     abort ();
423*fae548d3Szrj 
424*fae548d3Szrj   return 0;
425*fae548d3Szrj }
426*fae548d3Szrj 
427*fae548d3Szrj /* Subroutine of pex_unix_exec_child.  Move SAVE_FD back to OLD_FD
428*fae548d3Szrj    restoring FLAGS.  If SAVE_FD < 0, OLD_FD is to be closed.  */
429*fae548d3Szrj 
430*fae548d3Szrj static int
restore_fd(int old_fd,int save_fd,int flags)431*fae548d3Szrj restore_fd(int old_fd, int save_fd, int flags)
432*fae548d3Szrj {
433*fae548d3Szrj   /* For SAVE_FD < 0, all we have to do is restore the
434*fae548d3Szrj      "closed-ness" of the original.  */
435*fae548d3Szrj   if (save_fd < 0)
436*fae548d3Szrj     return close (old_fd);
437*fae548d3Szrj 
438*fae548d3Szrj   /* For SAVE_FD == OLD_FD, all we have to do is restore the
439*fae548d3Szrj      original setting of the CLOEXEC flag.  */
440*fae548d3Szrj   if (save_fd == old_fd)
441*fae548d3Szrj     {
442*fae548d3Szrj       if (flags & FD_CLOEXEC)
443*fae548d3Szrj 	return 0;
444*fae548d3Szrj       return fcntl (old_fd, F_SETFD, flags);
445*fae548d3Szrj     }
446*fae548d3Szrj 
447*fae548d3Szrj   /* Otherwise we have to move the descriptor back, restore the flags,
448*fae548d3Szrj      and close the saved copy.  */
449*fae548d3Szrj #ifdef HAVE_DUP3
450*fae548d3Szrj   if (flags == FD_CLOEXEC)
451*fae548d3Szrj     {
452*fae548d3Szrj       if (dup3 (save_fd, old_fd, O_CLOEXEC) < 0)
453*fae548d3Szrj 	return -1;
454*fae548d3Szrj     }
455*fae548d3Szrj   else
456*fae548d3Szrj #endif
457*fae548d3Szrj     {
458*fae548d3Szrj       if (dup2 (save_fd, old_fd) < 0)
459*fae548d3Szrj 	return -1;
460*fae548d3Szrj       if (flags != 0 && fcntl (old_fd, F_SETFD, flags) < 0)
461*fae548d3Szrj 	return -1;
462*fae548d3Szrj     }
463*fae548d3Szrj   return close (save_fd);
464*fae548d3Szrj }
465*fae548d3Szrj 
466*fae548d3Szrj static pid_t
pex_unix_exec_child(struct pex_obj * obj ATTRIBUTE_UNUSED,int flags,const char * executable,char * const * argv,char * const * env,int in,int out,int errdes,int toclose,const char ** errmsg,int * err)467*fae548d3Szrj pex_unix_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED,
468*fae548d3Szrj 		     int flags, const char *executable,
469*fae548d3Szrj 		     char * const * argv, char * const * env,
470*fae548d3Szrj                      int in, int out, int errdes, int toclose,
471*fae548d3Szrj 		     const char **errmsg, int *err)
472*fae548d3Szrj {
473*fae548d3Szrj   int fl_in = 0, fl_out = 0, fl_err = 0, fl_tc = 0;
474*fae548d3Szrj   int save_in = -1, save_out = -1, save_err = -1;
475*fae548d3Szrj   int max, retries;
476*fae548d3Szrj   pid_t pid;
477*fae548d3Szrj 
478*fae548d3Szrj   if (flags & PEX_STDERR_TO_STDOUT)
479*fae548d3Szrj     errdes = out;
480*fae548d3Szrj 
481*fae548d3Szrj   /* We need the three standard file descriptors to be set up as for
482*fae548d3Szrj      the child before we perform the spawn.  The file descriptors for
483*fae548d3Szrj      the parent need to be moved and marked for close-on-exec.  */
484*fae548d3Szrj   if (in != STDIN_FILE_NO
485*fae548d3Szrj       && save_and_install_fd (&save_in, &fl_in, STDIN_FILE_NO, in) < 0)
486*fae548d3Szrj     goto error_dup2;
487*fae548d3Szrj   if (out != STDOUT_FILE_NO
488*fae548d3Szrj       && save_and_install_fd (&save_out, &fl_out, STDOUT_FILE_NO, out) < 0)
489*fae548d3Szrj     goto error_dup2;
490*fae548d3Szrj   if (errdes != STDERR_FILE_NO
491*fae548d3Szrj       && save_and_install_fd (&save_err, &fl_err, STDERR_FILE_NO, errdes) < 0)
492*fae548d3Szrj     goto error_dup2;
493*fae548d3Szrj   if (toclose >= 0
494*fae548d3Szrj       && save_and_install_fd (NULL, &fl_tc, toclose, -1) < 0)
495*fae548d3Szrj     goto error_dup2;
496*fae548d3Szrj 
497*fae548d3Szrj   /* Now that we've moved the file descriptors for the child into place,
498*fae548d3Szrj      close the originals.  Be careful not to close any of the standard
499*fae548d3Szrj      file descriptors that we just set up.  */
500*fae548d3Szrj   max = -1;
501*fae548d3Szrj   if (errdes >= 0)
502*fae548d3Szrj     max = STDERR_FILE_NO;
503*fae548d3Szrj   else if (out >= 0)
504*fae548d3Szrj     max = STDOUT_FILE_NO;
505*fae548d3Szrj   else if (in >= 0)
506*fae548d3Szrj     max = STDIN_FILE_NO;
507*fae548d3Szrj   if (in > max)
508*fae548d3Szrj     close (in);
509*fae548d3Szrj   if (out > max)
510*fae548d3Szrj     close (out);
511*fae548d3Szrj   if (errdes > max && errdes != out)
512*fae548d3Szrj     close (errdes);
513*fae548d3Szrj 
514*fae548d3Szrj   /* If we were not given an environment, use the global environment.  */
515*fae548d3Szrj   if (env == NULL)
516*fae548d3Szrj     env = environ;
517*fae548d3Szrj 
518*fae548d3Szrj   /* Launch the program.  If we get EAGAIN (normally out of pid's), try
519*fae548d3Szrj      again a few times with increasing backoff times.  */
520*fae548d3Szrj   retries = 0;
521*fae548d3Szrj   while (1)
522*fae548d3Szrj     {
523*fae548d3Szrj       typedef const char * const *cc_cp;
524*fae548d3Szrj 
525*fae548d3Szrj       if (flags & PEX_SEARCH)
526*fae548d3Szrj 	pid = spawnvpe (_P_NOWAITO, executable, (cc_cp)argv, (cc_cp)env);
527*fae548d3Szrj       else
528*fae548d3Szrj 	pid = spawnve (_P_NOWAITO, executable, (cc_cp)argv, (cc_cp)env);
529*fae548d3Szrj 
530*fae548d3Szrj       if (pid > 0)
531*fae548d3Szrj 	break;
532*fae548d3Szrj 
533*fae548d3Szrj       *err = errno;
534*fae548d3Szrj       *errmsg = "spawn";
535*fae548d3Szrj       if (errno != EAGAIN || ++retries == 4)
536*fae548d3Szrj 	return (pid_t) -1;
537*fae548d3Szrj       sleep (1 << retries);
538*fae548d3Szrj     }
539*fae548d3Szrj 
540*fae548d3Szrj   /* Success.  Restore the parent's file descriptors that we saved above.  */
541*fae548d3Szrj   if (toclose >= 0
542*fae548d3Szrj       && restore_fd (toclose, toclose, fl_tc) < 0)
543*fae548d3Szrj     goto error_dup2;
544*fae548d3Szrj   if (in != STDIN_FILE_NO
545*fae548d3Szrj       && restore_fd (STDIN_FILE_NO, save_in, fl_in) < 0)
546*fae548d3Szrj     goto error_dup2;
547*fae548d3Szrj   if (out != STDOUT_FILE_NO
548*fae548d3Szrj       && restore_fd (STDOUT_FILE_NO, save_out, fl_out) < 0)
549*fae548d3Szrj     goto error_dup2;
550*fae548d3Szrj   if (errdes != STDERR_FILE_NO
551*fae548d3Szrj       && restore_fd (STDERR_FILE_NO, save_err, fl_err) < 0)
552*fae548d3Szrj     goto error_dup2;
553*fae548d3Szrj 
554*fae548d3Szrj   return pid;
555*fae548d3Szrj 
556*fae548d3Szrj  error_dup2:
557*fae548d3Szrj   *err = errno;
558*fae548d3Szrj   *errmsg = "dup2";
559*fae548d3Szrj   return (pid_t) -1;
560*fae548d3Szrj }
561*fae548d3Szrj 
562*fae548d3Szrj #else
563*fae548d3Szrj /* Implementation of pex->exec_child using standard vfork + exec.  */
564*fae548d3Szrj 
565*fae548d3Szrj static pid_t
pex_unix_exec_child(struct pex_obj * obj,int flags,const char * executable,char * const * argv,char * const * env,int in,int out,int errdes,int toclose,const char ** errmsg,int * err)566*fae548d3Szrj pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable,
567*fae548d3Szrj 		     char * const * argv, char * const * env,
568*fae548d3Szrj                      int in, int out, int errdes,
569*fae548d3Szrj 		     int toclose, const char **errmsg, int *err)
570*fae548d3Szrj {
571*fae548d3Szrj   pid_t pid = -1;
572*fae548d3Szrj   /* Tuple to communicate error from child to parent.  We can safely
573*fae548d3Szrj      transfer string literal pointers as both run with identical
574*fae548d3Szrj      address mappings.  */
575*fae548d3Szrj   struct fn_err
576*fae548d3Szrj   {
577*fae548d3Szrj     const char *fn;
578*fae548d3Szrj     int err;
579*fae548d3Szrj   };
580*fae548d3Szrj   volatile int do_pipe = 0;
581*fae548d3Szrj   volatile int pipes[2]; /* [0]:reader,[1]:writer.  */
582*fae548d3Szrj #ifdef O_CLOEXEC
583*fae548d3Szrj   do_pipe = 1;
584*fae548d3Szrj #endif
585*fae548d3Szrj   if (do_pipe)
586*fae548d3Szrj     {
587*fae548d3Szrj #ifdef HAVE_PIPE2
588*fae548d3Szrj       if (pipe2 ((int *)pipes, O_CLOEXEC))
589*fae548d3Szrj 	do_pipe = 0;
590*fae548d3Szrj #else
591*fae548d3Szrj       if (pipe ((int *)pipes))
592*fae548d3Szrj 	do_pipe = 0;
593*fae548d3Szrj       else
594*fae548d3Szrj 	{
595*fae548d3Szrj 	  if (fcntl (pipes[1], F_SETFD, FD_CLOEXEC) == -1)
596*fae548d3Szrj 	    {
597*fae548d3Szrj 	      close (pipes[0]);
598*fae548d3Szrj 	      close (pipes[1]);
599*fae548d3Szrj 	      do_pipe = 0;
600*fae548d3Szrj 	    }
601*fae548d3Szrj 	}
602*fae548d3Szrj #endif
603*fae548d3Szrj     }
604*fae548d3Szrj 
605*fae548d3Szrj   /* We declare these to be volatile to avoid warnings from gcc about
606*fae548d3Szrj      them being clobbered by vfork.  */
607*fae548d3Szrj   volatile int sleep_interval = 1;
608*fae548d3Szrj   volatile int retries;
609*fae548d3Szrj 
610*fae548d3Szrj   /* We vfork and then set environ in the child before calling execvp.
611*fae548d3Szrj      This clobbers the parent's environ so we need to restore it.
612*fae548d3Szrj      It would be nice to use one of the exec* functions that takes an
613*fae548d3Szrj      environment as a parameter, but that may have portability
614*fae548d3Szrj      issues.  It is marked volatile so the child doesn't consider it a
615*fae548d3Szrj      dead variable and therefore clobber where ever it is stored.  */
616*fae548d3Szrj   char **volatile save_environ = environ;
617*fae548d3Szrj 
618*fae548d3Szrj   for (retries = 0; retries < 4; ++retries)
619*fae548d3Szrj     {
620*fae548d3Szrj       pid = vfork ();
621*fae548d3Szrj       if (pid >= 0)
622*fae548d3Szrj 	break;
623*fae548d3Szrj       sleep (sleep_interval);
624*fae548d3Szrj       sleep_interval *= 2;
625*fae548d3Szrj     }
626*fae548d3Szrj 
627*fae548d3Szrj   switch (pid)
628*fae548d3Szrj     {
629*fae548d3Szrj     case -1:
630*fae548d3Szrj       if (do_pipe)
631*fae548d3Szrj 	{
632*fae548d3Szrj 	  close (pipes[0]);
633*fae548d3Szrj 	  close (pipes[1]);
634*fae548d3Szrj 	}
635*fae548d3Szrj       *err = errno;
636*fae548d3Szrj       *errmsg = VFORK_STRING;
637*fae548d3Szrj       return (pid_t) -1;
638*fae548d3Szrj 
639*fae548d3Szrj     case 0:
640*fae548d3Szrj       /* Child process.  */
641*fae548d3Szrj       {
642*fae548d3Szrj 	struct fn_err failed;
643*fae548d3Szrj 	failed.fn = NULL;
644*fae548d3Szrj 
645*fae548d3Szrj 	if (do_pipe)
646*fae548d3Szrj 	  close (pipes[0]);
647*fae548d3Szrj 	if (!failed.fn && in != STDIN_FILE_NO)
648*fae548d3Szrj 	  {
649*fae548d3Szrj 	    if (dup2 (in, STDIN_FILE_NO) < 0)
650*fae548d3Szrj 	      failed.fn = "dup2", failed.err = errno;
651*fae548d3Szrj 	    else if (close (in) < 0)
652*fae548d3Szrj 	      failed.fn = "close", failed.err = errno;
653*fae548d3Szrj 	  }
654*fae548d3Szrj 	if (!failed.fn && out != STDOUT_FILE_NO)
655*fae548d3Szrj 	  {
656*fae548d3Szrj 	    if (dup2 (out, STDOUT_FILE_NO) < 0)
657*fae548d3Szrj 	      failed.fn = "dup2", failed.err = errno;
658*fae548d3Szrj 	    else if (close (out) < 0)
659*fae548d3Szrj 	      failed.fn = "close", failed.err = errno;
660*fae548d3Szrj 	  }
661*fae548d3Szrj 	if (!failed.fn && errdes != STDERR_FILE_NO)
662*fae548d3Szrj 	  {
663*fae548d3Szrj 	    if (dup2 (errdes, STDERR_FILE_NO) < 0)
664*fae548d3Szrj 	      failed.fn = "dup2", failed.err = errno;
665*fae548d3Szrj 	    else if (close (errdes) < 0)
666*fae548d3Szrj 	      failed.fn = "close", failed.err = errno;
667*fae548d3Szrj 	  }
668*fae548d3Szrj 	if (!failed.fn && toclose >= 0)
669*fae548d3Szrj 	  {
670*fae548d3Szrj 	    if (close (toclose) < 0)
671*fae548d3Szrj 	      failed.fn = "close", failed.err = errno;
672*fae548d3Szrj 	  }
673*fae548d3Szrj 	if (!failed.fn && (flags & PEX_STDERR_TO_STDOUT) != 0)
674*fae548d3Szrj 	  {
675*fae548d3Szrj 	    if (dup2 (STDOUT_FILE_NO, STDERR_FILE_NO) < 0)
676*fae548d3Szrj 	      failed.fn = "dup2", failed.err = errno;
677*fae548d3Szrj 	  }
678*fae548d3Szrj 	if (!failed.fn)
679*fae548d3Szrj 	  {
680*fae548d3Szrj 	    if (env)
681*fae548d3Szrj 	      /* NOTE: In a standard vfork implementation this clobbers
682*fae548d3Szrj 		 the parent's copy of environ "too" (in reality there's
683*fae548d3Szrj 		 only one copy).  This is ok as we restore it below.  */
684*fae548d3Szrj 	      environ = (char**) env;
685*fae548d3Szrj 	    if ((flags & PEX_SEARCH) != 0)
686*fae548d3Szrj 	      {
687*fae548d3Szrj 		execvp (executable, to_ptr32 (argv));
688*fae548d3Szrj 		failed.fn = "execvp", failed.err = errno;
689*fae548d3Szrj 	      }
690*fae548d3Szrj 	    else
691*fae548d3Szrj 	      {
692*fae548d3Szrj 		execv (executable, to_ptr32 (argv));
693*fae548d3Szrj 		failed.fn = "execv", failed.err = errno;
694*fae548d3Szrj 	      }
695*fae548d3Szrj 	  }
696*fae548d3Szrj 
697*fae548d3Szrj 	/* Something failed, report an error.  We don't use stdio
698*fae548d3Szrj 	   routines, because we might be here due to a vfork call.  */
699*fae548d3Szrj 	ssize_t retval = 0;
700*fae548d3Szrj 
701*fae548d3Szrj 	if (!do_pipe
702*fae548d3Szrj 	    || write (pipes[1], &failed, sizeof (failed)) != sizeof (failed))
703*fae548d3Szrj 	  {
704*fae548d3Szrj 	    /* The parent will not see our scream above, so write to
705*fae548d3Szrj 	       stdout.  */
706*fae548d3Szrj #define writeerr(s) (retval |= write (STDERR_FILE_NO, s, strlen (s)))
707*fae548d3Szrj 	    writeerr (obj->pname);
708*fae548d3Szrj 	    writeerr (": error trying to exec '");
709*fae548d3Szrj 	    writeerr (executable);
710*fae548d3Szrj 	    writeerr ("': ");
711*fae548d3Szrj 	    writeerr (failed.fn);
712*fae548d3Szrj 	    writeerr (": ");
713*fae548d3Szrj 	    writeerr (xstrerror (failed.err));
714*fae548d3Szrj 	    writeerr ("\n");
715*fae548d3Szrj #undef writeerr
716*fae548d3Szrj 	  }
717*fae548d3Szrj 
718*fae548d3Szrj 	/* Exit with -2 if the error output failed, too.  */
719*fae548d3Szrj 	_exit (retval < 0 ? -2 : -1);
720*fae548d3Szrj       }
721*fae548d3Szrj       /* NOTREACHED */
722*fae548d3Szrj       return (pid_t) -1;
723*fae548d3Szrj 
724*fae548d3Szrj     default:
725*fae548d3Szrj       /* Parent process.  */
726*fae548d3Szrj       {
727*fae548d3Szrj 	/* Restore environ.  Note that the parent either doesn't run
728*fae548d3Szrj 	   until the child execs/exits (standard vfork behaviour), or
729*fae548d3Szrj 	   if it does run then vfork is behaving more like fork.  In
730*fae548d3Szrj 	   either case we needn't worry about clobbering the child's
731*fae548d3Szrj 	   copy of environ.  */
732*fae548d3Szrj 	environ = save_environ;
733*fae548d3Szrj 
734*fae548d3Szrj 	struct fn_err failed;
735*fae548d3Szrj 	failed.fn = NULL;
736*fae548d3Szrj 	if (do_pipe)
737*fae548d3Szrj 	  {
738*fae548d3Szrj 	    close (pipes[1]);
739*fae548d3Szrj 	    ssize_t len = read (pipes[0], &failed, sizeof (failed));
740*fae548d3Szrj 	    if (len < 0)
741*fae548d3Szrj 	      failed.fn = NULL;
742*fae548d3Szrj 	    close (pipes[0]);
743*fae548d3Szrj 	  }
744*fae548d3Szrj 
745*fae548d3Szrj 	if (!failed.fn && in != STDIN_FILE_NO)
746*fae548d3Szrj 	  if (close (in) < 0)
747*fae548d3Szrj 	    failed.fn = "close", failed.err = errno;
748*fae548d3Szrj 	if (!failed.fn && out != STDOUT_FILE_NO)
749*fae548d3Szrj 	  if (close (out) < 0)
750*fae548d3Szrj 	    failed.fn = "close", failed.err = errno;
751*fae548d3Szrj 	if (!failed.fn && errdes != STDERR_FILE_NO)
752*fae548d3Szrj 	  if (close (errdes) < 0)
753*fae548d3Szrj 	    failed.fn = "close", failed.err = errno;
754*fae548d3Szrj 
755*fae548d3Szrj 	if (failed.fn)
756*fae548d3Szrj 	  {
757*fae548d3Szrj 	    *err = failed.err;
758*fae548d3Szrj 	    *errmsg = failed.fn;
759*fae548d3Szrj 	    return (pid_t) -1;
760*fae548d3Szrj 	  }
761*fae548d3Szrj       }
762*fae548d3Szrj       return pid;
763*fae548d3Szrj     }
764*fae548d3Szrj }
765*fae548d3Szrj #endif /* SPAWN */
766*fae548d3Szrj 
767*fae548d3Szrj /* Wait for a child process to complete.  */
768*fae548d3Szrj 
769*fae548d3Szrj static int
pex_unix_wait(struct pex_obj * obj,pid_t pid,int * status,struct pex_time * time,int done,const char ** errmsg,int * err)770*fae548d3Szrj pex_unix_wait (struct pex_obj *obj, pid_t pid, int *status,
771*fae548d3Szrj 	       struct pex_time *time, int done, const char **errmsg,
772*fae548d3Szrj 	       int *err)
773*fae548d3Szrj {
774*fae548d3Szrj   /* If we are cleaning up when the caller didn't retrieve process
775*fae548d3Szrj      status for some reason, encourage the process to go away.  */
776*fae548d3Szrj   if (done)
777*fae548d3Szrj     kill (pid, SIGTERM);
778*fae548d3Szrj 
779*fae548d3Szrj   if (pex_wait (obj, pid, status, time) < 0)
780*fae548d3Szrj     {
781*fae548d3Szrj       *err = errno;
782*fae548d3Szrj       *errmsg = "wait";
783*fae548d3Szrj       return -1;
784*fae548d3Szrj     }
785*fae548d3Szrj 
786*fae548d3Szrj   return 0;
787*fae548d3Szrj }
788*fae548d3Szrj 
789*fae548d3Szrj /* Create a pipe.  */
790*fae548d3Szrj 
791*fae548d3Szrj static int
pex_unix_pipe(struct pex_obj * obj ATTRIBUTE_UNUSED,int * p,int binary ATTRIBUTE_UNUSED)792*fae548d3Szrj pex_unix_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
793*fae548d3Szrj 	       int binary ATTRIBUTE_UNUSED)
794*fae548d3Szrj {
795*fae548d3Szrj   return pipe (p);
796*fae548d3Szrj }
797*fae548d3Szrj 
798*fae548d3Szrj /* Get a FILE pointer to read from a file descriptor.  */
799*fae548d3Szrj 
800*fae548d3Szrj static FILE *
pex_unix_fdopenr(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary ATTRIBUTE_UNUSED)801*fae548d3Szrj pex_unix_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
802*fae548d3Szrj 		  int binary ATTRIBUTE_UNUSED)
803*fae548d3Szrj {
804*fae548d3Szrj   return fdopen (fd, "r");
805*fae548d3Szrj }
806*fae548d3Szrj 
807*fae548d3Szrj static FILE *
pex_unix_fdopenw(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary ATTRIBUTE_UNUSED)808*fae548d3Szrj pex_unix_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
809*fae548d3Szrj 		  int binary ATTRIBUTE_UNUSED)
810*fae548d3Szrj {
811*fae548d3Szrj   if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
812*fae548d3Szrj     return NULL;
813*fae548d3Szrj   return fdopen (fd, "w");
814*fae548d3Szrj }
815*fae548d3Szrj 
816*fae548d3Szrj static void
pex_unix_cleanup(struct pex_obj * obj ATTRIBUTE_UNUSED)817*fae548d3Szrj pex_unix_cleanup (struct pex_obj *obj ATTRIBUTE_UNUSED)
818*fae548d3Szrj {
819*fae548d3Szrj #if !defined (HAVE_WAIT4) && !defined (HAVE_WAITPID)
820*fae548d3Szrj   while (obj->sysdep != NULL)
821*fae548d3Szrj     {
822*fae548d3Szrj       struct status_list *this;
823*fae548d3Szrj       struct status_list *next;
824*fae548d3Szrj 
825*fae548d3Szrj       this = (struct status_list *) obj->sysdep;
826*fae548d3Szrj       next = this->next;
827*fae548d3Szrj       free (this);
828*fae548d3Szrj       obj->sysdep = (void *) next;
829*fae548d3Szrj     }
830*fae548d3Szrj #endif
831*fae548d3Szrj }
832