[SV 44555] Use vfork() instead of fork() where available.

Testing has shown that vfork() is actually significantly
more efficient on systems where it's supported, even for
copy-on-write implementations.  If make is big enough,
duplicating the page tables is significant overhead.

* configure.ac: Check for fork/vfork.
* makeint.h: Include vfork.h and set up #define for it.
* os.h, posixos.c (get_bad_stdin): For children who can't use
the normal stdin file descriptor, get a broken one.
* job.c (start_job_command): Avoid so many ifdefs and simplify
the invocation of child_execute_job()
(child_execute_job): move the fork operation here so it can
return early for the parent process.  Switch to use vfork().
* function.c (func_shell_base): Use new child_execute_job() and
simplify ifdefs.
* job.h, main.c, remote-cstms.c, vmsjobs.c, w32os.c: Update
declarations and calls.
diff --git a/configure.ac b/configure.ac
index 4e2b637..42ec448 100644
--- a/configure.ac
+++ b/configure.ac
@@ -144,6 +144,8 @@
 AC_CHECK_DECLS([bsd_signal], [], [], [[#define _GNU_SOURCE 1
 #include <signal.h>]])
 
+AC_FUNC_FORK
+
 AC_FUNC_SETVBUF_REVERSED
 
 # Rumor has it that strcasecmp lives in -lresolv on some odd systems.
diff --git a/function.c b/function.c
index 7dea72f..9f9f0fc 100644
--- a/function.c
+++ b/function.c
@@ -1751,6 +1751,7 @@
       perror_with_name (error_prefix, "pipe");
       return o;
     }
+
 #elif defined(WINDOWS32)
   windows32_openpipe (pipedes, errfd, &pid, command_argv, envp);
   /* Restore the value of just_print_flag.  */
@@ -1763,7 +1764,7 @@
       perror_with_name (error_prefix, "pipe");
       return o;
     }
-  else
+
 #else
   if (pipe (pipedes) < 0)
     {
@@ -1771,118 +1772,113 @@
       return o;
     }
 
-# ifdef __EMX__
-  /* close some handles that are unnecessary for the child process */
+  /* Close handles that are unnecessary for the child process.  */
   CLOSE_ON_EXEC(pipedes[1]);
   CLOSE_ON_EXEC(pipedes[0]);
-  /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
-  pid = child_execute_job (FD_STDIN, pipedes[1], errfd, command_argv, envp);
-  if (pid < 0)
-    perror_with_name (error_prefix, "spawn");
-# else /* ! __EMX__ */
-  pid = fork ();
-  if (pid < 0)
-    perror_with_name (error_prefix, "fork");
-  else if (pid == 0)
-    {
-#  ifdef SET_STACK_SIZE
-      /* Reset limits, if necessary.  */
-      if (stack_limit.rlim_cur)
-       setrlimit (RLIMIT_STACK, &stack_limit);
-#  endif
-      child_execute_job (FD_STDIN, pipedes[1], errfd, command_argv, envp);
-    }
-  else
-# endif
-#endif
-    {
-      /* We are the parent.  */
-      char *buffer;
-      unsigned int maxlen, i;
-      int cc;
 
-      /* Record the PID for reap_children.  */
-      shell_function_pid = pid;
+  {
+    struct output out;
+    out.syncout = 1;
+    out.out = pipedes[1];
+    out.err = errfd;
+
+    pid = child_execute_job (&out, 1, command_argv, envp);
+  }
+
+  if (pid < 0)
+    {
+      perror_with_name (error_prefix, "fork");
+      return o;
+    }
+#endif
+
+  {
+    char *buffer;
+    unsigned int maxlen, i;
+    int cc;
+
+    /* Record the PID for reap_children.  */
+    shell_function_pid = pid;
 #ifndef  __MSDOS__
-      shell_function_completed = 0;
+    shell_function_completed = 0;
 
-      /* Free the storage only the child needed.  */
-      free (command_argv[0]);
-      free (command_argv);
+    /* Free the storage only the child needed.  */
+    free (command_argv[0]);
+    free (command_argv);
 
-      /* Close the write side of the pipe.  We test for -1, since
-         pipedes[1] is -1 on MS-Windows, and some versions of MS
-         libraries barf when 'close' is called with -1.  */
-      if (pipedes[1] >= 0)
-        close (pipedes[1]);
+    /* Close the write side of the pipe.  We test for -1, since
+       pipedes[1] is -1 on MS-Windows, and some versions of MS
+       libraries barf when 'close' is called with -1.  */
+    if (pipedes[1] >= 0)
+      close (pipedes[1]);
 #endif
 
-      /* Set up and read from the pipe.  */
+    /* Set up and read from the pipe.  */
 
-      maxlen = 200;
-      buffer = xmalloc (maxlen + 1);
+    maxlen = 200;
+    buffer = xmalloc (maxlen + 1);
 
-      /* Read from the pipe until it gets EOF.  */
-      for (i = 0; ; i += cc)
-        {
-          if (i == maxlen)
-            {
-              maxlen += 512;
-              buffer = xrealloc (buffer, maxlen + 1);
-            }
+    /* Read from the pipe until it gets EOF.  */
+    for (i = 0; ; i += cc)
+      {
+        if (i == maxlen)
+          {
+            maxlen += 512;
+            buffer = xrealloc (buffer, maxlen + 1);
+          }
 
-          EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i));
-          if (cc <= 0)
-            break;
-        }
-      buffer[i] = '\0';
+        EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i));
+        if (cc <= 0)
+          break;
+      }
+    buffer[i] = '\0';
 
