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