]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - jobs.c
Bash-4.4 distribution sources and documentation
[thirdparty/bash.git] / jobs.c
diff --git a/jobs.c b/jobs.c
index b6e59eba0de8b45fe7e5c1a789ecba8578b06974..cef3c79aa6b60a6d999b9c986074a183ed278d9f 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-2013 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2015 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
 #include "builtins/builtext.h"
 #include "builtins/common.h"
 
+#if defined (READLINE)
+# include <readline/readline.h>
+#endif
+
 #if !defined (errno)
 extern int errno;
 #endif /* !errno */
@@ -97,6 +101,10 @@ extern int killpg __P((pid_t, int));
 #define MAX_JOBS_IN_ARRAY 128          /* testing */
 #endif
 
+/* XXX for now */
+#define PIDSTAT_TABLE_SZ 4096
+#define BGPIDS_TABLE_SZ 512
+
 /* Flag values for second argument to delete_job */
 #define DEL_WARNSTOPPED                1       /* warn about deleting stopped jobs */
 #define DEL_NOBGPID            2       /* don't add pgrp leader to bgpids */
@@ -165,12 +173,18 @@ extern sh_builtin_func_t *this_shell_builtin;
 extern char *shell_name, *this_command_name;
 extern sigset_t top_level_mask;
 extern procenv_t wait_intr_buf;
+extern int wait_intr_flag;
 extern int wait_signal_received;
 extern WORD_LIST *subst_assign_varlist;
 
+extern SigHandler **original_signals;
+
+extern void set_original_signal __P((int, SigHandler *));
+
 static struct jobstats zerojs = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB, 0, 0 };
 struct jobstats js = { -1L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NO_JOB, NO_JOB, 0, 0 };
 
+ps_index_t pidstat_table[PIDSTAT_TABLE_SZ];
 struct bgpids bgpids = { 0, 0, 0 };
 
 /* The array of known jobs. */
@@ -223,6 +237,9 @@ PROCESS *the_pipeline = (PROCESS *)NULL;
 /* If this is non-zero, do job control. */
 int job_control = 1;
 
+/* Are we running in background? (terminal_pgrp != shell_pgrp) */
+int running_in_background = 0;
+
 /* Call this when you start making children. */
 int already_making_children = 0;
 
@@ -230,6 +247,8 @@ int already_making_children = 0;
    exits from get_tty_state(). */
 int check_window_size = CHECKWINSIZE_DEFAULT;
 
+PROCESS *last_procsub_child = (PROCESS *)NULL;
+
 /* Functions local to this file. */
 
 static sighandler wait_sigint_handler __P((int));
@@ -270,7 +289,6 @@ static void cleanup_dead_jobs __P((void));
 static int processes_in_job __P((int));
 static void realloc_jobs_list __P((void));
 static int compact_jobs_list __P((int));
-static int discard_pipeline __P((PROCESS *));
 static void add_process __P((char *, pid_t));
 static void print_pipeline __P((PROCESS *, int, int, FILE *));
 static void pretty_print_job __P((int, int, FILE *));
@@ -286,12 +304,19 @@ static void restore_sigint_handler __P((void));
 static void pipe_read __P((int *));
 #endif
 
-static struct pidstat *bgp_alloc __P((pid_t, int));
+/* Hash table manipulation */
+
+static ps_index_t *pshash_getbucket __P((pid_t));
+static void pshash_delindex __P((ps_index_t));
+
+/* Saved background process status management */
 static struct pidstat *bgp_add __P((pid_t, int));
 static int bgp_delete __P((pid_t));
 static void bgp_clear __P((void));
 static int bgp_search __P((pid_t));
-static void bgp_prune __P((void));
+
+static ps_index_t bgp_getindex __P((void));
+static void bgp_resize __P((void));    /* XXX */
 
 #if defined (ARRAY_VARS)
 static int *pstatuses;         /* list of pipeline statuses */
@@ -316,7 +341,7 @@ static SigHandler *old_tstp, *old_ttou, *old_ttin;
 static SigHandler *old_cont = (SigHandler *)SIG_DFL;
 
 /* A place to temporarily save the current pipeline. */