-      /* Close the read side of the pipe.  */
+    /* Close the read side of the pipe.  */
 #ifdef  __MSDOS__
-      if (fpipe)
-        {
-          int st = pclose (fpipe);
-          shell_completed (st, 0);
-        }
+    if (fpipe)
+      {
+        int st = pclose (fpipe);
+        shell_completed (st, 0);
+      }
 #else
-      (void) close (pipedes[0]);
+    (void) close (pipedes[0]);
 #endif
 
-      /* Loop until child_handler or reap_children()  sets
-         shell_function_completed to the status of our child shell.  */
-      while (shell_function_completed == 0)
-        reap_children (1, 0);
+    /* Loop until child_handler or reap_children()  sets
+       shell_function_completed to the status of our child shell.  */
+    while (shell_function_completed == 0)
+      reap_children (1, 0);
 
-      if (batch_filename)
-        {
-          DB (DB_VERBOSE, (_("Cleaning up temporary batch file %s\n"),
-                           batch_filename));
-          remove (batch_filename);
-          free (batch_filename);
-        }
-      shell_function_pid = 0;
+    if (batch_filename)
+      {
+        DB (DB_VERBOSE, (_("Cleaning up temporary batch file %s\n"),
+                         batch_filename));
+        remove (batch_filename);
+        free (batch_filename);
+      }
+    shell_function_pid = 0;
 
-      /* shell_completed() will set shell_function_completed to 1 when the
-         child dies normally, or to -1 if it dies with status 127, which is
-         most likely an exec fail.  */
+    /* shell_completed() will set shell_function_completed to 1 when the
+       child dies normally, or to -1 if it dies with status 127, which is
+       most likely an exec fail.  */
 
-      if (shell_function_completed == -1)
-        {
-          /* This likely means that the execvp failed, so we should just
-             write the error message in the pipe from the child.  */
-          fputs (buffer, stderr);
-          fflush (stderr);
-        }
-      else
-        {
-          /* The child finished normally.  Replace all newlines in its output
-             with spaces, and put that in the variable output buffer.  */
-          fold_newlines (buffer, &i, trim_newlines);
-          o = variable_buffer_output (o, buffer, i);
-        }
+    if (shell_function_completed == -1)
+      {
+        /* This likely means that the execvp failed, so we should just
+           write the error message in the pipe from the child.  */
+        fputs (buffer, stderr);
+        fflush (stderr);
+      }
+    else
+      {
+        /* The child finished normally.  Replace all newlines in its output
+           with spaces, and put that in the variable output buffer.  */
+        fold_newlines (buffer, &i, trim_newlines);
+        o = variable_buffer_output (o, buffer, i);
+      }
 
