From 3112c8799358cb5d051e0b63d0e916357169942c Mon Sep 17 00:00:00 2001
From: Paul Smith <psmith@gnu.org>
Date: Sat, 4 Aug 2018 12:18:39 -0400
Subject: [PATCH 085/104] Queue failed fork() (etc.) to be handled like any
 other failed job.

If we failed to fork() we were essentially exiting make immediately
without respect to ignore flags, etc.  On one hand that makes sense
because if you can't fork you're in real trouble, but it doesn't
work so well on systems where we don't fork at all.  Instead, treat
a fork error like any other error by delaying the handling until
the next call to reap_children().  Any child with a PID of -1 is
considered to have died before starting so check these first without
waiting for them.

* src/commands.c (fatal_error_signal): Don't kill children that
never started.
* src/function.c (func_shell_base): Handle cleanup properly if the
child doesn't start.
* src/job.c (reap_children): Check for children that died before
starting and handle them without waiting for the PID.
(start_job_command): Free memory when the child doesn't start.
(start_waiting_job): Don't manage children who never started.
(child_execute_job): If the fork fails return PID -1.
* src/vmsjobs.c: Check for children that never started.
* tests/run_make_tests.pl: Parse config.status to get all options.
---
 commands.c          |   6 +-
 function.c          |  28 +++++----
 job.c               | 123 ++++++++++++++++++----------------------
 job.h               |   8 ++-
 vmsjobs.c           |   3 +-
 tests/run_make_tests.pl |  13 ++++-
 6 files changed, 93 insertions(+), 88 deletions(-)

--- a/commands.c	2019-04-10 22:50:51.317814695 -0400
+++ b/commands.c	2019-04-10 23:13:38.411814695 -0400
@@ -538,7 +538,7 @@ fatal_error_signal (int sig)
     {
       struct child *c;
       for (c = children; c != 0; c = c->next)
-        if (!c->remote)
+        if (!c->remote && c->pid > 0)
           (void) kill (c->pid, SIGTERM);
     }
 
@@ -559,7 +559,7 @@ fatal_error_signal (int sig)
       /* Remote children won't automatically get signals sent
          to the process group, so we must send them.  */
       for (c = children; c != 0; c = c->next)
-        if (c->remote)
+        if (c->remote && c->pid > 0)
           (void) remote_kill (c->pid, sig);
 
       for (c = children; c != 0; c = c->next)