-static PROCESS *saved_pipeline;
+static struct pipeline_saver *saved_pipeline;
 static int saved_already_making_children;
 
 /* Set this to non-zero whenever you don't want the jobs list to change at
@@ -428,27 +453,58 @@ cleanup_the_pipeline ()
     discard_pipeline (disposer);
 }
 
+struct pipeline_saver *
+alloc_pipeline_saver ()
+{
+  struct pipeline_saver *ret;
+
+  ret = (struct pipeline_saver *)xmalloc (sizeof (struct pipeline_saver));
+  ret->pipeline = 0;
+  ret->next = 0;
+  return ret;
+}
+
 void
 save_pipeline (clear)
      int clear;
 {
-  saved_pipeline = the_pipeline;
+  sigset_t set, oset;
+  struct pipeline_saver *saver;
+
+  BLOCK_CHILD (set, oset);
+  saver = alloc_pipeline_saver ();
+  saver->pipeline = the_pipeline;
+  saver->next = saved_pipeline;
+  saved_pipeline = saver;
   if (clear)
     the_pipeline = (PROCESS *)NULL;
   saved_already_making_children = already_making_children;
+  UNBLOCK_CHILD (oset);
 }
 
-void
+PROCESS *
 restore_pipeline (discard)
      int discard;
 {
   PROCESS *old_pipeline;
+  sigset_t set, oset;
+  struct pipeline_saver *saver;
 
+  BLOCK_CHILD (set, oset);
   old_pipeline = the_pipeline;
-  the_pipeline = saved_pipeline;
+  the_pipeline = saved_pipeline->pipeline;
+  saver = saved_pipeline;
+  saved_pipeline = saved_pipeline->next;
+  free (saver);
   already_making_children = saved_already_making_children;
+  UNBLOCK_CHILD (oset);
+
   if (discard && old_pipeline)
-    discard_pipeline (old_pipeline);
+    {
+      discard_pipeline (old_pipeline);
+      return ((PROCESS *)NULL);
+    }
+  return old_pipeline;
 }
 
 /* Start building a pipeline.  */
@@ -640,10 +696,11 @@ stop_pipeline (async, deferred)
           * the parent gives it away.
           *
           * Don't give the terminal away if this shell is an asynchronous
-          * subshell.
+          * subshell or if we're a (presumably non-interactive) shell running
+          * in the background.
           *
           */
-         if (job_control && newjob->pgrp && (subshell_environment&SUBSHELL_ASYNC) == 0)
+         if (job_control && newjob->pgrp && (subshell_environment&SUBSHELL_ASYNC) == 0 && running_in_background == 0)
            maybe_give_terminal_to (shell_pgrp, newjob->pgrp, 0);
        }
     }
@@ -654,20 +711,82 @@ stop_pipeline (async, deferred)
 }
 
 /* Functions to manage the list of exited background pids whose status has
-   been saved. */
+   been saved.
 
