]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - jobs.c
bash-20140228 remove leftover and stray files
[thirdparty/bash.git] / jobs.c
diff --git a/jobs.c b/jobs.c
index bb0425833ed24a88fca357908e4b5101480cc76e..4982019072369ca06cdb20d4d8e4e15557567adf 100644 (file)
--- a/jobs.c
+++ b/jobs.c
@@ -3,7 +3,7 @@
 /* This file works with both POSIX and BSD systems.  It implements job
    control. */
 
-/* Copyright (C) 1989-2012 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2013 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -212,10 +212,10 @@ int previous_job = NO_JOB;
 #endif
 
 /* Last child made by the shell.  */
-pid_t last_made_pid = NO_PID;
+volatile pid_t last_made_pid = NO_PID;
 
 /* Pid of the last asynchronous child. */
-pid_t last_asynchronous_pid = NO_PID;
+volatile pid_t last_asynchronous_pid = NO_PID;
 
 /* The pipeline currently being built. */
 PROCESS *the_pipeline = (PROCESS *)NULL;
@@ -889,7 +889,9 @@ delete_old_job (pid)
        delete_job (job, DEL_NOBGPID);
       else
        {
-         internal_warning (_("forked pid %d appears in running job %d"), pid, job);
+#ifdef DEBUG
+         internal_warning (_("forked pid %d appears in running job %d"), pid, job+1);
+#endif
          if (p)
            p->pid = 0;
        }
@@ -932,7 +934,7 @@ realloc_jobs_list ()
          }
       }
 
-#if defined (DEBUG)
+#if 0
   itrace ("realloc_jobs_list: resize jobs list from %d to %d", js.j_jobslots, nsize);
   itrace ("realloc_jobs_list: j_lastj changed from %d to %d", js.j_lastj, (j > 0) ? j - 1 : 0);
   itrace ("realloc_jobs_list: j_njobs changed from %d to %d", js.j_njobs, j);
@@ -963,14 +965,14 @@ realloc_jobs_list ()
   if (js.j_current == NO_JOB || js.j_previous == NO_JOB || js.j_current > js.j_lastj || js.j_previous > js.j_lastj)
     reset_current ();
 
-#ifdef DEBUG
+#if 0
   itrace ("realloc_jobs_list: reset js.j_current (%d) and js.j_previous (%d)", js.j_current, js.j_previous);
 #endif
 
   UNBLOCK_CHILD (oset);
 }
 
-/* Compact the jobs list by removing dead jobs.  Assumed that we have filled
+/* Compact the jobs list by removing dead jobs.  Assume that we have filled
    the jobs array to some predefined maximum.  Called when the shell is not
    the foreground process (subshell_environment != 0).  Returns the first
    available slot in the compacted list.  If that value is js.j_jobslots, then
@@ -986,7 +988,7 @@ compact_jobs_list (flags)
   reap_dead_jobs ();
   realloc_jobs_list ();
 
-#ifdef DEBUG
+#if 0
   itrace("compact_jobs_list: returning %d", (js.j_lastj || jobs[js.j_lastj]) ? js.j_lastj + 1 : 0);
 #endif
 
@@ -1717,6 +1719,8 @@ make_child (command, async_p)
   sigset_t set, oset;
   pid_t pid;
 
+  /* XXX - block SIGTERM here and unblock in child after fork resets the
+     set of pending signals? */
   sigemptyset (&set);
   sigaddset (&set, SIGCHLD);
   sigaddset (&set, SIGINT);
@@ -1737,6 +1741,8 @@ make_child (command, async_p)
     sync_buffered_stream (default_buffered_input);
 #endif /* BUFFERED_INPUT */
 
+  RESET_SIGTERM;
+
   /* Create the child, handle severe errors.  Retry on EAGAIN. */
   while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX)
     {
@@ -1745,11 +1751,16 @@ make_child (command, async_p)
       waitchld (-1, 0);
 
       sys_error ("fork: retry");
+      RESET_SIGTERM;
+
       if (sleep (forksleep) != 0)
        break;
       forksleep <<= 1;
     }
 
