]> git.ipfire.org Git - thirdparty/bash.git/blame - builtins/wait.def
Bash-5.2 patch 17: fix for optimizing forks when using the . builtin in a subshell
[thirdparty/bash.git] / builtins / wait.def
CommitLineData
74091dd4 1'This file is wait.def, from which is created wait.c.
726f6388
JA
2It implements the builtin "wait" in Bash.
3
74091dd4 4Copyright (C) 1987-2021 Free Software Foundation, Inc.
726f6388
JA
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
3185942a
JA
8Bash is free software: you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 3 of the License, or
11(at your option) any later version.
726f6388 12
3185942a
JA
13Bash is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
726f6388 17
3185942a
JA
18You should have received a copy of the GNU General Public License
19along with Bash. If not, see <http://www.gnu.org/licenses/>.
726f6388 20
726f6388
JA
21$BUILTIN wait
22$FUNCTION wait_builtin
23$DEPENDS_ON JOB_CONTROL
24$PRODUCES wait.c
8868edaf 25$SHORT_DOC wait [-fn] [-p var] [id ...]
3185942a
JA
26Wait for job completion and return exit status.
27
ac50fbac 28Waits for each process identified by an ID, which may be a process ID or a
3185942a
JA
29job specification, and reports its termination status. If ID is not
30given, waits for all currently active child processes, and the return
d233b485 31status is zero. If ID is a job specification, waits for all processes
ac50fbac
CR
32in that job's pipeline.
33
8868edaf
CR
34If the -n option is supplied, waits for a single job from the list of IDs,
35or, if no IDs are supplied, for the next job to complete and returns its
36exit status.
37
38If the -p option is supplied, the process or job identifier of the job
39for which the exit status is returned is assigned to the variable VAR
40named by the option argument. The variable will be unset initially, before
41any assignment. This is useful only when the -n option is supplied.
3185942a 42
d233b485
CR
43If the -f option is supplied, and job control is enabled, waits for the
44specified ID to terminate, instead of waiting for it to change status.
45
3185942a 46Exit Status:
ac50fbac 47Returns the status of the last ID; fails if ID is invalid or an invalid
8868edaf
CR
48option is given, or if -n is supplied and the shell has no unwaited-for
49children.
726f6388
JA
50$END
51
52$BUILTIN wait
53$FUNCTION wait_builtin
54$DEPENDS_ON !JOB_CONTROL
ac50fbac 55$SHORT_DOC wait [pid ...]
3185942a
JA
56Wait for process completion and return exit status.
57
ac50fbac
CR
58Waits for each process specified by a PID and reports its termination status.
59If PID is not given, waits for all currently active child processes,
60and the return status is zero. PID must be a process ID.
3185942a
JA
61
62Exit Status:
ac50fbac
CR
63Returns the status of the last PID; fails if PID is invalid or an invalid
64option is given.
726f6388
JA
65$END
66
ccc6cda3
JA
67#include <config.h>
68
69#include "../bashtypes.h"
726f6388 70#include <signal.h>
ccc6cda3
JA
71
72#if defined (HAVE_UNISTD_H)
73# include <unistd.h>
74#endif
75
f73dda09
JA
76#include <chartypes.h>
77
d166f048
JA
78#include "../bashansi.h"
79
726f6388 80#include "../shell.h"
d233b485 81#include "../execute_cmd.h"
726f6388 82#include "../jobs.h"
8868edaf
CR
83#include "../trap.h"
84#include "../sig.h"
ccc6cda3 85#include "common.h"
d166f048 86#include "bashgetopt.h"
726f6388 87
7117c2d2 88extern int wait_signal_received;
726f6388 89
bb70624e 90procenv_t wait_intr_buf;
a0c0a00f 91int wait_intr_flag;
bb70624e 92
8868edaf
CR
93static int set_waitlist PARAMS((WORD_LIST *));
94static void unset_waitlist PARAMS((void));
95
726f6388
JA
96/* Wait for the pid in LIST to stop or die. If no arguments are given, then
97 wait for all of the active background processes of the shell and return
98 0. If a list of pids or job specs are given, return the exit status of
99 the last one waited for. */
ccc6cda3 100
28ef6c31
JA
101#define WAIT_RETURN(s) \
102 do \
103 { \
ac50fbac 104 wait_signal_received = 0; \
a0c0a00f 105 wait_intr_flag = 0; \
28ef6c31
JA
106 return (s);\
107 } \
108 while (0)
ccc6cda3
JA
109
110int
726f6388
JA
111wait_builtin (list)
112 WORD_LIST *list;
113{
74091dd4 114 int status, code, opt, nflag, vflags, bindflags;
910fcdc4 115 volatile int wflags;
8868edaf
CR
116 char *vname;
117 SHELL_VAR *pidvar;
118 struct procstat pstat;
ccc6cda3 119
f73dda09
JA
120 USE_VAR(list);
121
74091dd4 122 nflag = wflags = vflags = 0;
8868edaf
CR
123 vname = NULL;
124 pidvar = (SHELL_VAR *)NULL;
ac50fbac 125 reset_internal_getopt ();
8868edaf 126 while ((opt = internal_getopt (list, "fnp:")) != -1)
ac50fbac
CR
127 {
128 switch (opt)
129 {
130#if defined (JOB_CONTROL)
131 case 'n':
132 nflag = 1;
133 break;
d233b485
CR
134 case 'f':
135 wflags |= JWAIT_FORCE;
136 break;
8868edaf
CR
137 case 'p':
138 vname = list_optarg;
74091dd4 139 vflags = list_optflags;
8868edaf 140 break;
ac50fbac 141#endif
a0c0a00f 142 CASE_HELPOPT;
ac50fbac
CR
143 default:
144 builtin_usage ();
145 return (EX_USAGE);
146 }
147 }
7117c2d2 148 list = loptend;
726f6388 149
8868edaf
CR
150 /* Sanity-check variable name if -p supplied. */
151 if (vname)
152 {
153#if defined (ARRAY_VARS)
154 int arrayflags;
155
74091dd4 156 SET_VFLAGS (vflags, arrayflags, bindflags);
8868edaf
CR
157 if (legal_identifier (vname) == 0 && valid_array_reference (vname, arrayflags) == 0)
158#else
74091dd4 159 bindflags = 0;
8868edaf 160 if (legal_identifier (vname) == 0)
ac50fbac 161#endif
8868edaf
CR
162 {
163 sh_invalidid (vname);
164 WAIT_RETURN (EXECUTION_FAILURE);
165 }
166 if (builtin_unbind_variable (vname) == -2)
167 WAIT_RETURN (EXECUTION_FAILURE);
168 }
726f6388 169
bb70624e
JA
170 /* POSIX.2 says: When the shell is waiting (by means of the wait utility)
171 for asynchronous commands to complete, the reception of a signal for
172 which a trap has been set shall cause the wait utility to return
173 immediately with an exit status greater than 128, after which the trap
174 associated with the signal shall be taken.
175
176 We handle SIGINT here; it's the only one that needs to be treated
177 specially (I think), since it's handled specially in {no,}jobs.c. */
a0c0a00f
CR
178 wait_intr_flag = 1;
179 code = setjmp_sigs (wait_intr_buf);
180
bb70624e
JA
181 if (code)
182 {
a0c0a00f 183 last_command_exit_signal = wait_signal_received;
7117c2d2 184 status = 128 + wait_signal_received;
a0c0a00f 185 wait_sigint_cleanup ();
74091dd4 186#if defined (JOB_CONTROL)
910fcdc4
CR
187 if (wflags & JWAIT_WAITING)
188 unset_waitlist ();
74091dd4 189#endif
bb70624e
JA
190 WAIT_RETURN (status);
191 }
192
8868edaf
CR
193 opt = first_pending_trap ();
194#if defined (SIGCHLD)
195 /* We special case SIGCHLD when not in posix mode because we don't break
196 out of the wait even when the signal is trapped; we run the trap after
197 the wait completes. See how it's handled in jobs.c:waitchld(). */
198 if (opt == SIGCHLD && posixly_correct == 0)
199 opt = next_pending_trap (opt+1);
200#endif
201 if (opt != -1)
202 {
203 last_command_exit_signal = wait_signal_received = opt;
204 status = opt + 128;
205 WAIT_RETURN (status);
206 }
207
726f6388
JA
208 /* We support jobs or pids.
209 wait <pid-or-job> [pid-or-job ...] */
210
ac50fbac
CR
211#if defined (JOB_CONTROL)
212 if (nflag)
213 {
8868edaf
CR
214 if (list)
215 {
216 opt = set_waitlist (list);
217 if (opt == 0)
218 WAIT_RETURN (127);
219 wflags |= JWAIT_WAITING;
220 }
221
222 status = wait_for_any_job (wflags, &pstat);
8868edaf 223 if (vname && status >= 0)
74091dd4 224 builtin_bind_var_to_int (vname, pstat.pid, bindflags);
ba970fdb
CR
225
226 if (status < 0)
227 status = 127;
8868edaf
CR
228 if (list)
229 unset_waitlist ();
ac50fbac
CR
230 WAIT_RETURN (status);
231 }
232#endif
233
726f6388
JA
234 /* But wait without any arguments means to wait for all of the shell's
235 currently active background processes. */
ccc6cda3 236 if (list == 0)
726f6388 237 {
74091dd4
CR
238 opt = wait_for_background_pids (&pstat);
239#if 0
240 /* Compatibility with NetBSD sh: don't set VNAME since it doesn't
241 correspond to the return status. */
242 if (vname && opt)
243 builtin_bind_var_to_int (vname, pstat.pid, bindflags);
244#endif
ccc6cda3 245 WAIT_RETURN (EXECUTION_SUCCESS);
726f6388
JA
246 }
247
ccc6cda3 248 status = EXECUTION_SUCCESS;
726f6388
JA
249 while (list)
250 {
251 pid_t pid;
252 char *w;
7117c2d2 253 intmax_t pid_value;
726f6388
JA
254
255 w = list->word->word;
f73dda09 256 if (DIGIT (*w))
726f6388 257 {
f73dda09 258 if (legal_number (w, &pid_value) && pid_value == (pid_t)pid_value)
726f6388 259 {
f73dda09 260 pid = (pid_t)pid_value;
d233b485 261 status = wait_for_single_pid (pid, wflags|JWAIT_PERROR);
74091dd4
CR
262 /* status > 256 means pid error */
263 pstat.pid = (status > 256) ? NO_PID : pid;
264 pstat.status = (status > 256) ? 127 : status;
265 if (status > 256)
266 status = 127;
726f6388
JA
267 }
268 else
269 {
7117c2d2 270 sh_badpid (w);
8868edaf
CR
271 pstat.pid = NO_PID;
272 pstat.status = 127;
ccc6cda3 273 WAIT_RETURN (EXECUTION_FAILURE);
726f6388
JA
274 }
275 }
276#if defined (JOB_CONTROL)
b80f6443 277 else if (*w && *w == '%')
726f6388
JA
278 /* Must be a job spec. Check it out. */
279 {
280 int job;
281 sigset_t set, oset;
282
283 BLOCK_CHILD (set, oset);
284 job = get_job_spec (list);
285
95732b49 286 if (INVALID_JOB (job))
726f6388
JA
287 {
288 if (job != DUP_JOB)
7117c2d2 289 sh_badjob (list->word->word);
726f6388
JA
290 UNBLOCK_CHILD (oset);
291 status = 127; /* As per Posix.2, section 4.70.2 */
8868edaf
CR
292 pstat.pid = NO_PID;
293 pstat.status = status;
726f6388
JA
294 list = list->next;
295 continue;
296 }
297
298 /* Job spec used. Wait for the last pid in the pipeline. */
299 UNBLOCK_CHILD (oset);
8868edaf 300 status = wait_for_job (job, wflags, &pstat);
726f6388
JA
301 }
302#endif /* JOB_CONTROL */
303 else
304 {
7117c2d2 305 sh_badpid (w);
8868edaf
CR
306 pstat.pid = NO_PID;
307 pstat.status = 127;
726f6388
JA
308 status = EXECUTION_FAILURE;
309 }
8868edaf
CR
310
311 /* Don't waste time with a longjmp. */
312 if (wait_signal_received)
313 {
314 last_command_exit_signal = wait_signal_received;
315 status = 128 + wait_signal_received;
316 wait_sigint_cleanup ();
317 WAIT_RETURN (status);
318 }
319
726f6388
JA
320 list = list->next;
321 }
ccc6cda3 322
74091dd4
CR
323 if (vname && pstat.pid != NO_PID)
324 builtin_bind_var_to_int (vname, pstat.pid, bindflags);
325
ccc6cda3 326 WAIT_RETURN (status);
726f6388 327}
8868edaf
CR
328
329#if defined (JOB_CONTROL)
330/* Take each valid pid or jobspec in LIST and mark the corresponding job as
331 J_WAITING, so wait -n knows which jobs to wait for. Return the number of
332 jobs we found. */
333static int
334set_waitlist (list)
335 WORD_LIST *list;
336{
337 sigset_t set, oset;
338 int job, r, njob;
339 intmax_t pid;
340 WORD_LIST *l;
341
342 BLOCK_CHILD (set, oset);
343 njob = 0;
344 for (l = list; l; l = l->next)
345 {
346 job = NO_JOB;
347 job = (l && legal_number (l->word->word, &pid) && pid == (pid_t) pid)
348 ? get_job_by_pid ((pid_t) pid, 0, 0)
349 : get_job_spec (l);
350 if (job == NO_JOB || jobs == 0 || INVALID_JOB (job))
351 {
352 sh_badjob (l->word->word);
353 continue;
354 }
355 /* We don't check yet to see if one of the desired jobs has already
356 terminated, but we could. We wait until wait_for_any_job(). This
357 has the advantage of validating all the arguments. */
358 if ((jobs[job]->flags & J_WAITING) == 0)
359 {
360 njob++;
361 jobs[job]->flags |= J_WAITING;
362 }
363 }
364 UNBLOCK_CHILD (oset);
365 return (njob);
366}
367
368/* Clean up after a call to wait -n jobs */
369static void
370unset_waitlist ()
371{
372 int i;
373 sigset_t set, oset;
374
375 BLOCK_CHILD (set, oset);
376 for (i = 0; i < js.j_jobslots; i++)
377 if (jobs[i] && (jobs[i]->flags & J_WAITING))
378 jobs[i]->flags &= ~J_WAITING;
379 UNBLOCK_CHILD (oset);
380}
381#endif