-static struct pidstat *
-bgp_alloc (pid, status)
+   pidstat_table:
+
+   The current implementation is a hash table using a single (separate) arena
+   for storage that can be allocated and freed as a unit.  The size of the hash
+   table is a multiple of PIDSTAT_TABLE_SZ (4096) and multiple PIDs that hash
+   to the same value are chained through the bucket_next and bucket_prev
+   pointers (basically coalesced hashing for collision resolution).
+
+   bgpids.storage:
+
+   All pid/status storage is done using the circular buffer bgpids.storage.
+   This must contain at least js.c_childmax entries.  The circular buffer is
+   used to supply the ordered list Posix requires ("the last CHILD_MAX
+   processes").  To avoid searching the entire storage table for a given PID,
+   the hash table (pidstat_table) holds pointers into the storage arena and
+   uses a doubly-linked list of cells (bucket_next/bucket_prev, also pointers
+   into the arena) to implement collision resolution. */
+
+/* The number of elements in bgpids.storage always has to be > js.c_childmax for
+   the circular buffer to work right. */
+static void
+bgp_resize ()
+{
+  ps_index_t nsize;
+  ps_index_t psi;
+
+  if (bgpids.nalloc == 0)
+    {
+      /* invalidate hash table when bgpids table is reallocated */
+      for (psi = 0; psi < PIDSTAT_TABLE_SZ; psi++)
+        pidstat_table[psi] = NO_PIDSTAT;
+      nsize = BGPIDS_TABLE_SZ; /* should be power of 2 */
+      bgpids.head = 0;
+    }
+  else
+    nsize = bgpids.nalloc;
+
+  while (nsize < js.c_childmax)
+    nsize *= 2;
+
+  if (bgpids.nalloc < js.c_childmax)
+    {
+      bgpids.storage = (struct pidstat *)xrealloc (bgpids.storage, nsize * sizeof (struct pidstat));
+
+      for (psi = bgpids.nalloc; psi < nsize; psi++)
+       bgpids.storage[psi].pid = NO_PID;
+
+      bgpids.nalloc = nsize;
+
+    }
+  else if (bgpids.head >= bgpids.nalloc)       /* wrap around */
+    bgpids.head = 0;
+}
+
+static ps_index_t
+bgp_getindex ()
+{
+  ps_index_t psi;
+
+  if (bgpids.nalloc < js.c_childmax || bgpids.head >= bgpids.nalloc)
+    bgp_resize ();
+
+  pshash_delindex (bgpids.head);               /* XXX - clear before reusing */
+  return bgpids.head++;
+}
+
+static ps_index_t *
+pshash_getbucket (pid)
      pid_t pid;
-     int status;
 {
-  struct pidstat *ps;
+  unsigned long hash;          /* XXX - u_bits32_t */
 
-  ps = (struct pidstat *)xmalloc (sizeof (struct pidstat));
-  ps->pid = pid;
-  ps->status = status;
-  ps->next = (struct pidstat *)0;
-  return ps;
+  hash = pid * 0x9e370001UL;
+  return (&pidstat_table[hash % PIDSTAT_TABLE_SZ]);
 }
 
 static struct pidstat *
@@ -675,61 +794,75 @@ bgp_add (pid, status)
      pid_t pid;
      int status;
 {
+  ps_index_t *bucket, psi;
   struct pidstat *ps;
 
-  ps = bgp_alloc (pid, status);
+  bucket = pshash_getbucket (pid);
+  psi = bgp_getindex ();
+  ps = &bgpids.storage[psi];
+
+  ps->pid = pid;
+  ps->status = status;
+  ps->bucket_next = *bucket;
+  ps->bucket_prev = NO_PIDSTAT;
 
-  if (bgpids.list == 0)
-    {
-      bgpids.list = bgpids.end = ps;
-      bgpids.npid = 0;                 /* just to make sure */
-    }
-  else
-    {
-      bgpids.end->next = ps;
-      bgpids.end = ps;
-    }
   bgpids.npid++;
 
+#if 0
   if (bgpids.npid > js.c_childmax)
     bgp_prune ();
+#endif
+
+  if (ps->bucket_next != NO_PIDSTAT)
+    bgpids.storage[ps->bucket_next].bucket_prev = psi;
+
+  *bucket = psi;               /* set chain head in hash table */
 
   return ps;
 }
 
+static void
+pshash_delindex (psi)
+     ps_index_t psi;
+{
+  struct pidstat *ps;
+
+  ps = &bgpids.storage[psi];
+  if (ps->pid == NO_PID)
+    return;
+
+  if (ps->bucket_next != NO_PID)
+    bgpids.storage[ps->bucket_next].bucket_prev = ps->bucket_prev;
+  if (ps->bucket_prev != NO_PID)
+    bgpids.storage[ps->bucket_prev].bucket_next = ps->bucket_next;
+  else
+    *(pshash_getbucket (ps->pid)) = ps->bucket_next;
+}
+
 static int
 bgp_delete (pid)
      pid_t pid;
 {
-  struct pidstat *prev, *p;
+  ps_index_t psi;
 
-  for (prev = p = bgpids.list; p; prev = p, p = p->next)
-    if (p->pid == pid)
-      {
-       prev->next = p->next;   /* remove from list */
-       break;
-      }
+  if (bgpids.storage == 0 || bgpids.nalloc == 0 || bgpids.npid == 0)
+    return 0;
 
-  if (p == 0)
+  /* Search chain using hash to find bucket in pidstat_table */
+  for (psi = *(pshash_getbucket (pid)); psi != NO_PIDSTAT; psi = bgpids.storage[psi].bucket_next)
+    if (bgpids.storage[psi].pid == pid)
+      break;
+
+  if (psi == NO_PIDSTAT)
     return 0;          /* not found */
 
 #if defined (DEBUG)
   itrace("bgp_delete: deleting %d", pid);
 #endif
 
-  /* Housekeeping in the border cases. */
-  if (p == bgpids.list)
-    bgpids.list = bgpids.list->next;
-  else if (p == bgpids.end)
-    bgpids.end = prev;
+  pshash_delindex (psi);       /* hash table management */
 
   bgpids.npid--;
-  if (bgpids.npid == 0)
-    bgpids.list = bgpids.end = 0;
-  else if (bgpids.npid == 1)
-    bgpids.end = bgpids.list;          /* just to make sure */
-
-  free (p);
   return 1;
 }
 
@@ -737,45 +870,45 @@ bgp_delete (pid)
 static void
 bgp_clear ()
 {
-  struct pidstat *ps, *p;
+  if (bgpids.storage == 0 || bgpids.nalloc == 0)
+    return;
+
+  free (bgpids.storage);
+
+  bgpids.storage = 0;
+  bgpids.nalloc = 0;
+  bgpids.head = 0;
 
-  for (ps = bgpids.list; ps; )
-    {
-      p = ps;
-      ps = ps->next;
-      free (p);
-    }
-  bgpids.list = bgpids.end = 0;
   bgpids.npid = 0;
 }
 
 /* Search for PID in the list of saved background pids; return its status if
-   found.  If not found, return -1. */
+   found.  If not found, return -1.  We hash to the right spot in pidstat_table
+   and follow the bucket chain to the end. */
 static int
 bgp_search (pid)
      pid_t pid;
 {
-  struct pidstat *ps;
+  ps_index_t psi;
+
+  if (bgpids.storage == 0 || bgpids.nalloc == 0 || bgpids.npid == 0)
+    return -1;
+
+  /* Search chain using hash to find bucket in pidstat_table */
+  for (psi = *(pshash_getbucket (pid)); psi != NO_PIDSTAT; psi = bgpids.storage[psi].bucket_next)
+    if (bgpids.storage[psi].pid == pid)
+      return (bgpids.storage[psi].status);
 
-  for (ps = bgpids.list ; ps; ps = ps->next)
-    if (ps->pid == pid)
-      return ps->status;
   return -1;
 }
 
+#if 0
 static void
 bgp_prune ()
 {
-  struct pidstat *ps;
-
-  while (bgpids.npid > js.c_childmax)
-    {
-      ps = bgpids.list;
-      bgpids.list = bgpids.list->next;
-      free (ps);
-      bgpids.npid--;
-    }
+  return;
 }
+#endif
 
 /* Reset the values of js.j_lastj and js.j_firstj after one or both have
    been deleted.  The caller should check whether js.j_njobs is 0 before
@@ -846,6 +979,15 @@ cleanup_dead_jobs ()
        delete_job (i, 0);
     }
 
+#if defined (PROCESS_SUBSTITUTION)
+  if (last_procsub_child && last_procsub_child->running == PS_DONE)
+    {
+      bgp_add (last_procsub_child->pid, process_exit_status (last_procsub_child->status));     /* XXX */
+      discard_pipeline (last_procsub_child);
+      last_procsub_child = (PROCESS *)NULL;
+    }
+#endif
+
 #if defined (COPROCESS_SUPPORT)
   coproc_reap ();
 #endif