+  if (pid != 0)
+    RESET_SIGTERM;
+
   if (pid < 0)
     {
       sys_error ("fork");
@@ -2394,6 +2405,7 @@ wait_for (pid)
   /* In the case that this code is interrupted, and we longjmp () out of it,
      we are relying on the code in throw_to_top_level () to restore the
      top-level signal mask. */
+  child = 0;
   BLOCK_CHILD (set, oset);
 
   /* Ignore interrupts while waiting for a job run without job control
@@ -2431,7 +2443,8 @@ wait_for (pid)
   job = NO_JOB;
   do
     {
-      FIND_CHILD (pid, child);
+      if (pid != ANY_PID)
+       FIND_CHILD (pid, child);
 
       /* If this child is part of a job, then we are really waiting for the
         job to finish.  Otherwise, we are waiting for the child to finish.
@@ -2444,7 +2457,7 @@ wait_for (pid)
         has already exited before this is called, sigchld_handler will have
         called waitchld and the state will be set to JDEAD. */
 
-      if (PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
+      if (pid == ANY_PID || PRUNNING(child) || (job != NO_JOB && RUNNING (job)))
        {
 #if defined (WAITPID_BROKEN)    /* SCOv4 */
          sigset_t suspend_set;
@@ -2462,12 +2475,18 @@ wait_for (pid)
          sigemptyset (&act.sa_mask);
          sigemptyset (&oact.sa_mask);
          act.sa_flags = 0;
-         sigaction (SIGCHLD, &act, &oact);
+#  if defined (SA_RESTART)
+         act.sa_flags |= SA_RESTART;
 #  endif
+         sigaction (SIGCHLD, &act, &oact);
+#  endif /* MUST_UNBLOCK_CHLD */
          queue_sigchld = 1;
          waiting_for_child++;
-         r = waitchld (pid, 1);
+         r = waitchld (pid, 1);        /* XXX */
          waiting_for_child--;
+#if 0
+itrace("wait_for: blocking wait for %d returns %d child = %p", (int)pid, r, child);
+#endif
 #  if defined (MUST_UNBLOCK_CHLD)
          sigaction (SIGCHLD, &oact, (struct sigaction *)NULL);
          sigprocmask (SIG_SETMASK, &chldset, (sigset_t *)NULL);
@@ -2476,6 +2495,7 @@ wait_for (pid)
          if (r == -1 && errno == ECHILD && this_shell_builtin == wait_builtin)
            {
              termination_state = -1;
+             /* XXX - restore sigint handler here? */
              goto wait_for_return;
            }
 
@@ -2485,8 +2505,11 @@ wait_for (pid)
             if it exists, as JDEAD. */
          if (r == -1 && errno == ECHILD)
            {
-             child->running = PS_DONE;
-             WSTATUS (child->status) = 0;      /* XXX -- can't find true status */
+             if (child)
+               {
+                 child->running = PS_DONE;
+                 WSTATUS (child->status) = 0;  /* XXX -- can't find true status */
+               }
              js.c_living = 0;          /* no living child processes */
              if (job != NO_JOB)
                {
@@ -2494,6 +2517,11 @@ wait_for (pid)
                  js.c_reaped++;
                  js.j_ndead++;
                }
+             if (pid == ANY_PID)
+               {
+                 termination_state = -1;
+                 break;
+               }
            }
 #endif /* WAITPID_BROKEN */
        }
@@ -2509,6 +2537,11 @@ wait_for (pid)
 
       /* Check for a trapped signal interrupting the wait builtin and jump out */
       CHECK_WAIT_INTR;
+
+      if (pid == ANY_PID)
+        /* XXX - could set child but we don't have a handle on what waitchld
+          reaps.  Leave termination_state alone. */
+       goto wait_for_return;
     }
   while (PRUNNING (child) || (job != NO_JOB && RUNNING (job)));
 
@@ -2674,6 +2707,76 @@ wait_for_job (job)
   return r;
 }
 
+/* Wait for any background job started by this shell to finish.  Very
+   similar to wait_for_background_pids().  Returns the exit status of
+   the next exiting job, -1 if there are no background jobs.  The caller
+   is responsible for translating -1 into the right return value. */
+int
+wait_for_any_job ()
+{
+  pid_t pid;
+  int i, r, waited_for;
+  sigset_t set, oset;
+
+  if (jobs_list_frozen)
+    return -1;
+
+  /* First see if there are any unnotified dead jobs that we can report on */
+  BLOCK_CHILD (set, oset);
+  for (i = 0; i < js.j_jobslots; i++)
+    {
+      if (jobs[i] && DEADJOB (i) && IS_NOTIFIED (i) == 0)
+       {
+return_job:
+         r = job_exit_status (i);
+         notify_of_job_status ();              /* XXX */
+         delete_job (i, 0);
+#if defined (COPROCESS_SUPPORT)
+         coproc_reap ();
+#endif
+         UNBLOCK_CHILD (oset);
+         return r;
+       }
+    }
+  UNBLOCK_CHILD (oset);
+
+  /* At this point, we have no dead jobs in the jobs table.  Wait until we
+     get one, even if it takes multiple pids exiting. */
+  for (waited_for = 0;;)
+    {
+      /* Make sure there is a background job to wait for */
+      BLOCK_CHILD (set, oset);
+      for (i = 0; i < js.j_jobslots; i++)
+        if (jobs[i] && RUNNING (i) && IS_FOREGROUND (i) == 0)
+          break;
+      if (i == js.j_jobslots)
+       {
+         UNBLOCK_CHILD (oset);
+         return -1;
+       }
+
+      UNBLOCK_CHILD (oset);
+
+      QUIT;
+      CHECK_TERMSIG;
+      CHECK_WAIT_INTR;
+
+      errno = 0;
+      r = wait_for (ANY_PID);  /* special sentinel value for wait_for */
+      if (r == -1 && errno == ECHILD)
+       mark_all_jobs_as_dead ();
+       
+      /* Now we see if we have any dead jobs and return the first one */
+      BLOCK_CHILD (set, oset);
+      for (i = 0; i < js.j_jobslots; i++)
+       if (jobs[i] && DEADJOB (i))
+         goto return_job;
+      UNBLOCK_CHILD (oset);
+    }
+
+  return -1;
+}
+
 /* Print info about dead jobs, and then delete them from the list
    of known jobs.  This does not actually delete jobs when the
    shell is not interactive, because the dead jobs are not marked
@@ -3090,6 +3193,7 @@ waitchld (wpid, block)
   WAIT status;
   PROCESS *child;
   pid_t pid;
+
   int call_set_current, last_stopped_job, job, children_exited, waitpid_flags;
   static int wcontinued = WCONTINUED;  /* run-time fix for glibc problem */
 
