]>
Commit | Line | Data |
---|---|---|
74091dd4 | 1 | 'This file is wait.def, from which is created wait.c. |
726f6388 JA |
2 | It implements the builtin "wait" in Bash. |
3 | ||
74091dd4 | 4 | Copyright (C) 1987-2021 Free Software Foundation, Inc. |
726f6388 JA |
5 | |
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
3185942a JA |
8 | Bash is free software: you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation, either version 3 of the License, or | |
11 | (at your option) any later version. | |
726f6388 | 12 | |
3185942a JA |
13 | Bash is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
726f6388 | 17 | |
3185942a JA |
18 | You should have received a copy of the GNU General Public License |
19 | along 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 |
26 | Wait for job completion and return exit status. |
27 | ||
ac50fbac | 28 | Waits for each process identified by an ID, which may be a process ID or a |
3185942a JA |
29 | job specification, and reports its termination status. If ID is not |
30 | given, waits for all currently active child processes, and the return | |
d233b485 | 31 | status is zero. If ID is a job specification, waits for all processes |
ac50fbac CR |
32 | in that job's pipeline. |
33 | ||
8868edaf CR |
34 | If the -n option is supplied, waits for a single job from the list of IDs, |
35 | or, if no IDs are supplied, for the next job to complete and returns its | |
36 | exit status. | |
37 | ||
38 | If the -p option is supplied, the process or job identifier of the job | |
39 | for which the exit status is returned is assigned to the variable VAR | |
40 | named by the option argument. The variable will be unset initially, before | |
41 | any assignment. This is useful only when the -n option is supplied. | |
3185942a | 42 | |
d233b485 CR |
43 | If the -f option is supplied, and job control is enabled, waits for the |
44 | specified ID to terminate, instead of waiting for it to change status. | |
45 | ||
3185942a | 46 | Exit Status: |
ac50fbac | 47 | Returns the status of the last ID; fails if ID is invalid or an invalid |
8868edaf CR |
48 | option is given, or if -n is supplied and the shell has no unwaited-for |
49 | children. | |
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 |
56 | Wait for process completion and return exit status. |
57 | ||
ac50fbac CR |
58 | Waits for each process specified by a PID and reports its termination status. |
59 | If PID is not given, waits for all currently active child processes, | |
60 | and the return status is zero. PID must be a process ID. | |
3185942a JA |
61 | |
62 | Exit Status: | |
ac50fbac CR |
63 | Returns the status of the last PID; fails if PID is invalid or an invalid |
64 | option 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 | 88 | extern int wait_signal_received; |
726f6388 | 89 | |
bb70624e | 90 | procenv_t wait_intr_buf; |
a0c0a00f | 91 | int wait_intr_flag; |
bb70624e | 92 | |
8868edaf CR |
93 | static int set_waitlist PARAMS((WORD_LIST *)); |
94 | static 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 | |
110 | int | |
726f6388 JA |
111 | wait_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. */ | |
333 | static int | |
334 | set_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 */ | |
369 | static void | |
370 | unset_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 |