pex_child_error (struct pex_obj *obj, const char *executable,
const char *errmsg, int err)
{
-#define writeerr(s) if (write (STDERR_FILE_NO, s, strlen (s))) {}
+ int retval = 0;
+#define writeerr(s) retval |= (write (STDERR_FILE_NO, s, strlen (s)) < 0)
writeerr (obj->pname);
writeerr (": error trying to exec '");
writeerr (executable);
writeerr (xstrerror (err));
writeerr ("\n");
#undef writeerr
- _exit (-1);
+ /* Exit with -2 if the error output failed, too. */
+ _exit (retval == 0 ? -1 : -2);
}
/* Execute a child. */
volatile int sleep_interval;
volatile int retries;
+ /* We vfork and then set environ in the child before calling execvp.
+ This clobbers the parent's environ so we need to restore it.
+ It would be nice to use one of the exec* functions that takes an
+ environment as a parameter, but that may have portability issues. */
+ char **save_environ = environ;
+
sleep_interval = 1;
pid = -1;
for (retries = 0; retries < 4; ++retries)
}
if (env)
- environ = (char**) env;
+ {
+ /* NOTE: In a standard vfork implementation this clobbers the
+ parent's copy of environ "too" (in reality there's only one copy).
+ This is ok as we restore it below. */
+ environ = (char**) env;
+ }
if ((flags & PEX_SEARCH) != 0)
{
default:
/* Parent process. */
+
+ /* Restore environ.
+ Note that the parent either doesn't run until the child execs/exits
+ (standard vfork behaviour), or if it does run then vfork is behaving
+ more like fork. In either case we needn't worry about clobbering
+ the child's copy of environ. */
+ environ = save_environ;
+
if (in != STDIN_FILE_NO)
{
if (close (in) < 0)