@@ -660,7 +660,7 @@ delete_child_targets (struct child *chil
 {
   struct dep *d;
 
-  if (child->deleted)
+  if (child->deleted || child->pid < 0)
     return;
 
   /* Delete the target file if it changed.  */
--- a/function.c	2019-04-10 22:50:51.293814695 -0400
+++ b/function.c	2019-04-10 23:19:59.282814695 -0400
@@ -1690,7 +1690,7 @@ func_shell_base (char *o, char **argv, i
 #ifdef __MSDOS__
   FILE *fpipe;
 #endif
-  char **command_argv;
+  char **command_argv = NULL;
   const char *error_prefix;
   char **envp;
   int pipedes[2];
@@ -1753,7 +1753,8 @@ func_shell_base (char *o, char **argv, i
   if (pipedes[0] < 0)
     {
       perror_with_name (error_prefix, "pipe");
-      return o;
+      pid = -1;
+      goto done;
     }

 #elif defined(WINDOWS32)
@@ -1766,14 +1767,16 @@ func_shell_base (char *o, char **argv, i
       /* Open of the pipe failed, mark as failed execution.  */
       shell_completed (127, 0);
       perror_with_name (error_prefix, "pipe");
-      return o;
+      pid = -1;
+      goto done;
     }

 #else
   if (pipe (pipedes) < 0)
     {
       perror_with_name (error_prefix, "pipe");
-      return o;
+      pid = -1;
+      goto done;
     }

   /* Close handles that are unnecessary for the child process.  */
@@ -1790,10 +1793,7 @@ func_shell_base (char *o, char **argv, i
   }

   if (pid < 0)
-    {
-      perror_with_name (error_prefix, "fork");
-      return o;
-    }
+    goto done;
 #endif

   {
@@ -1806,10 +1806,6 @@ func_shell_base (char *o, char **argv, i
 #ifndef  __MSDOS__
     shell_function_completed = 0;

-    /* 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.  */
@@ -1884,6 +1880,14 @@ func_shell_base (char *o, char **argv, i
     free (buffer);
   }

+ done:
+  if (command_argv)
+    {
+      /* Free the storage only the child needed.  */
+      free (command_argv[0]);
+      free (command_argv);
+    }
+
   return o;
 }

 
--- a/job.c	2019-04-10 22:50:51.306814695 -0400
+++ b/job.c	2019-04-10 23:44:37.378814695 -0400
@@ -622,10 +622,22 @@ reap_children (int block, int err)
 
       any_remote = 0;
       any_local = shell_function_pid != 0;
-      for (c = children; c != 0; c = c->next)
+      lastc = 0;
+      for (c = children; c != 0; lastc = c, c = c->next)
         {
           any_remote |= c->remote;
           any_local |= ! c->remote;
+
+	  /* If pid < 0, this child never even started.  Handle it.  */
+	  if (c->pid < 0)
+	    {
+	      exit_sig = 0;
+	      coredump = 0;
+	      /* According to POSIX, 127 is used for command not found.  */
+	      exit_code = 127;
+	      goto process_child;
+	    }
+
           DB (DB_JOBS, (_("Live child %p (%s) PID %s %s\n"),
                         c, c->file->name, pid2str (c->pid),
                         c->remote ? _(" (remote)") : ""));
@@ -691,9 +703,6 @@ reap_children (int block, int err)
               exit_sig = WIFSIGNALED (status) ? WTERMSIG (status) : 0;
               coredump = WCOREDUMP (status);
 
-              /* If we have started jobs in this second, remove one.  */
-              if (job_counter)
-                --job_counter;
             }
           else
             {
@@ -818,6 +827,16 @@ reap_children (int block, int err)
            Ignore it; it was inherited from our invoker.  */
         continue;
 
+      DB (DB_JOBS, (exit_sig == 0 && exit_code == 0
+                    ? _("Reaping losing child %p PID %s %s\n")
+                    : _("Reaping winning child %p PID %s %s\n"),
+                    c, pid2str (c->pid), c->remote ? _(" (remote)") : ""));
+
+      /* If we have started jobs in this second, remove one.  */
+      if (job_counter)
+        --job_counter;
+
+    process_child:
       /* Determine the failure status: 0 for success, 1 for updating target in
          question mode, 2 for anything else.  */
       if (exit_sig == 0 && exit_code == 0)
@@ -827,11 +846,6 @@ reap_children (int block, int err)
       else
         child_failed = MAKE_FAILURE;
 
-      DB (DB_JOBS, (child_failed
-                    ? _("Reaping losing child %p PID %s %s\n")
-                    : _("Reaping winning child %p PID %s %s\n"),
-                    c, pid2str (c->pid), c->remote ? _(" (remote)") : ""));
-
       if (c->sh_batch_file)
         {
           int rm_status;
@@ -939,7 +953,7 @@ reap_children (int block, int err)
 
       /* At this point c->file->update_status is success or failed.  But
          c->file->command_state is still cs_running if all the commands
-         ran; notice_finish_file looks for cs_running to tell it that
+         ran; notice_finished_file looks for cs_running to tell it that
          it's interesting to check the file's modtime again now.  */
 
       if (! handling_fatal_signal)
@@ -948,9 +962,6 @@ reap_children (int block, int err)
            update_status to its also_make files.  */
         notice_finished_file (c->file);
 
-      DB (DB_JOBS, (_("Removing child %p PID %s%s from chain.\n"),
-                    c, pid2str (c->pid), c->remote ? _(" (remote)") : ""));
-
       /* Block fatal signals while frobnicating the list, so that
          children and job_slots_used are always consistent.  Otherwise
          a fatal signal arriving after the child is off the chain and
@@ -958,9 +969,15 @@ reap_children (int block, int err)
          live and call reap_children again.  */
       block_sigs ();
 
-      /* There is now another slot open.  */
-      if (job_slots_used > 0)
-        --job_slots_used;
+      if (c->pid > 0)
+        {
+          DB (DB_JOBS, (_("Removing child %p PID %s%s from chain.\n"),
+                        c, pid2str (c->pid), c->remote ? _(" (remote)") : ""));
+
+          /* There is now another slot open.  */
+          if (job_slots_used > 0)
+            --job_slots_used;
+        }
 
       /* Remove the child from the chain and free it.  */
       if (lastc == 0)
@@ -1071,8 +1088,10 @@ start_job_command (struct child *child)
   int flags;
   char *p;
 #ifdef VMS
+# define FREE_ARGV(_a)
   char *argv;
 #else
+# define FREE_ARGV(_a) do{ if (_a) { free ((_a)[0]); free (_a); } }while(0)
   char **argv;
 #endif
 
@@ -1190,10 +1209,7 @@ start_job_command (struct child *child)
      error is 2.  */
   if (argv != 0 && question_flag && !(flags & COMMANDS_RECURSE))
     {
-#ifndef VMS
-      free (argv[0]);
-      free (argv);
-#endif
+      FREE_ARGV (argv);
 #ifdef VMS
       /* On VMS, argv[0] can be a null string here */
       if (argv[0] != 0)
@@ -1211,13 +1227,7 @@ start_job_command (struct child *child)
     {
       /* Go on to the next command.  It might be the recursive one.
          We construct ARGV only to find the end of the command line.  */
-#ifndef VMS
-      if (argv)
-        {
-          free (argv[0]);
-          free (argv);
-        }
-#endif
+      FREE_ARGV (argv);
       argv = 0;
     }
 
@@ -1293,8 +1303,7 @@ start_job_command (struct child *child)
       && (argv[2] && argv[2][0] == ':' && argv[2][1] == '\0')
       && argv[3] == NULL)
     {
-      free (argv[0]);
-      free (argv);
+      FREE_ARGV (argv);
       goto next_command;
     }
 #endif  /* !VMS && !_AMIGA */
@@ -1303,10 +1312,7 @@ start_job_command (struct child *child)
 
   if (just_print_flag && !(flags & COMMANDS_RECURSE))
     {
-#ifndef VMS
-      free (argv[0]);
-      free (argv);
-#endif
+      FREE_ARGV (argv);
       goto next_command;
     }
 
@@ -1373,11 +1379,7 @@ start_job_command (struct child *child)
 
 #ifdef VMS
       if (!child_execute_job (child, argv))
-        {
-          /* Fork failed!  */
-          perror_with_name ("fork", "");
-          goto error;
-        }
+	child->pid = -1;
 
 #else
 
@@ -1385,18 +1387,11 @@ start_job_command (struct child *child)
 
       jobserver_pre_child (flags & COMMANDS_RECURSE);
 
-      child->pid = child_execute_job (&child->output, child->good_stdin, argv, child->environment);
+      child->pid = child_execute_job (&child->output, child->good_stdin,
+                                      argv, child->environment);
 
       environ = parent_environ; /* Restore value child may have clobbered.  */
       jobserver_post_child (flags & COMMANDS_RECURSE);
-
-      if (child->pid < 0)
-        {
-          /* Fork failed!  */
-          unblock_sigs ();
-          perror_with_name ("fork", "");
-          goto error;
-        }
 #endif /* !VMS */
     }
 
@@ -1511,33 +1506,25 @@ start_job_command (struct child *child)
           for (i = 0; argv[i]; i++)
             fprintf (stderr, "%s ", argv[i]);
           fprintf (stderr, _("\nCounted %d args in failed launch\n"), i);
-          goto error;
+          child->pid = -1;
         }
   }
 #endif /* WINDOWS32 */
 #endif  /* __MSDOS__ or Amiga or WINDOWS32 */
 
   /* Bump the number of jobs started in this second.  */
-  ++job_counter;
-
-  /* We are the parent side.  Set the state to
-     say the commands are running and return.  */
+  if (child->pid >= 0)
+    ++job_counter;
 
+  /* Set the state to running.  */
   set_command_state (child->file, cs_running);
 
   /* Free the storage used by the child's argument list.  */
-#ifndef VMS
-  free (argv[0]);
-  free (argv);
-#endif
+  FREE_ARGV (argv);
 
   OUTPUT_UNSET();
-  return;
 
- error:
-  child->file->update_status = us_failed;
-  notice_finished_file (child->file);
-  OUTPUT_UNSET();
+#undef FREE_ARGV
 }
 
 /* Try to start a child running.
@@ -1579,12 +1566,15 @@ start_waiting_job (struct child *c)
     {
     case cs_running:
       c->next = children;
-      DB (DB_JOBS, (_("Putting child %p (%s) PID %s%s on the chain.\n"),
-                    c, c->file->name, pid2str (c->pid),
-                    c->remote ? _(" (remote)") : ""));
+      if (c->pid > 0)
+        {
+          DB (DB_JOBS, (_("Putting child %p (%s) PID %s%s on the chain.\n"),
+                        c, c->file->name, pid2str (c->pid),
+                        c->remote ? _(" (remote)") : ""));
+          /* One more job slot is in use.  */
+          ++job_slots_used;
+        }
       children = c;
-      /* One more job slot is in use.  */
-      ++job_slots_used;
       unblock_sigs ();
       break;
 
--- a/job.h	2019-04-10 22:50:51.319814695 -0400
+++ b/job.h	2019-04-10 23:46:53.284814695 -0400
@@ -95,6 +95,8 @@ struct child
     char **command_lines;       /* Array of variable-expanded cmd lines.  */
     char *command_ptr;          /* Ptr into command_lines[command_line].  */
 
+    struct output output;       /* Output for this child.  */
+
 #ifdef VMS
     char *comname;              /* Temporary command file name */
     int efn;                    /* Completion event flag number */
@@ -103,7 +105,7 @@ struct child
 #endif
 
     unsigned int  command_line; /* Index into command_lines.  */
-    struct output output;       /* Output for this child.  */
+
     pid_t         pid;          /* Child process's ID number.  */
     unsigned int  remote:1;     /* Nonzero if executing remotely.  */
     unsigned int  noerror:1;    /* Nonzero if commands contained a '-'.  */
@@ -131,7 +133,8 @@ int child_execute_job (struct child *chi
 # define FD_STDIN       (fileno (stdin))
 # define FD_STDOUT      (fileno (stdout))
 # define FD_STDERR      (fileno (stderr))
-pid_t child_execute_job (struct output *out, int good_stdin, char **argv, char **envp);
+pid_t child_execute_job (struct output *out, int good_stdin,
+                         char **argv, char **envp);
 #endif
 
 #ifdef _AMIGA
--- a/vmsjobs.c	2019-04-10 22:50:51.318814695 -0400
+++ b/vmsjobs.c	2019-04-10 23:47:44.179814695 -0400
@@ -191,7 +191,8 @@ astYHandler (void)
 {
   struct child *c;
   for (c = children; c != 0; c = c->next)
-    sys$delprc (&c->pid, 0, 0);
+    if (c->pid > 0)
+      sys$delprc (&c->pid, 0, 0);
   ctrlYPressed= 1;
   kill (getpid(),SIGQUIT);
   return SS$_NORMAL;