@@ -1014,10 +1156,9 @@ delete_job (job_index, dflags)
   if (temp == 0)
     return;
 
-  if ((dflags & DEL_NOBGPID) == 0)
+  if ((dflags & DEL_NOBGPID) == 0 && (temp->flags & (J_ASYNC|J_FOREGROUND)) == J_ASYNC)
     {
       proc = find_last_proc (job_index, 0);
-      /* Could do this just for J_ASYNC jobs, but we save all. */
       if (proc)
        bgp_add (proc->pid, process_exit_status (proc->status));
     }
@@ -1075,7 +1216,7 @@ nohup_job (job_index)
 }
 
 /* Get rid of the data structure associated with a process chain. */
-static int
+int
 discard_pipeline (chain)
      register PROCESS *chain;
 {
@@ -1330,6 +1471,20 @@ find_pipeline (pid, alive_only, jobp)
        }
       while (p != the_pipeline);
     }
+  /* Now look in the last process substitution pipeline, since that sets $! */
+  if (last_procsub_child)
+    {
+      p = last_procsub_child;
+      do
+       {
+         /* Return it if we found it.  Don't ever return a recycled pid. */
+         if (p->pid == pid && ((alive_only == 0 && PRECYCLED(p) == 0) || PALIVE(p)))
+           return (p);
+
+         p = p->next;
+       }
+      while (p != last_procsub_child);
+    }
 
   job = find_job (pid, alive_only, &p);
   if (jobp)
@@ -1447,7 +1602,7 @@ j_strsignal (s)
   if (x == 0)
     {
       x = retcode_name_buffer;
-      sprintf (x, _("Signal %d"), s);
+      snprintf (x, sizeof(retcode_name_buffer), _("Signal %d"), s);
     }
   return x;
 }
@@ -1470,7 +1625,7 @@ printable_job_status (j, p, format)
       else
        {
          temp = retcode_name_buffer;
-         sprintf (temp, _("Stopped(%s)"), signal_name (WSTOPSIG (p->status)));
+         snprintf (temp, sizeof(retcode_name_buffer), _("Stopped(%s)"), signal_name (WSTOPSIG (p->status)));
        }
     }
   else if (RUNNING (j))
@@ -1486,11 +1641,14 @@ printable_job_status (j, p, format)
          temp = retcode_name_buffer;
          es = WEXITSTATUS (p->status);
          if (es == 0)
-           strcpy (temp, _("Done"));
+           {
+             strncpy (temp, _("Done"), sizeof (retcode_name_buffer) - 1);
+             temp[sizeof (retcode_name_buffer) - 1] = '\0';
+           }
          else if (posixly_correct)
-           sprintf (temp, _("Done(%d)"), es);
+           snprintf (temp, sizeof(retcode_name_buffer), _("Done(%d)"), es);
          else
-           sprintf (temp, _("Exit %d"), es);
+           snprintf (temp, sizeof(retcode_name_buffer), _("Exit %d"), es);
        }
       else
        temp = _("Unknown status");
@@ -1747,15 +1905,21 @@ make_child (command, async_p)
   while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX)
     {
       /* bash-4.2 */
+      sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
       /* If we can't create any children, try to reap some dead ones. */
       waitchld (-1, 0);
 
+      errno = EAGAIN;          /* restore errno */
       sys_error ("fork: retry");
       RESET_SIGTERM;
 
       if (sleep (forksleep) != 0)
        break;
       forksleep <<= 1;
+
+      if (interrupt_state)
+       break;
+      sigprocmask (SIG_SETMASK, &set, (sigset_t *)NULL);
     }
 
   if (pid != 0)
@@ -1790,6 +1954,8 @@ make_child (command, async_p)
       unset_bash_input (0);
 #endif /* BUFFERED_INPUT */
 
