xref: /netbsd-src/external/gpl3/binutils/dist/libiberty/pex-unix.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
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*cb63e24eSchristos    Copyright (C) 1996-2024 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"
258cbf5cb7Schristos #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
58be9ac0eaSchristos #ifdef HAVE_PROCESS_H
59be9ac0eaSchristos #include <process.h>
60be9ac0eaSchristos #endif
61*cb63e24eSchristos #ifdef HAVE_SPAWN_H
62*cb63e24eSchristos #include <spawn.h>
63*cb63e24eSchristos #endif
642a6b7db3Sskrll 
652a6b7db3Sskrll #ifdef vfork /* Autoconf may define this to fork for us. */
662a6b7db3Sskrll # define VFORK_STRING "fork"
672a6b7db3Sskrll #else
682a6b7db3Sskrll # define VFORK_STRING "vfork"
692a6b7db3Sskrll #endif
702a6b7db3Sskrll #ifdef HAVE_VFORK_H
712a6b7db3Sskrll #include <vfork.h>
722a6b7db3Sskrll #endif
73be9ac0eaSchristos #if defined(VMS) && defined (__LONG_POINTERS)
74be9ac0eaSchristos #ifndef __CHAR_PTR32
75be9ac0eaSchristos typedef char * __char_ptr32
76be9ac0eaSchristos __attribute__ ((mode (SI)));
77be9ac0eaSchristos #endif
782a6b7db3Sskrll 
79be9ac0eaSchristos typedef __char_ptr32 *__char_ptr_char_ptr32
80be9ac0eaSchristos __attribute__ ((mode (SI)));
81be9ac0eaSchristos 
82be9ac0eaSchristos /* Return a 32 bit pointer to an array of 32 bit pointers
83be9ac0eaSchristos    given a 64 bit pointer to an array of 64 bit pointers.  */
84be9ac0eaSchristos 
85be9ac0eaSchristos static __char_ptr_char_ptr32
to_ptr32(char ** ptr64)86be9ac0eaSchristos to_ptr32 (char **ptr64)
87be9ac0eaSchristos {
88be9ac0eaSchristos   int argc;
89be9ac0eaSchristos   __char_ptr_char_ptr32 short_argv;
90be9ac0eaSchristos 
91883529b6Schristos   /* Count number of arguments.  */
92883529b6Schristos   for (argc = 0; ptr64[argc] != NULL; argc++)
93883529b6Schristos     ;
94be9ac0eaSchristos 
95be9ac0eaSchristos   /* Reallocate argv with 32 bit pointers.  */
96be9ac0eaSchristos   short_argv = (__char_ptr_char_ptr32) decc$malloc
97be9ac0eaSchristos     (sizeof (__char_ptr32) * (argc + 1));
98be9ac0eaSchristos 
99883529b6Schristos   for (argc = 0; ptr64[argc] != NULL; argc++)
100be9ac0eaSchristos     short_argv[argc] = (__char_ptr32) decc$strdup (ptr64[argc]);
101be9ac0eaSchristos 
102be9ac0eaSchristos   short_argv[argc] = (__char_ptr32) 0;
103be9ac0eaSchristos   return short_argv;
104be9ac0eaSchristos 
105be9ac0eaSchristos }
106be9ac0eaSchristos #else
107be9ac0eaSchristos #define to_ptr32(argv) argv
108be9ac0eaSchristos #endif
1092a6b7db3Sskrll 
1102a6b7db3Sskrll /* File mode to use for private and world-readable files.  */
1112a6b7db3Sskrll 
1122a6b7db3Sskrll #if defined (S_IRUSR) && defined (S_IWUSR) && defined (S_IRGRP) && defined (S_IWGRP) && defined (S_IROTH) && defined (S_IWOTH)
1132a6b7db3Sskrll #define PUBLIC_MODE  \
1142a6b7db3Sskrll     (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
1152a6b7db3Sskrll #else
1162a6b7db3Sskrll #define PUBLIC_MODE 0666
1172a6b7db3Sskrll #endif
1182a6b7db3Sskrll 
1192a6b7db3Sskrll /* Get the exit status of a particular process, and optionally get the
1202a6b7db3Sskrll    time that it took.  This is simple if we have wait4, slightly
1212a6b7db3Sskrll    harder if we have waitpid, and is a pain if we only have wait.  */
1222a6b7db3Sskrll 
1232a6b7db3Sskrll static pid_t pex_wait (struct pex_obj *, pid_t, int *, struct pex_time *);
1242a6b7db3Sskrll 
1252a6b7db3Sskrll #ifdef HAVE_WAIT4
1262a6b7db3Sskrll 
1272a6b7db3Sskrll static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)1282a6b7db3Sskrll pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
1292a6b7db3Sskrll 	  struct pex_time *time)
1302a6b7db3Sskrll {
1312a6b7db3Sskrll   pid_t ret;
1322a6b7db3Sskrll   struct rusage r;
1332a6b7db3Sskrll 
1342a6b7db3Sskrll #ifdef HAVE_WAITPID
1352a6b7db3Sskrll   if (time == NULL)
1362a6b7db3Sskrll     return waitpid (pid, status, 0);
1372a6b7db3Sskrll #endif
1382a6b7db3Sskrll 
1392a6b7db3Sskrll   ret = wait4 (pid, status, 0, &r);
1402a6b7db3Sskrll 
1412a6b7db3Sskrll   if (time != NULL)
1422a6b7db3Sskrll     {
1432a6b7db3Sskrll       time->user_seconds = r.ru_utime.tv_sec;
1442a6b7db3Sskrll       time->user_microseconds= r.ru_utime.tv_usec;
1452a6b7db3Sskrll       time->system_seconds = r.ru_stime.tv_sec;
1462a6b7db3Sskrll       time->system_microseconds= r.ru_stime.tv_usec;
1472a6b7db3Sskrll     }
1482a6b7db3Sskrll 
1492a6b7db3Sskrll   return ret;
1502a6b7db3Sskrll }
1512a6b7db3Sskrll 
1522a6b7db3Sskrll #else /* ! defined (HAVE_WAIT4) */
1532a6b7db3Sskrll 
1542a6b7db3Sskrll #ifdef HAVE_WAITPID
1552a6b7db3Sskrll 
1562a6b7db3Sskrll #ifndef HAVE_GETRUSAGE
1572a6b7db3Sskrll 
1582a6b7db3Sskrll static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)1592a6b7db3Sskrll pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
1602a6b7db3Sskrll 	  struct pex_time *time)
1612a6b7db3Sskrll {
1622a6b7db3Sskrll   if (time != NULL)
1632a6b7db3Sskrll     memset (time, 0, sizeof (struct pex_time));
1642a6b7db3Sskrll   return waitpid (pid, status, 0);
1652a6b7db3Sskrll }
1662a6b7db3Sskrll 
1672a6b7db3Sskrll #else /* defined (HAVE_GETRUSAGE) */
1682a6b7db3Sskrll 
1692a6b7db3Sskrll static pid_t
pex_wait(struct pex_obj * obj ATTRIBUTE_UNUSED,pid_t pid,int * status,struct pex_time * time)1702a6b7db3Sskrll pex_wait (struct pex_obj *obj ATTRIBUTE_UNUSED, pid_t pid, int *status,
1712a6b7db3Sskrll 	  struct pex_time *time)
1722a6b7db3Sskrll {
1732a6b7db3Sskrll   struct rusage r1, r2;
1742a6b7db3Sskrll   pid_t ret;
1752a6b7db3Sskrll 
1762a6b7db3Sskrll   if (time == NULL)
1772a6b7db3Sskrll     return waitpid (pid, status, 0);
1782a6b7db3Sskrll 
1792a6b7db3Sskrll   getrusage (RUSAGE_CHILDREN, &r1);
1802a6b7db3Sskrll 
1812a6b7db3Sskrll   ret = waitpid (pid, status, 0);
1822a6b7db3Sskrll   if (ret < 0)
1832a6b7db3Sskrll     return ret;
1842a6b7db3Sskrll 
1852a6b7db3Sskrll   getrusage (RUSAGE_CHILDREN, &r2);
1862a6b7db3Sskrll 
1872a6b7db3Sskrll   time->user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
1882a6b7db3Sskrll   time->user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
1892a6b7db3Sskrll   if (r2.ru_utime.tv_usec < r1.ru_utime.tv_usec)
1902a6b7db3Sskrll     {
1912a6b7db3Sskrll       --time->user_seconds;
1922a6b7db3Sskrll       time->user_microseconds += 1000000;
1932a6b7db3Sskrll     }
1942a6b7db3Sskrll 
1952a6b7db3Sskrll   time->system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
1962a6b7db3Sskrll   time->system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
1972a6b7db3Sskrll   if (r2.ru_stime.tv_usec < r1.ru_stime.tv_usec)
1982a6b7db3Sskrll     {
1992a6b7db3Sskrll       --time->system_seconds;
2002a6b7db3Sskrll       time->system_microseconds += 1000000;
2012a6b7db3Sskrll     }
2022a6b7db3Sskrll 
2032a6b7db3Sskrll   return ret;
2042a6b7db3Sskrll }
2052a6b7db3Sskrll 
2062a6b7db3Sskrll #endif /* defined (HAVE_GETRUSAGE) */
2072a6b7db3Sskrll 
2082a6b7db3Sskrll #else /* ! defined (HAVE_WAITPID) */
2092a6b7db3Sskrll 
2102a6b7db3Sskrll struct status_list
2112a6b7db3Sskrll {
2122a6b7db3Sskrll   struct status_list *next;
2132a6b7db3Sskrll   pid_t pid;
2142a6b7db3Sskrll   int status;
2152a6b7db3Sskrll   struct pex_time time;
2162a6b7db3Sskrll };
2172a6b7db3Sskrll 
2182a6b7db3Sskrll static pid_t
pex_wait(struct pex_obj * obj,pid_t pid,int * status,struct pex_time * time)2192a6b7db3Sskrll pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time)
2202a6b7db3Sskrll {
2212a6b7db3Sskrll   struct status_list **pp;
2222a6b7db3Sskrll 
2232a6b7db3Sskrll   for (pp = (struct status_list **) &obj->sysdep;
2242a6b7db3Sskrll        *pp != NULL;
2252a6b7db3Sskrll        pp = &(*pp)->next)
2262a6b7db3Sskrll     {
2272a6b7db3Sskrll       if ((*pp)->pid == pid)
2282a6b7db3Sskrll 	{
2292a6b7db3Sskrll 	  struct status_list *p;
2302a6b7db3Sskrll 
2312a6b7db3Sskrll 	  p = *pp;
2322a6b7db3Sskrll 	  *status = p->status;
2332a6b7db3Sskrll 	  if (time != NULL)
2342a6b7db3Sskrll 	    *time = p->time;
2352a6b7db3Sskrll 	  *pp = p->next;
2362a6b7db3Sskrll 	  free (p);
2372a6b7db3Sskrll 	  return pid;
2382a6b7db3Sskrll 	}
2392a6b7db3Sskrll     }
2402a6b7db3Sskrll 
2412a6b7db3Sskrll   while (1)
2422a6b7db3Sskrll     {
2432a6b7db3Sskrll       pid_t cpid;
2442a6b7db3Sskrll       struct status_list *psl;
2452a6b7db3Sskrll       struct pex_time pt;
2462a6b7db3Sskrll #ifdef HAVE_GETRUSAGE
2472a6b7db3Sskrll       struct rusage r1, r2;
2482a6b7db3Sskrll #endif
2492a6b7db3Sskrll 
2502a6b7db3Sskrll       if (time != NULL)
2512a6b7db3Sskrll 	{
2522a6b7db3Sskrll #ifdef HAVE_GETRUSAGE
2532a6b7db3Sskrll 	  getrusage (RUSAGE_CHILDREN, &r1);
2542a6b7db3Sskrll #else
2552a6b7db3Sskrll 	  memset (&pt, 0, sizeof (struct pex_time));
2562a6b7db3Sskrll #endif
2572a6b7db3Sskrll 	}
2582a6b7db3Sskrll 
2592a6b7db3Sskrll       cpid = wait (status);
2602a6b7db3Sskrll 
2612a6b7db3Sskrll #ifdef HAVE_GETRUSAGE
2622a6b7db3Sskrll       if (time != NULL && cpid >= 0)
2632a6b7db3Sskrll 	{
2642a6b7db3Sskrll 	  getrusage (RUSAGE_CHILDREN, &r2);
2652a6b7db3Sskrll 
2662a6b7db3Sskrll 	  pt.user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
2672a6b7db3Sskrll 	  pt.user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
2682a6b7db3Sskrll 	  if (pt.user_microseconds < 0)
2692a6b7db3Sskrll 	    {
2702a6b7db3Sskrll 	      --pt.user_seconds;
2712a6b7db3Sskrll 	      pt.user_microseconds += 1000000;
2722a6b7db3Sskrll 	    }
2732a6b7db3Sskrll 
2742a6b7db3Sskrll 	  pt.system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
2752a6b7db3Sskrll 	  pt.system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
2762a6b7db3Sskrll 	  if (pt.system_microseconds < 0)
2772a6b7db3Sskrll 	    {
2782a6b7db3Sskrll 	      --pt.system_seconds;
2792a6b7db3Sskrll 	      pt.system_microseconds += 1000000;
2802a6b7db3Sskrll 	    }
2812a6b7db3Sskrll 	}
2822a6b7db3Sskrll #endif
2832a6b7db3Sskrll 
2842a6b7db3Sskrll       if (cpid < 0 || cpid == pid)
2852a6b7db3Sskrll 	{
2862a6b7db3Sskrll 	  if (time != NULL)
2872a6b7db3Sskrll 	    *time = pt;
2882a6b7db3Sskrll 	  return cpid;
2892a6b7db3Sskrll 	}
2902a6b7db3Sskrll 
2912a6b7db3Sskrll       psl = XNEW (struct status_list);
2922a6b7db3Sskrll       psl->pid = cpid;
2932a6b7db3Sskrll       psl->status = *status;
2942a6b7db3Sskrll       if (time != NULL)
2952a6b7db3Sskrll 	psl->time = pt;
2962a6b7db3Sskrll       psl->next = (struct status_list *) obj->sysdep;
2972a6b7db3Sskrll       obj->sysdep = (void *) psl;
2982a6b7db3Sskrll     }
2992a6b7db3Sskrll }
3002a6b7db3Sskrll 
3012a6b7db3Sskrll #endif /* ! defined (HAVE_WAITPID) */
3022a6b7db3Sskrll #endif /* ! defined (HAVE_WAIT4) */
3032a6b7db3Sskrll 
3042a6b7db3Sskrll static int pex_unix_open_read (struct pex_obj *, const char *, int);
3059573673dSchristos static int pex_unix_open_write (struct pex_obj *, const char *, int, int);
3062a6b7db3Sskrll static pid_t pex_unix_exec_child (struct pex_obj *, int, const char *,
3072a6b7db3Sskrll 				 char * const *, char * const *,
3082a6b7db3Sskrll 				 int, int, int, int,
3092a6b7db3Sskrll 				 const char **, int *);
3102a6b7db3Sskrll static int pex_unix_close (struct pex_obj *, int);
311*cb63e24eSchristos static pid_t pex_unix_wait (struct pex_obj *, pid_t, int *, struct pex_time *,
3122a6b7db3Sskrll 			   int, const char **, int *);
3132a6b7db3Sskrll static int pex_unix_pipe (struct pex_obj *, int *, int);
3142a6b7db3Sskrll static FILE *pex_unix_fdopenr (struct pex_obj *, int, int);
3152a6b7db3Sskrll static FILE *pex_unix_fdopenw (struct pex_obj *, int, int);
3162a6b7db3Sskrll static void pex_unix_cleanup (struct pex_obj *);
3172a6b7db3Sskrll 
3182a6b7db3Sskrll /* The list of functions we pass to the common routines.  */
3192a6b7db3Sskrll 
3202a6b7db3Sskrll const struct pex_funcs funcs =
3212a6b7db3Sskrll {
3222a6b7db3Sskrll   pex_unix_open_read,
3232a6b7db3Sskrll   pex_unix_open_write,
3242a6b7db3Sskrll   pex_unix_exec_child,
3252a6b7db3Sskrll   pex_unix_close,
3262a6b7db3Sskrll   pex_unix_wait,
3272a6b7db3Sskrll   pex_unix_pipe,
3282a6b7db3Sskrll   pex_unix_fdopenr,
3292a6b7db3Sskrll   pex_unix_fdopenw,
3302a6b7db3Sskrll   pex_unix_cleanup
3312a6b7db3Sskrll };
3322a6b7db3Sskrll 
3332a6b7db3Sskrll /* Return a newly initialized pex_obj structure.  */
3342a6b7db3Sskrll 
3352a6b7db3Sskrll struct pex_obj *
pex_init(int flags,const char * pname,const char * tempbase)3362a6b7db3Sskrll pex_init (int flags, const char *pname, const char *tempbase)
3372a6b7db3Sskrll {
3382a6b7db3Sskrll   return pex_init_common (flags, pname, tempbase, &funcs);
3392a6b7db3Sskrll }
3402a6b7db3Sskrll 
3412a6b7db3Sskrll /* Open a file for reading.  */
3422a6b7db3Sskrll 
3432a6b7db3Sskrll static int
pex_unix_open_read(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary ATTRIBUTE_UNUSED)3442a6b7db3Sskrll pex_unix_open_read (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
3452a6b7db3Sskrll 		    int binary ATTRIBUTE_UNUSED)
3462a6b7db3Sskrll {
3472a6b7db3Sskrll   return open (name, O_RDONLY);
3482a6b7db3Sskrll }
3492a6b7db3Sskrll 
3502a6b7db3Sskrll /* Open a file for writing.  */
3512a6b7db3Sskrll 
3522a6b7db3Sskrll static int
pex_unix_open_write(struct pex_obj * obj ATTRIBUTE_UNUSED,const char * name,int binary ATTRIBUTE_UNUSED,int append)3532a6b7db3Sskrll pex_unix_open_write (struct pex_obj *obj ATTRIBUTE_UNUSED, const char *name,
3549573673dSchristos 		     int binary ATTRIBUTE_UNUSED, int append)
3552a6b7db3Sskrll {
3562a6b7db3Sskrll   /* Note that we can't use O_EXCL here because gcc may have already
3572a6b7db3Sskrll      created the temporary file via make_temp_file.  */
3589573673dSchristos   return open (name, O_WRONLY | O_CREAT
3599573673dSchristos 		     | (append ? O_APPEND : O_TRUNC), PUBLIC_MODE);
3602a6b7db3Sskrll }
3612a6b7db3Sskrll 
3622a6b7db3Sskrll /* Close a file.  */
3632a6b7db3Sskrll 
3642a6b7db3Sskrll static int
pex_unix_close(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd)3652a6b7db3Sskrll pex_unix_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
3662a6b7db3Sskrll {
3672a6b7db3Sskrll   return close (fd);
3682a6b7db3Sskrll }
3692a6b7db3Sskrll 
3702a6b7db3Sskrll /* Execute a child.  */
3712a6b7db3Sskrll 
372be9ac0eaSchristos #if defined(HAVE_SPAWNVE) && defined(HAVE_SPAWNVPE)
373be9ac0eaSchristos /* Implementation of pex->exec_child using the Cygwin spawn operation.  */
374be9ac0eaSchristos 
375be9ac0eaSchristos /* Subroutine of pex_unix_exec_child.  Move OLD_FD to a new file descriptor
376be9ac0eaSchristos    to be stored in *PNEW_FD, save the flags in *PFLAGS, and arrange for the
377be9ac0eaSchristos    saved copy to be close-on-exec.  Move CHILD_FD into OLD_FD.  If CHILD_FD
378be9ac0eaSchristos    is -1, OLD_FD is to be closed.  Return -1 on error.  */
379be9ac0eaSchristos 
380be9ac0eaSchristos static int
save_and_install_fd(int * pnew_fd,int * pflags,int old_fd,int child_fd)381be9ac0eaSchristos save_and_install_fd(int *pnew_fd, int *pflags, int old_fd, int child_fd)
382be9ac0eaSchristos {
383be9ac0eaSchristos   int new_fd, flags;
384be9ac0eaSchristos 
385be9ac0eaSchristos   flags = fcntl (old_fd, F_GETFD);
386be9ac0eaSchristos 
387be9ac0eaSchristos   /* If we could not retrieve the flags, then OLD_FD was not open.  */
388be9ac0eaSchristos   if (flags < 0)
389be9ac0eaSchristos     {
390be9ac0eaSchristos       new_fd = -1, flags = 0;
391be9ac0eaSchristos       if (child_fd >= 0 && dup2 (child_fd, old_fd) < 0)
392be9ac0eaSchristos 	return -1;
393be9ac0eaSchristos     }
394be9ac0eaSchristos   /* If we wish to close OLD_FD, just mark it CLOEXEC.  */
395be9ac0eaSchristos   else if (child_fd == -1)
396be9ac0eaSchristos     {
397be9ac0eaSchristos       new_fd = old_fd;
398be9ac0eaSchristos       if ((flags & FD_CLOEXEC) == 0 && fcntl (old_fd, F_SETFD, FD_CLOEXEC) < 0)
399be9ac0eaSchristos 	return -1;
400be9ac0eaSchristos     }
401be9ac0eaSchristos   /* Otherwise we need to save a copy of OLD_FD before installing CHILD_FD.  */
402be9ac0eaSchristos   else
403be9ac0eaSchristos     {
404be9ac0eaSchristos #ifdef F_DUPFD_CLOEXEC
405be9ac0eaSchristos       new_fd = fcntl (old_fd, F_DUPFD_CLOEXEC, 3);
406be9ac0eaSchristos       if (new_fd < 0)
407be9ac0eaSchristos 	return -1;
408be9ac0eaSchristos #else
409be9ac0eaSchristos       /* Prefer F_DUPFD over dup in order to avoid getting a new fd
410be9ac0eaSchristos 	 in the range 0-2, right where a new stderr fd might get put.  */
411be9ac0eaSchristos       new_fd = fcntl (old_fd, F_DUPFD, 3);
412be9ac0eaSchristos       if (new_fd < 0)
413be9ac0eaSchristos 	return -1;
414be9ac0eaSchristos       if (fcntl (new_fd, F_SETFD, FD_CLOEXEC) < 0)
415be9ac0eaSchristos 	return -1;
416be9ac0eaSchristos #endif
417be9ac0eaSchristos       if (dup2 (child_fd, old_fd) < 0)
418be9ac0eaSchristos 	return -1;
419be9ac0eaSchristos     }
420be9ac0eaSchristos 
421be9ac0eaSchristos   *pflags = flags;
422be9ac0eaSchristos   if (pnew_fd)
423be9ac0eaSchristos     *pnew_fd = new_fd;
424be9ac0eaSchristos   else if (new_fd != old_fd)
425be9ac0eaSchristos     abort ();
426be9ac0eaSchristos 
427be9ac0eaSchristos   return 0;
428be9ac0eaSchristos }
429be9ac0eaSchristos 
430be9ac0eaSchristos /* Subroutine of pex_unix_exec_child.  Move SAVE_FD back to OLD_FD
431be9ac0eaSchristos    restoring FLAGS.  If SAVE_FD < 0, OLD_FD is to be closed.  */
432be9ac0eaSchristos 
433be9ac0eaSchristos static int
restore_fd(int old_fd,int save_fd,int flags)434be9ac0eaSchristos restore_fd(int old_fd, int save_fd, int flags)
435be9ac0eaSchristos {
436be9ac0eaSchristos   /* For SAVE_FD < 0, all we have to do is restore the
437be9ac0eaSchristos      "closed-ness" of the original.  */
438be9ac0eaSchristos   if (save_fd < 0)
439be9ac0eaSchristos     return close (old_fd);
440be9ac0eaSchristos 
441be9ac0eaSchristos   /* For SAVE_FD == OLD_FD, all we have to do is restore the
442be9ac0eaSchristos      original setting of the CLOEXEC flag.  */
443be9ac0eaSchristos   if (save_fd == old_fd)
444be9ac0eaSchristos     {
445be9ac0eaSchristos       if (flags & FD_CLOEXEC)
446be9ac0eaSchristos 	return 0;
447be9ac0eaSchristos       return fcntl (old_fd, F_SETFD, flags);
448be9ac0eaSchristos     }
449be9ac0eaSchristos 
450be9ac0eaSchristos   /* Otherwise we have to move the descriptor back, restore the flags,
451be9ac0eaSchristos      and close the saved copy.  */
452be9ac0eaSchristos #ifdef HAVE_DUP3
453be9ac0eaSchristos   if (flags == FD_CLOEXEC)
454be9ac0eaSchristos     {
455be9ac0eaSchristos       if (dup3 (save_fd, old_fd, O_CLOEXEC) < 0)
456be9ac0eaSchristos 	return -1;
457be9ac0eaSchristos     }
458be9ac0eaSchristos   else
459be9ac0eaSchristos #endif
460be9ac0eaSchristos     {
461be9ac0eaSchristos       if (dup2 (save_fd, old_fd) < 0)
462be9ac0eaSchristos 	return -1;
463be9ac0eaSchristos       if (flags != 0 && fcntl (old_fd, F_SETFD, flags) < 0)
464be9ac0eaSchristos 	return -1;
465be9ac0eaSchristos     }
466be9ac0eaSchristos   return close (save_fd);
467be9ac0eaSchristos }
468be9ac0eaSchristos 
469be9ac0eaSchristos 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)470be9ac0eaSchristos pex_unix_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED,
471be9ac0eaSchristos 		     int flags, const char *executable,
472be9ac0eaSchristos 		     char * const * argv, char * const * env,
473be9ac0eaSchristos                      int in, int out, int errdes, int toclose,
474be9ac0eaSchristos 		     const char **errmsg, int *err)
475be9ac0eaSchristos {
476be9ac0eaSchristos   int fl_in = 0, fl_out = 0, fl_err = 0, fl_tc = 0;
477be9ac0eaSchristos   int save_in = -1, save_out = -1, save_err = -1;
478be9ac0eaSchristos   int max, retries;
479be9ac0eaSchristos   pid_t pid;
480be9ac0eaSchristos 
481be9ac0eaSchristos   if (flags & PEX_STDERR_TO_STDOUT)
482be9ac0eaSchristos     errdes = out;
483be9ac0eaSchristos 
484be9ac0eaSchristos   /* We need the three standard file descriptors to be set up as for
485be9ac0eaSchristos      the child before we perform the spawn.  The file descriptors for
486be9ac0eaSchristos      the parent need to be moved and marked for close-on-exec.  */
487be9ac0eaSchristos   if (in != STDIN_FILE_NO
488be9ac0eaSchristos       && save_and_install_fd (&save_in, &fl_in, STDIN_FILE_NO, in) < 0)
489be9ac0eaSchristos     goto error_dup2;
490be9ac0eaSchristos   if (out != STDOUT_FILE_NO
491be9ac0eaSchristos       && save_and_install_fd (&save_out, &fl_out, STDOUT_FILE_NO, out) < 0)
492be9ac0eaSchristos     goto error_dup2;
493be9ac0eaSchristos   if (errdes != STDERR_FILE_NO
494be9ac0eaSchristos       && save_and_install_fd (&save_err, &fl_err, STDERR_FILE_NO, errdes) < 0)
495be9ac0eaSchristos     goto error_dup2;
496be9ac0eaSchristos   if (toclose >= 0
497be9ac0eaSchristos       && save_and_install_fd (NULL, &fl_tc, toclose, -1) < 0)
498be9ac0eaSchristos     goto error_dup2;
499be9ac0eaSchristos 
500be9ac0eaSchristos   /* Now that we've moved the file descriptors for the child into place,
501be9ac0eaSchristos      close the originals.  Be careful not to close any of the standard
502be9ac0eaSchristos      file descriptors that we just set up.  */
503be9ac0eaSchristos   max = -1;
504be9ac0eaSchristos   if (errdes >= 0)
505be9ac0eaSchristos     max = STDERR_FILE_NO;
506be9ac0eaSchristos   else if (out >= 0)
507be9ac0eaSchristos     max = STDOUT_FILE_NO;
508be9ac0eaSchristos   else if (in >= 0)
509be9ac0eaSchristos     max = STDIN_FILE_NO;
510be9ac0eaSchristos   if (in > max)
511be9ac0eaSchristos     close (in);
512be9ac0eaSchristos   if (out > max)
513be9ac0eaSchristos     close (out);
514be9ac0eaSchristos   if (errdes > max && errdes != out)
515be9ac0eaSchristos     close (errdes);
516be9ac0eaSchristos 
517be9ac0eaSchristos   /* If we were not given an environment, use the global environment.  */
518be9ac0eaSchristos   if (env == NULL)
519be9ac0eaSchristos     env = environ;
520be9ac0eaSchristos 
521be9ac0eaSchristos   /* Launch the program.  If we get EAGAIN (normally out of pid's), try
522be9ac0eaSchristos      again a few times with increasing backoff times.  */
523be9ac0eaSchristos   retries = 0;
524be9ac0eaSchristos   while (1)
525be9ac0eaSchristos     {
526be9ac0eaSchristos       typedef const char * const *cc_cp;
527be9ac0eaSchristos 
528be9ac0eaSchristos       if (flags & PEX_SEARCH)
529be9ac0eaSchristos 	pid = spawnvpe (_P_NOWAITO, executable, (cc_cp)argv, (cc_cp)env);
530be9ac0eaSchristos       else
531be9ac0eaSchristos 	pid = spawnve (_P_NOWAITO, executable, (cc_cp)argv, (cc_cp)env);
532be9ac0eaSchristos 
533be9ac0eaSchristos       if (pid > 0)
534be9ac0eaSchristos 	break;
535be9ac0eaSchristos 
536be9ac0eaSchristos       *err = errno;
537be9ac0eaSchristos       *errmsg = "spawn";
538be9ac0eaSchristos       if (errno != EAGAIN || ++retries == 4)
539be9ac0eaSchristos 	return (pid_t) -1;
540be9ac0eaSchristos       sleep (1 << retries);
541be9ac0eaSchristos     }
542be9ac0eaSchristos 
543be9ac0eaSchristos   /* Success.  Restore the parent's file descriptors that we saved above.  */
544be9ac0eaSchristos   if (toclose >= 0
545be9ac0eaSchristos       && restore_fd (toclose, toclose, fl_tc) < 0)
546be9ac0eaSchristos     goto error_dup2;
547be9ac0eaSchristos   if (in != STDIN_FILE_NO
548be9ac0eaSchristos       && restore_fd (STDIN_FILE_NO, save_in, fl_in) < 0)
549be9ac0eaSchristos     goto error_dup2;
550be9ac0eaSchristos   if (out != STDOUT_FILE_NO
551be9ac0eaSchristos       && restore_fd (STDOUT_FILE_NO, save_out, fl_out) < 0)
552be9ac0eaSchristos     goto error_dup2;
553be9ac0eaSchristos   if (errdes != STDERR_FILE_NO
554be9ac0eaSchristos       && restore_fd (STDERR_FILE_NO, save_err, fl_err) < 0)
555be9ac0eaSchristos     goto error_dup2;
556be9ac0eaSchristos 
557be9ac0eaSchristos   return pid;
558be9ac0eaSchristos 
559be9ac0eaSchristos  error_dup2:
560be9ac0eaSchristos   *err = errno;
561be9ac0eaSchristos   *errmsg = "dup2";
562be9ac0eaSchristos   return (pid_t) -1;
563be9ac0eaSchristos }
564be9ac0eaSchristos 
565*cb63e24eSchristos #elif defined(HAVE_POSIX_SPAWN) && defined(HAVE_POSIX_SPAWNP)
566*cb63e24eSchristos /* Implementation of pex->exec_child using posix_spawn.            */
567*cb63e24eSchristos 
568*cb63e24eSchristos 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)569*cb63e24eSchristos pex_unix_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED,
570*cb63e24eSchristos 		     int flags, const char *executable,
571*cb63e24eSchristos 		     char * const * argv, char * const * env,
572*cb63e24eSchristos 		     int in, int out, int errdes,
573*cb63e24eSchristos 		     int toclose, const char **errmsg, int *err)
574*cb63e24eSchristos {
575*cb63e24eSchristos   int ret;
576*cb63e24eSchristos   pid_t pid = -1;
577*cb63e24eSchristos   posix_spawnattr_t attr;
578*cb63e24eSchristos   posix_spawn_file_actions_t actions;
579*cb63e24eSchristos   int attr_initialized = 0, actions_initialized = 0;
580*cb63e24eSchristos 
581*cb63e24eSchristos   *err = 0;
582*cb63e24eSchristos 
583*cb63e24eSchristos   ret = posix_spawnattr_init (&attr);
584*cb63e24eSchristos   if (ret)
585*cb63e24eSchristos     {
586*cb63e24eSchristos       *err = ret;
587*cb63e24eSchristos       *errmsg = "posix_spawnattr_init";
588*cb63e24eSchristos       goto exit;
589*cb63e24eSchristos     }
590*cb63e24eSchristos   attr_initialized = 1;
591*cb63e24eSchristos 
592*cb63e24eSchristos   /* Use vfork() on glibc <=2.24. */
593*cb63e24eSchristos #ifdef POSIX_SPAWN_USEVFORK
594*cb63e24eSchristos   ret = posix_spawnattr_setflags (&attr, POSIX_SPAWN_USEVFORK);
595*cb63e24eSchristos   if (ret)
596*cb63e24eSchristos     {
597*cb63e24eSchristos       *err = ret;
598*cb63e24eSchristos       *errmsg = "posix_spawnattr_setflags";
599*cb63e24eSchristos       goto exit;
600*cb63e24eSchristos     }
601*cb63e24eSchristos #endif
602*cb63e24eSchristos 
603*cb63e24eSchristos   ret = posix_spawn_file_actions_init (&actions);
604*cb63e24eSchristos   if (ret)
605*cb63e24eSchristos     {
606*cb63e24eSchristos       *err = ret;
607*cb63e24eSchristos       *errmsg = "posix_spawn_file_actions_init";
608*cb63e24eSchristos       goto exit;
609*cb63e24eSchristos     }
610*cb63e24eSchristos   actions_initialized = 1;
611*cb63e24eSchristos 
612*cb63e24eSchristos   if (in != STDIN_FILE_NO)
613*cb63e24eSchristos     {
614*cb63e24eSchristos       ret = posix_spawn_file_actions_adddup2 (&actions, in, STDIN_FILE_NO);
615*cb63e24eSchristos       if (ret)
616*cb63e24eSchristos 	{
617*cb63e24eSchristos 	  *err = ret;
618*cb63e24eSchristos 	  *errmsg = "posix_spawn_file_actions_adddup2";
619*cb63e24eSchristos 	  goto exit;
620*cb63e24eSchristos 	}
621*cb63e24eSchristos 
622*cb63e24eSchristos       ret = posix_spawn_file_actions_addclose (&actions, in);
623*cb63e24eSchristos       if (ret)
624*cb63e24eSchristos 	{
625*cb63e24eSchristos 	  *err = ret;
626*cb63e24eSchristos 	  *errmsg = "posix_spawn_file_actions_addclose";
627*cb63e24eSchristos 	  goto exit;
628*cb63e24eSchristos 	}
629*cb63e24eSchristos     }
630*cb63e24eSchristos 
631*cb63e24eSchristos   if (out != STDOUT_FILE_NO)
632*cb63e24eSchristos     {
633*cb63e24eSchristos       ret = posix_spawn_file_actions_adddup2 (&actions, out, STDOUT_FILE_NO);
634*cb63e24eSchristos       if (ret)
635*cb63e24eSchristos 	{
636*cb63e24eSchristos 	  *err = ret;
637*cb63e24eSchristos 	  *errmsg = "posix_spawn_file_actions_adddup2";
638*cb63e24eSchristos 	  goto exit;
639*cb63e24eSchristos 	}
640*cb63e24eSchristos 
641*cb63e24eSchristos       ret = posix_spawn_file_actions_addclose (&actions, out);
642*cb63e24eSchristos       if (ret)
643*cb63e24eSchristos 	{
644*cb63e24eSchristos 	  *err = ret;
645*cb63e24eSchristos 	  *errmsg = "posix_spawn_file_actions_addclose";
646*cb63e24eSchristos 	  goto exit;
647*cb63e24eSchristos 	}
648*cb63e24eSchristos     }
649*cb63e24eSchristos 
650*cb63e24eSchristos   if (errdes != STDERR_FILE_NO)
651*cb63e24eSchristos     {
652*cb63e24eSchristos       ret = posix_spawn_file_actions_adddup2 (&actions, errdes, STDERR_FILE_NO);
653*cb63e24eSchristos       if (ret)
654*cb63e24eSchristos 	{
655*cb63e24eSchristos 	  *err = ret;
656*cb63e24eSchristos 	  *errmsg = "posix_spawn_file_actions_adddup2";
657*cb63e24eSchristos 	  goto exit;
658*cb63e24eSchristos 	}
659*cb63e24eSchristos 
660*cb63e24eSchristos       ret = posix_spawn_file_actions_addclose (&actions, errdes);
661*cb63e24eSchristos       if (ret)
662*cb63e24eSchristos 	{
663*cb63e24eSchristos 	  *err = ret;
664*cb63e24eSchristos 	  *errmsg = "posix_spawn_file_actions_addclose";
665*cb63e24eSchristos 	  goto exit;
666*cb63e24eSchristos 	}
667*cb63e24eSchristos     }
668*cb63e24eSchristos 
669*cb63e24eSchristos   if (toclose >= 0)
670*cb63e24eSchristos     {
671*cb63e24eSchristos       ret = posix_spawn_file_actions_addclose (&actions, toclose);
672*cb63e24eSchristos       if (ret)
673*cb63e24eSchristos 	{
674*cb63e24eSchristos 	  *err = ret;
675*cb63e24eSchristos 	  *errmsg = "posix_spawn_file_actions_addclose";
676*cb63e24eSchristos 	  goto exit;
677*cb63e24eSchristos 	}
678*cb63e24eSchristos     }
679*cb63e24eSchristos 
680*cb63e24eSchristos   if ((flags & PEX_STDERR_TO_STDOUT) != 0)
681*cb63e24eSchristos     {
682*cb63e24eSchristos       ret = posix_spawn_file_actions_adddup2 (&actions, STDOUT_FILE_NO, STDERR_FILE_NO);
683*cb63e24eSchristos       if (ret)
684*cb63e24eSchristos 	{
685*cb63e24eSchristos 	  *err = ret;
686*cb63e24eSchristos 	  *errmsg = "posix_spawn_file_actions_adddup2";
687*cb63e24eSchristos 	  goto exit;
688*cb63e24eSchristos 	}
689*cb63e24eSchristos     }
690*cb63e24eSchristos 
691*cb63e24eSchristos   if ((flags & PEX_SEARCH) != 0)
692*cb63e24eSchristos     {
693*cb63e24eSchristos       ret = posix_spawnp (&pid, executable, &actions, &attr, argv, env ? env : environ);
694*cb63e24eSchristos       if (ret)
695*cb63e24eSchristos 	{
696*cb63e24eSchristos 	  *err = ret;
697*cb63e24eSchristos 	  *errmsg = "posix_spawnp";
698*cb63e24eSchristos 	  goto exit;
699*cb63e24eSchristos 	}
700*cb63e24eSchristos     }
701*cb63e24eSchristos   else
702*cb63e24eSchristos     {
703*cb63e24eSchristos       ret = posix_spawn (&pid, executable, &actions, &attr, argv, env ? env : environ);
704*cb63e24eSchristos       if (ret)
705*cb63e24eSchristos 	{
706*cb63e24eSchristos 	  *err = ret;
707*cb63e24eSchristos 	  *errmsg = "posix_spawn";
708*cb63e24eSchristos 	  goto exit;
709*cb63e24eSchristos 	}
710*cb63e24eSchristos     }
711*cb63e24eSchristos 
712*cb63e24eSchristos exit:
713*cb63e24eSchristos   if (actions_initialized)
714*cb63e24eSchristos     posix_spawn_file_actions_destroy (&actions);
715*cb63e24eSchristos   if (attr_initialized)
716*cb63e24eSchristos     posix_spawnattr_destroy (&attr);
717*cb63e24eSchristos 
718*cb63e24eSchristos   if (!*err && in != STDIN_FILE_NO)
719*cb63e24eSchristos     if (close (in))
720*cb63e24eSchristos       *errmsg = "close", *err = errno, pid = -1;
721*cb63e24eSchristos   if (!*err && out != STDOUT_FILE_NO)
722*cb63e24eSchristos     if (close (out))
723*cb63e24eSchristos       *errmsg = "close", *err = errno, pid = -1;
724*cb63e24eSchristos   if (!*err && errdes != STDERR_FILE_NO)
725*cb63e24eSchristos     if (close (errdes))
726*cb63e24eSchristos       *errmsg = "close", *err = errno, pid = -1;
727*cb63e24eSchristos 
728*cb63e24eSchristos   return pid;
729*cb63e24eSchristos }
730be9ac0eaSchristos #else
731be9ac0eaSchristos /* Implementation of pex->exec_child using standard vfork + exec.  */
732be9ac0eaSchristos 
7332a6b7db3Sskrll 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)7342a6b7db3Sskrll pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable,
7352a6b7db3Sskrll 		     char * const * argv, char * const * env,
7362a6b7db3Sskrll                      int in, int out, int errdes,
7372a6b7db3Sskrll 		     int toclose, const char **errmsg, int *err)
7382a6b7db3Sskrll {
7396f4ced0bSchristos   pid_t pid = -1;
7406f4ced0bSchristos   /* Tuple to communicate error from child to parent.  We can safely
7416f4ced0bSchristos      transfer string literal pointers as both run with identical
7426f4ced0bSchristos      address mappings.  */
7436f4ced0bSchristos   struct fn_err
7446f4ced0bSchristos   {
7456f4ced0bSchristos     const char *fn;
7466f4ced0bSchristos     int err;
7476f4ced0bSchristos   };
7486f4ced0bSchristos   volatile int do_pipe = 0;
7496f4ced0bSchristos   volatile int pipes[2]; /* [0]:reader,[1]:writer.  */
7506f4ced0bSchristos #ifdef O_CLOEXEC
7516f4ced0bSchristos   do_pipe = 1;
7526f4ced0bSchristos #endif
7536f4ced0bSchristos   if (do_pipe)
7546f4ced0bSchristos     {
7556f4ced0bSchristos #ifdef HAVE_PIPE2
7566f4ced0bSchristos       if (pipe2 ((int *)pipes, O_CLOEXEC))
7576f4ced0bSchristos 	do_pipe = 0;
7586f4ced0bSchristos #else
7596f4ced0bSchristos       if (pipe ((int *)pipes))
7606f4ced0bSchristos 	do_pipe = 0;
7616f4ced0bSchristos       else
7626f4ced0bSchristos 	{
7636f4ced0bSchristos 	  if (fcntl (pipes[1], F_SETFD, FD_CLOEXEC) == -1)
7646f4ced0bSchristos 	    {
7656f4ced0bSchristos 	      close (pipes[0]);
7666f4ced0bSchristos 	      close (pipes[1]);
7676f4ced0bSchristos 	      do_pipe = 0;
7686f4ced0bSchristos 	    }
7696f4ced0bSchristos 	}
7706f4ced0bSchristos #endif
7716f4ced0bSchristos     }
7722a6b7db3Sskrll 
7732a6b7db3Sskrll   /* We declare these to be volatile to avoid warnings from gcc about
7742a6b7db3Sskrll      them being clobbered by vfork.  */
7756f4ced0bSchristos   volatile int sleep_interval = 1;
7762a6b7db3Sskrll   volatile int retries;
7772a6b7db3Sskrll 
778be9ac0eaSchristos   /* We vfork and then set environ in the child before calling execvp.
779be9ac0eaSchristos      This clobbers the parent's environ so we need to restore it.
780be9ac0eaSchristos      It would be nice to use one of the exec* functions that takes an
7816f4ced0bSchristos      environment as a parameter, but that may have portability
7826f4ced0bSchristos      issues.  It is marked volatile so the child doesn't consider it a
7836f4ced0bSchristos      dead variable and therefore clobber where ever it is stored.  */
7846f4ced0bSchristos   char **volatile save_environ = environ;
785be9ac0eaSchristos 
7862a6b7db3Sskrll   for (retries = 0; retries < 4; ++retries)
7872a6b7db3Sskrll     {
7882a6b7db3Sskrll       pid = vfork ();
7892a6b7db3Sskrll       if (pid >= 0)
7902a6b7db3Sskrll 	break;
7912a6b7db3Sskrll       sleep (sleep_interval);
7922a6b7db3Sskrll       sleep_interval *= 2;
7932a6b7db3Sskrll     }
7942a6b7db3Sskrll 
7952a6b7db3Sskrll   switch (pid)
7962a6b7db3Sskrll     {
7972a6b7db3Sskrll     case -1:
7986f4ced0bSchristos       if (do_pipe)
7996f4ced0bSchristos 	{
8006f4ced0bSchristos 	  close (pipes[0]);
8016f4ced0bSchristos 	  close (pipes[1]);
8026f4ced0bSchristos 	}
8032a6b7db3Sskrll       *err = errno;
8042a6b7db3Sskrll       *errmsg = VFORK_STRING;
8052a6b7db3Sskrll       return (pid_t) -1;
8062a6b7db3Sskrll 
8072a6b7db3Sskrll     case 0:
8082a6b7db3Sskrll       /* Child process.  */
8096f4ced0bSchristos       {
8106f4ced0bSchristos 	struct fn_err failed;
8116f4ced0bSchristos 	failed.fn = NULL;
8126f4ced0bSchristos 
8136f4ced0bSchristos 	if (do_pipe)
8146f4ced0bSchristos 	  close (pipes[0]);
8156f4ced0bSchristos 	if (!failed.fn && in != STDIN_FILE_NO)
8162a6b7db3Sskrll 	  {
8172a6b7db3Sskrll 	    if (dup2 (in, STDIN_FILE_NO) < 0)
8186f4ced0bSchristos 	      failed.fn = "dup2", failed.err = errno;
8196f4ced0bSchristos 	    else if (close (in) < 0)
8206f4ced0bSchristos 	      failed.fn = "close", failed.err = errno;
8212a6b7db3Sskrll 	  }
8226f4ced0bSchristos 	if (!failed.fn && out != STDOUT_FILE_NO)
8232a6b7db3Sskrll 	  {
8242a6b7db3Sskrll 	    if (dup2 (out, STDOUT_FILE_NO) < 0)
8256f4ced0bSchristos 	      failed.fn = "dup2", failed.err = errno;
8266f4ced0bSchristos 	    else if (close (out) < 0)
8276f4ced0bSchristos 	      failed.fn = "close", failed.err = errno;
8282a6b7db3Sskrll 	  }
8296f4ced0bSchristos 	if (!failed.fn && errdes != STDERR_FILE_NO)
8302a6b7db3Sskrll 	  {
8312a6b7db3Sskrll 	    if (dup2 (errdes, STDERR_FILE_NO) < 0)
8326f4ced0bSchristos 	      failed.fn = "dup2", failed.err = errno;
8336f4ced0bSchristos 	    else if (close (errdes) < 0)
8346f4ced0bSchristos 	      failed.fn = "close", failed.err = errno;
8352a6b7db3Sskrll 	  }
8366f4ced0bSchristos 	if (!failed.fn && toclose >= 0)
8372a6b7db3Sskrll 	  {
8382a6b7db3Sskrll 	    if (close (toclose) < 0)
8396f4ced0bSchristos 	      failed.fn = "close", failed.err = errno;
8402a6b7db3Sskrll 	  }
8416f4ced0bSchristos 	if (!failed.fn && (flags & PEX_STDERR_TO_STDOUT) != 0)
8422a6b7db3Sskrll 	  {
8432a6b7db3Sskrll 	    if (dup2 (STDOUT_FILE_NO, STDERR_FILE_NO) < 0)
8446f4ced0bSchristos 	      failed.fn = "dup2", failed.err = errno;
8452a6b7db3Sskrll 	  }
8466f4ced0bSchristos 	if (!failed.fn)
847be9ac0eaSchristos 	  {
8486f4ced0bSchristos 	    if (env)
8496f4ced0bSchristos 	      /* NOTE: In a standard vfork implementation this clobbers
8506f4ced0bSchristos 		 the parent's copy of environ "too" (in reality there's
8516f4ced0bSchristos 		 only one copy).  This is ok as we restore it below.  */
8522a6b7db3Sskrll 	      environ = (char**) env;
8532a6b7db3Sskrll 	    if ((flags & PEX_SEARCH) != 0)
8542a6b7db3Sskrll 	      {
855be9ac0eaSchristos 		execvp (executable, to_ptr32 (argv));
8566f4ced0bSchristos 		failed.fn = "execvp", failed.err = errno;
8572a6b7db3Sskrll 	      }
8582a6b7db3Sskrll 	    else
8592a6b7db3Sskrll 	      {
860be9ac0eaSchristos 		execv (executable, to_ptr32 (argv));
8616f4ced0bSchristos 		failed.fn = "execv", failed.err = errno;
8626f4ced0bSchristos 	      }
8632a6b7db3Sskrll 	  }
8642a6b7db3Sskrll 
8656f4ced0bSchristos 	/* Something failed, report an error.  We don't use stdio
8666f4ced0bSchristos 	   routines, because we might be here due to a vfork call.  */
8676f4ced0bSchristos 	ssize_t retval = 0;
8686f4ced0bSchristos 
8696f4ced0bSchristos 	if (!do_pipe
8706f4ced0bSchristos 	    || write (pipes[1], &failed, sizeof (failed)) != sizeof (failed))
8716f4ced0bSchristos 	  {
8726f4ced0bSchristos 	    /* The parent will not see our scream above, so write to
8736f4ced0bSchristos 	       stdout.  */
8746f4ced0bSchristos #define writeerr(s) (retval |= write (STDERR_FILE_NO, s, strlen (s)))
8756f4ced0bSchristos 	    writeerr (obj->pname);
8766f4ced0bSchristos 	    writeerr (": error trying to exec '");
8776f4ced0bSchristos 	    writeerr (executable);
8786f4ced0bSchristos 	    writeerr ("': ");
8796f4ced0bSchristos 	    writeerr (failed.fn);
8806f4ced0bSchristos 	    writeerr (": ");
8816f4ced0bSchristos 	    writeerr (xstrerror (failed.err));
8826f4ced0bSchristos 	    writeerr ("\n");
8836f4ced0bSchristos #undef writeerr
8846f4ced0bSchristos 	  }
8856f4ced0bSchristos 
8866f4ced0bSchristos 	/* Exit with -2 if the error output failed, too.  */
8876f4ced0bSchristos 	_exit (retval < 0 ? -2 : -1);
8886f4ced0bSchristos       }
8892a6b7db3Sskrll       /* NOTREACHED */
8902a6b7db3Sskrll       return (pid_t) -1;
8912a6b7db3Sskrll 
8922a6b7db3Sskrll     default:
8932a6b7db3Sskrll       /* Parent process.  */
8946f4ced0bSchristos       {
8956f4ced0bSchristos 	/* Restore environ.  Note that the parent either doesn't run
8966f4ced0bSchristos 	   until the child execs/exits (standard vfork behaviour), or
8976f4ced0bSchristos 	   if it does run then vfork is behaving more like fork.  In
8986f4ced0bSchristos 	   either case we needn't worry about clobbering the child's
8996f4ced0bSchristos 	   copy of environ.  */
900be9ac0eaSchristos 	environ = save_environ;
901be9ac0eaSchristos 
9026f4ced0bSchristos 	struct fn_err failed;
9036f4ced0bSchristos 	failed.fn = NULL;
9046f4ced0bSchristos 	if (do_pipe)
9052a6b7db3Sskrll 	  {
9066f4ced0bSchristos 	    close (pipes[1]);
9076f4ced0bSchristos 	    ssize_t len = read (pipes[0], &failed, sizeof (failed));
9086f4ced0bSchristos 	    if (len < 0)
9096f4ced0bSchristos 	      failed.fn = NULL;
9106f4ced0bSchristos 	    close (pipes[0]);
9112a6b7db3Sskrll 	  }
9122a6b7db3Sskrll 
9136f4ced0bSchristos 	if (!failed.fn && in != STDIN_FILE_NO)
9146f4ced0bSchristos 	  if (close (in) < 0)
9156f4ced0bSchristos 	    failed.fn = "close", failed.err = errno;
9166f4ced0bSchristos 	if (!failed.fn && out != STDOUT_FILE_NO)
9176f4ced0bSchristos 	  if (close (out) < 0)
9186f4ced0bSchristos 	    failed.fn = "close", failed.err = errno;
9196f4ced0bSchristos 	if (!failed.fn && errdes != STDERR_FILE_NO)
9206f4ced0bSchristos 	  if (close (errdes) < 0)
9216f4ced0bSchristos 	    failed.fn = "close", failed.err = errno;
9226f4ced0bSchristos 
9236f4ced0bSchristos 	if (failed.fn)
9246f4ced0bSchristos 	  {
9256f4ced0bSchristos 	    *err = failed.err;
9266f4ced0bSchristos 	    *errmsg = failed.fn;
9276f4ced0bSchristos 	    return (pid_t) -1;
9286f4ced0bSchristos 	  }
9296f4ced0bSchristos       }
9302a6b7db3Sskrll       return pid;
9312a6b7db3Sskrll     }
9322a6b7db3Sskrll }
933be9ac0eaSchristos #endif /* SPAWN */
9342a6b7db3Sskrll 
9352a6b7db3Sskrll /* Wait for a child process to complete.  */
9362a6b7db3Sskrll 
937*cb63e24eSchristos static pid_t
pex_unix_wait(struct pex_obj * obj,pid_t pid,int * status,struct pex_time * time,int done,const char ** errmsg,int * err)9382a6b7db3Sskrll pex_unix_wait (struct pex_obj *obj, pid_t pid, int *status,
9392a6b7db3Sskrll 	       struct pex_time *time, int done, const char **errmsg,
9402a6b7db3Sskrll 	       int *err)
9412a6b7db3Sskrll {
9422a6b7db3Sskrll   /* If we are cleaning up when the caller didn't retrieve process
9432a6b7db3Sskrll      status for some reason, encourage the process to go away.  */
9442a6b7db3Sskrll   if (done)
9452a6b7db3Sskrll     kill (pid, SIGTERM);
9462a6b7db3Sskrll 
9472a6b7db3Sskrll   if (pex_wait (obj, pid, status, time) < 0)
9482a6b7db3Sskrll     {
9492a6b7db3Sskrll       *err = errno;
9502a6b7db3Sskrll       *errmsg = "wait";
9512a6b7db3Sskrll       return -1;
9522a6b7db3Sskrll     }
9532a6b7db3Sskrll 
9542a6b7db3Sskrll   return 0;
9552a6b7db3Sskrll }
9562a6b7db3Sskrll 
9572a6b7db3Sskrll /* Create a pipe.  */
9582a6b7db3Sskrll 
9592a6b7db3Sskrll static int
pex_unix_pipe(struct pex_obj * obj ATTRIBUTE_UNUSED,int * p,int binary ATTRIBUTE_UNUSED)9602a6b7db3Sskrll pex_unix_pipe (struct pex_obj *obj ATTRIBUTE_UNUSED, int *p,
9612a6b7db3Sskrll 	       int binary ATTRIBUTE_UNUSED)
9622a6b7db3Sskrll {
9632a6b7db3Sskrll   return pipe (p);
9642a6b7db3Sskrll }
9652a6b7db3Sskrll 
9662a6b7db3Sskrll /* Get a FILE pointer to read from a file descriptor.  */
9672a6b7db3Sskrll 
9682a6b7db3Sskrll static FILE *
pex_unix_fdopenr(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary ATTRIBUTE_UNUSED)9692a6b7db3Sskrll pex_unix_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
9702a6b7db3Sskrll 		  int binary ATTRIBUTE_UNUSED)
9712a6b7db3Sskrll {
9722a6b7db3Sskrll   return fdopen (fd, "r");
9732a6b7db3Sskrll }
9742a6b7db3Sskrll 
9752a6b7db3Sskrll static FILE *
pex_unix_fdopenw(struct pex_obj * obj ATTRIBUTE_UNUSED,int fd,int binary ATTRIBUTE_UNUSED)9762a6b7db3Sskrll pex_unix_fdopenw (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
9772a6b7db3Sskrll 		  int binary ATTRIBUTE_UNUSED)
9782a6b7db3Sskrll {
9792a6b7db3Sskrll   if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
9802a6b7db3Sskrll     return NULL;
9812a6b7db3Sskrll   return fdopen (fd, "w");
9822a6b7db3Sskrll }
9832a6b7db3Sskrll 
9842a6b7db3Sskrll static void
pex_unix_cleanup(struct pex_obj * obj ATTRIBUTE_UNUSED)9852a6b7db3Sskrll pex_unix_cleanup (struct pex_obj *obj ATTRIBUTE_UNUSED)
9862a6b7db3Sskrll {
9872a6b7db3Sskrll #if !defined (HAVE_WAIT4) && !defined (HAVE_WAITPID)
9882a6b7db3Sskrll   while (obj->sysdep != NULL)
9892a6b7db3Sskrll     {
9902a6b7db3Sskrll       struct status_list *this;
9912a6b7db3Sskrll       struct status_list *next;
9922a6b7db3Sskrll 
9932a6b7db3Sskrll       this = (struct status_list *) obj->sysdep;
9942a6b7db3Sskrll       next = this->next;
9952a6b7db3Sskrll       free (this);
9962a6b7db3Sskrll       obj->sysdep = (void *) next;
9972a6b7db3Sskrll     }
9982a6b7db3Sskrll #endif
9992a6b7db3Sskrll }
1000