]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* trap.c -- Not the trap command, but useful functions for manipulating |
2 | those objects. The trap command is in builtins/trap.def. */ | |
3 | ||
7117c2d2 | 4 | /* Copyright (C) 1987-2002 Free Software Foundation, Inc. |
726f6388 JA |
5 | |
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
8 | Bash is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
bb70624e | 10 | Software Foundation; either version 2, or (at your option) any later |
726f6388 JA |
11 | version. |
12 | ||
13 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License along | |
19 | with Bash; see the file COPYING. If not, write to the Free Software | |
bb70624e | 20 | Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ |
726f6388 | 21 | |
ccc6cda3 JA |
22 | #include "config.h" |
23 | ||
e8ce775d JA |
24 | #if defined (HAVE_UNISTD_H) |
25 | # include <unistd.h> | |
26 | #endif | |
27 | ||
726f6388 | 28 | #include "bashtypes.h" |
d166f048 | 29 | #include "bashansi.h" |
726f6388 | 30 | |
cce855bc | 31 | #include <stdio.h> |
bb70624e | 32 | #include <errno.h> |
cce855bc JA |
33 | |
34 | #include "trap.h" | |
35 | ||
726f6388 | 36 | #include "shell.h" |
bb70624e | 37 | #include "input.h" /* for save_token_state, restore_token_state */ |
726f6388 | 38 | #include "signames.h" |
7117c2d2 | 39 | #include "builtins.h" |
ccc6cda3 | 40 | #include "builtins/common.h" |
7117c2d2 | 41 | #include "builtins/builtext.h" |
726f6388 | 42 | |
bb70624e JA |
43 | #ifndef errno |
44 | extern int errno; | |
45 | #endif | |
46 | ||
726f6388 JA |
47 | /* Flags which describe the current handling state of a signal. */ |
48 | #define SIG_INHERITED 0x0 /* Value inherited from parent. */ | |
49 | #define SIG_TRAPPED 0x1 /* Currently trapped. */ | |
50 | #define SIG_HARD_IGNORE 0x2 /* Signal was ignored on shell entry. */ | |
51 | #define SIG_SPECIAL 0x4 /* Treat this signal specially. */ | |
52 | #define SIG_NO_TRAP 0x8 /* Signal cannot be trapped. */ | |
ccc6cda3 JA |
53 | #define SIG_INPROGRESS 0x10 /* Signal handler currently executing. */ |
54 | #define SIG_CHANGED 0x20 /* Trap value changed in trap handler. */ | |
55 | #define SIG_IGNORED 0x40 /* The signal is currently being ignored. */ | |
726f6388 | 56 | |
f73dda09 JA |
57 | #define SPECIAL_TRAP(s) ((s) == EXIT_TRAP || (s) == DEBUG_TRAP || (s) == ERROR_TRAP) |
58 | ||
726f6388 | 59 | /* An array of such flags, one for each signal, describing what the |
ccc6cda3 JA |
60 | shell will do with a signal. DEBUG_TRAP == NSIG; some code below |
61 | assumes this. */ | |
f73dda09 JA |
62 | static int sigmodes[BASH_NSIG]; |
63 | ||
64 | static void free_trap_command __P((int)); | |
65 | static void change_signal __P((int, char *)); | |
66 | ||
67 | static void get_original_signal __P((int)); | |
68 | ||
69 | static void _run_trap_internal __P((int, char *)); | |
726f6388 | 70 | |
f73dda09 JA |
71 | static void reset_signal __P((int)); |
72 | static void restore_signal __P((int)); | |
73 | static void reset_or_restore_signal_handlers __P((sh_resetsig_func_t *)); | |
726f6388 JA |
74 | |
75 | /* Variables used here but defined in other files. */ | |
726f6388 JA |
76 | extern int interrupt_immediately; |
77 | extern int last_command_exit_value; | |
d166f048 | 78 | extern int line_number; |
726f6388 | 79 | |
7117c2d2 JA |
80 | extern sh_builtin_func_t *this_shell_builtin; |
81 | extern procenv_t wait_intr_buf; | |
82 | ||
726f6388 JA |
83 | /* The list of things to do originally, before we started trapping. */ |
84 | SigHandler *original_signals[NSIG]; | |
85 | ||
86 | /* For each signal, a slot for a string, which is a command to be | |
87 | executed when that signal is recieved. The slot can also contain | |
88 | DEFAULT_SIG, which means do whatever you were going to do before | |
89 | you were so rudely interrupted, or IGNORE_SIG, which says ignore | |
90 | this signal. */ | |
f73dda09 | 91 | char *trap_list[BASH_NSIG]; |
726f6388 JA |
92 | |
93 | /* A bitmap of signals received for which we have trap handlers. */ | |
94 | int pending_traps[NSIG]; | |
95 | ||
ccc6cda3 JA |
96 | /* Set to the number of the signal we're running the trap for + 1. |
97 | Used in execute_cmd.c and builtins/common.c to clean up when | |
98 | parse_and_execute does not return normally after executing the | |
99 | trap command (e.g., when `return' is executed in the trap command). */ | |
100 | int running_trap; | |
101 | ||
d166f048 JA |
102 | /* The value of line_number when the trap started executing, since |
103 | parse_and_execute resets it to 1 and the trap command might want | |
104 | it. */ | |
105 | int trap_line_number; | |
106 | ||
7117c2d2 JA |
107 | /* The (trapped) signal received while executing in the `wait' builtin */ |
108 | int wait_signal_received; | |
109 | ||
726f6388 JA |
110 | /* A value which can never be the target of a trap handler. */ |
111 | #define IMPOSSIBLE_TRAP_HANDLER (SigHandler *)initialize_traps | |
112 | ||
113 | void | |
114 | initialize_traps () | |
115 | { | |
116 | register int i; | |
117 | ||
f73dda09 JA |
118 | trap_list[EXIT_TRAP] = trap_list[DEBUG_TRAP] = trap_list[ERROR_TRAP] = (char *)NULL; |
119 | sigmodes[EXIT_TRAP] = sigmodes[DEBUG_TRAP] = sigmodes[ERROR_TRAP] = SIG_INHERITED; | |
ccc6cda3 | 120 | original_signals[EXIT_TRAP] = IMPOSSIBLE_TRAP_HANDLER; |
726f6388 JA |
121 | |
122 | for (i = 1; i < NSIG; i++) | |
123 | { | |
124 | pending_traps[i] = 0; | |
125 | trap_list[i] = (char *)DEFAULT_SIG; | |
126 | sigmodes[i] = SIG_INHERITED; | |
127 | original_signals[i] = IMPOSSIBLE_TRAP_HANDLER; | |
128 | } | |
129 | ||
130 | /* Show which signals are treated specially by the shell. */ | |
131 | #if defined (SIGCHLD) | |
ccc6cda3 JA |
132 | original_signals[SIGCHLD] = |
133 | (SigHandler *) set_signal_handler (SIGCHLD, SIG_DFL); | |
726f6388 JA |
134 | set_signal_handler (SIGCHLD, original_signals[SIGCHLD]); |
135 | sigmodes[SIGCHLD] |= (SIG_SPECIAL | SIG_NO_TRAP); | |
136 | #endif /* SIGCHLD */ | |
137 | ||
138 | original_signals[SIGINT] = | |
139 | (SigHandler *) set_signal_handler (SIGINT, SIG_DFL); | |
140 | set_signal_handler (SIGINT, original_signals[SIGINT]); | |
141 | sigmodes[SIGINT] |= SIG_SPECIAL; | |
142 | ||
b72432fd JA |
143 | #if defined (__BEOS__) |
144 | /* BeOS sets SIGINT to SIG_IGN! */ | |
145 | original_signals[SIGINT] = SIG_DFL; | |
146 | #endif | |
147 | ||
726f6388 JA |
148 | original_signals[SIGQUIT] = |
149 | (SigHandler *) set_signal_handler (SIGQUIT, SIG_DFL); | |
150 | set_signal_handler (SIGQUIT, original_signals[SIGQUIT]); | |
151 | sigmodes[SIGQUIT] |= SIG_SPECIAL; | |
152 | ||
153 | if (interactive) | |
154 | { | |
ccc6cda3 JA |
155 | original_signals[SIGTERM] = |
156 | (SigHandler *)set_signal_handler (SIGTERM, SIG_DFL); | |
726f6388 JA |
157 | set_signal_handler (SIGTERM, original_signals[SIGTERM]); |
158 | sigmodes[SIGTERM] |= SIG_SPECIAL; | |
159 | } | |
160 | } | |
161 | ||
bb70624e JA |
162 | #ifdef INCLUDE_UNUSED |
163 | /* Return a printable representation of the trap handler for SIG. */ | |
164 | static char * | |
165 | trap_handler_string (sig) | |
166 | int sig; | |
167 | { | |
168 | if (trap_list[sig] == (char *)DEFAULT_SIG) | |
169 | return "DEFAULT_SIG"; | |
170 | else if (trap_list[sig] == (char *)IGNORE_SIG) | |
171 | return "IGNORE_SIG"; | |
172 | else if (trap_list[sig] == (char *)IMPOSSIBLE_TRAP_HANDLER) | |
173 | return "IMPOSSIBLE_TRAP_HANDLER"; | |
174 | else if (trap_list[sig]) | |
175 | return trap_list[sig]; | |
176 | else | |
177 | return "NULL"; | |
178 | } | |
179 | #endif | |
180 | ||
726f6388 JA |
181 | /* Return the print name of this signal. */ |
182 | char * | |
183 | signal_name (sig) | |
184 | int sig; | |
185 | { | |
cce855bc JA |
186 | char *ret; |
187 | ||
188 | /* on cygwin32, signal_names[sig] could be null */ | |
7117c2d2 | 189 | ret = (sig >= BASH_NSIG || sig < 0) ? "bad signal number" : signal_names[sig]; |
cce855bc JA |
190 | if (ret == NULL) |
191 | ret = "unrecognized signal number"; | |
192 | return ret; | |
726f6388 JA |
193 | } |
194 | ||
195 | /* Turn a string into a signal number, or a number into | |
196 | a signal number. If STRING is "2", "SIGINT", or "INT", | |
197 | then (int)2 is returned. Return NO_SIG if STRING doesn't | |
198 | contain a valid signal descriptor. */ | |
199 | int | |
200 | decode_signal (string) | |
201 | char *string; | |
202 | { | |
7117c2d2 | 203 | intmax_t sig; |
726f6388 | 204 | |
ccc6cda3 | 205 | if (legal_number (string, &sig)) |
f73dda09 | 206 | return ((sig >= 0 && sig < NSIG) ? (int)sig : NO_SIG); |
726f6388 | 207 | |
e8ce775d | 208 | /* A leading `SIG' may be omitted. */ |
f73dda09 | 209 | for (sig = 0; sig < BASH_NSIG; sig++) |
b72432fd JA |
210 | { |
211 | if (signal_names[sig] == 0 || signal_names[sig][0] == '\0') | |
212 | continue; | |
213 | if (strcasecmp (string, signal_names[sig]) == 0 || | |
214 | (STREQN (signal_names[sig], "SIG", 3) && | |
215 | strcasecmp (string, &(signal_names[sig])[3]) == 0)) | |
216 | return ((int)sig); | |
217 | } | |
726f6388 JA |
218 | |
219 | return (NO_SIG); | |
220 | } | |
221 | ||
222 | /* Non-zero when we catch a trapped signal. */ | |
ccc6cda3 | 223 | static int catch_flag; |
726f6388 JA |
224 | |
225 | void | |
226 | run_pending_traps () | |
227 | { | |
228 | register int sig; | |
bb70624e | 229 | int old_exit_value, *token_state; |
726f6388 JA |
230 | |
231 | if (catch_flag == 0) /* simple optimization */ | |
232 | return; | |
233 | ||
234 | catch_flag = 0; | |
235 | ||
236 | /* Preserve $? when running trap. */ | |
237 | old_exit_value = last_command_exit_value; | |
238 | ||
239 | for (sig = 1; sig < NSIG; sig++) | |
240 | { | |
241 | /* XXX this could be made into a counter by using | |
28ef6c31 | 242 | while (pending_traps[sig]--) instead of the if statement. */ |
726f6388 JA |
243 | if (pending_traps[sig]) |
244 | { | |
ccc6cda3 | 245 | #if defined (HAVE_POSIX_SIGNALS) |
726f6388 JA |
246 | sigset_t set, oset; |
247 | ||
248 | sigemptyset (&set); | |
249 | sigemptyset (&oset); | |
250 | ||
251 | sigaddset (&set, sig); | |
252 | sigprocmask (SIG_BLOCK, &set, &oset); | |
253 | #else | |
254 | # if defined (HAVE_BSD_SIGNALS) | |
255 | int oldmask = sigblock (sigmask (sig)); | |
256 | # endif | |
ccc6cda3 | 257 | #endif /* HAVE_POSIX_SIGNALS */ |
726f6388 JA |
258 | |
259 | if (sig == SIGINT) | |
260 | { | |
261 | run_interrupt_trap (); | |
ccc6cda3 | 262 | CLRINTERRUPT; |
726f6388 | 263 | } |
bb70624e JA |
264 | else if (trap_list[sig] == (char *)DEFAULT_SIG || |
265 | trap_list[sig] == (char *)IGNORE_SIG || | |
266 | trap_list[sig] == (char *)IMPOSSIBLE_TRAP_HANDLER) | |
267 | { | |
268 | /* This is possible due to a race condition. Say a bash | |
269 | process has SIGTERM trapped. A subshell is spawned | |
270 | using { list; } & and the parent does something and kills | |
271 | the subshell with SIGTERM. It's possible for the subshell | |
272 | to set pending_traps[SIGTERM] to 1 before the code in | |
273 | execute_cmd.c eventually calls restore_original_signals | |
274 | to reset the SIGTERM signal handler in the subshell. The | |
275 | next time run_pending_traps is called, pending_traps[SIGTERM] | |
276 | will be 1, but the trap handler in trap_list[SIGTERM] will | |
277 | be invalid (probably DEFAULT_SIG, but it could be IGNORE_SIG). | |
278 | Unless we catch this, the subshell will dump core when | |
279 | trap_list[SIGTERM] == DEFAULT_SIG, because DEFAULT_SIG is | |
280 | usually 0x0. */ | |
f73dda09 JA |
281 | internal_warning ("run_pending_traps: bad value in trap_list[%d]: %p", |
282 | sig, trap_list[sig]); | |
bb70624e JA |
283 | if (trap_list[sig] == (char *)DEFAULT_SIG) |
284 | { | |
285 | internal_warning ("run_pending_traps: signal handler is SIG_DFL, resending %d (%s) to myself", sig, signal_name (sig)); | |
286 | kill (getpid (), sig); | |
287 | } | |
288 | } | |
726f6388 | 289 | else |
bb70624e JA |
290 | { |
291 | token_state = save_token_state (); | |
292 | parse_and_execute (savestring (trap_list[sig]), "trap", SEVAL_NONINT|SEVAL_NOHIST); | |
293 | restore_token_state (token_state); | |
294 | free (token_state); | |
295 | } | |
726f6388 JA |
296 | |
297 | pending_traps[sig] = 0; | |
298 | ||
ccc6cda3 | 299 | #if defined (HAVE_POSIX_SIGNALS) |
726f6388 JA |
300 | sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL); |
301 | #else | |
302 | # if defined (HAVE_BSD_SIGNALS) | |
303 | sigsetmask (oldmask); | |
304 | # endif | |
305 | #endif /* POSIX_VERSION */ | |
306 | } | |
307 | } | |
308 | ||
309 | last_command_exit_value = old_exit_value; | |
310 | } | |
311 | ||
312 | sighandler | |
313 | trap_handler (sig) | |
314 | int sig; | |
315 | { | |
bb70624e JA |
316 | int oerrno; |
317 | ||
726f6388 JA |
318 | if ((sig >= NSIG) || |
319 | (trap_list[sig] == (char *)DEFAULT_SIG) || | |
320 | (trap_list[sig] == (char *)IGNORE_SIG)) | |
ccc6cda3 | 321 | programming_error ("trap_handler: bad signal %d", sig); |
726f6388 JA |
322 | else |
323 | { | |
28ef6c31 | 324 | oerrno = errno; |
ccc6cda3 | 325 | #if defined (MUST_REINSTALL_SIGHANDLERS) |
726f6388 | 326 | set_signal_handler (sig, trap_handler); |
ccc6cda3 | 327 | #endif /* MUST_REINSTALL_SIGHANDLERS */ |
726f6388 JA |
328 | |
329 | catch_flag = 1; | |
330 | pending_traps[sig]++; | |
331 | ||
7117c2d2 JA |
332 | if (interrupt_immediately && this_shell_builtin && (this_shell_builtin == wait_builtin)) |
333 | { | |
334 | wait_signal_received = sig; | |
335 | longjmp (wait_intr_buf, 1); | |
336 | } | |
337 | ||
726f6388 JA |
338 | if (interrupt_immediately) |
339 | run_pending_traps (); | |
bb70624e JA |
340 | |
341 | errno = oerrno; | |
726f6388 | 342 | } |
ccc6cda3 JA |
343 | |
344 | SIGRETURN (0); | |
726f6388 JA |
345 | } |
346 | ||
347 | #if defined (JOB_CONTROL) && defined (SIGCHLD) | |
cce855bc JA |
348 | |
349 | #ifdef INCLUDE_UNUSED | |
726f6388 JA |
350 | /* Make COMMAND_STRING be executed when SIGCHLD is caught. */ |
351 | void | |
352 | set_sigchld_trap (command_string) | |
353 | char *command_string; | |
354 | { | |
726f6388 JA |
355 | set_signal (SIGCHLD, command_string); |
356 | } | |
cce855bc | 357 | #endif |
726f6388 JA |
358 | |
359 | /* Make COMMAND_STRING be executed when SIGCHLD is caught iff the current | |
360 | SIGCHLD trap handler is DEFAULT_SIG. */ | |
361 | void | |
362 | maybe_set_sigchld_trap (command_string) | |
363 | char *command_string; | |
364 | { | |
726f6388 JA |
365 | if ((sigmodes[SIGCHLD] & SIG_TRAPPED) == 0) |
366 | set_signal (SIGCHLD, command_string); | |
367 | } | |
368 | #endif /* JOB_CONTROL && SIGCHLD */ | |
369 | ||
ccc6cda3 JA |
370 | void |
371 | set_debug_trap (command) | |
726f6388 JA |
372 | char *command; |
373 | { | |
ccc6cda3 JA |
374 | set_signal (DEBUG_TRAP, command); |
375 | } | |
726f6388 | 376 | |
f73dda09 JA |
377 | void |
378 | set_error_trap (command) | |
379 | char *command; | |
380 | { | |
381 | set_signal (ERROR_TRAP, command); | |
382 | } | |
383 | ||
cce855bc | 384 | #ifdef INCLUDE_UNUSED |
ccc6cda3 JA |
385 | void |
386 | set_sigint_trap (command) | |
387 | char *command; | |
388 | { | |
726f6388 JA |
389 | set_signal (SIGINT, command); |
390 | } | |
cce855bc | 391 | #endif |
726f6388 JA |
392 | |
393 | /* Reset the SIGINT handler so that subshells that are doing `shellsy' | |
394 | things, like waiting for command substitution or executing commands | |
395 | in explicit subshells ( ( cmd ) ), can catch interrupts properly. */ | |
396 | SigHandler * | |
397 | set_sigint_handler () | |
398 | { | |
399 | if (sigmodes[SIGINT] & SIG_HARD_IGNORE) | |
400 | return ((SigHandler *)SIG_IGN); | |
401 | ||
402 | else if (sigmodes[SIGINT] & SIG_IGNORED) | |
ccc6cda3 JA |
403 | return ((SigHandler *)set_signal_handler (SIGINT, SIG_IGN)); /* XXX */ |
404 | ||
726f6388 JA |
405 | else if (sigmodes[SIGINT] & SIG_TRAPPED) |
406 | return ((SigHandler *)set_signal_handler (SIGINT, trap_handler)); | |
407 | ||
408 | /* The signal is not trapped, so set the handler to the shell's special | |
409 | interrupt handler. */ | |
410 | else if (interactive) /* XXX - was interactive_shell */ | |
411 | return (set_signal_handler (SIGINT, sigint_sighandler)); | |
412 | else | |
413 | return (set_signal_handler (SIGINT, termination_unwind_protect)); | |
414 | } | |
415 | ||
e8ce775d JA |
416 | /* Return the correct handler for signal SIG according to the values in |
417 | sigmodes[SIG]. */ | |
418 | SigHandler * | |
419 | trap_to_sighandler (sig) | |
420 | int sig; | |
421 | { | |
422 | if (sigmodes[sig] & (SIG_IGNORED|SIG_HARD_IGNORE)) | |
423 | return (SIG_IGN); | |
424 | else if (sigmodes[sig] & SIG_TRAPPED) | |
425 | return (trap_handler); | |
426 | else | |
427 | return (SIG_DFL); | |
428 | } | |
429 | ||
726f6388 JA |
430 | /* Set SIG to call STRING as a command. */ |
431 | void | |
432 | set_signal (sig, string) | |
433 | int sig; | |
434 | char *string; | |
435 | { | |
f73dda09 | 436 | if (SPECIAL_TRAP (sig)) |
ccc6cda3 JA |
437 | { |
438 | change_signal (sig, savestring (string)); | |
d166f048 JA |
439 | if (sig == EXIT_TRAP && interactive == 0) |
440 | initialize_terminating_signals (); | |
ccc6cda3 JA |
441 | return; |
442 | } | |
443 | ||
726f6388 JA |
444 | /* A signal ignored on entry to the shell cannot be trapped or reset, but |
445 | no error is reported when attempting to do so. -- Posix.2 */ | |
446 | if (sigmodes[sig] & SIG_HARD_IGNORE) | |
447 | return; | |
448 | ||
449 | /* Make sure we have original_signals[sig] if the signal has not yet | |
450 | been trapped. */ | |
451 | if ((sigmodes[sig] & SIG_TRAPPED) == 0) | |
452 | { | |
453 | /* If we aren't sure of the original value, check it. */ | |
454 | if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) | |
455 | { | |
456 | original_signals[sig] = (SigHandler *)set_signal_handler (sig, SIG_DFL); | |
457 | set_signal_handler (sig, original_signals[sig]); | |
458 | } | |
459 | ||
460 | /* Signals ignored on entry to the shell cannot be trapped or reset. */ | |
461 | if (original_signals[sig] == SIG_IGN) | |
462 | { | |
463 | sigmodes[sig] |= SIG_HARD_IGNORE; | |
464 | return; | |
465 | } | |
466 | } | |
467 | ||
468 | /* Only change the system signal handler if SIG_NO_TRAP is not set. | |
469 | The trap command string is changed in either case. The shell signal | |
470 | handlers for SIGINT and SIGCHLD run the user specified traps in an | |
471 | environment in which it is safe to do so. */ | |
472 | if ((sigmodes[sig] & SIG_NO_TRAP) == 0) | |
473 | { | |
474 | set_signal_handler (sig, SIG_IGN); | |
475 | change_signal (sig, savestring (string)); | |
476 | set_signal_handler (sig, trap_handler); | |
477 | } | |
478 | else | |
479 | change_signal (sig, savestring (string)); | |
480 | } | |
481 | ||
482 | static void | |
483 | free_trap_command (sig) | |
484 | int sig; | |
485 | { | |
486 | if ((sigmodes[sig] & SIG_TRAPPED) && trap_list[sig] && | |
487 | (trap_list[sig] != (char *)IGNORE_SIG) && | |
488 | (trap_list[sig] != (char *)DEFAULT_SIG) && | |
489 | (trap_list[sig] != (char *)IMPOSSIBLE_TRAP_HANDLER)) | |
490 | free (trap_list[sig]); | |
491 | } | |
ccc6cda3 | 492 | |
726f6388 JA |
493 | /* If SIG has a string assigned to it, get rid of it. Then give it |
494 | VALUE. */ | |
495 | static void | |
496 | change_signal (sig, value) | |
497 | int sig; | |
498 | char *value; | |
499 | { | |
d166f048 JA |
500 | if ((sigmodes[sig] & SIG_INPROGRESS) == 0) |
501 | free_trap_command (sig); | |
726f6388 JA |
502 | trap_list[sig] = value; |
503 | ||
504 | sigmodes[sig] |= SIG_TRAPPED; | |
505 | if (value == (char *)IGNORE_SIG) | |
506 | sigmodes[sig] |= SIG_IGNORED; | |
507 | else | |
508 | sigmodes[sig] &= ~SIG_IGNORED; | |
509 | if (sigmodes[sig] & SIG_INPROGRESS) | |
510 | sigmodes[sig] |= SIG_CHANGED; | |
511 | } | |
512 | ||
513 | #define GET_ORIGINAL_SIGNAL(sig) \ | |
514 | if (sig && sig < NSIG && original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) \ | |
515 | get_original_signal (sig) | |
516 | ||
517 | static void | |
518 | get_original_signal (sig) | |
519 | int sig; | |
520 | { | |
521 | /* If we aren't sure the of the original value, then get it. */ | |
522 | if (original_signals[sig] == (SigHandler *)IMPOSSIBLE_TRAP_HANDLER) | |
523 | { | |
524 | original_signals[sig] = | |
525 | (SigHandler *) set_signal_handler (sig, SIG_DFL); | |
526 | set_signal_handler (sig, original_signals[sig]); | |
527 | ||
528 | /* Signals ignored on entry to the shell cannot be trapped. */ | |
529 | if (original_signals[sig] == SIG_IGN) | |
530 | sigmodes[sig] |= SIG_HARD_IGNORE; | |
531 | } | |
532 | } | |
533 | ||
534 | /* Restore the default action for SIG; i.e., the action the shell | |
535 | would have taken before you used the trap command. This is called | |
536 | from trap_builtin (), which takes care to restore the handlers for | |
537 | the signals the shell treats specially. */ | |
538 | void | |
539 | restore_default_signal (sig) | |
540 | int sig; | |
541 | { | |
f73dda09 | 542 | if (SPECIAL_TRAP (sig)) |
726f6388 | 543 | { |
f73dda09 | 544 | if ((sig != DEBUG_TRAP && sig != ERROR_TRAP) || (sigmodes[sig] & SIG_INPROGRESS) == 0) |
d166f048 | 545 | free_trap_command (sig); |
726f6388 JA |
546 | trap_list[sig] = (char *)NULL; |
547 | sigmodes[sig] &= ~SIG_TRAPPED; | |
d166f048 JA |
548 | if (sigmodes[sig] & SIG_INPROGRESS) |
549 | sigmodes[sig] |= SIG_CHANGED; | |
726f6388 JA |
550 | return; |
551 | } | |
552 | ||
553 | GET_ORIGINAL_SIGNAL (sig); | |
554 | ||
555 | /* A signal ignored on entry to the shell cannot be trapped or reset, but | |
556 | no error is reported when attempting to do so. Thanks Posix.2. */ | |
557 | if (sigmodes[sig] & SIG_HARD_IGNORE) | |
558 | return; | |
559 | ||
560 | /* If we aren't trapping this signal, don't bother doing anything else. */ | |
ccc6cda3 | 561 | if ((sigmodes[sig] & SIG_TRAPPED) == 0) |
726f6388 JA |
562 | return; |
563 | ||
564 | /* Only change the signal handler for SIG if it allows it. */ | |
ccc6cda3 | 565 | if ((sigmodes[sig] & SIG_NO_TRAP) == 0) |
726f6388 JA |
566 | set_signal_handler (sig, original_signals[sig]); |
567 | ||
568 | /* Change the trap command in either case. */ | |
569 | change_signal (sig, (char *)DEFAULT_SIG); | |
570 | ||
571 | /* Mark the signal as no longer trapped. */ | |
572 | sigmodes[sig] &= ~SIG_TRAPPED; | |
573 | } | |
574 | ||
575 | /* Make this signal be ignored. */ | |
576 | void | |
577 | ignore_signal (sig) | |
578 | int sig; | |
579 | { | |
f73dda09 | 580 | if (SPECIAL_TRAP (sig) && ((sigmodes[sig] & SIG_IGNORED) == 0)) |
ccc6cda3 JA |
581 | { |
582 | change_signal (sig, (char *)IGNORE_SIG); | |
583 | return; | |
584 | } | |
585 | ||
726f6388 JA |
586 | GET_ORIGINAL_SIGNAL (sig); |
587 | ||
588 | /* A signal ignored on entry to the shell cannot be trapped or reset. | |
ccc6cda3 | 589 | No error is reported when the user attempts to do so. */ |
726f6388 JA |
590 | if (sigmodes[sig] & SIG_HARD_IGNORE) |
591 | return; | |
592 | ||
593 | /* If already trapped and ignored, no change necessary. */ | |
ccc6cda3 | 594 | if (sigmodes[sig] & SIG_IGNORED) |
726f6388 JA |
595 | return; |
596 | ||
597 | /* Only change the signal handler for SIG if it allows it. */ | |
ccc6cda3 | 598 | if ((sigmodes[sig] & SIG_NO_TRAP) == 0) |
726f6388 JA |
599 | set_signal_handler (sig, SIG_IGN); |
600 | ||
601 | /* Change the trap command in either case. */ | |
602 | change_signal (sig, (char *)IGNORE_SIG); | |
603 | } | |
604 | ||
605 | /* Handle the calling of "trap 0". The only sticky situation is when | |
606 | the command to be executed includes an "exit". This is why we have | |
607 | to provide our own place for top_level to jump to. */ | |
608 | int | |
609 | run_exit_trap () | |
610 | { | |
ccc6cda3 JA |
611 | char *trap_command; |
612 | int code, old_exit_value; | |
726f6388 JA |
613 | |
614 | old_exit_value = last_command_exit_value; | |
615 | ||
ccc6cda3 JA |
616 | /* Run the trap only if signal 0 is trapped and not ignored, and we are not |
617 | currently running in the trap handler (call to exit in the list of | |
618 | commands given to trap 0). */ | |
619 | if ((sigmodes[EXIT_TRAP] & SIG_TRAPPED) && | |
620 | (sigmodes[EXIT_TRAP] & (SIG_IGNORED|SIG_INPROGRESS)) == 0) | |
726f6388 | 621 | { |
ccc6cda3 JA |
622 | trap_command = savestring (trap_list[EXIT_TRAP]); |
623 | sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED; | |
624 | sigmodes[EXIT_TRAP] |= SIG_INPROGRESS; | |
726f6388 JA |
625 | |
626 | code = setjmp (top_level); | |
627 | ||
628 | if (code == 0) | |
cce855bc JA |
629 | { |
630 | reset_parser (); | |
631 | parse_and_execute (trap_command, "exit trap", SEVAL_NONINT|SEVAL_NOHIST); | |
632 | } | |
726f6388 | 633 | else if (code == EXITPROG) |
28ef6c31 | 634 | return (last_command_exit_value); |
726f6388 JA |
635 | else |
636 | return (old_exit_value); | |
637 | } | |
638 | ||
639 | return (old_exit_value); | |
640 | } | |
641 | ||
ccc6cda3 JA |
642 | void |
643 | run_trap_cleanup (sig) | |
644 | int sig; | |
645 | { | |
646 | sigmodes[sig] &= ~(SIG_INPROGRESS|SIG_CHANGED); | |
647 | } | |
648 | ||
649 | /* Run a trap command for SIG. SIG is one of the signals the shell treats | |
650 | specially. */ | |
726f6388 | 651 | static void |
ccc6cda3 | 652 | _run_trap_internal (sig, tag) |
726f6388 | 653 | int sig; |
ccc6cda3 | 654 | char *tag; |
726f6388 | 655 | { |
ccc6cda3 | 656 | char *trap_command, *old_trap; |
f73dda09 | 657 | int old_exit_value, *token_state; |
ccc6cda3 JA |
658 | |
659 | /* Run the trap only if SIG is trapped and not ignored, and we are not | |
660 | currently executing in the trap handler. */ | |
661 | if ((sigmodes[sig] & SIG_TRAPPED) && ((sigmodes[sig] & SIG_IGNORED) == 0) && | |
662 | (trap_list[sig] != (char *)IMPOSSIBLE_TRAP_HANDLER) && | |
663 | ((sigmodes[sig] & SIG_INPROGRESS) == 0)) | |
664 | { | |
665 | old_trap = trap_list[sig]; | |
666 | sigmodes[sig] |= SIG_INPROGRESS; | |
667 | sigmodes[sig] &= ~SIG_CHANGED; /* just to be sure */ | |
668 | trap_command = savestring (old_trap); | |
669 | ||
670 | running_trap = sig + 1; | |
671 | old_exit_value = last_command_exit_value; | |
d166f048 JA |
672 | /* Need to copy the value of line_number because parse_and_execute |
673 | resets it to 1, and the trap command might want it. */ | |
674 | trap_line_number = line_number; | |
bb70624e JA |
675 | |
676 | token_state = save_token_state (); | |
d166f048 | 677 | parse_and_execute (trap_command, tag, SEVAL_NONINT|SEVAL_NOHIST); |
bb70624e JA |
678 | restore_token_state (token_state); |
679 | free (token_state); | |
680 | ||
ccc6cda3 JA |
681 | last_command_exit_value = old_exit_value; |
682 | running_trap = 0; | |
683 | ||
684 | sigmodes[sig] &= ~SIG_INPROGRESS; | |
685 | ||
686 | if (sigmodes[sig] & SIG_CHANGED) | |
687 | { | |
688 | free (old_trap); | |
689 | sigmodes[sig] &= ~SIG_CHANGED; | |
690 | } | |
691 | } | |
692 | } | |
693 | ||
694 | void | |
695 | run_debug_trap () | |
696 | { | |
7117c2d2 | 697 | if ((sigmodes[DEBUG_TRAP] & SIG_TRAPPED) && ((sigmodes[DEBUG_TRAP] & SIG_INPROGRESS) == 0)) |
ccc6cda3 JA |
698 | _run_trap_internal (DEBUG_TRAP, "debug trap"); |
699 | } | |
700 | ||
f73dda09 JA |
701 | void |
702 | run_error_trap () | |
703 | { | |
704 | if ((sigmodes[ERROR_TRAP] & SIG_TRAPPED) && (sigmodes[ERROR_TRAP] & SIG_INPROGRESS) == 0) | |
705 | _run_trap_internal (ERROR_TRAP, "error trap"); | |
706 | } | |
707 | ||
ccc6cda3 JA |
708 | /* Run a trap set on SIGINT. This is called from throw_to_top_level (), and |
709 | declared here to localize the trap functions. */ | |
710 | void | |
711 | run_interrupt_trap () | |
712 | { | |
713 | _run_trap_internal (SIGINT, "interrupt trap"); | |
726f6388 JA |
714 | } |
715 | ||
cce855bc | 716 | #ifdef INCLUDE_UNUSED |
726f6388 JA |
717 | /* Free all the allocated strings in the list of traps and reset the trap |
718 | values to the default. */ | |
719 | void | |
720 | free_trap_strings () | |
721 | { | |
722 | register int i; | |
723 | ||
f73dda09 | 724 | for (i = 0; i < BASH_NSIG; i++) |
726f6388 JA |
725 | { |
726 | free_trap_command (i); | |
727 | trap_list[i] = (char *)DEFAULT_SIG; | |
728 | sigmodes[i] &= ~SIG_TRAPPED; | |
729 | } | |
f73dda09 | 730 | trap_list[DEBUG_TRAP] = trap_list[EXIT_TRAP] = trap_list[ERROR_TRAP] = (char *)NULL; |
726f6388 | 731 | } |
cce855bc | 732 | #endif |
726f6388 JA |
733 | |
734 | /* Reset the handler for SIG to the original value. */ | |
735 | static void | |
736 | reset_signal (sig) | |
737 | int sig; | |
738 | { | |
739 | set_signal_handler (sig, original_signals[sig]); | |
7117c2d2 | 740 | sigmodes[sig] &= ~SIG_TRAPPED; |
726f6388 JA |
741 | } |
742 | ||
ccc6cda3 JA |
743 | /* Set the handler signal SIG to the original and free any trap |
744 | command associated with it. */ | |
745 | static void | |
746 | restore_signal (sig) | |
747 | int sig; | |
726f6388 | 748 | { |
ccc6cda3 JA |
749 | set_signal_handler (sig, original_signals[sig]); |
750 | change_signal (sig, (char *)DEFAULT_SIG); | |
751 | sigmodes[sig] &= ~SIG_TRAPPED; | |
726f6388 JA |
752 | } |
753 | ||
ccc6cda3 JA |
754 | static void |
755 | reset_or_restore_signal_handlers (reset) | |
f73dda09 | 756 | sh_resetsig_func_t *reset; |
726f6388 JA |
757 | { |
758 | register int i; | |
759 | ||
ccc6cda3 JA |
760 | /* Take care of the exit trap first */ |
761 | if (sigmodes[EXIT_TRAP] & SIG_TRAPPED) | |
726f6388 | 762 | { |
ccc6cda3 JA |
763 | free_trap_command (EXIT_TRAP); |
764 | trap_list[EXIT_TRAP] = (char *)NULL; | |
765 | sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED; | |
726f6388 | 766 | } |
726f6388 JA |
767 | for (i = 1; i < NSIG; i++) |
768 | { | |
ccc6cda3 | 769 | if (sigmodes[i] & SIG_TRAPPED) |
726f6388 JA |
770 | { |
771 | if (trap_list[i] == (char *)IGNORE_SIG) | |
772 | set_signal_handler (i, SIG_IGN); | |
773 | else | |
ccc6cda3 | 774 | (*reset) (i); |
726f6388 | 775 | } |
ccc6cda3 JA |
776 | else if (sigmodes[i] & SIG_SPECIAL) |
777 | (*reset) (i); | |
726f6388 | 778 | } |
f73dda09 JA |
779 | |
780 | /* Command substitution and other child processes don't inherit the | |
781 | debug or error traps. */ | |
782 | sigmodes[DEBUG_TRAP] &= ~SIG_TRAPPED; | |
783 | sigmodes[ERROR_TRAP] &= ~SIG_TRAPPED; | |
726f6388 JA |
784 | } |
785 | ||
f73dda09 JA |
786 | /* Reset trapped signals to their original values, but don't free the |
787 | trap strings. Called by the command substitution code. */ | |
726f6388 | 788 | void |
ccc6cda3 | 789 | reset_signal_handlers () |
726f6388 | 790 | { |
ccc6cda3 JA |
791 | reset_or_restore_signal_handlers (reset_signal); |
792 | } | |
726f6388 | 793 | |
ccc6cda3 JA |
794 | /* Reset all trapped signals to their original values. Signals set to be |
795 | ignored with trap '' SIGNAL should be ignored, so we make sure that they | |
796 | are. Called by child processes after they are forked. */ | |
797 | void | |
798 | restore_original_signals () | |
799 | { | |
800 | reset_or_restore_signal_handlers (restore_signal); | |
726f6388 JA |
801 | } |
802 | ||
803 | /* If a trap handler exists for signal SIG, then call it; otherwise just | |
804 | return failure. */ | |
805 | int | |
806 | maybe_call_trap_handler (sig) | |
807 | int sig; | |
808 | { | |
809 | /* Call the trap handler for SIG if the signal is trapped and not ignored. */ | |
ccc6cda3 | 810 | if ((sigmodes[sig] & SIG_TRAPPED) && ((sigmodes[sig] & SIG_IGNORED) == 0)) |
726f6388 JA |
811 | { |
812 | switch (sig) | |
813 | { | |
814 | case SIGINT: | |
815 | run_interrupt_trap (); | |
816 | break; | |
ccc6cda3 | 817 | case EXIT_TRAP: |
726f6388 JA |
818 | run_exit_trap (); |
819 | break; | |
ccc6cda3 JA |
820 | case DEBUG_TRAP: |
821 | run_debug_trap (); | |
822 | break; | |
f73dda09 JA |
823 | case ERROR_TRAP: |
824 | run_error_trap (); | |
825 | break; | |
726f6388 JA |
826 | default: |
827 | trap_handler (sig); | |
828 | break; | |
829 | } | |
830 | return (1); | |
831 | } | |
832 | else | |
833 | return (0); | |
834 | } | |
835 | ||
836 | int | |
837 | signal_is_trapped (sig) | |
838 | int sig; | |
839 | { | |
840 | return (sigmodes[sig] & SIG_TRAPPED); | |
841 | } | |
842 | ||
843 | int | |
844 | signal_is_special (sig) | |
845 | int sig; | |
846 | { | |
847 | return (sigmodes[sig] & SIG_SPECIAL); | |
848 | } | |
849 | ||
850 | int | |
851 | signal_is_ignored (sig) | |
852 | int sig; | |
853 | { | |
854 | return (sigmodes[sig] & SIG_IGNORED); | |
855 | } | |
856 | ||
857 | void | |
858 | set_signal_ignored (sig) | |
859 | int sig; | |
860 | { | |
861 | sigmodes[sig] |= SIG_HARD_IGNORE; | |
ccc6cda3 | 862 | original_signals[sig] = SIG_IGN; |
726f6388 | 863 | } |