+      CLRINTERRUPT;    /* XXX - children have their own interrupt state */
+
       /* Restore top-level signal mask. */
       sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
 
@@ -1824,7 +1990,7 @@ make_child (command, async_p)
             In this case, we don't want to give the terminal to the
             shell's process group (we could be in the middle of a
             pipeline, for example). */
-         if (async_p == 0 && pipeline_pgrp != shell_pgrp && ((subshell_environment&SUBSHELL_ASYNC) == 0))
+         if (async_p == 0 && pipeline_pgrp != shell_pgrp && ((subshell_environment&(SUBSHELL_ASYNC|SUBSHELL_PIPE)) == 0) && running_in_background == 0)
            give_terminal_to (pipeline_pgrp, 0);
 
 #if defined (PGRP_PIPE)
@@ -1935,12 +2101,51 @@ ignore_tty_job_signals ()
   set_signal_handler (SIGTTOU, SIG_IGN);
 }
 
+/* Reset the tty-generated job control signals to SIG_DFL unless that signal
+   was ignored at entry to the shell, in which case we need to set it to
+   SIG_IGN in the child.  We can't rely on resetting traps, since the hard
+   ignored signals can't be trapped. */
 void
 default_tty_job_signals ()
 {
-  set_signal_handler (SIGTSTP, SIG_DFL);
-  set_signal_handler (SIGTTIN, SIG_DFL);
-  set_signal_handler (SIGTTOU, SIG_DFL);
+  if (signal_is_trapped (SIGTSTP) == 0 && signal_is_hard_ignored (SIGTSTP))
+    set_signal_handler (SIGTSTP, SIG_IGN);
+  else
+    set_signal_handler (SIGTSTP, SIG_DFL);
+
+  if (signal_is_trapped (SIGTTIN) == 0 && signal_is_hard_ignored (SIGTTIN))
+    set_signal_handler (SIGTTIN, SIG_IGN);
+  else
+    set_signal_handler (SIGTTIN, SIG_DFL);
+
+  if (signal_is_trapped (SIGTTOU) == 0 && signal_is_hard_ignored (SIGTTOU))
+    set_signal_handler (SIGTTOU, SIG_IGN);
+  else
+    set_signal_handler (SIGTTOU, SIG_DFL);
+}
+
+/* Called once in a parent process. */
+void
+get_original_tty_job_signals ()
+{
+  static int fetched = 0;
+
+  if (fetched == 0)
+    {
+      if (interactive_shell)
+       {
+         set_original_signal (SIGTSTP, SIG_DFL);
+         set_original_signal (SIGTTIN, SIG_DFL);
+         set_original_signal (SIGTTOU, SIG_DFL);
+       }
+      else
+       {
+         get_original_signal (SIGTSTP);
+         get_original_signal (SIGTTIN);
+         get_original_signal (SIGTTOU);
+       }
+      fetched = 1;
+    }
 }
 
 /* When we end a job abnormally, or if we stop a job, we set the tty to the
@@ -2113,10 +2318,13 @@ find_last_pid (job, block)
    This low-level function prints an error message if PID is not
    a child of this shell.  It returns -1 if it fails, or whatever
    wait_for returns otherwise.  If the child is not found in the
-   jobs table, it returns 127. */
+   jobs table, it returns 127.  If FLAGS doesn't include 1, we
+   suppress the error message if PID isn't found. */
+
 int
-wait_for_single_pid (pid)
+wait_for_single_pid (pid, flags)
      pid_t pid;
