X-Git-Url: http://review.tizen.org/git/?p=platform%2Fupstream%2Fbash.git;a=blobdiff_plain;f=jobs.c;h=f38b0c3f4446fc48cf2dc314f35c94f5c749b705;hp=d8bb7ae1e7ba541b84bb161a007a00c868b774ae;hb=HEAD;hpb=89a92869e56aba4e4cab2d639c00a86f0545c862 diff --git a/jobs.c b/jobs.c index d8bb7ae..f38b0c3 100644 --- a/jobs.c +++ b/jobs.c @@ -3,7 +3,7 @@ /* This file works with both POSIX and BSD systems. It implements job control. */ -/* Copyright (C) 1989-2009 Free Software Foundation, Inc. +/* Copyright (C) 1989-2013 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -45,7 +45,9 @@ #include "filecntl.h" #include +#if defined (HAVE_SYS_PARAM_H) #include +#endif #if defined (BUFFERED_INPUT) # include "input.h" @@ -77,7 +79,18 @@ extern int errno; #endif /* !errno */ -#define DEFAULT_CHILD_MAX 32 +#if !defined (HAVE_KILLPG) +extern int killpg __P((pid_t, int)); +#endif + +#if !DEFAULT_CHILD_MAX +# define DEFAULT_CHILD_MAX 32 +#endif + +#if !MAX_CHILD_MAX +# define MAX_CHILD_MAX 8192 +#endif + #if !defined (DEBUG) #define MAX_JOBS_IN_ARRAY 4096 /* production */ #else @@ -199,10 +212,10 @@ int previous_job = NO_JOB; #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; @@ -215,7 +228,7 @@ int already_making_children = 0; /* If this is non-zero, $LINES and $COLUMNS are reset after every process exits from get_tty_state(). */ -int check_window_size; +int check_window_size = CHECKWINSIZE_DEFAULT; /* Functions local to this file. */ @@ -246,8 +259,6 @@ static int find_job __P((pid_t, int, PROCESS **)); static int print_job __P((JOB *, int, int, int)); static int process_exit_status __P((WAIT)); static int process_exit_signal __P((WAIT)); -static int job_exit_status __P((int)); -static int job_exit_signal __P((int)); static int set_job_status_and_cleanup __P((int)); static WAIT job_signal_status __P((int)); @@ -316,10 +327,6 @@ static int jobs_list_frozen; static char retcode_name_buffer[64]; -/* flags to detect pid wraparound */ -static pid_t first_pid = NO_PID; -static int pid_wrap = -1; - #if !defined (_POSIX_VERSION) /* These are definitions to map POSIX 1003.1 functions onto existing BSD @@ -346,8 +353,6 @@ void init_job_stats () { js = zerojs; - first_pid = NO_PID; - pid_wrap = -1; } /* Return the working directory for the current process. Unlike @@ -645,7 +650,7 @@ stop_pipeline (async, deferred) stop_making_children (); UNBLOCK_CHILD (oset); - return (js.j_current); + return (newjob ? i : js.j_current); } /* Functions to manage the list of exited background pids whose status has @@ -701,8 +706,8 @@ bgp_delete (pid) for (prev = p = bgpids.list; p; prev = p, p = p->next) if (p->pid == pid) { - prev->next = p->next; /* remove from list */ - break; + prev->next = p->next; /* remove from list */ + break; } if (p == 0) @@ -884,7 +889,9 @@ delete_old_job (pid) 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; } @@ -927,7 +934,7 @@ realloc_jobs_list () } } -#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); @@ -958,14 +965,14 @@ realloc_jobs_list () 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 @@ -981,7 +988,7 @@ compact_jobs_list (flags) 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 @@ -1134,6 +1141,33 @@ add_process (name, pid) } } +/* Create a (dummy) PROCESS with NAME, PID, and STATUS, and make it the last + process in jobs[JID]->pipe. Used by the lastpipe code. */ +void +append_process (name, pid, status, jid) + char *name; + pid_t pid; + int status; + int jid; +{ + PROCESS *t, *p; + + t = (PROCESS *)xmalloc (sizeof (PROCESS)); + t->next = (PROCESS *)NULL; + t->pid = pid; + /* set process exit status using offset discovered by configure */ + t->status = (status & 0xff) << WEXITSTATUS_OFFSET; + t->running = PS_DONE; + t->command = name; + + js.c_reaped++; /* XXX */ + + for (p = jobs[jid]->pipe; p->next != jobs[jid]->pipe; p = p->next) + ; + p->next = t; + t->next = jobs[jid]->pipe; +} + #if 0 /* Take the last job and make it the first job. Must be called with SIGCHLD blocked. */ @@ -1685,6 +1719,8 @@ make_child (command, async_p) 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); @@ -1705,15 +1741,26 @@ make_child (command, async_p) 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) { + /* bash-4.2 */ + /* If we can't create any children, try to reap some dead ones. */ + 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"); @@ -1725,6 +1772,7 @@ make_child (command, async_p) if (the_pipeline) kill_current_pipeline (); + last_command_exit_value = EX_NOEXEC; throw_to_top_level (); /* Reset signals, etc. */ } @@ -1822,13 +1870,6 @@ make_child (command, async_p) /* In the parent. Remember the pid of the child just created as the proper pgrp if this is the first child. */ - if (first_pid == NO_PID) - first_pid = pid; - else if (pid_wrap == -1 && pid < first_pid) - pid_wrap = 0; - else if (pid_wrap == 0 && pid >= first_pid) - pid_wrap = 1; - if (job_control) { if (pipeline_pgrp == 0) @@ -1862,15 +1903,13 @@ make_child (command, async_p) last_asynchronous_pid = 1; #endif - if (pid_wrap > 0) - delete_old_job (pid); + /* Delete the saved status for any job containing this PID in case it's + been reused. */ + delete_old_job (pid); -#if !defined (RECYCLES_PIDS) - /* Only check for saved status if we've saved more than CHILD_MAX - statuses, unless the system recycles pids. */ - if ((js.c_reaped + bgpids.npid) >= js.c_childmax) -#endif - bgp_delete (pid); /* new process, discard any saved status */ + /* Perform the check for pid reuse unconditionally. Some systems reuse + PIDs before giving a process CHILD_MAX/_SC_CHILD_MAX unique ones. */ + bgp_delete (pid); /* new process, discard any saved status */ last_made_pid = pid; @@ -2120,7 +2159,7 @@ wait_for_single_pid (pid) return r; } -/* Wait for all of the backgrounds of this shell to finish. */ +/* Wait for all of the background processes started by this shell to finish. */ void wait_for_background_pids () { @@ -2178,6 +2217,10 @@ wait_for_background_pids () #define INVALID_SIGNAL_HANDLER (SigHandler *)wait_for_background_pids static SigHandler *old_sigint_handler = INVALID_SIGNAL_HANDLER; +static int wait_sigint_received; +static int child_caught_sigint; +static int waiting_for_child; + static void restore_sigint_handler () { @@ -2185,11 +2228,10 @@ restore_sigint_handler () { set_signal_handler (SIGINT, old_sigint_handler); old_sigint_handler = INVALID_SIGNAL_HANDLER; + waiting_for_child = 0; } } -static int wait_sigint_received; - /* Handle SIGINT while we are waiting for children in a script to exit. The `wait' builtin should be interruptible, but all others should be effectively ignored (i.e. not cause the shell to exit). */ @@ -2202,7 +2244,7 @@ wait_sigint_handler (sig) if (interrupt_immediately || (this_shell_builtin && this_shell_builtin == wait_builtin)) { - last_command_exit_value = EXECUTION_FAILURE; + last_command_exit_value = 128+SIGINT; restore_sigint_handler (); /* If we got a SIGINT while in `wait', and SIGINT is trapped, do what POSIX.2 says (see builtins/wait.def for more info). */ @@ -2210,19 +2252,36 @@ wait_sigint_handler (sig) signal_is_trapped (SIGINT) && ((sigint_handler = trap_to_sighandler (SIGINT)) == trap_handler)) { - interrupt_immediately = 0; trap_handler (SIGINT); /* set pending_traps[SIGINT] */ wait_signal_received = SIGINT; - longjmp (wait_intr_buf, 1); + if (interrupt_immediately) + { + interrupt_immediately = 0; + longjmp (wait_intr_buf, 1); + } + else + /* Let CHECK_WAIT_INTR handle it in wait_for/waitchld */ + SIGRETURN (0); } - - ADDINTERRUPT; - QUIT; + else if (interrupt_immediately) + { + ADDINTERRUPT; + QUIT; + } + else /* wait_builtin but signal not trapped, treat as interrupt */ + kill (getpid (), SIGINT); } /* XXX - should this be interrupt_state? If it is, the shell will act as if it got the SIGINT interrupt. */ - wait_sigint_received = 1; + if (waiting_for_child) + wait_sigint_received = 1; + else + { + last_command_exit_value = 128+SIGINT; + restore_sigint_handler (); + kill (getpid (), SIGINT); + } /* Otherwise effectively ignore the SIGINT and allow the running job to be killed. */ @@ -2301,14 +2360,14 @@ raw_job_exit_status (job) /* Return the exit status of job JOB. This is the exit status of the last (rightmost) process in the job's pipeline, modified if the job was killed by a signal or stopped. */ -static int +int job_exit_status (job) int job; { return (process_exit_status (raw_job_exit_status (job))); } -static int +int job_exit_signal (job) int job; { @@ -2342,11 +2401,11 @@ wait_for (pid) WAIT s; register PROCESS *child; sigset_t set, oset; - register PROCESS *p; /* 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 @@ -2359,10 +2418,11 @@ wait_for (pid) substitution. */ /* This is possibly a race condition -- should it go in stop_pipeline? */ - wait_sigint_received = 0; + wait_sigint_received = child_caught_sigint = 0; if (job_control == 0 || (subshell_environment&SUBSHELL_COMSUB)) { old_sigint_handler = set_signal_handler (SIGINT, wait_sigint_handler); + waiting_for_child = 0; if (old_sigint_handler == SIG_IGN) set_signal_handler (SIGINT, old_sigint_handler); } @@ -2371,6 +2431,11 @@ wait_for (pid) if (interactive && job_control == 0) QUIT; + /* 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; /* If we say wait_for (), then we have a record of this child somewhere. If it and none of its peers are running, don't call waitchld(). */ @@ -2378,7 +2443,8 @@ wait_for (pid) 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. @@ -2391,7 +2457,7 @@ wait_for (pid) 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; @@ -2409,10 +2475,18 @@ wait_for (pid) 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; - r = waitchld (pid, 1); + waiting_for_child++; + 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); @@ -2421,6 +2495,7 @@ wait_for (pid) if (r == -1 && errno == ECHILD && this_shell_builtin == wait_builtin) { termination_state = -1; + /* XXX - restore sigint handler here? */ goto wait_for_return; } @@ -2430,8 +2505,11 @@ wait_for (pid) 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) { @@ -2439,6 +2517,11 @@ wait_for (pid) js.c_reaped++; js.j_ndead++; } + if (pid == ANY_PID) + { + termination_state = -1; + break; + } } #endif /* WAITPID_BROKEN */ } @@ -2449,9 +2532,22 @@ wait_for (pid) old SIGINT signal handler. */ if (interactive && job_control == 0) QUIT; + /* 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; + + 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))); + /* Restore the original SIGINT signal handler before we return. */ + restore_sigint_handler (); + /* The exit state of the command is either the termination state of the child, or the termination state of the job. If a job, the status of the last child in the pipeline is the significant one. If the command @@ -2540,17 +2636,18 @@ if (job == NO_JOB) } } } - else if ((subshell_environment & SUBSHELL_COMSUB) && wait_sigint_received) + else if ((subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PIPE)) && wait_sigint_received) { /* If waiting for a job in a subshell started to do command - substitution, simulate getting and being killed by the SIGINT to - pass the status back to our parent. */ + substitution or to run a pipeline element that consists of + 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 (WIFSIGNALED (s) && WTERMSIG (s) == SIGINT && signal_is_trapped (SIGINT) == 0) + + if (child_caught_sigint == 0 && signal_is_trapped (SIGINT) == 0) { UNBLOCK_CHILD (oset); - restore_sigint_handler (); old_sigint_handler = set_signal_handler (SIGINT, SIG_DFL); if (old_sigint_handler == SIG_IGN) restore_sigint_handler (); @@ -2558,6 +2655,8 @@ if (job == NO_JOB) kill (getpid (), SIGINT); } } + else if (interactive_shell == 0 && IS_FOREGROUND (job) && check_window_size) + get_new_window_size (0, (int *)0, (int *)0); /* Moved here from set_job_status_and_cleanup, which is in the SIGCHLD signal handler path */ @@ -2576,9 +2675,6 @@ wait_for_return: UNBLOCK_CHILD (oset); - /* Restore the original SIGINT signal handler before we return. */ - restore_sigint_handler (); - return (termination_state); } @@ -2611,6 +2707,76 @@ wait_for_job (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 @@ -3027,6 +3193,7 @@ waitchld (wpid, block) 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 */ @@ -3042,10 +3209,24 @@ waitchld (wpid, block) : 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; + + if (block == 1 && queue_sigchld == 0 && (waitpid_flags & WNOHANG) == 0) + { + internal_warning (_("waitchld: turning on WNOHANG to avoid indefinite block")); + waitpid_flags |= WNOHANG; + } 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) { @@ -3057,7 +3238,7 @@ waitchld (wpid, block) if it was non-zero before we called waitpid. */ if (sigchld > 0 && (waitpid_flags & WNOHANG)) sigchld--; - + /* If waitpid returns -1 with errno == ECHILD, there are no more unwaited-for child processes of this shell. */ if (pid < 0 && errno == ECHILD) @@ -3068,12 +3249,30 @@ waitchld (wpid, block) 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; + CHECK_WAIT_INTR; + + /* If waitpid returns -1/EINTR and the shell saw a SIGINT, then we + assume the child has blocked or handled SIGINT. In that case, we + require the child to actually die due to SIGINT to act on the + SIGINT we received; otherwise we assume the child handled it and + let it go. */ + if (pid < 0 && errno == EINTR && wait_sigint_received) + child_caught_sigint = 1; + if (pid <= 0) continue; /* jumps right to the test */ + /* 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) + child_caught_sigint = 0; + /* children_exited is used to run traps on SIGCHLD. We don't want to run the trap if a process is just being continued. */ if (WIFCONTINUED(status) == 0) @@ -3086,7 +3285,7 @@ waitchld (wpid, block) 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 @@ -3137,15 +3336,27 @@ waitchld (wpid, block) if (job_control && signal_is_trapped (SIGCHLD) && children_exited && trap_list[SIGCHLD] != (char *)IGNORE_SIG) { - if (this_shell_builtin && this_shell_builtin == wait_builtin) + if (posixly_correct && 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); + /* 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); } - - run_sigchld_trap (children_exited); + /* 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 + queue_sigchld_trap (children_exited); } /* We have successfully recorded the useful information about this process @@ -3194,8 +3405,7 @@ set_job_status_and_cleanup (job) #endif { any_stopped = 1; - any_tstped |= interactive && job_control && - (WSTOPSIG (child->status) == SIGTSTP); + any_tstped |= job_control && (WSTOPSIG (child->status) == SIGTSTP); } child = child->next; } @@ -3261,7 +3471,7 @@ set_job_status_and_cleanup (job) does not exit due to SIGINT, run the trap handler but do not otherwise act as if we got the interrupt. */ if (wait_sigint_received && interactive_shell == 0 && - WIFSIGNALED (child->status) == 0 && IS_FOREGROUND (job) && + child_caught_sigint && IS_FOREGROUND (job) && signal_is_trapped (SIGINT)) { int old_frozen; @@ -3283,7 +3493,8 @@ set_job_status_and_cleanup (job) 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. */ - else if (wait_sigint_received && (WTERMSIG (child->status) == SIGINT) && + else if (wait_sigint_received && + child_caught_sigint == 0 && IS_FOREGROUND (job) && IS_JOBCONTROL (job) == 0) { int old_frozen; @@ -3323,7 +3534,7 @@ set_job_status_and_cleanup (job) temp_handler = trap_to_sighandler (SIGINT); restore_sigint_handler (); if (temp_handler == SIG_DFL) - termsig_handler (SIGINT); + termsig_handler (SIGINT); /* XXX */ else if (temp_handler != SIG_IGN) (*temp_handler) (SIGINT); } @@ -3386,6 +3597,7 @@ run_sigchld_trap (nchild) unwind_protect_int (jobs_list_frozen); unwind_protect_pointer (the_pipeline); unwind_protect_pointer (subst_assign_varlist); + unwind_protect_pointer (this_shell_builtin); /* 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 () @@ -3396,15 +3608,20 @@ run_sigchld_trap (nchild) 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 @@ -3483,6 +3700,9 @@ notify_of_job_status () case JDEAD: if (interactive_shell == 0 && termsig && WIFSIGNALED (s) && termsig != SIGINT && +#if defined (DONT_REPORT_SIGTERM) + termsig != SIGTERM && +#endif #if defined (DONT_REPORT_SIGPIPE) termsig != SIGPIPE && #endif @@ -3564,8 +3784,8 @@ initialize_job_control (force) exit (1); } - /* We can only have job control if we are interactive. */ - if (interactive == 0) + /* We can only have job control if we are interactive unless we force it. */ + if (interactive == 0 && force == 0) { job_control = 0; original_pgrp = NO_PID; @@ -3589,7 +3809,8 @@ initialize_job_control (force) if (shell_tty == -1) shell_tty = dup (fileno (stderr)); /* fd 2 */ - shell_tty = move_to_high_fd (shell_tty, 1, -1); + if (shell_tty != -1) + shell_tty = move_to_high_fd (shell_tty, 1, -1); /* Compensate for a bug in systems that compiled the BSD rlogind with DEBUG defined, like NeXT and Alliant. */ @@ -3650,6 +3871,8 @@ initialize_job_control (force) t_errno = errno; setpgid (0, original_pgrp); shell_pgrp = original_pgrp; + errno = t_errno; + sys_error (_("cannot set terminal process group (%d)"), shell_pgrp); job_control = 0; } } @@ -4039,8 +4262,10 @@ mark_dead_jobs_as_notified (force) } #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 @@ -4098,7 +4323,13 @@ itrace("mark_dead_jobs_as_notified: child_max = %d ndead = %d ndeadproc = %d", j } /* Here to allow other parts of the shell (like the trap stuff) to - unfreeze the jobs list. */ + freeze and unfreeze the jobs list. */ +void +freeze_jobs_list () +{ + jobs_list_frozen = 1; +} + void unfreeze_jobs_list () { @@ -4144,7 +4375,7 @@ without_job_control () 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 (); @@ -4166,6 +4397,27 @@ restart_job_control () initialize_job_control (0); } +void +set_maxchild (nchild) + int nchild; +{ + static int lmaxchild = -1; + + if (lmaxchild < 0) + lmaxchild = getmaxchild (); + if (lmaxchild < 0) + lmaxchild = DEFAULT_CHILD_MAX; + + /* Clamp value we set. Minimum is what Posix requires, maximum is defined + above as MAX_CHILD_MAX. */ + if (nchild < lmaxchild) + nchild = lmaxchild; + else if (nchild > MAX_CHILD_MAX) + nchild = MAX_CHILD_MAX; + + js.c_childmax = nchild; +} + /* Set the handler to run when the shell receives a SIGCHLD signal. */ void set_sigchld_handler ()