/* 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 */
#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 */
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. */
/* 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;
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));
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 *));
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 */
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
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. */
* 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);
}
}
}
/* 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 *
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;
}
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
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
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));
}
}
/* Get rid of the data structure associated with a process chain. */
-static int
+int
discard_pipeline (chain)
register PROCESS *chain;
{
}
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)
if (x == 0)
{
x = retcode_name_buffer;
- sprintf (x, _("Signal %d"), s);
+ snprintf (x, sizeof(retcode_name_buffer), _("Signal %d"), s);
}
return x;
}
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))
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");
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)
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);
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)
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
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;
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);
}
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. */
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 ()
{
{
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 */
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
struct sigaction act, oact;
sigset_t nullset, chldset;
+ queue_sigchld = 1;
sigemptyset (&nullset);
sigemptyset (&chldset);
sigprocmask (SIG_SETMASK, &nullset, &chldset);
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))
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
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
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');
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);
#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)
}
#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. */
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)
if (job != NO_JOB)
js.c_reaped++;
}
-
+
if (job == NO_JOB)
continue;
}
/* 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)
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)
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)
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)
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 ()
subst_assign_varlist = (WORD_LIST *)NULL;
the_pipeline = (PROCESS *)NULL;
+ temporary_env = 0; /* traps should not run with temporary env */
running_trap = SIGCHLD + 1;
job_control = 0;
original_pgrp = NO_PID;
shell_tty = fileno (stderr);
+ terminal_pgrp = tcgetpgrp (shell_tty); /* for checking later */
}
else
{
{
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)
{
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;
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);
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;
}
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. */
}
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;
}
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)
{
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)
/* 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
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)