+     int flags;
 {
   register PROCESS *child;
   sigset_t set, oset;
@@ -2135,7 +2343,8 @@ wait_for_single_pid (pid)
 
   if (child == 0)
     {
-      internal_error (_("wait: pid %ld is not a child of this shell"), (long)pid);
+      if (flags & 1)
+       internal_error (_("wait: pid %ld is not a child of this shell"), (long)pid);
       return (127);
     }
 
@@ -2195,7 +2404,7 @@ wait_for_background_pids ()
       UNBLOCK_CHILD (oset);
       QUIT;
       errno = 0;               /* XXX */
-      r = wait_for_single_pid (pid);
+      r = wait_for_single_pid (pid, 1);
       if (r == -1)
        {
          /* If we're mistaken about job state, compensate. */
@@ -2221,6 +2430,14 @@ static int wait_sigint_received;
 static int child_caught_sigint;
 static int waiting_for_child;
 
+/* Clean up state after longjmp to wait_intr_buf */
+void
+wait_sigint_cleanup ()
+{
+  queue_sigchld = 0;
+  waiting_for_child = 0;
+}
+
 static void
 restore_sigint_handler ()
 {
@@ -2254,10 +2471,10 @@ wait_sigint_handler (sig)
        {
          trap_handler (SIGINT);        /* set pending_traps[SIGINT] */
          wait_signal_received = SIGINT;
-         if (interrupt_immediately)
+         if (interrupt_immediately && wait_intr_flag)
            {
              interrupt_immediately = 0;
-             longjmp (wait_intr_buf, 1);
+             sh_longjmp (wait_intr_buf, 1);
            }
          else
            /* Let CHECK_WAIT_INTR handle it in wait_for/waitchld */
@@ -2450,7 +2667,7 @@ wait_for (pid)
         job to finish.  Otherwise, we are waiting for the child to finish.
         We check for JDEAD in case the job state has been set by waitchld
         after receipt of a SIGCHLD. */
-      if (job == NO_JOB)
+      if (job == NO_JOB)               /* XXX -- && pid != ANY_PID ? */
        job = find_job (pid, 0, NULL);
 
       /* waitchld() takes care of setting the state of the job.  If the job
@@ -2468,6 +2685,7 @@ wait_for (pid)
          struct sigaction act, oact;
          sigset_t nullset, chldset;
 
+         queue_sigchld = 1;
          sigemptyset (&nullset);
          sigemptyset (&chldset);
          sigprocmask (SIG_SETMASK, &nullset, &chldset);
@@ -2553,12 +2771,12 @@ itrace("wait_for: blocking wait for %d returns %d child = %p", (int)pid, r, chil
      of the last child in the pipeline is the significant one.  If the command
      or job was terminated by a signal, note that value also. */
   termination_state = (job != NO_JOB) ? job_exit_status (job)
-                                     : process_exit_status (child->status);
+                                     : (child ? process_exit_status (child->status) : EXECUTION_SUCCESS);
   last_command_exit_signal = (job != NO_JOB) ? job_exit_signal (job)
-                                            : process_exit_signal (child->status);
+                                            : (child ? process_exit_signal (child->status) : 0);
 
   /* XXX */
-  if ((job != NO_JOB && JOBSTATE (job) == JSTOPPED) || WIFSTOPPED (child->status))
+  if ((job != NO_JOB && JOBSTATE (job) == JSTOPPED) || (child && WIFSTOPPED (child->status)))
     termination_state = 128 + WSTOPSIG (child->status);
 
   if (job == NO_JOB || IS_JOBCONTROL (job))
@@ -2580,7 +2798,13 @@ itrace("wait_for: blocking wait for %d returns %d child = %p", (int)pid, r, chil
 if (job == NO_JOB)
   itrace("wait_for: job == NO_JOB, giving the terminal to shell_pgrp (%ld)", (long)shell_pgrp);
 #endif
-      give_terminal_to (shell_pgrp, 0);
+      /* Don't modify terminal pgrp if we are running in background or a
+        subshell.  Make sure subst.c:command_substitute uses the same
+        conditions to determine whether or not it should undo this and
+        give the terminal to pipeline_pgrp. */
+      
+      if (running_in_background == 0 && (subshell_environment&(SUBSHELL_ASYNC|SUBSHELL_PIPE)) == 0)
+       give_terminal_to (shell_pgrp, 0);
     }
 
   /* If the command did not exit cleanly, or the job is just
@@ -2614,6 +2838,11 @@ if (job == NO_JOB)
                get_new_window_size (0, (int *)0, (int *)0);
            }
          else
+#if defined (READLINE)
+           /* We don't want to do this if we are running a process during
+              programmable completion. */
+           if (RL_ISSTATE (RL_STATE_COMPLETING) == 0)
+#endif
            get_tty_state ();
 
          /* If job control is enabled, the job was started with job
@@ -2629,6 +2858,14 @@ if (job == NO_JOB)
                 SIGINT signal handler; maybe it should. */
              if (signal_is_trapped (SIGINT) == 0 && (loop_level || (shell_compatibility_level > 32 && executing_list)))
                ADDINTERRUPT;
+             /* Call any SIGINT trap handler if the shell is running a loop, so
+                the loop can be broken.  This seems more useful and matches the
+                behavior when the shell is running a builtin command in a loop
+                when it is interrupted.  Change ADDINTERRUPT to
+                trap_handler (SIGINT) to run the trap without interrupting the
+                loop. */
+             else if (signal_is_trapped (SIGINT) && loop_level)
+               ADDINTERRUPT;
              else
                {
                  putchar ('\n');
@@ -2643,8 +2880,6 @@ if (job == NO_JOB)
             something like a while loop or a for loop, simulate getting
             and being killed by the SIGINT to pass the status back to our
             parent. */
-         s = job_signal_status (job);
-
          if (child_caught_sigint == 0 && signal_is_trapped (SIGINT) == 0)
            {
              UNBLOCK_CHILD (oset);
@@ -3226,6 +3461,10 @@ waitchld (wpid, block)
 #if 0
 if (wpid != -1 && block)
   itrace("waitchld: blocking waitpid returns %d", pid);
+#endif
+#if 0
+if (wpid != -1)
+  itrace("waitchld: %s waitpid returns %d", block?"blocking":"non-blocking", pid);
 #endif
       /* WCONTINUED may be rejected by waitpid as invalid even when defined */
       if (wcontinued && pid < 0 && errno == EINVAL)
@@ -3250,7 +3489,7 @@ if (wpid != -1 && block)
        }
 
 #if 0
-itrace("waitchld: waitpid returns %d block = %d", pid, block);
+itrace("waitchld: waitpid returns %d block = %d children_exited = %d", pid, block, children_exited);
 #endif
       /* If waitpid returns 0, there are running children.  If it returns -1,
         the only other error POSIX says it can return is EINTR. */
@@ -3268,6 +3507,13 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block);
       if (pid <= 0)
        continue;       /* jumps right to the test */
 
+      /* Linux kernels appear to signal the parent but not interrupt the
+        waitpid() (or restart it even without SA_RESTART) on SIGINT, so if
+        we saw a SIGINT and the process exited or died due to some other
+        signal, assume the child caught the SIGINT. */
+      if (wait_sigint_received && (WIFSIGNALED (status) == 0 || WTERMSIG (status) != SIGINT))
+       child_caught_sigint = 1;
+
       /* If the child process did die due to SIGINT, forget our assumption
         that it caught or otherwise handled it. */
       if (WIFSIGNALED (status) && WTERMSIG (status) == SIGINT)
@@ -3309,7 +3555,7 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block);
          if (job != NO_JOB)
            js.c_reaped++;
        }
-        
+
       if (job == NO_JOB)
        continue;
 
@@ -3333,7 +3579,8 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block);
     }
 
   /* Call a SIGCHLD trap handler for each child that exits, if one is set. */