@@ -3105,9 +3209,9 @@ waitchld (wpid, block)
                        : 0;
       if (sigchld || block == 0)
        waitpid_flags |= WNOHANG;
+
       /* Check for terminating signals and exit the shell if we receive one */
       CHECK_TERMSIG;
-
       /* Check for a trapped signal interrupting the wait builtin and jump out */
       CHECK_WAIT_INTR;
 
@@ -3119,6 +3223,10 @@ waitchld (wpid, block)
 
       pid = WAITPID (-1, &status, waitpid_flags);
 
+#if 0
+if (wpid != -1 && block)
+  itrace("waitchld: blocking waitpid returns %d", pid);
+#endif
       /* WCONTINUED may be rejected by waitpid as invalid even when defined */
       if (wcontinued && pid < 0 && errno == EINVAL)
        {
@@ -3141,6 +3249,9 @@ waitchld (wpid, block)
            break;
        }
 
+#if 0
+itrace("waitchld: waitpid returns %d block = %d", pid, block);
+#endif
       /* If waitpid returns 0, there are running children.  If it returns -1,
         the only other error POSIX says it can return is EINTR. */
       CHECK_TERMSIG;
@@ -3174,7 +3285,7 @@ waitchld (wpid, block)
       child = find_process (pid, 1, &job);     /* want living procs only */
 
 #if defined (COPROCESS_SUPPORT)
-      coproc_pidchk (pid, status);
+      coproc_pidchk (pid, WSTATUS(status));
 #endif
 
       /* It is not an error to have a child terminate that we did
@@ -3235,10 +3346,17 @@ waitchld (wpid, block)
          if (sigchld == 0)
            longjmp (wait_intr_buf, 1);
        }
+      /* If not in posix mode and not executing the wait builtin, queue the
+        signal for later handling.  Run the trap immediately if we are
+        executing the wait builtin, but don't break out of `wait'. */
       else if (sigchld)        /* called from signal handler */
        queue_sigchld_trap (children_exited);
+      else if (running_trap)
+       queue_sigchld_trap (children_exited);
+      else if (this_shell_builtin == wait_builtin)
+       run_sigchld_trap (children_exited);     /* XXX */
       else
-       run_sigchld_trap (children_exited);
+       queue_sigchld_trap (children_exited);
     }
 
   /* We have successfully recorded the useful information about this process
@@ -3489,15 +3607,20 @@ run_sigchld_trap (nchild)
   subst_assign_varlist = (WORD_LIST *)NULL;
   the_pipeline = (PROCESS *)NULL;
 
+  running_trap = SIGCHLD + 1;
+
   set_impossible_sigchld_trap ();
   jobs_list_frozen = 1;
   for (i = 0; i < nchild; i++)
     {
+#if 0
       interrupt_immediately = 1;
+#endif
       parse_and_execute (savestring (trap_command), "trap", SEVAL_NOHIST|SEVAL_RESETLINE);
     }
 
   run_unwind_frame ("SIGCHLD trap");
+  running_trap = 0;
 }
 
 /* Function to call when you want to notify people of changes
@@ -4138,8 +4261,10 @@ mark_dead_jobs_as_notified (force)
     }
 
 #ifdef DEBUG
+# if 0
   if (ndeadproc != js.c_reaped)
     itrace("mark_dead_jobs_as_notified: ndeadproc (%d) != js.c_reaped (%d)", ndeadproc, js.c_reaped);
+# endif
   if (ndead != js.j_ndead)
     itrace("mark_dead_jobs_as_notified: ndead (%d) != js.j_ndead (%d)", ndead, js.j_ndead);
 #endif
@@ -4249,7 +4374,7 @@ without_job_control ()
 void
 end_job_control ()
 {
-  if (interactive_shell)               /* XXX - should it be interactive? */
+  if (interactive_shell || job_control)                /* XXX - should it be just job_control? */
     {
       terminate_stopped_jobs ();