aboutsummaryrefslogtreecommitdiffstats
path: root/jobs.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>2009-01-12 13:36:28 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:47:00 +0000
commit3185942a5234e26ab13fa02f9c51d340cec514f8 (patch)
tree2c6fcd01779faef1f91c5d84d63981ca3479137d /jobs.c
parentf1be666c7d78939ad775078d290bec2758fa29a2 (diff)
downloadandroid_external_bash-3185942a5234e26ab13fa02f9c51d340cec514f8.tar.gz
android_external_bash-3185942a5234e26ab13fa02f9c51d340cec514f8.tar.bz2
android_external_bash-3185942a5234e26ab13fa02f9c51d340cec514f8.zip
Imported from ../bash-4.0-rc1.tar.gz.
Diffstat (limited to 'jobs.c')
-rw-r--r--jobs.c244
1 files changed, 167 insertions, 77 deletions
diff --git a/jobs.c b/jobs.c
index d183394..3de0f4c 100644
--- a/jobs.c
+++ b/jobs.c
@@ -1,25 +1,25 @@
-/* The thing that makes children, remembers them, and contains wait loops. */
+/* jobs.c - functions that make children, remember them, and handle their termination. */
/* This file works with both POSIX and BSD systems. It implements job
control. */
-/* Copyright (C) 1989-2006 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2009 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
- Bash is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2, or (at your option) any later
- version.
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- Bash is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with Bash; see the file COPYING. If not, write to the Free Software
- Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
#include "config.h"
@@ -67,6 +67,7 @@
#include "bashintl.h"
#include "shell.h"
#include "jobs.h"
+#include "execute_cmd.h"
#include "flags.h"
#include "builtins/builtext.h"
@@ -144,6 +145,7 @@ extern int subshell_environment, line_number;
extern int posixly_correct, shell_level;
extern int last_command_exit_value, last_command_exit_signal;
extern int loop_level, breaking;
+extern int executing_list;
extern int sourcelevel;
extern int running_trap;
extern sh_builtin_func_t *this_shell_builtin;
@@ -217,8 +219,6 @@ int check_window_size;
/* Functions local to this file. */
-static void run_sigchld_trap __P((int));
-
static sighandler wait_sigint_handler __P((int));
static sighandler sigchld_handler __P((int));
static sighandler sigcont_sighandler __P((int));
@@ -267,12 +267,12 @@ static void set_current_job __P((int));
static void reset_current __P((void));
static void set_job_running __P((int));
static void setjstatus __P((int));
+static int maybe_give_terminal_to __P((pid_t, pid_t, int));
static void mark_all_jobs_as_dead __P((void));
static void mark_dead_jobs_as_notified __P((int));
static void restore_sigint_handler __P((void));
#if defined (PGRP_PIPE)
static void pipe_read __P((int *));
-static void pipe_close __P((int *));
#endif
static struct pidstat *bgp_alloc __P((pid_t, int));
@@ -455,7 +455,7 @@ start_pipeline ()
cleanup_the_pipeline ();
pipeline_pgrp = 0;
#if defined (PGRP_PIPE)
- pipe_close (pgrp_pipe);
+ sh_closepipe (pgrp_pipe);
#endif
}
@@ -463,7 +463,7 @@ start_pipeline ()
if (job_control)
{
if (pipe (pgrp_pipe) == -1)
- sys_error ("start_pipeline: pgrp pipe");
+ sys_error (_("start_pipeline: pgrp pipe"));
}
#endif
}
@@ -485,7 +485,7 @@ stop_pipeline (async, deferred)
#if defined (PGRP_PIPE)
/* The parent closes the process group synchronization pipe. */
- pipe_close (pgrp_pipe);
+ sh_closepipe (pgrp_pipe);
#endif
cleanup_dead_jobs ();
@@ -639,7 +639,7 @@ stop_pipeline (async, deferred)
*
*/
if (job_control && newjob->pgrp && (subshell_environment&SUBSHELL_ASYNC) == 0)
- give_terminal_to (newjob->pgrp, 0);
+ maybe_give_terminal_to (shell_pgrp, newjob->pgrp, 0);
}
}
@@ -771,7 +771,7 @@ bgp_prune ()
bgpids.npid--;
}
}
-
+
/* 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
calling this. This wraps around, but the rest of the code does not. At
@@ -845,6 +845,7 @@ cleanup_dead_jobs ()
static int
processes_in_job (job)
+ int job;
{
int nproc;
register PROCESS *p;
@@ -905,6 +906,7 @@ realloc_jobs_list ()
BLOCK_CHILD (set, oset);
nlist = (js.j_jobslots == nsize) ? jobs : (JOB **) xmalloc (nsize * sizeof (JOB *));
+ js.c_reaped = js.j_ndead = 0;
for (i = j = 0; i < js.j_jobslots; i++)
if (jobs[i])
{
@@ -913,12 +915,18 @@ realloc_jobs_list ()
if (i == js.j_previous)
nprev = j;
nlist[j++] = jobs[i];
+ if (jobs[i]->state == JDEAD)
+ {
+ js.j_ndead++;
+ js.c_reaped += processes_in_job (i);
+ }
}
#if defined (DEBUG)
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 > 0) ? j - 1 : 0);
+ itrace ("realloc_jobs_list: j_njobs changed from %d to %d", js.j_njobs, j);
+ itrace ("realloc_jobs_list: js.j_ndead %d js.c_reaped %d", js.j_ndead, js.c_reaped);
#endif
js.j_firstj = 0;
@@ -1094,10 +1102,10 @@ add_process (name, pid)
{
# ifdef DEBUG
if (j == NO_JOB)
- internal_warning ("add_process: process %5ld (%s) in the_pipeline", (long)p->pid, p->command);
+ internal_warning (_("add_process: process %5ld (%s) in the_pipeline"), (long)p->pid, p->command);
# endif
if (PALIVE (p))
- internal_warning ("add_process: pid %5ld (%s) marked as still alive", (long)p->pid, p->command);
+ internal_warning (_("add_process: pid %5ld (%s) marked as still alive"), (long)p->pid, p->command);
p->running = PS_RECYCLED; /* mark as recycled */
}
#endif
@@ -1400,7 +1408,7 @@ j_strsignal (s)
if (x == 0)
{
x = retcode_name_buffer;
- sprintf (x, "Signal %d", s);
+ sprintf (x, _("Signal %d"), s);
}
return x;
}
@@ -1414,20 +1422,20 @@ printable_job_status (j, p, format)
static char *temp;
int es;
- temp = "Done";
+ temp = _("Done");
if (STOPPED (j) && format == 0)
{
if (posixly_correct == 0 || p == 0 || (WIFSTOPPED (p->status) == 0))
- temp = "Stopped";
+ temp = _("Stopped");
else
{
temp = retcode_name_buffer;
- sprintf (temp, "Stopped(%s)", signal_name (WSTOPSIG (p->status)));
+ sprintf (temp, _("Stopped(%s)"), signal_name (WSTOPSIG (p->status)));
}
}
else if (RUNNING (j))
- temp = "Running";
+ temp = _("Running");
else
{
if (WIFSTOPPED (p->status))
@@ -1439,14 +1447,14 @@ printable_job_status (j, p, format)
temp = retcode_name_buffer;
es = WEXITSTATUS (p->status);
if (es == 0)
- strcpy (temp, "Done");
+ strcpy (temp, _("Done"));
else if (posixly_correct)
- sprintf (temp, "Done(%d)", es);
+ sprintf (temp, _("Done(%d)"), es);
else
- sprintf (temp, "Exit %d", es);
+ sprintf (temp, _("Exit %d"), es);
}
else
- temp = "Unknown status";
+ temp = _("Unknown status");
}
return temp;
@@ -1533,7 +1541,7 @@ print_pipeline (p, job_index, format, stream)
if ((WIFSTOPPED (show->status) == 0) &&
(WIFCONTINUED (show->status) == 0) &&
WIFCORED (show->status))
- fprintf (stream, "(core dumped) ");
+ fprintf (stream, _("(core dumped) "));
}
}
@@ -1552,7 +1560,7 @@ print_pipeline (p, job_index, format, stream)
if (strcmp (temp, jobs[job_index]->wd) != 0)
fprintf (stream,
- " (wd: %s)", polite_directory_format (jobs[job_index]->wd));
+ _(" (wd: %s)"), polite_directory_format (jobs[job_index]->wd));
}
if (format || (p == last))
@@ -1668,6 +1676,7 @@ make_child (command, async_p)
char *command;
int async_p;
{
+ int forksleep;
sigset_t set, oset;
pid_t pid;
@@ -1679,6 +1688,8 @@ make_child (command, async_p)
making_children ();
+ forksleep = 1;
+
#if defined (BUFFERED_INPUT)
/* If default_buffered_input is active, we are reading a script. If
the command is asynchronous, we have already duplicated /dev/null
@@ -1689,8 +1700,16 @@ make_child (command, async_p)
sync_buffered_stream (default_buffered_input);
#endif /* BUFFERED_INPUT */
- /* Create the child, handle severe errors. */
- if ((pid = fork ()) < 0)
+ /* Create the child, handle severe errors. Retry on EAGAIN. */
+ while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX)
+ {
+ sys_error ("fork: retry");
+ if (sleep (forksleep) != 0)
+ break;
+ forksleep <<= 1;
+ }
+
+ if (pid < 0)
{
sys_error ("fork");
@@ -1744,7 +1763,7 @@ make_child (command, async_p)
B.4.3.3, p. 237 also covers this, in the context of job control
shells. */
if (setpgid (mypid, pipeline_pgrp) < 0)
- sys_error ("child setpgid (%ld to %ld)", (long)mypid, (long)pipeline_pgrp);
+ sys_error (_("child setpgid (%ld to %ld)"), (long)mypid, (long)pipeline_pgrp);
/* By convention (and assumption above), if
pipeline_pgrp == shell_pgrp, we are making a child for
@@ -1777,14 +1796,18 @@ make_child (command, async_p)
#if defined (PGRP_PIPE)
/* Release the process group pipe, since our call to setpgid ()
- is done. The last call to pipe_close is done in stop_pipeline. */
- pipe_close (pgrp_pipe);
+ is done. The last call to sh_closepipe is done in stop_pipeline. */
+ sh_closepipe (pgrp_pipe);
#endif /* PGRP_PIPE */
+#if 0
+ /* Don't set last_asynchronous_pid in the child */
if (async_p)
- last_asynchronous_pid = mypid;
+ last_asynchronous_pid = mypid; /* XXX */
+ else
+#endif
#if defined (RECYCLES_PIDS)
- else if (last_asynchronous_pid == mypid)
+ if (last_asynchronous_pid == mypid)
/* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */
last_asynchronous_pid = 1;
#endif
@@ -1959,7 +1982,7 @@ get_tty_state ()
/* Only print an error message if we're really interactive at
this time. */
if (interactive)
- sys_error ("[%ld: %d] tcgetattr", (long)getpid (), shell_level);
+ sys_error ("[%ld: %d (%d)] tcgetattr", (long)getpid (), shell_level, tty);
#endif
return -1;
}
@@ -1998,7 +2021,7 @@ set_tty_state ()
/* Only print an error message if we're really interactive at
this time. */
if (interactive)
- sys_error ("[%ld: %d] tcsetattr", (long)getpid (), shell_level);
+ sys_error ("[%ld: %d (%d)] tcsetattr", (long)getpid (), shell_level, tty);
return -1;
}
#endif /* TERMIOS_TTY_DRIVER */
@@ -2248,6 +2271,7 @@ raw_job_exit_status (job)
{
register PROCESS *p;
int fail;
+ WAIT ret;
if (pipefail_opt)
{
@@ -2255,11 +2279,13 @@ raw_job_exit_status (job)
p = jobs[job]->pipe;
do
{
- if (p->status != EXECUTION_SUCCESS) fail = p->status;
+ if (WSTATUS (p->status) != EXECUTION_SUCCESS)
+ fail = WSTATUS(p->status);
p = p->next;
}
while (p != jobs[job]->pipe);
- return fail;
+ WSTATUS (ret) = fail;
+ return ret;
}
for (p = jobs[job]->pipe; p->next != jobs[job]->pipe; p = p->next)
@@ -2400,7 +2426,7 @@ wait_for (pid)
if (r == -1 && errno == ECHILD)
{
child->running = PS_DONE;
- child->status = 0; /* XXX -- can't find true status */
+ WSTATUS (child->status) = 0; /* XXX -- can't find true status */
js.c_living = 0; /* no living child processes */
if (job != NO_JOB)
{
@@ -2500,7 +2526,7 @@ if (job == NO_JOB)
or until loop, act as if the shell received SIGINT as
well, so the loop can be broken. This doesn't call the
SIGINT signal handler; maybe it should. */
- if (signal_is_trapped (SIGINT) == 0 && loop_level)
+ if (signal_is_trapped (SIGINT) == 0 && (loop_level || (shell_compatibility_level > 32 && executing_list)))
ADDINTERRUPT;
else
{
@@ -3012,6 +3038,7 @@ waitchld (wpid, block)
if (sigchld || block == 0)
waitpid_flags |= WNOHANG;
CHECK_TERMSIG;
+
pid = WAITPID (-1, &status, waitpid_flags);
/* WCONTINUED may be rejected by waitpid as invalid even when defined */
@@ -3053,12 +3080,20 @@ waitchld (wpid, block)
/* Locate our PROCESS for this pid. */
child = find_process (pid, 1, &job); /* want living procs only */
+#if defined (COPROCESS_SUPPORT)
+ coproc_pidchk (pid);
+#endif
+
/* It is not an error to have a child terminate that we did
not have a record of. This child could have been part of
a pipeline in backquote substitution. Even so, I'm not
sure child is ever non-zero. */
if (child == 0)
- continue;
+ {
+ if (WIFEXITED (status) || WIFSIGNALED (status))
+ js.c_reaped++;
+ continue;
+ }
/* Remember status, and whether or not the process is running. */
child->status = status;
@@ -3096,7 +3131,17 @@ waitchld (wpid, block)
/* Call a SIGCHLD trap handler for each child that exits, if one is set. */
if (job_control && signal_is_trapped (SIGCHLD) && children_exited &&
trap_list[SIGCHLD] != (char *)IGNORE_SIG)
- run_sigchld_trap (children_exited);
+ {
+ if (this_shell_builtin && this_shell_builtin == wait_builtin)
+ {
+ interrupt_immediately = 0;
+ trap_handler (SIGCHLD); /* set pending_traps[SIGCHLD] */
+ wait_signal_received = SIGCHLD;
+ longjmp (wait_intr_buf, 1);
+ }
+
+ run_sigchld_trap (children_exited);
+ }
/* We have successfully recorded the useful information about this process
that has just changed state. If we notify asynchronously, and the job
@@ -3315,7 +3360,7 @@ setjstatus (j)
#endif
}
-static void
+void
run_sigchld_trap (nchild)
int nchild;
{
@@ -3346,7 +3391,7 @@ run_sigchld_trap (nchild)
subst_assign_varlist = (WORD_LIST *)NULL;
the_pipeline = (PROCESS *)NULL;
- restore_default_signal (SIGCHLD);
+ set_impossible_sigchld_trap ();
jobs_list_frozen = 1;
for (i = 0; i < nchild; i++)
{
@@ -3439,7 +3484,7 @@ notify_of_job_status ()
signal_is_trapped (termsig) == 0)
{
/* Don't print `0' for a line number. */
- fprintf (stderr, "%s: line %d: ", get_name_for_error (), (line_number == 0) ? 1 : line_number);
+ fprintf (stderr, _("%s: line %d: "), get_name_for_error (), (line_number == 0) ? 1 : line_number);
pretty_print_job (job, JLIST_NONINTERACTIVE, stderr);
}
else if (IS_FOREGROUND (job))
@@ -3453,7 +3498,7 @@ notify_of_job_status ()
fprintf (stderr, "%s", j_strsignal (termsig));
if (WIFCORED (s))
- fprintf (stderr, " (core dumped)");
+ fprintf (stderr, _(" (core dumped)"));
fprintf (stderr, "\n");
}
@@ -3465,7 +3510,7 @@ notify_of_job_status ()
pretty_print_job (job, JLIST_STANDARD, stderr);
if (dir && strcmp (dir, jobs[job]->wd) != 0)
fprintf (stderr,
- "(wd now: %s)\n", polite_directory_format (dir));
+ _("(wd now: %s)\n"), polite_directory_format (dir));
}
jobs[job]->flags |= J_NOTIFIED;
@@ -3478,7 +3523,7 @@ notify_of_job_status ()
pretty_print_job (job, JLIST_STANDARD, stderr);
if (dir && (strcmp (dir, jobs[job]->wd) != 0))
fprintf (stderr,
- "(wd now: %s)\n", polite_directory_format (dir));
+ _("(wd now: %s)\n"), polite_directory_format (dir));
jobs[job]->flags |= J_NOTIFIED;
break;
@@ -3502,11 +3547,15 @@ int
initialize_job_control (force)
int force;
{
+ pid_t t;
+ int t_errno;
+
+ t_errno = -1;
shell_pgrp = getpgid (0);
if (shell_pgrp == -1)
{
- sys_error ("initialize_job_control: getpgrp failed");
+ sys_error (_("initialize_job_control: getpgrp failed"));
exit (1);
}
@@ -3519,10 +3568,21 @@ initialize_job_control (force)
}
else
{
+ shell_tty = -1;
+
+ /* If forced_interactive is set, we skip the normal check that stderr
+ is attached to a tty, so we need to check here. If it's not, we
+ need to see whether we have a controlling tty by opening /dev/tty,
+ since trying to use job control tty pgrp manipulations on a non-tty
+ is going to fail. */
+ if (forced_interactive && isatty (fileno (stderr)) == 0)
+ shell_tty = open ("/dev/tty", O_RDWR|O_NONBLOCK);
+
/* Get our controlling terminal. If job_control is set, or
interactive is set, then this is an interactive shell no
matter where fd 2 is directed. */
- shell_tty = dup (fileno (stderr)); /* fd 2 */
+ if (shell_tty == -1)
+ shell_tty = dup (fileno (stderr)); /* fd 2 */
shell_tty = move_to_high_fd (shell_tty, 1, -1);
@@ -3549,10 +3609,13 @@ initialize_job_control (force)
break;
}
+ if (terminal_pgrp == -1)
+ t_errno = errno;
+
/* Make sure that we are using the new line discipline. */
if (set_new_line_discipline (shell_tty) < 0)
{
- sys_error ("initialize_job_control: line discipline");
+ sys_error (_("initialize_job_control: line discipline"));
job_control = 0;
}
else
@@ -3562,7 +3625,7 @@ initialize_job_control (force)
if ((original_pgrp != shell_pgrp) && (setpgid (0, shell_pgrp) < 0))
{
- sys_error ("initialize_job_control: setpgid");
+ sys_error (_("initialize_job_control: setpgid"));
shell_pgrp = original_pgrp;
}
@@ -3579,11 +3642,20 @@ initialize_job_control (force)
{
if (give_terminal_to (shell_pgrp, 0) < 0)
{
+ t_errno = errno;
setpgid (0, original_pgrp);
shell_pgrp = original_pgrp;
job_control = 0;
}
}
+
+ if (job_control && ((t = tcgetpgrp (shell_tty)) == -1 || t != shell_pgrp))
+ {
+ if (t_errno != -1)
+ errno = t_errno;
+ sys_error (_("cannot set terminal process group (%d)"), t);
+ job_control = 0;
+ }
}
if (job_control == 0)
internal_error (_("no job control in this shell"));
@@ -3733,7 +3805,7 @@ give_terminal_to (pgrp, force)
int force;
{
sigset_t set, oset;
- int r;
+ int r, e;
r = 0;
if (job_control || force)
@@ -3754,15 +3826,47 @@ give_terminal_to (pgrp, force)
shell_tty, (long)getpid(), (long)pgrp);
#endif
r = -1;
+ e = errno;
}
else
terminal_pgrp = pgrp;
sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
}
+ if (r == -1)
+ errno = e;
+
return r;
}
+/* Give terminal to NPGRP iff it's currently owned by OPGRP. FLAGS are the
+ flags to pass to give_terminal_to(). */
+static int
+maybe_give_terminal_to (opgrp, npgrp, flags)
+ pid_t opgrp, npgrp;
+ int flags;
+{
+ int tpgrp;
+
+ tpgrp = tcgetpgrp (shell_tty);
+ if (tpgrp < 0 && errno == ENOTTY)
+ return -1;
+ if (tpgrp == npgrp)
+ {
+ terminal_pgrp = npgrp;
+ return 0;
+ }
+ 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);
+#endif
+ return -1;
+ }
+ else
+ return (give_terminal_to (npgrp, flags));
+}
+
/* Clear out any jobs in the job array. This is intended to be used by
children of the shell, who should not have any job structures as baggage
when they start executing (forking subshells for parenthesized execution
@@ -4023,7 +4127,7 @@ without_job_control ()
stop_making_children ();
start_pipeline ();
#if defined (PGRP_PIPE)
- pipe_close (pgrp_pipe);
+ sh_closepipe (pgrp_pipe);
#endif
delete_all_jobs (0);
set_job_control (0);
@@ -4086,25 +4190,11 @@ pipe_read (pp)
}
}
-/* Close the read and write ends of PP, an array of file descriptors. */
-static void
-pipe_close (pp)
- int *pp;
-{
- if (pp[0] >= 0)
- close (pp[0]);
-
- if (pp[1] >= 0)
- close (pp[1]);
-
- pp[0] = pp[1] = -1;
-}
-
/* Functional interface closes our local-to-job-control pipes. */
void
close_pgrp_pipe ()
{
- pipe_close (pgrp_pipe);
+ sh_closepipe (pgrp_pipe);
}
#endif /* PGRP_PIPE */