diff options
Diffstat (limited to 'builtins/wait.def')
-rw-r--r-- | builtins/wait.def | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/builtins/wait.def b/builtins/wait.def new file mode 100644 index 0000000..b066d78 --- /dev/null +++ b/builtins/wait.def @@ -0,0 +1,381 @@ +'This file is wait.def, from which is created wait.c. +It implements the builtin "wait" in Bash. + +Copyright (C) 1987-2021 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 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. + +You should have received a copy of the GNU General Public License +along with Bash. If not, see <http://www.gnu.org/licenses/>. + +$BUILTIN wait +$FUNCTION wait_builtin +$DEPENDS_ON JOB_CONTROL +$PRODUCES wait.c +$SHORT_DOC wait [-fn] [-p var] [id ...] +Wait for job completion and return exit status. + +Waits for each process identified by an ID, which may be a process ID or a +job specification, and reports its termination status. If ID is not +given, waits for all currently active child processes, and the return +status is zero. If ID is a job specification, waits for all processes +in that job's pipeline. + +If the -n option is supplied, waits for a single job from the list of IDs, +or, if no IDs are supplied, for the next job to complete and returns its +exit status. + +If the -p option is supplied, the process or job identifier of the job +for which the exit status is returned is assigned to the variable VAR +named by the option argument. The variable will be unset initially, before +any assignment. This is useful only when the -n option is supplied. + +If the -f option is supplied, and job control is enabled, waits for the +specified ID to terminate, instead of waiting for it to change status. + +Exit Status: +Returns the status of the last ID; fails if ID is invalid or an invalid +option is given, or if -n is supplied and the shell has no unwaited-for +children. +$END + +$BUILTIN wait +$FUNCTION wait_builtin +$DEPENDS_ON !JOB_CONTROL +$SHORT_DOC wait [pid ...] +Wait for process completion and return exit status. + +Waits for each process specified by a PID and reports its termination status. +If PID is not given, waits for all currently active child processes, +and the return status is zero. PID must be a process ID. + +Exit Status: +Returns the status of the last PID; fails if PID is invalid or an invalid +option is given. +$END + +#include <config.h> + +#include "../bashtypes.h" +#include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <chartypes.h> + +#include "../bashansi.h" + +#include "../shell.h" +#include "../execute_cmd.h" +#include "../jobs.h" +#include "../trap.h" +#include "../sig.h" +#include "common.h" +#include "bashgetopt.h" + +extern int wait_signal_received; + +procenv_t wait_intr_buf; +int wait_intr_flag; + +static int set_waitlist PARAMS((WORD_LIST *)); +static void unset_waitlist PARAMS((void)); + +/* Wait for the pid in LIST to stop or die. If no arguments are given, then + wait for all of the active background processes of the shell and return + 0. If a list of pids or job specs are given, return the exit status of + the last one waited for. */ + +#define WAIT_RETURN(s) \ + do \ + { \ + wait_signal_received = 0; \ + wait_intr_flag = 0; \ + return (s);\ + } \ + while (0) + +int +wait_builtin (list) + WORD_LIST *list; +{ + int status, code, opt, nflag, vflags, bindflags; + volatile int wflags; + char *vname; + SHELL_VAR *pidvar; + struct procstat pstat; + + USE_VAR(list); + + nflag = wflags = vflags = 0; + vname = NULL; + pidvar = (SHELL_VAR *)NULL; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "fnp:")) != -1) + { + switch (opt) + { +#if defined (JOB_CONTROL) + case 'n': + nflag = 1; + break; + case 'f': + wflags |= JWAIT_FORCE; + break; + case 'p': + vname = list_optarg; + vflags = list_optflags; + break; +#endif + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + /* Sanity-check variable name if -p supplied. */ + if (vname) + { +#if defined (ARRAY_VARS) + int arrayflags; + + SET_VFLAGS (vflags, arrayflags, bindflags); + if (legal_identifier (vname) == 0 && valid_array_reference (vname, arrayflags) == 0) +#else + bindflags = 0; + if (legal_identifier (vname) == 0) +#endif + { + sh_invalidid (vname); + WAIT_RETURN (EXECUTION_FAILURE); + } + if (builtin_unbind_variable (vname) == -2) + WAIT_RETURN (EXECUTION_FAILURE); + } + + /* POSIX.2 says: When the shell is waiting (by means of the wait utility) + for asynchronous commands to complete, the reception of a signal for + which a trap has been set shall cause the wait utility to return + immediately with an exit status greater than 128, after which the trap + associated with the signal shall be taken. + + We handle SIGINT here; it's the only one that needs to be treated + specially (I think), since it's handled specially in {no,}jobs.c. */ + wait_intr_flag = 1; + code = setjmp_sigs (wait_intr_buf); + + if (code) + { + last_command_exit_signal = wait_signal_received; + status = 128 + wait_signal_received; + wait_sigint_cleanup (); +#if defined (JOB_CONTROL) + if (wflags & JWAIT_WAITING) + unset_waitlist (); +#endif + WAIT_RETURN (status); + } + + opt = first_pending_trap (); +#if defined (SIGCHLD) + /* We special case SIGCHLD when not in posix mode because we don't break + out of the wait even when the signal is trapped; we run the trap after + the wait completes. See how it's handled in jobs.c:waitchld(). */ + if (opt == SIGCHLD && posixly_correct == 0) + opt = next_pending_trap (opt+1); +#endif + if (opt != -1) + { + last_command_exit_signal = wait_signal_received = opt; + status = opt + 128; + WAIT_RETURN (status); + } + + /* We support jobs or pids. + wait <pid-or-job> [pid-or-job ...] */ + +#if defined (JOB_CONTROL) + if (nflag) + { + if (list) + { + opt = set_waitlist (list); + if (opt == 0) + WAIT_RETURN (127); + wflags |= JWAIT_WAITING; + } + + status = wait_for_any_job (wflags, &pstat); + if (vname && status >= 0) + builtin_bind_var_to_int (vname, pstat.pid, bindflags); + + if (status < 0) + status = 127; + if (list) + unset_waitlist (); + WAIT_RETURN (status); + } +#endif + + /* But wait without any arguments means to wait for all of the shell's + currently active background processes. */ + if (list == 0) + { + opt = wait_for_background_pids (&pstat); +#if 0 + /* Compatibility with NetBSD sh: don't set VNAME since it doesn't + correspond to the return status. */ + if (vname && opt) + builtin_bind_var_to_int (vname, pstat.pid, bindflags); +#endif + WAIT_RETURN (EXECUTION_SUCCESS); + } + + status = EXECUTION_SUCCESS; + while (list) + { + pid_t pid; + char *w; + intmax_t pid_value; + + w = list->word->word; + if (DIGIT (*w)) + { + if (legal_number (w, &pid_value) && pid_value == (pid_t)pid_value) + { + pid = (pid_t)pid_value; + status = wait_for_single_pid (pid, wflags|JWAIT_PERROR); + /* status > 256 means pid error */ + pstat.pid = (status > 256) ? NO_PID : pid; + pstat.status = (status > 256) ? 127 : status; + if (status > 256) + status = 127; + } + else + { + sh_badpid (w); + pstat.pid = NO_PID; + pstat.status = 127; + WAIT_RETURN (EXECUTION_FAILURE); + } + } +#if defined (JOB_CONTROL) + else if (*w && *w == '%') + /* Must be a job spec. Check it out. */ + { + int job; + sigset_t set, oset; + + BLOCK_CHILD (set, oset); + job = get_job_spec (list); + + if (INVALID_JOB (job)) + { + if (job != DUP_JOB) + sh_badjob (list->word->word); + UNBLOCK_CHILD (oset); + status = 127; /* As per Posix.2, section 4.70.2 */ + pstat.pid = NO_PID; + pstat.status = status; + list = list->next; + continue; + } + + /* Job spec used. Wait for the last pid in the pipeline. */ + UNBLOCK_CHILD (oset); + status = wait_for_job (job, wflags, &pstat); + } +#endif /* JOB_CONTROL */ + else + { + sh_badpid (w); + pstat.pid = NO_PID; + pstat.status = 127; + status = EXECUTION_FAILURE; + } + + /* Don't waste time with a longjmp. */ + if (wait_signal_received) + { + last_command_exit_signal = wait_signal_received; + status = 128 + wait_signal_received; + wait_sigint_cleanup (); + WAIT_RETURN (status); + } + + list = list->next; + } + + if (vname && pstat.pid != NO_PID) + builtin_bind_var_to_int (vname, pstat.pid, bindflags); + + WAIT_RETURN (status); +} + +#if defined (JOB_CONTROL) +/* Take each valid pid or jobspec in LIST and mark the corresponding job as + J_WAITING, so wait -n knows which jobs to wait for. Return the number of + jobs we found. */ +static int +set_waitlist (list) + WORD_LIST *list; +{ + sigset_t set, oset; + int job, r, njob; + intmax_t pid; + WORD_LIST *l; + + BLOCK_CHILD (set, oset); + njob = 0; + for (l = list; l; l = l->next) + { + job = NO_JOB; + job = (l && legal_number (l->word->word, &pid) && pid == (pid_t) pid) + ? get_job_by_pid ((pid_t) pid, 0, 0) + : get_job_spec (l); + if (job == NO_JOB || jobs == 0 || INVALID_JOB (job)) + { + sh_badjob (l->word->word); + continue; + } + /* We don't check yet to see if one of the desired jobs has already + terminated, but we could. We wait until wait_for_any_job(). This + has the advantage of validating all the arguments. */ + if ((jobs[job]->flags & J_WAITING) == 0) + { + njob++; + jobs[job]->flags |= J_WAITING; + } + } + UNBLOCK_CHILD (oset); + return (njob); +} + +/* Clean up after a call to wait -n jobs */ +static void +unset_waitlist () +{ + int i; + sigset_t set, oset; + + BLOCK_CHILD (set, oset); + for (i = 0; i < js.j_jobslots; i++) + if (jobs[i] && (jobs[i]->flags & J_WAITING)) + jobs[i]->flags &= ~J_WAITING; + UNBLOCK_CHILD (oset); +} +#endif |