-      free (buffer);
-    }
+    free (buffer);
+  }
 
   return o;
 }
diff --git a/job.c b/job.c
index 96f30ae..e6a71c0 100644
--- a/job.c
+++ b/job.c
@@ -1074,17 +1074,12 @@
 static void
 start_job_command (struct child *child)
 {
-#if !defined(_AMIGA) && !defined(WINDOWS32)
-  static int bad_stdin = -1;
-#endif
   int flags;
   char *p;
 #ifdef VMS
   char *argv;
 #else
   char **argv;
-  int outfd = FD_STDOUT;
-  int errfd = FD_STDERR;
 #endif
 
   /* If we have a completely empty commandset, stop now.  */
@@ -1328,32 +1323,6 @@
   fflush (stdout);
   fflush (stderr);
 
-#ifndef VMS
-#if !defined(WINDOWS32) && !defined(_AMIGA) && !defined(__MSDOS__)
-
-  /* Set up a bad standard input that reads from a broken pipe.  */
-
-  if (bad_stdin == -1)
-    {
-      /* Make a file descriptor that is the read end of a broken pipe.
-         This will be used for some children's standard inputs.  */
-      int pd[2];
-      if (pipe (pd) == 0)
-        {
-          /* Close the write side.  */
-          (void) close (pd[1]);
-          /* Save the read side.  */
-          bad_stdin = pd[0];
-
-          /* Set the descriptor to close on exec, so it does not litter any
-             child's descriptor table.  When it is dup2'd onto descriptor 0,
-             that descriptor will not close on exec.  */
-          CLOSE_ON_EXEC (bad_stdin);
-        }
-    }
-
-#endif /* !WINDOWS32 && !_AMIGA && !__MSDOS__ */
-
   /* Decide whether to give this child the 'good' standard input
      (one that points to the terminal or whatever), or the 'bad' one
      that points to the read side of a broken pipe.  */
@@ -1362,8 +1331,6 @@
   if (child->good_stdin)
     good_stdin_used = 1;
 
-#endif /* !VMS */
-
   child->deleted = 0;
 
 #ifndef _AMIGA
@@ -1380,7 +1347,7 @@
     {
       int is_remote, id, used_stdin;
       if (start_remote_job (argv, child->environment,
-                            child->good_stdin ? 0 : bad_stdin,
+                            child->good_stdin ? 0 : get_bad_stdin (),
                             &is_remote, &id, &used_stdin))
         /* Don't give up; remote execution may fail for various reasons.  If
            so, simply run the job locally.  */
@@ -1409,7 +1376,7 @@
       child->remote = 0;
 
 #ifdef VMS
-      if (!child_execute_job (argv, child))
+      if (!child_execute_job (child, argv))
         {
           /* Fork failed!  */
           perror_with_name ("fork", "");
@@ -1420,68 +1387,20 @@
 
       parent_environ = environ;
 
-#ifndef NO_OUTPUT_SYNC
-      /* Divert child output if output_sync in use.  */
-      if (child->output.syncout)
-        {
-          if (child->output.out >= 0)
-            outfd = child->output.out;
-          if (child->output.err >= 0)
-            errfd = child->output.err;
-        }
-#endif
-# ifdef __EMX__
-      /* If we aren't running a recursive command and we have a jobserver
-         pipe, close it before exec'ing.  */
-      if (!(flags & COMMANDS_RECURSE) && jobserver_enabled ())
-        jobserver_pre_child ();
+      jobserver_pre_child (flags & COMMANDS_RECURSE);
 
-      /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
-      child->pid = child_execute_job (child->good_stdin ? FD_STDIN : bad_stdin,
-                                      outfd, errfd,
-                                      argv, child->environment);
-      if (child->pid < 0)
-        {
-          /* spawn failed!  */
-          unblock_sigs ();
-          perror_with_name ("spawn", "");
-          goto error;
-        }
+      child->pid = child_execute_job (&child->output, child->good_stdin, argv, child->environment);
 
-      /* undo CLOSE_ON_EXEC() after the child process has been started */
-      if (!(flags & COMMANDS_RECURSE) && jobserver_enabled ())
-        jobserver_post_child ();
-
-#else  /* !__EMX__ */
-
-      child->pid = fork ();
       environ = parent_environ; /* Restore value child may have clobbered.  */
-      if (child->pid == 0)
-        {
-          /* We are the child side.  */
-          unblock_sigs ();
+      jobserver_post_child (flags & COMMANDS_RECURSE);
 
-          /* If we AREN'T running a recursive command and we have a jobserver,
-             clear it before exec'ing.  */
-          if (!(flags & COMMANDS_RECURSE) && jobserver_enabled ())
-            jobserver_clear ();
-
-#ifdef SET_STACK_SIZE
-          /* Reset limits, if necessary.  */
-          if (stack_limit.rlim_cur)
-            setrlimit (RLIMIT_STACK, &stack_limit);
-#endif
-          child_execute_job (child->good_stdin ? FD_STDIN : bad_stdin,
-                             outfd, errfd, argv, child->environment);
-        }
-      else if (child->pid < 0)
+      if (child->pid < 0)
         {
           /* Fork failed!  */
           unblock_sigs ();
           perror_with_name ("fork", "");
           goto error;
         }
-# endif  /* !__EMX__ */
 #endif /* !VMS */
     }
 
@@ -1557,6 +1476,8 @@
   {
       HANDLE hPID;
       char* arg0;
+      int outfd = FD_STDOUT;
+      int errfd = FD_STDERR;
 
       /* make UNC paths safe for CreateProcess -- backslash format */
       arg0 = argv[0];
@@ -2098,82 +2019,95 @@
 /* EMX: Start a child process. This function returns the new pid.  */
 # if defined __EMX__
 int
-child_execute_job (int stdin_fd, int stdout_fd, int stderr_fd,
-                   char **argv, char **envp)
+child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
 {
   int pid;
-  int save_stdin = -1;
-  int save_stdout = -1;
-  int save_stderr = -1;
+  int fdin = good_stdin ? FD_STDIN : get_bad_stdin ();
+  int fdout = FD_STDOUT;
+  int fderr = FD_STDERR;
+  int save_fdin = -1;
+  int save_fdout = -1;
+  int save_fderr = -1;
+
+#ifndef NO_OUTPUT_SYNC
+  /* Divert child output if output_sync in use.  */
+  if (out && out->syncout)
+    {
+      if (out->out >= 0)
+        fdout = out->out;
+      if (out->err >= 0)
+        fderr = out->err;
+    }
+#endif
 
   /* For each FD which needs to be redirected first make a dup of the standard
      FD to save and mark it close on exec so our child won't see it.  Then
      dup2() the standard FD to the redirect FD, and also mark the redirect FD
      as close on exec. */
-  if (stdin_fd != FD_STDIN)
+  if (fdin != FD_STDIN)
     {
-      save_stdin = dup (FD_STDIN);
-      if (save_stdin < 0)
+      save_fdin = dup (FD_STDIN);
+      if (save_fdin < 0)
         O (fatal, NILF, _("no more file handles: could not duplicate stdin\n"));
-      CLOSE_ON_EXEC (save_stdin);
+      CLOSE_ON_EXEC (save_fdin);
 
-      dup2 (stdin_fd, FD_STDIN);
-      CLOSE_ON_EXEC (stdin_fd);
+      dup2 (fdin, FD_STDIN);
+      CLOSE_ON_EXEC (fdin);
     }
 
-  if (stdout_fd != FD_STDOUT)
+  if (fdout != FD_STDOUT)
     {
-      save_stdout = dup (FD_STDOUT);
-      if (save_stdout < 0)
+      save_fdout = dup (FD_STDOUT);
+      if (save_fdout < 0)
         O (fatal, NILF,
            _("no more file handles: could not duplicate stdout\n"));
-      CLOSE_ON_EXEC (save_stdout);
+      CLOSE_ON_EXEC (save_fdout);
 
-      dup2 (stdout_fd, FD_STDOUT);
-      CLOSE_ON_EXEC (stdout_fd);
+      dup2 (fdout, FD_STDOUT);
+      CLOSE_ON_EXEC (fdout);
     }
 
-  if (stderr_fd != FD_STDERR)
+  if (fderr != FD_STDERR)
     {
-      if (stderr_fd != stdout_fd)
+      if (fderr != fdout)
         {
-          save_stderr = dup (FD_STDERR);
-          if (save_stderr < 0)
+          save_fderr = dup (FD_STDERR);
+          if (save_fderr < 0)
             O (fatal, NILF,
                _("no more file handles: could not duplicate stderr\n"));
-          CLOSE_ON_EXEC (save_stderr);
+          CLOSE_ON_EXEC (save_fderr);
         }
 
-      dup2 (stderr_fd, FD_STDERR);
-      CLOSE_ON_EXEC (stderr_fd);
+      dup2 (fderr, FD_STDERR);
+      CLOSE_ON_EXEC (fderr);
     }
 
   /* Run the command.  */
   pid = exec_command (argv, envp);
 
   /* Restore stdout/stdin/stderr of the parent and close temporary FDs.  */
-  if (save_stdin >= 0)
+  if (save_fdin >= 0)
     {
-      if (dup2 (save_stdin, FD_STDIN) != FD_STDIN)
+      if (dup2 (save_fdin, FD_STDIN) != FD_STDIN)
         O (fatal, NILF, _("Could not restore stdin\n"));
       else
-        close (save_stdin);
+        close (save_fdin);
     }
 
-  if (save_stdout >= 0)
+  if (save_fdout >= 0)
     {
-      if (dup2 (save_stdout, FD_STDOUT) != FD_STDOUT)
+      if (dup2 (save_fdout, FD_STDOUT) != FD_STDOUT)
         O (fatal, NILF, _("Could not restore stdout\n"));
       else
-        close (save_stdout);
+        close (save_fdout);
     }
 
-  if (save_stderr >= 0)
+  if (save_fderr >= 0)
     {
-      if (dup2 (save_stderr, FD_STDERR) != FD_STDERR)
+      if (dup2 (save_fderr, FD_STDERR) != FD_STDERR)
         O (fatal, NILF, _("Could not restore stderr\n"));
       else
-        close (save_stderr);
+        close (save_fderr);
     }
 
   return pid;
@@ -2181,32 +2115,50 @@
 
 #elif !defined (_AMIGA) && !defined (__MSDOS__) && !defined (VMS)
 
-/* UNIX:
-   Replace the current process with one executing the command in ARGV.
-   STDIN_FD/STDOUT_FD/STDERR_FD are used as the process's stdin/stdout/stderr;
-   ENVP is the environment of the new program.  This function does not return.  */
-void
-child_execute_job (int stdin_fd, int stdout_fd, int stderr_fd,
-                   char **argv, char **envp)
+/* POSIX:
+   Create a child process executing the command in ARGV.
+   ENVP is the environment of the new program.  Returns the PID or -1.  */
+int
+child_execute_job (struct output *out, int good_stdin, char **argv, char **envp)
 {
   int r;
+  int pid;
+  int fdin = good_stdin ? FD_STDIN : get_bad_stdin ();
+  int fdout = FD_STDOUT;
+  int fderr = FD_STDERR;
 
-  /* For any redirected FD, dup2() it to the standard FD then close it.  */
-  if (stdin_fd != FD_STDIN)
+#ifndef NO_OUTPUT_SYNC
+  /* Divert child output if output_sync in use.  */
+  if (out && out->syncout)
     {
-      EINTRLOOP (r, dup2 (stdin_fd, FD_STDIN));
-      close (stdin_fd);
+      if (out->out >= 0)
+        fdout = out->out;
+      if (out->err >= 0)
+        fderr = out->err;
     }
+#endif
 
-  if (stdout_fd != FD_STDOUT)
-    EINTRLOOP (r, dup2 (stdout_fd, FD_STDOUT));
-  if (stderr_fd != FD_STDERR)
-    EINTRLOOP (r, dup2 (stderr_fd, FD_STDERR));
+  pid = vfork();
+  if (pid != 0)
+    return pid;
 
-  if (stdout_fd != FD_STDOUT)
-    close (stdout_fd);
-  if (stderr_fd != FD_STDERR && stderr_fd != stdout_fd)
-    close (stderr_fd);
+  /* We are the child.  */
+  unblock_sigs ();
+
+#ifdef SET_STACK_SIZE
+  /* Reset limits, if necessary.  */
+  if (stack_limit.rlim_cur)
+    setrlimit (RLIMIT_STACK, &stack_limit);
+#endif
+
+  /* For any redirected FD, dup2() it to the standard FD.
+     They are all marked close-on-exec already.  */
+  if (fdin != FD_STDIN)
+    EINTRLOOP (r, dup2 (fdin, FD_STDIN));
+  if (fdout != FD_STDOUT)
+    EINTRLOOP (r, dup2 (fdout, FD_STDOUT));
+  if (fderr != FD_STDERR)
+    EINTRLOOP (r, dup2 (fderr, FD_STDERR));
 
   /* Run the command.  */
   exec_command (argv, envp);
diff --git a/job.h b/job.h
index 4d7f53f..37cceb6 100644
--- a/job.h
+++ b/job.h
@@ -24,7 +24,7 @@
 
 /* How to set close-on-exec for a file descriptor.  */
 
-#if !defined F_SETFD
+#if !defined(F_SETFD) || !defined(F_GETFD)
 # ifdef WINDOWS32
 #  define CLOSE_ON_EXEC(_d)  process_noinherit(_d)
 # else
@@ -124,20 +124,16 @@
 
 char **construct_command_argv (char *line, char **restp, struct file *file,
                                int cmd_flags, char** batch_file);
+
 #ifdef VMS
-int child_execute_job (char *argv, struct child *child);
+int child_execute_job (struct child *child, char *argv);
 #else
 # define FD_STDIN       (fileno (stdin))
 # define FD_STDOUT      (fileno (stdout))
 # define FD_STDERR      (fileno (stderr))
-# if defined(__EMX__)
-int child_execute_job (int stdin_fd, int stdout_fd, int stderr_fd,
-                       char **argv, char **envp);
-# else
-void child_execute_job (int stdin_fd, int stdout_fd, int stderr_fd,
-                        char **argv, char **envp) __attribute__ ((noreturn));
-# endif
+int child_execute_job (struct output *out, int good_stdin, char **argv, char **envp);
 #endif
+
 #ifdef _AMIGA
 void exec_command (char **argv) __attribute__ ((noreturn));
 #elif defined(__EMX__)
diff --git a/main.c b/main.c
index fd939a8..f16c6b4 100644
--- a/main.c
+++ b/main.c
@@ -2417,8 +2417,7 @@
                termination. */
             int pid;
             int r;
-            pid = child_execute_job (FD_STDIN, FD_STDOUT, FD_STDERR,
-                                     nargv, environ);
+            pid = child_execute_job (NULL, 1, nargv, environ);
 
             /* is this loop really necessary? */
             do {
diff --git a/makeint.h b/makeint.h
index bf75d0a..30bfe60 100644
--- a/makeint.h
+++ b/makeint.h
@@ -138,6 +138,13 @@
 # define SA_RESTART 0
 #endif
 
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+#if !HAVE_WORKING_VFORK
+# define vfork fork
+#endif
+
 #ifdef  HAVE_LIMITS_H
 # include <limits.h>
 #endif
diff --git a/os.h b/os.h
index 545018f..3ef222e 100644
--- a/os.h
+++ b/os.h
@@ -69,9 +69,16 @@
 #define jobserver_release(_fatal) (void)(0)
 #define jobserver_acquire_all()   (0)
 #define jobserver_signal()        (void)(0)
-#define jobserver_pre_child()     (void)(0)
-#define jobserver_post_child()    (void)(0)
+#define jobserver_pre_child(_r)   (void)(0)
+#define jobserver_post_child(_r)  (void)(0)
 #define jobserver_pre_acquire()   (void)(0)
 #define jobserver_acquire(_tmout) (0)
 
 #endif
+
+/* Create a "bad" file descriptor for stdin when parallel jobs are run.  */
+#if !defined(VMD) && !defined(WINDOWS32) && !defined(_AMIGA) && !defined(__MSDOS__)
+int get_bad_stdin ();
+#else
+# define get_bad_stdin() (-1)
+#endif
diff --git a/posixos.c b/posixos.c
index 7ff7511..ffd52eb 100644
--- a/posixos.c
+++ b/posixos.c
@@ -174,27 +174,31 @@
     }
 }
 
-/* This is really only invoked on OS/2.
-   On POSIX we just call jobserver_clear() in the child process.  */
-void jobserver_pre_child ()
+/* Prepare the jobserver to start a child process.  */
+void jobserver_pre_child (int recursive)
 {
-  CLOSE_ON_EXEC (job_fds[0]);
-  CLOSE_ON_EXEC (job_fds[1]);
+  /* If it's not a recursive make, avoid polutting the jobserver pipes.  */
+  if (!recursive && job_fds[0] >= 0)
+    {
+      CLOSE_ON_EXEC (job_fds[0]);
+      CLOSE_ON_EXEC (job_fds[1]);
+    }
 }
 
-void jobserver_post_child ()
+void jobserver_post_child (int recursive)
 {
 #if defined(F_GETFD) && defined(F_SETFD)
-  for (int i = 0; i < 2; ++i)
-    {
-      int flags;
-      EINTRLOOP (flags, fcntl (job_fds[i], F_GETFD));
-      if (flags >= 0)
-        {
-          int r;
-          EINTRLOOP (r, fcntl (job_fds[i], F_SETFD, flags & ~FD_CLOEXEC));
-        }
-    }
+  if (!recursive && job_fds[0] >= 0)
+    for (int i = 0; i < 2; ++i)
+      {
+        int flags;
+        EINTRLOOP (flags, fcntl (job_fds[i], F_GETFD));
+        if (flags >= 0)
+          {
+            int r;
+            EINTRLOOP (r, fcntl (job_fds[i], F_SETFD, flags & ~FD_CLOEXEC));
+          }
+      }
 #endif
 }
 
@@ -388,3 +392,33 @@
 #endif
 
 #endif /* MAKE_JOBSERVER */
+
+/* Create a "bad" file descriptor for stdin when parallel jobs are run.  */
+int
+get_bad_stdin ()
+{
+  static int bad_stdin = -1;
+
+  /* Set up a bad standard input that reads from a broken pipe.  */
+
+  if (bad_stdin == -1)
+    {
+      /* Make a file descriptor that is the read end of a broken pipe.
+         This will be used for some children's standard inputs.  */
+      int pd[2];
+      if (pipe (pd) == 0)
+        {
+          /* Close the write side.  */
+          (void) close (pd[1]);
+          /* Save the read side.  */
+          bad_stdin = pd[0];
+
+          /* Set the descriptor to close on exec, so it does not litter any
+             child's descriptor table.  When it is dup2'd onto descriptor 0,
+             that descriptor will not close on exec.  */
+          CLOSE_ON_EXEC (bad_stdin);
+        }
+    }
+
+  return bad_stdin;
+}
diff --git a/remote-cstms.c b/remote-cstms.c
index 65c1224..7c36b9d 100644
--- a/remote-cstms.c
+++ b/remote-cstms.c
@@ -222,7 +222,7 @@
     fflush (stderr);
   }
 
-  pid = fork ();
+  pid = vfork ();
   if (pid < 0)
     {
       /* The fork failed!  */
diff --git a/vmsjobs.c b/vmsjobs.c
index a9d0b78..649479e 100644
--- a/vmsjobs.c
+++ b/vmsjobs.c
@@ -803,7 +803,7 @@
 }
 
 int
-child_execute_job (char *argv, struct child *child)
+child_execute_job (struct child *child, char *argv)
 {
   int i;
 
diff --git a/w32/w32os.c b/w32/w32os.c
index 0b572fe..6255d15 100644
--- a/w32/w32os.c
+++ b/w32/w32os.c
@@ -148,11 +148,11 @@
 {
 }
 
-void jobserver_pre_child ()
+void jobserver_pre_child (int recursive)
 {
 }
 
-void jobserver_post_child ()
+void jobserver_post_child (int recursive)
 {
 }