/* 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.
#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;
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;
}
}
}
-#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);
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
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
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);
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)
{
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");
/* 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
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.
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;
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);
if (r == -1 && errno == ECHILD && this_shell_builtin == wait_builtin)
{
termination_state = -1;
+ /* XXX - restore sigint handler here? */
goto wait_for_return;
}
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)
{
js.c_reaped++;
js.j_ndead++;
}
+ if (pid == ANY_PID)
+ {
+ termination_state = -1;
+ break;
+ }
}
#endif /* WAITPID_BROKEN */
}
/* 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)));
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
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 */
: 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;
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)
{
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;
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
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
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
}
#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
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 ();