aboutsummaryrefslogtreecommitdiffstats
path: root/jobs.c
diff options
context:
space:
mode:
Diffstat (limited to 'jobs.c')
-rw-r--r--jobs.c156
1 files changed, 99 insertions, 57 deletions
diff --git a/jobs.c b/jobs.c
index e1340c4..b9ca743 100644
--- a/jobs.c
+++ b/jobs.c
@@ -107,6 +107,10 @@
extern int errno;
#endif /* !errno */
+#if !defined (CHILD_MAX)
+# define CHILD_MAX 32
+#endif
+
/* Take care of system dependencies that must be handled when waiting for
children. The arguments to the WAITPID macro match those to the Posix.1
waitpid() function. */
@@ -543,12 +547,7 @@ cleanup_dead_jobs ()
BLOCK_CHILD (set, oset);
for (i = 0; i < job_slots; i++)
-#if 0
- if (jobs[i] && DEADJOB (i) && IS_NOTIFIED (i) &&
- (interactive_shell || (find_last_pid (i) != last_asynchronous_pid)))
-#else
if (jobs[i] && DEADJOB (i) && IS_NOTIFIED (i))
-#endif
delete_job (i, 0);
UNBLOCK_CHILD (oset);
@@ -1150,9 +1149,9 @@ make_child (command, async_p)
{
/* In the child. Give this child the right process group, set the
signals to the default state for a new process. */
- pid_t mine;
+ pid_t mypid;
- mine = getpid ();
+ mypid = getpid ();
#if defined (BUFFERED_INPUT)
/* Close default_buffered_input if it's > 0. We don't close it if it's
0 because that's the file descriptor used when redirecting input,
@@ -1169,7 +1168,7 @@ make_child (command, async_p)
process group. */
if (pipeline_pgrp == 0) /* This is the first child. */
- pipeline_pgrp = mine;
+ pipeline_pgrp = mypid;
/* Check for running command in backquotes. */
if (pipeline_pgrp == shell_pgrp)
@@ -1185,12 +1184,18 @@ make_child (command, async_p)
this would have for the first child) is an error. Section
B.4.3.3, p. 237 also covers this, in the context of job control
shells. */
- if (setpgid (mine, pipeline_pgrp) < 0)
- sys_error ("child setpgid (%d to %d)", mine, pipeline_pgrp);
+ if (setpgid (mypid, pipeline_pgrp) < 0)
+ sys_error ("child setpgid (%d to %d)", mypid, pipeline_pgrp);
#if defined (PGRP_PIPE)
- if (pipeline_pgrp == mine)
+ if (pipeline_pgrp == mypid)
{
#endif
+ /* XXX - By convention (and assumption above), if
+ pipeline_pgrp == shell_pgrp, we are making a child for
+ command substitution.
+ 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)
give_terminal_to (pipeline_pgrp);
@@ -1471,16 +1476,13 @@ wait_for_single_pid (pid)
r = wait_for (pid);
- /* POSIX.2: if we just waited for $!, we can remove the job from the
- jobs table. */
- if (pid == last_asynchronous_pid)
- {
- BLOCK_CHILD (set, oset);
- job = find_job (pid);
- if (job != NO_JOB && jobs[job] && DEADJOB (job))
- jobs[job]->flags |= J_NOTIFIED;
- UNBLOCK_CHILD (oset);
- }
+ /* POSIX.2: if we just waited for a job, we can remove it from the jobs
+ table. */
+ BLOCK_CHILD (set, oset);
+ job = find_job (pid);
+ if (job != NO_JOB && jobs[job] && DEADJOB (job))
+ jobs[job]->flags |= J_NOTIFIED;
+ UNBLOCK_CHILD (oset);
return r;
}
@@ -1521,6 +1523,11 @@ wait_for_background_pids ()
break;
}
}
+
+ /* POSIX.2 says the shell can discard the statuses of all completed jobs if
+ `wait' is called with no arguments. */
+ mark_dead_jobs_as_notified (1);
+ cleanup_dead_jobs ();
}
/* Make OLD_SIGINT_HANDLER the SIGINT signal handler. */
@@ -1722,7 +1729,27 @@ wait_for (pid)
termination_state = process_exit_status (child->status);
if (job == NO_JOB || IS_JOBCONTROL (job))
- give_terminal_to (shell_pgrp);
+ {
+ /* XXX - under what circumstances is a job not present in the jobs
+ table (job == NO_JOB)?
+ 1. command substitution
+
+ In the case of command substitution, at least, it's probably not
+ the right thing to give the terminal to the shell's process group,
+ even though there is code in subst.c:command_substitute to work
+ around it.
+
+ Things that don't:
+ $PROMPT_COMMAND execution
+ process substitution
+ */
+#if 0
+if (job == NO_JOB)
+ itrace("wait_for: job == NO_JOB, giving the terminal to shell_pgrp (%d)", shell_pgrp);
+#endif
+
+ give_terminal_to (shell_pgrp);
+ }
/* If the command did not exit cleanly, or the job is just
being stopped, then reset the tty state back to what it
@@ -1775,13 +1802,7 @@ wait_for (pid)
sure we turn on the notify bit so we don't get an unwanted
message about the job's termination, and so delete_job really
clears the slot in the jobs table. */
-#if 0
- if (DEADJOB (job))
- jobs[job]->flags |= J_NOTIFIED;
- cleanup_dead_jobs ();
-#else
notify_and_cleanup ();
-#endif
}
}
@@ -1810,15 +1831,12 @@ wait_for_job (job)
pid = last_pid (job);
r = wait_for (pid);
- /* POSIX.2: if we just waited for $!, we can remove the job from the
- jobs table. */
- if (pid == last_asynchronous_pid)
- {
- BLOCK_CHILD (set, oset);
- if (job != NO_JOB && jobs[job] && DEADJOB (job))
- jobs[job]->flags |= J_NOTIFIED;
- UNBLOCK_CHILD (oset);
- }
+ /* POSIX.2: we can remove the job from the jobs table if we just waited
+ for it. */
+ BLOCK_CHILD (set, oset);
+ if (job != NO_JOB && jobs[job] && DEADJOB (job))
+ jobs[job]->flags |= J_NOTIFIED;
+ UNBLOCK_CHILD (oset);
return r;
}
@@ -1844,7 +1862,7 @@ notify_and_cleanup ()
void
reap_dead_jobs ()
{
- mark_dead_jobs_as_notified ();
+ mark_dead_jobs_as_notified (0);
cleanup_dead_jobs ();
}
@@ -2488,23 +2506,25 @@ notify_of_job_status ()
s = jobs[job]->pipe->status;
termsig = WTERMSIG (s);
+ /* POSIX.2 says we have to hang onto the statuses of at most the
+ last CHILD_MAX background processes if the shell is running a
+ script. If the shell is not interactive, don't print anything
+ unless the job was killed by a signal. */
+ if (startup_state == 0 && WIFSIGNALED (s) == 0 &&
+ ((DEADJOB (job) && IS_FOREGROUND (job) == 0) || STOPPED (job)))
+ continue;
+
/* If job control is disabled, don't print the status messages.
Mark dead jobs as notified so that they get cleaned up. If
startup_state == 2, we were started to run `-c command', so
- don't print anything. If the shell is not interactive, don't
- print anything unless the job was killed by a signal. */
- if ((job_control == 0 && interactive_shell) || startup_state == 2 ||
- (startup_state == 0 && WIFSIGNALED (s) == 0))
+ don't print anything. */
+ if ((job_control == 0 && interactive_shell) || startup_state == 2)
{
-#if 0
- if (DEADJOB (job))
-#else
/* POSIX.2 compatibility: if the shell is not interactive,
hang onto the job corresponding to the last asynchronous
pid until the user has been notified of its status or does
a `wait'. */
if (DEADJOB (job) && (interactive_shell || (find_last_pid (job) != last_asynchronous_pid)))
-#endif
jobs[job]->flags |= J_NOTIFIED;
continue;
}
@@ -2516,9 +2536,7 @@ notify_of_job_status ()
{
case JDEAD:
if (interactive_shell == 0 && termsig && WIFSIGNALED (s) &&
-#if 1
termsig != SIGINT &&
-#endif
#if defined (DONT_REPORT_SIGPIPE)
termsig != SIGPIPE &&
#endif
@@ -2943,24 +2961,48 @@ nohup_all_jobs (running_only)
}
/* Mark all dead jobs as notified, so delete_job () cleans them out
- of the job table properly. */
+ of the job table properly. POSIX.2 says we need to save the
+ status of the last CHILD_MAX jobs, so we count the number of dead
+ jobs and mark only enough as notified to save CHILD_MAX statuses. */
static void
-mark_dead_jobs_as_notified ()
+mark_dead_jobs_as_notified (force)
+ int force;
{
- register int i;
+ register int i, ndead;
sigset_t set, oset;
if (job_slots)
{
BLOCK_CHILD (set, oset);
+ /* Count the number of dead jobs */
+ for (i = ndead = 0; force == 0 && i < job_slots; i++)
+ {
+ if (jobs[i] && DEADJOB (i))
+ ndead++;
+ }
+
+ /* Don't do anything if the number of jobs is less than CHILD_MAX and
+ we're not forcing a cleanup. */
+ if (force == 0 && ndead <= CHILD_MAX)
+ {
+ UNBLOCK_CHILD (oset);
+ return;
+ }
+
+ /* Mark enough dead jobs as notified that we keep CHILD_MAX jobs in
+ the list. This isn't exactly right yet; changes need to be made
+ to stop_pipeline so we don't mark the newer jobs after we've
+ created CHILD_MAX slots in the jobs array. */
for (i = 0; i < job_slots; i++)
-#if 0
- if (jobs[i] && DEADJOB (i))
-#else
- if (jobs[i] && DEADJOB (i) && (interactive_shell || (find_last_pid (i) != last_asynchronous_pid)))
-#endif
- jobs[i]->flags |= J_NOTIFIED;
+ {
+ if (jobs[i] && DEADJOB (i) && (interactive_shell || (find_last_pid (i) != last_asynchronous_pid)))
+ {
+ jobs[i]->flags |= J_NOTIFIED;
+ if (force == 0 && --ndead <= CHILD_MAX)
+ break;
+ }
+ }
UNBLOCK_CHILD (oset);
}