12a6b7db3Sskrll /* Utilities to execute a program in a subprocess (possibly linked by pipes
22a6b7db3Sskrll with other subprocesses), and wait for it. Generic Unix version
32a6b7db3Sskrll (also used for UWIN and VMS).
4*f22f0ef4Schristos Copyright (C) 1996-2022 Free Software Foundation, Inc.
52a6b7db3Sskrll
62a6b7db3Sskrll This file is part of the libiberty library.
72a6b7db3Sskrll Libiberty is free software; you can redistribute it and/or
82a6b7db3Sskrll modify it under the terms of the GNU Library General Public
92a6b7db3Sskrll License as published by the Free Software Foundation; either
102a6b7db3Sskrll version 2 of the License, or (at your option) any later version.
112a6b7db3Sskrll
122a6b7db3Sskrll Libiberty is distributed in the hope that it will be useful,
132a6b7db3Sskrll but WITHOUT ANY WARRANTY; without even the implied warranty of
142a6b7db3Sskrll MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
152a6b7db3Sskrll Library General Public License for more details.
162a6b7db3Sskrll
172a6b7db3Sskrll You should have received a copy of the GNU Library General Public
182a6b7db3Sskrll License along with libiberty; see the file COPYING.LIB. If not,
192a6b7db3Sskrll write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
202a6b7db3Sskrll Boston, MA 02110-1301, USA. */
212a6b7db3Sskrll
222a6b7db3Sskrll #include "config.h"
232a6b7db3Sskrll #include "libiberty.h"
242a6b7db3Sskrll #include "pex-common.h"
2575f9f1baSchristos #include "environ.h"
262a6b7db3Sskrll
272a6b7db3Sskrll #include <stdio.h>
282a6b7db3Sskrll #include <signal.h>
292a6b7db3Sskrll #include <errno.h>
302a6b7db3Sskrll #ifdef NEED_DECLARATION_ERRNO
312a6b7db3Sskrll extern int errno;
322a6b7db3Sskrll #endif
332a6b7db3Sskrll #ifdef HAVE_STDLIB_H
342a6b7db3Sskrll #include <stdlib.h>
352a6b7db3Sskrll #endif
362a6b7db3Sskrll #ifdef HAVE_STRING_H
372a6b7db3Sskrll #include <string.h>
382a6b7db3Sskrll #endif
392a6b7db3Sskrll #ifdef HAVE_UNISTD_H
402a6b7db3Sskrll #include <unistd.h>
412a6b7db3Sskrll #endif
422a6b7db3Sskrll
432a6b7db3Sskrll #include <sys/types.h>
442a6b7db3Sskrll
452a6b7db3Sskrll #ifdef HAVE_FCNTL_H
462a6b7db3Sskrll #include <fcntl.h>
472a6b7db3Sskrll #endif
482a6b7db3Sskrll #ifdef HAVE_SYS_WAIT_H
492a6b7db3Sskrll #include <sys/wait.h>
502a6b7db3Sskrll #endif
512a6b7db3Sskrll #ifdef HAVE_GETRUSAGE
522a6b7db3Sskrll #include <sys/time.h>
532a6b7db3Sskrll #include <sys/resource.h>
542a6b7db3Sskrll #endif
552a6b7db3Sskrll #ifdef HAVE_SYS_STAT_H
562a6b7db3Sskrll #include <sys/stat.h>
572a6b7db3Sskrll #endif
58b3ac4aedSchristos #ifdef HAVE_PROCESS_H
59b3ac4aedSchristos #include <process.h>
60b3ac4aedSchristos #endif
612a6b7db3Sskrll
622a6b7db3Sskrll #ifdef vfork /* Autoconf may define this to fork for us. */
632a6b7db3Sskrll # define VFORK_STRING "fork"
642a6b7db3Sskrll #else
652a6b7db3Sskrll # define VFORK_STRING "vfork"
662a6b7db3Sskrll #endif
672a6b7db3Sskrll #ifdef HAVE_VFORK_H
682a6b7db3Sskrll #include <vfork.h>
692a6b7db3Sskrll #endif
70b3ac4aedSchristos #if defined(VMS) && defined (__LONG_POINTERS)
71b3ac4aedSchristos #ifndef __CHAR_PTR32
72b3ac4aedSchristos typedef char * __char_ptr32
73b3ac4aedSchristos __attribute__ ((mode (SI)));
74b3ac4aedSchristos #endif
752a6b7db3Sskrll
76b3ac4aedSchristos typedef __char_ptr32 *__char_ptr_char_ptr32
77b3ac4aedSchristos __attribute__ ((mode (SI)));
78b3ac4aedSchristos
79b3ac4aedSchristos /* Return a 32 bit pointer to an array of 32 bit pointers
80b3ac4aedSchristos given a 64 bit pointer to an array of 64 bit pointers. */
81b3ac4aedSchristos
82b3ac4aedSchristos static __char_ptr_char_ptr32
to_ptr32(char ** ptr64)83b3ac4aedSchristos to_ptr32 (char **ptr64)
84b3ac4aedSchristos {
85b3ac4aedSchristos int argc;
86b3ac4aedSchristos __char_ptr_char_ptr32 short_argv;
87b3ac4aedSchristos
8805caefcfSchristos /* Count number of arguments. */
8905caefcfSchristos for (argc = 0; ptr64[argc] != NULL; argc++)
9005caefcfSchristos ;
91b3ac4aedSchristos
92b3ac4aedSchristos /* Reallocate argv with 32 bit pointers. */
93b3ac4aedSchristos short_argv = (__char_ptr_char_ptr32) decc$malloc
94b3ac4aedSchristos (sizeof (__char_ptr32) * (argc + 1));
95b3ac4aedSchristos
9605caefcfSchristos for (argc = 0; ptr64[argc] != NULL; argc++)
97b3ac4aedSchristos short_argv[argc] = (__char_ptr32) decc$strdup (ptr64[argc]);
98b3ac4aedSchristos
99b3ac4aedSchristos short_argv[argc] = (__char_ptr32) 0;
100b3ac4aedSchristos return short_argv;
101b3ac4aedSchristos
102b3ac4aedSchristos }
103b3ac4aedSchristos #else
104b3ac4aedSchristos #define to_ptr32(argv) argv
105b3ac4aedSchristos #endif
1062a6b7db3Sskrll
1072a6b7db3Sskrll /* File mode to use for private and world-readable files. */
1082a6b7db3Sskrll
1092a6b7db3Sskrll #if defined (S_IRUSR) && defined (S_IWUSR) && defined (S_IRGRP) && defined (S_IWGRP) && defined (S_IROTH) && defined (S_IWOTH)
1102a6b7db3Sskrll #define PUBLIC_MODE \
1112a6b7db3Sskrll (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
1122a6b7db3Sskrll #else
1132a6b7db3Sskrll #define PUBLIC_MODE 0666
1142a6b7db3Sskrll #endif
1152a6b7db3Sskrll
1162a6b7db3Sskrll /* Get the exit status of a particular process, and optionally get the
1172a6b7db3Sskrll time that it took. This is simple if we have wait4, slightly
1182a6b7db3Sskrll harder if we have waitpid, and is a pain if we only have wait. */
1192a6b7db3Sskrll
1202a6b7db3Sskrll static pid_t pex_wait (struct pex_obj *, pid_t, int *, struct pex_time *);
1212a6b7db3Sskrll
1222a6b7db3Sskrll #ifdef HAVE_WAIT4
1232a6b7db3Sskrll
1242a6b7db3Sskrll static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)1252a6b7db3Sskrll pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
1262a6b7db3Sskrll struct pex_time *time)
1272a6b7db3Sskrll {
1282a6b7db3Sskrll pid_t ret;
1292a6b7db3Sskrll struct rusage r;
1302a6b7db3Sskrll
1312a6b7db3Sskrll #ifdef HAVE_WAITPID
1322a6b7db3Sskrll if (time == NULL)
1332a6b7db3Sskrll return waitpid (pid, status, 0);
1342a6b7db3Sskrll #endif
1352a6b7db3Sskrll
1362a6b7db3Sskrll ret = wait4 (pid, status, 0, &r);
1372a6b7db3Sskrll
1382a6b7db3Sskrll if (time != NULL)
1392a6b7db3Sskrll {
1402a6b7db3Sskrll time->user_seconds = r.ru_utime.tv_sec;
1412a6b7db3Sskrll time->user_microseconds= r.ru_utime.tv_usec;
1422a6b7db3Sskrll time->system_seconds = r.ru_stime.tv_sec;
1432a6b7db3Sskrll time->system_microseconds= r.ru_stime.tv_usec;
1442a6b7db3Sskrll }
1452a6b7db3Sskrll
1462a6b7db3Sskrll return ret;
1472a6b7db3Sskrll }
1482a6b7db3Sskrll
1492a6b7db3Sskrll #else /* ! defined (HAVE_WAIT4) */
1502a6b7db3Sskrll
1512a6b7db3Sskrll #ifdef HAVE_WAITPID
1522a6b7db3Sskrll
1532a6b7db3Sskrll #ifndef HAVE_GETRUSAGE
1542a6b7db3Sskrll
1552a6b7db3Sskrll static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)1562a6b7db3Sskrll pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
1572a6b7db3Sskrll struct pex_time *time)
1582a6b7db3Sskrll {
1592a6b7db3Sskrll if (time != NULL)
1602a6b7db3Sskrll memset (time, 0, sizeof (struct pex_time));
1612a6b7db3Sskrll return waitpid (pid, status, 0);
1622a6b7db3Sskrll }
1632a6b7db3Sskrll
1642a6b7db3Sskrll #else /* defined (HAVE_GETRUSAGE) */
1652a6b7db3Sskrll
1662a6b7db3Sskrll static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)1672a6b7db3Sskrll pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
1682a6b7db3Sskrll struct pex_time *time)
1692a6b7db3Sskrll {
1702a6b7db3Sskrll struct rusage r1, r2;
1712a6b7db3Sskrll pid_t ret;
1722a6b7db3Sskrll
1732a6b7db3Sskrll if (time == NULL)
1742a6b7db3Sskrll return waitpid (pid, status, 0);
1752a6b7db3Sskrll
1762a6b7db3Sskrll getrusage (RUSAGE_CHILDREN, &r1);
1772a6b7db3Sskrll
1782a6b7db3Sskrll ret = waitpid (pid, status, 0);
1792a6b7db3Sskrll if (ret < 0)
1802a6b7db3Sskrll return ret;
1812a6b7db3Sskrll
1822a6b7db3Sskrll getrusage (RUSAGE_CHILDREN, &r2);
1832a6b7db3Sskrll
1842a6b7db3Sskrll time->user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
1852a6b7db3Sskrll time->user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
1862a6b7db3Sskrll if (r2.ru_utime.tv_usec < r1.ru_utime.tv_usec)
1872a6b7db3Sskrll {
1882a6b7db3Sskrll --time->user_seconds;
1892a6b7db3Sskrll time->user_microseconds += 1000000;
1902a6b7db3Sskrll }
1912a6b7db3Sskrll
1922a6b7db3Sskrll time->system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
1932a6b7db3Sskrll time->system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
1942a6b7db3Sskrll if (r2.ru_stime.tv_usec < r1.ru_stime.tv_usec)
1952a6b7db3Sskrll {
1962a6b7db3Sskrll --time->system_seconds;
1972a6b7db3Sskrll time->system_microseconds += 1000000;
1982a6b7db3Sskrll }
1992a6b7db3Sskrll
2002a6b7db3Sskrll return ret;
2012a6b7db3Sskrll }
2022a6b7db3Sskrll
2032a6b7db3Sskrll #endif /* defined (HAVE_GETRUSAGE) */
2042a6b7db3Sskrll
2052a6b7db3Sskrll #else /* ! defined (HAVE_WAITPID) */
2062a6b7db3Sskrll
2072a6b7db3Sskrll struct status_list
2082a6b7db3Sskrll {
2092a6b7db3Sskrll struct status_list *next;
2102a6b7db3Sskrll pid_t pid;
2112a6b7db3Sskrll int status;
2122a6b7db3Sskrll struct pex_time time;
2132a6b7db3Sskrll };
2142a6b7db3Sskrll
2152a6b7db3Sskrll static pid_t
pex_wait(struct pex_obj * obj,pid_t pid,int * status,struct pex_time * time)2162a6b7db3Sskrll pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time)
2172a6b7db3Sskrll {
2182a6b7db3Sskrll struct status_list **pp;
2192a6b7db3Sskrll
2202a6b7db3Sskrll for (pp = (struct status_list **) &obj->sysdep;
2212a6b7db3Sskrll *pp != NULL;
2222a6b7db3Sskrll pp = &(*pp)->next)
2232a6b7db3Sskrll {
2242a6b7db3Sskrll if ((*pp)->pid == pid)
2252a6b7db3Sskrll {
2262a6b7db3Sskrll struct status_list *p;
2272a6b7db3Sskrll
2282a6b7db3Sskrll p = *pp;
2292a6b7db3Sskrll *status = p->status;
2302a6b7db3Sskrll if (time != NULL)
2312a6b7db3Sskrll *time = p->time;
2322a6b7db3Sskrll *pp = p->next;
2332a6b7db3Sskrll free (p);
2342a6b7db3Sskrll return pid;
2352a6b7db3Sskrll }
2362a6b7db3Sskrll }
2372a6b7db3Sskrll
2382a6b7db3Sskrll while (1)
2392a6b7db3Sskrll {
2402a6b7db3Sskrll pid_t cpid;
2412a6b7db3Sskrll struct status_list *psl;
2422a6b7db3Sskrll struct pex_time pt;
2432a6b7db3Sskrll #ifdef HAVE_GETRUSAGE
2442a6b7db3Sskrll struct rusage r1, r2;
2452a6b7db3Sskrll #endif
2462a6b7db3Sskrll
2472a6b7db3Sskrll if (time != NULL)
2482a6b7db3Sskrll {
2492a6b7db3Sskrll #ifdef HAVE_GETRUSAGE
2502a6b7db3Sskrll getrusage (RUSAGE_CHILDREN, &r1);
2512a6b7db3Sskrll #else
2522a6b7db3Sskrll memset (&pt, 0, sizeof (struct pex_time));
2532a6b7db3Sskrll #endif
2542a6b7db3Sskrll }
2552a6b7db3Sskrll
2562a6b7db3Sskrll cpid = wait (status);
2572a6b7db3Sskrll
2582a6b7db3Sskrll #ifdef HAVE_GETRUSAGE
2592a6b7db3Sskrll if (time != NULL && cpid >= 0)
2602a6b7db3Sskrll {
2612a6b7db3Sskrll getrusage (RUSAGE_CHILDREN, &r2);
2622a6b7db3Sskrll
2632a6b7db3Sskrll pt.user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
2642a6b7db3Sskrll pt.user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
2652a6b7db3Sskrll if (pt.user_microseconds < 0)
2662a6b7db3Sskrll {
2672a6b7db3Sskrll --pt.user_seconds;
2682a6b7db3Sskrll pt.user_microseconds += 1000000;
2692a6b7db3Sskrll }
2702a6b7db3Sskrll
2712a6b7db3Sskrll pt.system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
2722a6b7db3Sskrll pt.system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
2732a6b7db3Sskrll if (pt.system_microseconds < 0)
2742a6b7db3Sskrll {
2752a6b7db3Sskrll --pt.system_seconds;
2762a6b7db3Sskrll pt.system_microseconds += 1000000;
2772a6b7db3Sskrll }
2782a6b7db3Sskrll }
2792a6b7db3Sskrll #endif
2802a6b7db3Sskrll
2812a6b7db3Sskrll if (cpid < 0 || cpid == pid)
2822a6b7db3Sskrll {
2832a6b7db3Sskrll if (time != NULL)
2842a6b7db3Sskrll *time = pt;
2852a6b7db3Sskrll return cpid;
2862a6b7db3Sskrll }
2872a6b7db3Sskrll
2882a6b7db3Sskrll psl = XNEW (struct status_list);
2892a6b7db3Sskrll psl->pid = cpid;
2902a6b7db3Sskrll psl->status = *status;
2912a6b7db3Sskrll if (time != NULL)
2922a6b7db3Sskrll psl->time = pt;
2932a6b7db3Sskrll psl->next = (struct status_list *) obj->sysdep;
2942a6b7db3Sskrll obj->sysdep = (void *) psl;
2952a6b7db3Sskrll }
2962a6b7db3Sskrll }
2972a6b7db3Sskrll
2982a6b7db3Sskrll #endif /* ! defined (HAVE_WAITPID) */
2992a6b7db3Sskrll #endif /* ! defined (HAVE_WAIT4) */
3002a6b7db3Sskrll
3012a6b7db3Sskrll static int pex_unix_open_read (struct pex_obj *, const char *, int);
3025ba6b03cSchristos static int pex_unix_open_write (struct pex_obj *, const char *, int, int);
3032a6b7db3Sskrll static pid_t pex_unix_exec_child (struct pex_obj *, int, const char *,
3042a6b7db3Sskrll char * const *, char * const *,
3052a6b7db3Sskrll int, int, int, int,
3062a6b7db3Sskrll const char **, int *);
3072a6b7db3Sskrll static int pex_unix_close (struct pex_obj *, int);
3082a6b7db3Sskrll static int pex_unix_wait (struct pex_obj *, pid_t, int *, struct pex_time *,
3092a6b7db3Sskrll int, const char **, int *);
3102a6b7db3Sskrll static int pex_unix_pipe (struct pex_obj *, int *, int);
3112a6b7db3Sskrll static FILE *pex_unix_fdopenr (struct pex_obj *, int, int);
3122a6b7db3Sskrll static FILE *pex_unix_fdopenw (struct pex_obj *, int, int);
3132a6b7db3Sskrll static void pex_unix_cleanup (struct pex_obj *);
3142a6b7db3Sskrll
3152a6b7db3Sskrll /* The list of functions we pass to the common routines. */
3162a6b7db3Sskrll
3172a6b7db3Sskrll const struct pex_funcs funcs =
3182a6b7db3Sskrll {
3192a6b7db3Sskrll pex_unix_open_read,
3202a6b7db3Sskrll pex_unix_open_write,
3212a6b7db3Sskrll pex_unix_exec_child,
3222a6b7db3Sskrll pex_unix_close,
3232a6b7db3Sskrll pex_unix_wait,
3242a6b7db3Sskrll pex_unix_pipe,
3252a6b7db3Sskrll pex_unix_fdopenr,
3262a6b7db3Sskrll pex_unix_fdopenw,
3272a6b7db3Sskrll pex_unix_cleanup
3282a6b7db3Sskrll };
3292a6b7db3Sskrll
3302a6b7db3Sskrll /* Return a newly initialized pex_obj structure. */
3312a6b7db3Sskrll
3322a6b7db3Sskrll struct pex_obj *
pex_init(int flags,const char * pname,const char * tempbase)3332a6b7db3Sskrll pex_init (int flags, const char *pname, const char *tempbase)
3342a6b7db3Sskrll {
3352a6b7db3Sskrll return pex_init_common (flags, pname, tempbase, &funcs);
3362a6b7db3Sskrll }
3372a6b7db3Sskrll
3382a6b7db3Sskrll /* Open a file for reading. */
3392a6b7db3Sskrll
3402a6b7db3Sskrll static int
pex_unix_open_read(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary ATTRIBUTE_UNUSED)3412a6b7db3Sskrll pex_unix_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
3422a6b7db3Sskrll int binary ATTRIBUTE_UNUSED)
3432a6b7db3Sskrll {
3442a6b7db3Sskrll return open (name, O_RDONLY);
3452a6b7db3Sskrll }
3462a6b7db3Sskrll
3472a6b7db3Sskrll /* Open a file for writing. */
3482a6b7db3Sskrll
3492a6b7db3Sskrll static int
pex_unix_open_write(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary ATTRIBUTE_UNUSED,int append)3502a6b7db3Sskrll pex_unix_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
3515ba6b03cSchristos int binary ATTRIBUTE_UNUSED, int append)
3522a6b7db3Sskrll {
3532a6b7db3Sskrll /* Note that we can't use O_EXCL here because gcc may have already
3542a6b7db3Sskrll created the temporary file via make_temp_file. */
3555ba6b03cSchristos return open (name, O_WRONLY | O_CREAT
3565ba6b03cSchristos | (append ? O_APPEND : O_TRUNC), PUBLIC_MODE);
3572a6b7db3Sskrll }
3582a6b7db3Sskrll
3592a6b7db3Sskrll /* Close a file. */
3602a6b7db3Sskrll
3612a6b7db3Sskrll static int
pex_unix_close(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd)3622a6b7db3Sskrll pex_unix_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
3632a6b7db3Sskrll {
3642a6b7db3Sskrll return close (fd);
3652a6b7db3Sskrll }
3662a6b7db3Sskrll
3672a6b7db3Sskrll /* Execute a child. */
3682a6b7db3Sskrll
369b3ac4aedSchristos #if defined(HAVE_SPAWNVE) && defined(HAVE_SPAWNVPE)
370b3ac4aedSchristos /* Implementation of pex->exec_child using the Cygwin spawn operation. */
371b3ac4aedSchristos
372b3ac4aedSchristos /* Subroutine of pex_unix_exec_child. Move OLD_FD to a new file descriptor
373b3ac4aedSchristos to be stored in *PNEW_FD, save the flags in *PFLAGS, and arrange for the
374b3ac4aedSchristos saved copy to be close-on-exec. Move CHILD_FD into OLD_FD. If CHILD_FD
375b3ac4aedSchristos is -1, OLD_FD is to be closed. Return -1 on error. */
376b3ac4aedSchristos
377b3ac4aedSchristos static int
save_and_install_fd(int * pnew_fd,int * pflags,int old_fd,int child_fd)378b3ac4aedSchristos save_and_install_fd(int *pnew_fd, int *pflags, int old_fd, int child_fd)
379b3ac4aedSchristos {
380b3ac4aedSchristos int new_fd, flags;
381b3ac4aedSchristos
382b3ac4aedSchristos flags = fcntl (old_fd, F_GETFD);
383b3ac4aedSchristos
384b3ac4aedSchristos /* If we could not retrieve the flags, then OLD_FD was not open. */
385b3ac4aedSchristos if (flags < 0)
386b3ac4aedSchristos {
387b3ac4aedSchristos new_fd = -1, flags = 0;
388b3ac4aedSchristos if (child_fd >= 0 && dup2 (child_fd, old_fd) < 0)
389b3ac4aedSchristos return -1;
390b3ac4aedSchristos }
391b3ac4aedSchristos /* If we wish to close OLD_FD, just mark it CLOEXEC. */
392b3ac4aedSchristos else if (child_fd == -1)
393b3ac4aedSchristos {
394b3ac4aedSchristos new_fd = old_fd;
395b3ac4aedSchristos if ((flags & FD_CLOEXEC) == 0 && fcntl (old_fd, F_SETFD, FD_CLOEXEC) < 0)
396b3ac4aedSchristos return -1;
397b3ac4aedSchristos }
398b3ac4aedSchristos /* Otherwise we need to save a copy of OLD_FD before installing CHILD_FD. */
399b3ac4aedSchristos else
400b3ac4aedSchristos {
401b3ac4aedSchristos #ifdef F_DUPFD_CLOEXEC
402b3ac4aedSchristos new_fd = fcntl (old_fd, F_DUPFD_CLOEXEC, 3);
403b3ac4aedSchristos if (new_fd < 0)
404b3ac4aedSchristos return -1;
405b3ac4aedSchristos #else
406b3ac4aedSchristos /* Prefer F_DUPFD over dup in order to avoid getting a new fd
407b3ac4aedSchristos in the range 0-2, right where a new stderr fd might get put. */
408b3ac4aedSchristos new_fd = fcntl (old_fd, F_DUPFD, 3);
409b3ac4aedSchristos if (new_fd < 0)
410b3ac4aedSchristos return -1;
411b3ac4aedSchristos if (fcntl (new_fd, F_SETFD, FD_CLOEXEC) < 0)
412b3ac4aedSchristos return -1;
413b3ac4aedSchristos #endif
414b3ac4aedSchristos if (dup2 (child_fd, old_fd) < 0)
415b3ac4aedSchristos return -1;
416b3ac4aedSchristos }
417b3ac4aedSchristos
418b3ac4aedSchristos *pflags = flags;
419b3ac4aedSchristos if (pnew_fd)
420b3ac4aedSchristos *pnew_fd = new_fd;
421b3ac4aedSchristos else if (new_fd != old_fd)
422b3ac4aedSchristos abort ();
423b3ac4aedSchristos
424b3ac4aedSchristos return 0;
425b3ac4aedSchristos }
426b3ac4aedSchristos
427b3ac4aedSchristos /* Subroutine of pex_unix_exec_child. Move SAVE_FD back to OLD_FD
428b3ac4aedSchristos restoring FLAGS. If SAVE_FD < 0, OLD_FD is to be closed. */
429b3ac4aedSchristos
430b3ac4aedSchristos static int
restore_fd(int old_fd,int save_fd,int flags)431b3ac4aedSchristos restore_fd(int old_fd, int save_fd, int flags)
432b3ac4aedSchristos {
433b3ac4aedSchristos /* For SAVE_FD < 0, all we have to do is restore the
434b3ac4aedSchristos "closed-ness" of the original. */
435b3ac4aedSchristos if (save_fd < 0)
436b3ac4aedSchristos return close (old_fd);
437b3ac4aedSchristos
438b3ac4aedSchristos /* For SAVE_FD == OLD_FD, all we have to do is restore the
439b3ac4aedSchristos original setting of the CLOEXEC flag. */
440b3ac4aedSchristos if (save_fd == old_fd)
441b3ac4aedSchristos {
442b3ac4aedSchristos if (flags & FD_CLOEXEC)
443b3ac4aedSchristos return 0;
444b3ac4aedSchristos return fcntl (old_fd, F_SETFD, flags);
445b3ac4aedSchristos }
446b3ac4aedSchristos
447b3ac4aedSchristos /* Otherwise we have to move the descriptor back, restore the flags,
448b3ac4aedSchristos and close the saved copy. */
449b3ac4aedSchristos #ifdef HAVE_DUP3
450b3ac4aedSchristos if (flags == FD_CLOEXEC)
451b3ac4aedSchristos {
452b3ac4aedSchristos if (dup3 (save_fd, old_fd, O_CLOEXEC) < 0)
453b3ac4aedSchristos return -1;
454b3ac4aedSchristos }
455b3ac4aedSchristos else
456b3ac4aedSchristos #endif
457b3ac4aedSchristos {
458b3ac4aedSchristos if (dup2 (save_fd, old_fd) < 0)
459b3ac4aedSchristos return -1;
460b3ac4aedSchristos if (flags != 0 && fcntl (old_fd, F_SETFD, flags) < 0)
461b3ac4aedSchristos return -1;
462b3ac4aedSchristos }
463b3ac4aedSchristos return close (save_fd);
464b3ac4aedSchristos }
465b3ac4aedSchristos
466b3ac4aedSchristos 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)467b3ac4aedSchristos pex_unix_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED,
468b3ac4aedSchristos int flags, const char *executable,
469b3ac4aedSchristos char * const * argv, char * const * env,
470b3ac4aedSchristos int in, int out, int errdes, int toclose,
471b3ac4aedSchristos const char **errmsg, int *err)
472b3ac4aedSchristos {
473b3ac4aedSchristos int fl_in = 0, fl_out = 0, fl_err = 0, fl_tc = 0;
474b3ac4aedSchristos int save_in = -1, save_out = -1, save_err = -1;
475b3ac4aedSchristos int max, retries;
476b3ac4aedSchristos pid_t pid;
477b3ac4aedSchristos
478b3ac4aedSchristos if (flags & PEX_STDERR_TO_STDOUT)
479b3ac4aedSchristos errdes = out;
480b3ac4aedSchristos
481b3ac4aedSchristos /* We need the three standard file descriptors to be set up as for
482b3ac4aedSchristos the child before we perform the spawn. The file descriptors for
483b3ac4aedSchristos the parent need to be moved and marked for close-on-exec. */
484b3ac4aedSchristos if (in != STDIN_FILE_NO
485b3ac4aedSchristos && save_and_install_fd (&save_in, &fl_in, STDIN_FILE_NO, in) < 0)
486b3ac4aedSchristos goto error_dup2;
487b3ac4aedSchristos if (out != STDOUT_FILE_NO
488b3ac4aedSchristos && save_and_install_fd (&save_out, &fl_out, STDOUT_FILE_NO, out) < 0)
489b3ac4aedSchristos goto error_dup2;
490b3ac4aedSchristos if (errdes != STDERR_FILE_NO
491b3ac4aedSchristos && save_and_install_fd (&save_err, &fl_err, STDERR_FILE_NO, errdes) < 0)
492b3ac4aedSchristos goto error_dup2;
493b3ac4aedSchristos if (toclose >= 0
494b3ac4aedSchristos && save_and_install_fd (NULL, &fl_tc, toclose, -1) < 0)
495b3ac4aedSchristos goto error_dup2;
496b3ac4aedSchristos
497b3ac4aedSchristos /* Now that we've moved the file descriptors for the child into place,
498b3ac4aedSchristos close the originals. Be careful not to close any of the standard
499b3ac4aedSchristos file descriptors that we just set up. */
500b3ac4aedSchristos max = -1;
501b3ac4aedSchristos if (errdes >= 0)
502b3ac4aedSchristos max = STDERR_FILE_NO;
503b3ac4aedSchristos else if (out >= 0)
504b3ac4aedSchristos max = STDOUT_FILE_NO;
505b3ac4aedSchristos else if (in >= 0)
506b3ac4aedSchristos max = STDIN_FILE_NO;
507b3ac4aedSchristos if (in > max)
508b3ac4aedSchristos close (in);
509b3ac4aedSchristos if (out > max)
510b3ac4aedSchristos close (out);
511b3ac4aedSchristos if (errdes > max && errdes != out)
512b3ac4aedSchristos close (errdes);
513b3ac4aedSchristos
514b3ac4aedSchristos /* If we were not given an environment, use the global environment. */
515b3ac4aedSchristos if (env == NULL)
516b3ac4aedSchristos env = environ;
517b3ac4aedSchristos
518b3ac4aedSchristos /* Launch the program. If we get EAGAIN (normally out of pid's), try
519b3ac4aedSchristos again a few times with increasing backoff times. */
520b3ac4aedSchristos retries = 0;
521b3ac4aedSchristos while (1)
522b3ac4aedSchristos {
523b3ac4aedSchristos typedef const char * const *cc_cp;
524b3ac4aedSchristos
525b3ac4aedSchristos if (flags & PEX_SEARCH)
526b3ac4aedSchristos pid = spawnvpe (_P_NOWAITO, executable, (cc_cp)argv, (cc_cp)env);
527b3ac4aedSchristos else
528b3ac4aedSchristos pid = spawnve (_P_NOWAITO, executable, (cc_cp)argv, (cc_cp)env);
529b3ac4aedSchristos
530b3ac4aedSchristos if (pid > 0)
531b3ac4aedSchristos break;
532b3ac4aedSchristos
533b3ac4aedSchristos *err = errno;
534b3ac4aedSchristos *errmsg = "spawn";
535b3ac4aedSchristos if (errno != EAGAIN || ++retries == 4)
536b3ac4aedSchristos return (pid_t) -1;
537b3ac4aedSchristos sleep (1 << retries);
538b3ac4aedSchristos }
539b3ac4aedSchristos
540b3ac4aedSchristos /* Success. Restore the parent's file descriptors that we saved above. */
541b3ac4aedSchristos if (toclose >= 0
542b3ac4aedSchristos && restore_fd (toclose, toclose, fl_tc) < 0)
543b3ac4aedSchristos goto error_dup2;
544b3ac4aedSchristos if (in != STDIN_FILE_NO
545b3ac4aedSchristos && restore_fd (STDIN_FILE_NO, save_in, fl_in) < 0)
546b3ac4aedSchristos goto error_dup2;
547b3ac4aedSchristos if (out != STDOUT_FILE_NO
548b3ac4aedSchristos && restore_fd (STDOUT_FILE_NO, save_out, fl_out) < 0)
549b3ac4aedSchristos goto error_dup2;
550b3ac4aedSchristos if (errdes != STDERR_FILE_NO
551b3ac4aedSchristos && restore_fd (STDERR_FILE_NO, save_err, fl_err) < 0)
552b3ac4aedSchristos goto error_dup2;
553b3ac4aedSchristos
554b3ac4aedSchristos return pid;
555b3ac4aedSchristos
556b3ac4aedSchristos error_dup2:
557b3ac4aedSchristos *err = errno;
558b3ac4aedSchristos *errmsg = "dup2";
559b3ac4aedSchristos return (pid_t) -1;
560b3ac4aedSchristos }
561b3ac4aedSchristos
562b3ac4aedSchristos #else
563b3ac4aedSchristos /* Implementation of pex->exec_child using standard vfork + exec. */
564b3ac4aedSchristos
5652a6b7db3Sskrll 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)5662a6b7db3Sskrll pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable,
5672a6b7db3Sskrll char * const * argv, char * const * env,
5682a6b7db3Sskrll int in, int out, int errdes,
5692a6b7db3Sskrll int toclose, const char **errmsg, int *err)
5702a6b7db3Sskrll {
571f7172901Schristos pid_t pid = -1;
572f7172901Schristos /* Tuple to communicate error from child to parent. We can safely
573f7172901Schristos transfer string literal pointers as both run with identical
574f7172901Schristos address mappings. */
575f7172901Schristos struct fn_err
576f7172901Schristos {
577f7172901Schristos const char *fn;
578f7172901Schristos int err;
579f7172901Schristos };
580f7172901Schristos volatile int do_pipe = 0;
581f7172901Schristos volatile int pipes[2]; /* [0]:reader,[1]:writer. */
582f7172901Schristos #ifdef O_CLOEXEC
583f7172901Schristos do_pipe = 1;
584f7172901Schristos #endif
585f7172901Schristos if (do_pipe)
586f7172901Schristos {
587f7172901Schristos #ifdef HAVE_PIPE2
588f7172901Schristos if (pipe2 ((int *)pipes, O_CLOEXEC))
589f7172901Schristos do_pipe = 0;
590f7172901Schristos #else
591f7172901Schristos if (pipe ((int *)pipes))
592f7172901Schristos do_pipe = 0;
593f7172901Schristos else
594f7172901Schristos {
595f7172901Schristos if (fcntl (pipes[1], F_SETFD, FD_CLOEXEC) == -1)
596f7172901Schristos {
597f7172901Schristos close (pipes[0]);
598f7172901Schristos close (pipes[1]);
599f7172901Schristos do_pipe = 0;
600f7172901Schristos }
601f7172901Schristos }
602f7172901Schristos #endif
603f7172901Schristos }
6042a6b7db3Sskrll
6052a6b7db3Sskrll /* We declare these to be volatile to avoid warnings from gcc about
6062a6b7db3Sskrll them being clobbered by vfork. */
607f7172901Schristos volatile int sleep_interval = 1;
6082a6b7db3Sskrll volatile int retries;
6092a6b7db3Sskrll
610b3ac4aedSchristos /* We vfork and then set environ in the child before calling execvp.
611b3ac4aedSchristos This clobbers the parent's environ so we need to restore it.
612b3ac4aedSchristos It would be nice to use one of the exec* functions that takes an
613f7172901Schristos environment as a parameter, but that may have portability
614f7172901Schristos issues. It is marked volatile so the child doesn't consider it a
615f7172901Schristos dead variable and therefore clobber where ever it is stored. */
616f7172901Schristos char **volatile save_environ = environ;
617b3ac4aedSchristos
6182a6b7db3Sskrll for (retries = 0; retries < 4; ++retries)
6192a6b7db3Sskrll {
6202a6b7db3Sskrll pid = vfork ();
6212a6b7db3Sskrll if (pid >= 0)
6222a6b7db3Sskrll break;
6232a6b7db3Sskrll sleep (sleep_interval);
6242a6b7db3Sskrll sleep_interval *= 2;
6252a6b7db3Sskrll }
6262a6b7db3Sskrll
6272a6b7db3Sskrll switch (pid)
6282a6b7db3Sskrll {
6292a6b7db3Sskrll case -1:
630f7172901Schristos if (do_pipe)
631f7172901Schristos {
632f7172901Schristos close (pipes[0]);
633f7172901Schristos close (pipes[1]);
634f7172901Schristos }
6352a6b7db3Sskrll *err = errno;
6362a6b7db3Sskrll *errmsg = VFORK_STRING;
6372a6b7db3Sskrll return (pid_t) -1;
6382a6b7db3Sskrll
6392a6b7db3Sskrll case 0:
6402a6b7db3Sskrll /* Child process. */
641f7172901Schristos {
642f7172901Schristos struct fn_err failed;
643f7172901Schristos failed.fn = NULL;
644f7172901Schristos
645f7172901Schristos if (do_pipe)
646f7172901Schristos close (pipes[0]);
647f7172901Schristos if (!failed.fn && in != STDIN_FILE_NO)
6482a6b7db3Sskrll {
6492a6b7db3Sskrll if (dup2 (in, STDIN_FILE_NO) < 0)
650f7172901Schristos failed.fn = "dup2", failed.err = errno;
651f7172901Schristos else if (close (in) < 0)
652f7172901Schristos failed.fn = "close", failed.err = errno;
6532a6b7db3Sskrll }
654f7172901Schristos if (!failed.fn && out != STDOUT_FILE_NO)
6552a6b7db3Sskrll {
6562a6b7db3Sskrll if (dup2 (out, STDOUT_FILE_NO) < 0)
657f7172901Schristos failed.fn = "dup2", failed.err = errno;
658f7172901Schristos else if (close (out) < 0)
659f7172901Schristos failed.fn = "close", failed.err = errno;
6602a6b7db3Sskrll }
661f7172901Schristos if (!failed.fn && errdes != STDERR_FILE_NO)
6622a6b7db3Sskrll {
6632a6b7db3Sskrll if (dup2 (errdes, STDERR_FILE_NO) < 0)
664f7172901Schristos failed.fn = "dup2", failed.err = errno;
665f7172901Schristos else if (close (errdes) < 0)
666f7172901Schristos failed.fn = "close", failed.err = errno;
6672a6b7db3Sskrll }
668f7172901Schristos if (!failed.fn && toclose >= 0)
6692a6b7db3Sskrll {
6702a6b7db3Sskrll if (close (toclose) < 0)
671f7172901Schristos failed.fn = "close", failed.err = errno;
6722a6b7db3Sskrll }
673f7172901Schristos if (!failed.fn && (flags & PEX_STDERR_TO_STDOUT) != 0)
6742a6b7db3Sskrll {
6752a6b7db3Sskrll if (dup2 (STDOUT_FILE_NO, STDERR_FILE_NO) < 0)
676f7172901Schristos failed.fn = "dup2", failed.err = errno;
6772a6b7db3Sskrll }
678f7172901Schristos if (!failed.fn)
679b3ac4aedSchristos {
680f7172901Schristos if (env)
681f7172901Schristos /* NOTE: In a standard vfork implementation this clobbers
682f7172901Schristos the parent's copy of environ "too" (in reality there's
683f7172901Schristos only one copy). This is ok as we restore it below. */
6842a6b7db3Sskrll environ = (char**) env;
6852a6b7db3Sskrll if ((flags & PEX_SEARCH) != 0)
6862a6b7db3Sskrll {
687b3ac4aedSchristos execvp (executable, to_ptr32 (argv));
688f7172901Schristos failed.fn = "execvp", failed.err = errno;
6892a6b7db3Sskrll }
6902a6b7db3Sskrll else
6912a6b7db3Sskrll {
692b3ac4aedSchristos execv (executable, to_ptr32 (argv));
693f7172901Schristos failed.fn = "execv", failed.err = errno;
694f7172901Schristos }
6952a6b7db3Sskrll }
6962a6b7db3Sskrll
697f7172901Schristos /* Something failed, report an error. We don't use stdio
698f7172901Schristos routines, because we might be here due to a vfork call. */
699f7172901Schristos ssize_t retval = 0;
700f7172901Schristos
701f7172901Schristos if (!do_pipe
702f7172901Schristos || write (pipes[1], &failed, sizeof (failed)) != sizeof (failed))
703f7172901Schristos {
704f7172901Schristos /* The parent will not see our scream above, so write to
705f7172901Schristos stdout. */
706f7172901Schristos #define writeerr(s) (retval |= write (STDERR_FILE_NO, s, strlen (s)))
707f7172901Schristos writeerr (obj->pname);
708f7172901Schristos writeerr (": error trying to exec '");
709f7172901Schristos writeerr (executable);
710f7172901Schristos writeerr ("': ");
711f7172901Schristos writeerr (failed.fn);
712f7172901Schristos writeerr (": ");
713f7172901Schristos writeerr (xstrerror (failed.err));
714f7172901Schristos writeerr ("\n");
715f7172901Schristos #undef writeerr
716f7172901Schristos }
717f7172901Schristos
718f7172901Schristos /* Exit with -2 if the error output failed, too. */
719f7172901Schristos _exit (retval < 0 ? -2 : -1);
720f7172901Schristos }
7212a6b7db3Sskrll /* NOTREACHED */
7222a6b7db3Sskrll return (pid_t) -1;
7232a6b7db3Sskrll
7242a6b7db3Sskrll default:
7252a6b7db3Sskrll /* Parent process. */
726f7172901Schristos {
727f7172901Schristos /* Restore environ. Note that the parent either doesn't run
728f7172901Schristos until the child execs/exits (standard vfork behaviour), or
729f7172901Schristos if it does run then vfork is behaving more like fork. In
730f7172901Schristos either case we needn't worry about clobbering the child's
731f7172901Schristos copy of environ. */
732b3ac4aedSchristos environ = save_environ;
733b3ac4aedSchristos
734f7172901Schristos struct fn_err failed;
735f7172901Schristos failed.fn = NULL;
736f7172901Schristos if (do_pipe)
7372a6b7db3Sskrll {
738f7172901Schristos close (pipes[1]);
739f7172901Schristos ssize_t len = read (pipes[0], &failed, sizeof (failed));
740f7172901Schristos if (len < 0)
741f7172901Schristos failed.fn = NULL;
742f7172901Schristos close (pipes[0]);
7432a6b7db3Sskrll }
7442a6b7db3Sskrll
745f7172901Schristos if (!failed.fn && in != STDIN_FILE_NO)
746f7172901Schristos if (close (in) < 0)
747f7172901Schristos failed.fn = "close", failed.err = errno;
748f7172901Schristos if (!failed.fn && out != STDOUT_FILE_NO)
749f7172901Schristos if (close (out) < 0)
750f7172901Schristos failed.fn = "close", failed.err = errno;
751f7172901Schristos if (!failed.fn && errdes != STDERR_FILE_NO)
752f7172901Schristos if (close (errdes) < 0)
753f7172901Schristos failed.fn = "close", failed.err = errno;
754f7172901Schristos
755f7172901Schristos if (failed.fn)
756f7172901Schristos {
757f7172901Schristos *err = failed.err;
758f7172901Schristos *errmsg = failed.fn;
759f7172901Schristos return (pid_t) -1;
760f7172901Schristos }
761f7172901Schristos }
7622a6b7db3Sskrll return pid;
7632a6b7db3Sskrll }
7642a6b7db3Sskrll }
765b3ac4aedSchristos #endif /* SPAWN */
7662a6b7db3Sskrll
7672a6b7db3Sskrll /* Wait for a child process to complete. */
7682a6b7db3Sskrll
7692a6b7db3Sskrll 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)7702a6b7db3Sskrll pex_unix_wait (struct pex_obj *obj, pid_t pid, int *status,
7712a6b7db3Sskrll struct pex_time *time, int done, const char **errmsg,
7722a6b7db3Sskrll int *err)
7732a6b7db3Sskrll {
7742a6b7db3Sskrll /* If we are cleaning up when the caller didn't retrieve process
7752a6b7db3Sskrll status for some reason, encourage the process to go away. */
7762a6b7db3Sskrll if (done)
7772a6b7db3Sskrll kill (pid, SIGTERM);
7782a6b7db3Sskrll
7792a6b7db3Sskrll if (pex_wait (obj, pid, status, time) < 0)
7802a6b7db3Sskrll {
7812a6b7db3Sskrll *err = errno;
7822a6b7db3Sskrll *errmsg = "wait";
7832a6b7db3Sskrll return -1;
7842a6b7db3Sskrll }
7852a6b7db3Sskrll
7862a6b7db3Sskrll return 0;
7872a6b7db3Sskrll }
7882a6b7db3Sskrll
7892a6b7db3Sskrll /* Create a pipe. */
7902a6b7db3Sskrll
7912a6b7db3Sskrll static int
pex_unix_pipe(struct pex_obj * obj ATTRIBUTE_UNUSED,int * p,int binary ATTRIBUTE_UNUSED)7922a6b7db3Sskrll pex_unix_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
7932a6b7db3Sskrll int binary ATTRIBUTE_UNUSED)
7942a6b7db3Sskrll {
7952a6b7db3Sskrll return pipe (p);
7962a6b7db3Sskrll }
7972a6b7db3Sskrll
7982a6b7db3Sskrll /* Get a FILE pointer to read from a file descriptor. */
7992a6b7db3Sskrll
8002a6b7db3Sskrll static FILE *
pex_unix_fdopenr(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary ATTRIBUTE_UNUSED)8012a6b7db3Sskrll pex_unix_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
8022a6b7db3Sskrll int binary ATTRIBUTE_UNUSED)
8032a6b7db3Sskrll {
8042a6b7db3Sskrll return fdopen (fd, "r");
8052a6b7db3Sskrll }
8062a6b7db3Sskrll
8072a6b7db3Sskrll static FILE *
pex_unix_fdopenw(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary ATTRIBUTE_UNUSED)8082a6b7db3Sskrll pex_unix_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
8092a6b7db3Sskrll int binary ATTRIBUTE_UNUSED)
8102a6b7db3Sskrll {
8112a6b7db3Sskrll if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
8122a6b7db3Sskrll return NULL;
8132a6b7db3Sskrll return fdopen (fd, "w");
8142a6b7db3Sskrll }
8152a6b7db3Sskrll
8162a6b7db3Sskrll static void
pex_unix_cleanup(struct pex_obj * obj ATTRIBUTE_UNUSED)8172a6b7db3Sskrll pex_unix_cleanup (struct pex_obj *obj ATTRIBUTE_UNUSED)
8182a6b7db3Sskrll {
8192a6b7db3Sskrll #if !defined (HAVE_WAIT4) && !defined (HAVE_WAITPID)
8202a6b7db3Sskrll while (obj->sysdep != NULL)
8212a6b7db3Sskrll {
8222a6b7db3Sskrll struct status_list *this;
8232a6b7db3Sskrll struct status_list *next;
8242a6b7db3Sskrll
8252a6b7db3Sskrll this = (struct status_list *) obj->sysdep;
8262a6b7db3Sskrll next = this->next;
8272a6b7db3Sskrll free (this);
8282a6b7db3Sskrll obj->sysdep = (void *) next;
8292a6b7db3Sskrll }
8302a6b7db3Sskrll #endif
8312a6b7db3Sskrll }
832