-  if (job_control && signal_is_trapped (SIGCHLD) && children_exited &&
+  if (job_control && children_exited &&
+      (signal_is_trapped (SIGCHLD) || trap_list[SIGCHLD] == (char *)IMPOSSIBLE_TRAP_HANDLER) &&
       trap_list[SIGCHLD] != (char *)IGNORE_SIG)
     {
       if (posixly_correct && this_shell_builtin && this_shell_builtin == wait_builtin)
@@ -3345,14 +3592,18 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block);
          wait_signal_received = SIGCHLD;
          /* If we're in a signal handler, let CHECK_WAIT_INTR pick it up;
             run_pending_traps will call run_sigchld_trap later  */
-         if (sigchld == 0)
-           longjmp (wait_intr_buf, 1);
+         if (sigchld == 0 && wait_intr_flag)
+           sh_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 (signal_in_progress (SIGCHLD))
+       queue_sigchld_trap (children_exited);     
+      else if (trap_list[SIGCHLD] == (char *)IMPOSSIBLE_TRAP_HANDLER)
+       queue_sigchld_trap (children_exited);
       else if (running_trap)
        queue_sigchld_trap (children_exited);
       else if (this_shell_builtin == wait_builtin)
@@ -3494,7 +3745,10 @@ set_job_status_and_cleanup (job)
         seen it, and wait_sigint_received is non-zero, because keyboard
         signals are sent to process groups) or via kill(2) to the foreground
         process by another process (or itself).  If the shell did receive the
-        SIGINT, it needs to perform normal SIGINT processing. */
+        SIGINT, it needs to perform normal SIGINT processing.  XXX - should
+        this change its behavior depending on whether the last command in an
+        pipeline exited due to SIGINT, or any process in the pipeline?  Right
+        now it does this if any process in the pipeline exits due to SIGINT. */
       else if (wait_sigint_received &&
              child_caught_sigint == 0 &&
              IS_FOREGROUND (job) && IS_JOBCONTROL (job) == 0)
@@ -3534,7 +3788,7 @@ set_job_status_and_cleanup (job)
                 In this case, we have to fix things up.  What a crock. */
              if (temp_handler == trap_handler && signal_is_trapped (SIGINT) == 0)
                  temp_handler = trap_to_sighandler (SIGINT);
-               restore_sigint_handler ();
+             restore_sigint_handler ();
              if (temp_handler == SIG_DFL)
                termsig_handler (SIGINT);       /* XXX */
              else if (temp_handler != SIG_IGN)
@@ -3600,6 +3854,7 @@ run_sigchld_trap (nchild)
   unwind_protect_pointer (the_pipeline);
   unwind_protect_pointer (subst_assign_varlist);
   unwind_protect_pointer (this_shell_builtin);
+  unwind_protect_pointer (temporary_env);
 
   /* We have to add the commands this way because they will be run
      in reverse order of adding.  We don't want maybe_set_sigchld_trap ()
@@ -3609,6 +3864,7 @@ run_sigchld_trap (nchild)
 
   subst_assign_varlist = (WORD_LIST *)NULL;
   the_pipeline = (PROCESS *)NULL;
+  temporary_env = 0;   /* traps should not run with temporary env */
 
   running_trap = SIGCHLD + 1;
 
@@ -3792,6 +4048,7 @@ initialize_job_control (force)
       job_control = 0;
       original_pgrp = NO_PID;
       shell_tty = fileno (stderr);
