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