diff options
author | Jari Aalto <jari.aalto@cante.net> | 2009-01-12 13:36:28 +0000 |
---|---|---|
committer | Jari Aalto <jari.aalto@cante.net> | 2009-09-12 16:47:00 +0000 |
commit | 3185942a5234e26ab13fa02f9c51d340cec514f8 (patch) | |
tree | 2c6fcd01779faef1f91c5d84d63981ca3479137d /jobs.c | |
parent | f1be666c7d78939ad775078d290bec2758fa29a2 (diff) | |
download | android_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.c | 244 |
1 files changed, 167 insertions, 77 deletions
@@ -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 */ |