+      terminal_pgrp = tcgetpgrp (shell_tty);   /* for checking later */
     }
   else
     {
@@ -3820,7 +4077,8 @@ initialize_job_control (force)
        {
          shell_pgrp = getpid ();
          setpgid (0, shell_pgrp);
-         tcsetpgrp (shell_tty, shell_pgrp);
+         if (shell_tty != -1)
+           tcsetpgrp (shell_tty, shell_pgrp);
        }
 
       while ((terminal_pgrp = tcgetpgrp (shell_tty)) != -1)
@@ -3829,7 +4087,7 @@ initialize_job_control (force)
            {
              SigHandler *ottin;
 
-             ottin = set_signal_handler(SIGTTIN, SIG_DFL);
+             ottin = set_signal_handler (SIGTTIN, SIG_DFL);
              kill (0, SIGTTIN);
              set_signal_handler (SIGTTIN, ottin);
              continue;
@@ -3891,6 +4149,8 @@ initialize_job_control (force)
        internal_error (_("no job control in this shell"));
     }
 
+  running_in_background = terminal_pgrp != shell_pgrp;
+
   if (shell_tty != fileno (stderr))
     SET_CLOSE_ON_EXEC (shell_tty);
 
@@ -3906,6 +4166,11 @@ initialize_job_control (force)
   if (js.c_childmax < 0)
     js.c_childmax = DEFAULT_CHILD_MAX;
 
+#if 0
+  if (js.c_childmax > MAX_CHILD_MAX)
+    js.c_childmax = MAX_CHILD_MAX;
+#endif
+
   return job_control;
 }
 
@@ -3994,7 +4259,7 @@ initialize_job_signals ()
       old_ttin = set_signal_handler (SIGTTIN, sigstop_sighandler);
       old_ttou = set_signal_handler (SIGTTOU, sigstop_sighandler);
     }
-  /* Leave these things alone for non-interactive shells without job
+  /* Leave disposition unmodified for non-interactive shells without job
      control. */
 }
 
@@ -4089,7 +4354,7 @@ maybe_give_terminal_to (opgrp, npgrp, flags)
   else if (tpgrp != opgrp)
     {
 #if defined (DEBUG)
-      internal_warning ("maybe_give_terminal_to: terminal pgrp == %d shell pgrp = %d new pgrp = %d", tpgrp, opgrp, npgrp);
+      internal_warning ("%d: maybe_give_terminal_to: terminal pgrp == %d shell pgrp = %d new pgrp = %d in_background = %d", (int)getpid(), tpgrp, opgrp, npgrp, running_in_background);
 #endif
       return -1;
     }
@@ -4126,7 +4391,12 @@ delete_all_jobs (running_only)
            itrace("delete_all_jobs: job %d non-null after js.j_lastj (%d)", i, js.j_lastj);
 #endif
          if (jobs[i] && (running_only == 0 || (running_only && RUNNING(i))))
-           delete_job (i, DEL_WARNSTOPPED);
+           /* We don't want to add any of these pids to bgpids.  If running_only
+              is non-zero, we don't want to add running jobs to the list.
+              If we are interested in all jobs, not just running jobs, and
+              we are going to clear the bgpids list below (bgp_clear()), we
+              don't need to bother. */
+           delete_job (i, DEL_WARNSTOPPED|DEL_NOBGPID);
        }
       if (running_only == 0)
        {
@@ -4277,6 +4547,11 @@ mark_dead_jobs_as_notified (force)
   if (js.c_childmax < 0)
     js.c_childmax = DEFAULT_CHILD_MAX;
 
+#if 0
+  if (js.c_childmax > MAX_CHILD_MAX)
+    js.c_childmax = MAX_CHILD_MAX;
+#endif
+
   /* Don't do anything if the number of dead processes is less than CHILD_MAX
      and we're not forcing a cleanup. */
   if (ndeadproc <= js.c_childmax)
@@ -4326,10 +4601,14 @@ itrace("mark_dead_jobs_as_notified: child_max = %d ndead = %d ndeadproc = %d", j
 
 /* Here to allow other parts of the shell (like the trap stuff) to
    freeze and unfreeze the jobs list. */
-void
+int
 freeze_jobs_list ()
 {
+  int o;
+
+  o = jobs_list_frozen;
   jobs_list_frozen = 1;
+  return o;
 }
 
 void
@@ -4349,6 +4628,21 @@ set_job_control (arg)
   old = job_control;
   job_control = arg;
 
+  if (terminal_pgrp == NO_PID)
+    terminal_pgrp = tcgetpgrp (shell_tty);
+  
+  running_in_background = (terminal_pgrp != shell_pgrp);
+
+#if 0
+  if (interactive_shell == 0 && running_in_background == 0 && job_control != old)
+    {
+      if (job_control)
+       initialize_job_signals ();
+      else
+       default_tty_job_signals ();
+    }
+#endif
+
   /* If we're turning on job control, reset pipeline_pgrp so make_child will
      put new child processes into the right pgrp */
   if (job_control != old && job_control)