]> git.ipfire.org Git - thirdparty/bash.git/blob - sig.c
commit bash-20150619 snapshot
[thirdparty/bash.git] / sig.c
1 /* sig.c - interface for shell signal handlers and signal initialization. */
2
3 /* Copyright (C) 1994-2015 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include "bashtypes.h"
24
25 #if defined (HAVE_UNISTD_H)
26 # ifdef _MINIX
27 # include <sys/types.h>
28 # endif
29 # include <unistd.h>
30 #endif
31
32 #include <stdio.h>
33 #include <signal.h>
34
35 #include "bashintl.h"
36
37 #include "shell.h"
38 #if defined (JOB_CONTROL)
39 #include "jobs.h"
40 #endif /* JOB_CONTROL */
41 #include "siglist.h"
42 #include "sig.h"
43 #include "trap.h"
44
45 #include "builtins/common.h"
46 #include "builtins/builtext.h"
47
48 #if defined (READLINE)
49 # include "bashline.h"
50 # include <readline/readline.h>
51 #endif
52
53 #if defined (HISTORY)
54 # include "bashhist.h"
55 #endif
56
57 extern int last_command_exit_value;
58 extern int last_command_exit_signal;
59 extern int return_catch_flag;
60 extern int running_trap;
61 extern int loop_level, continuing, breaking, funcnest;
62 extern int executing_list;
63 extern int comsub_ignore_return;
64 extern int parse_and_execute_level, shell_initialized;
65 #if defined (HISTORY)
66 extern int history_lines_this_session;
67 #endif
68 extern int no_line_editing;
69 extern int wait_signal_received;
70 extern sh_builtin_func_t *this_shell_builtin;
71
72 extern void initialize_siglist ();
73
74 /* Non-zero after SIGINT. */
75 volatile sig_atomic_t interrupt_state = 0;
76
77 /* Non-zero after SIGWINCH */
78 volatile sig_atomic_t sigwinch_received = 0;
79
80 /* Non-zero after SIGTERM */
81 volatile sig_atomic_t sigterm_received = 0;
82
83 /* Set to the value of any terminating signal received. */
84 volatile sig_atomic_t terminating_signal = 0;
85
86 /* The environment at the top-level R-E loop. We use this in
87 the case of error return. */
88 procenv_t top_level;
89
90 #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
91 /* The signal masks that this shell runs with. */
92 sigset_t top_level_mask;
93 #endif /* JOB_CONTROL */
94
95 /* When non-zero, we throw_to_top_level (). */
96 int interrupt_immediately = 0;
97
98 /* When non-zero, we call the terminating signal handler immediately. */
99 int terminate_immediately = 0;
100
101 #if defined (SIGWINCH)
102 static SigHandler *old_winch = (SigHandler *)SIG_DFL;
103 #endif
104
105 static void initialize_shell_signals __P((void));
106
107 void
108 initialize_signals (reinit)
109 int reinit;
110 {
111 initialize_shell_signals ();
112 initialize_job_signals ();
113 #if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL)
114 if (reinit == 0)
115 initialize_siglist ();
116 #endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */
117 }
118
119 /* A structure describing a signal that terminates the shell if not
120 caught. The orig_handler member is present so children can reset
121 these signals back to their original handlers. */
122 struct termsig {
123 int signum;
124 SigHandler *orig_handler;
125 int orig_flags;
126 };
127
128 #define NULL_HANDLER (SigHandler *)SIG_DFL
129
130 /* The list of signals that would terminate the shell if not caught.
131 We catch them, but just so that we can write the history file,
132 and so forth. */
133 static struct termsig terminating_signals[] = {
134 #ifdef SIGHUP
135 { SIGHUP, NULL_HANDLER, 0 },
136 #endif
137
138 #ifdef SIGINT
139 { SIGINT, NULL_HANDLER, 0 },
140 #endif
141
142 #ifdef SIGILL
143 { SIGILL, NULL_HANDLER, 0 },
144 #endif
145
146 #ifdef SIGTRAP
147 { SIGTRAP, NULL_HANDLER, 0 },
148 #endif
149
150 #ifdef SIGIOT
151 { SIGIOT, NULL_HANDLER, 0 },
152 #endif
153
154 #ifdef SIGDANGER
155 { SIGDANGER, NULL_HANDLER, 0 },
156 #endif
157
158 #ifdef SIGEMT
159 { SIGEMT, NULL_HANDLER, 0 },
160 #endif
161
162 #ifdef SIGFPE
163 { SIGFPE, NULL_HANDLER, 0 },
164 #endif
165
166 #ifdef SIGBUS
167 { SIGBUS, NULL_HANDLER, 0 },
168 #endif
169
170 #ifdef SIGSEGV
171 { SIGSEGV, NULL_HANDLER, 0 },
172 #endif
173
174 #ifdef SIGSYS
175 { SIGSYS, NULL_HANDLER, 0 },
176 #endif
177
178 #ifdef SIGPIPE
179 { SIGPIPE, NULL_HANDLER, 0 },
180 #endif
181
182 #ifdef SIGALRM
183 { SIGALRM, NULL_HANDLER, 0 },
184 #endif
185
186 #ifdef SIGTERM
187 { SIGTERM, NULL_HANDLER, 0 },
188 #endif
189
190 #ifdef SIGXCPU
191 { SIGXCPU, NULL_HANDLER, 0 },
192 #endif
193
194 #ifdef SIGXFSZ
195 { SIGXFSZ, NULL_HANDLER, 0 },
196 #endif
197
198 #ifdef SIGVTALRM
199 { SIGVTALRM, NULL_HANDLER, 0 },
200 #endif
201
202 #if 0
203 #ifdef SIGPROF
204 { SIGPROF, NULL_HANDLER, 0 },
205 #endif
206 #endif
207
208 #ifdef SIGLOST
209 { SIGLOST, NULL_HANDLER, 0 },
210 #endif
211
212 #ifdef SIGUSR1
213 { SIGUSR1, NULL_HANDLER, 0 },
214 #endif
215
216 #ifdef SIGUSR2
217 { SIGUSR2, NULL_HANDLER, 0 },
218 #endif
219 };
220
221 #define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig))
222
223 #define XSIG(x) (terminating_signals[x].signum)
224 #define XHANDLER(x) (terminating_signals[x].orig_handler)
225 #define XSAFLAGS(x) (terminating_signals[x].orig_flags)
226
227 static int termsigs_initialized = 0;
228
229 /* Initialize signals that will terminate the shell to do some
230 unwind protection. For non-interactive shells, we only call
231 this when a trap is defined for EXIT (0) or when trap is run
232 to display signal dispositions. */
233 void
234 initialize_terminating_signals ()
235 {
236 register int i;
237 #if defined (HAVE_POSIX_SIGNALS)
238 struct sigaction act, oact;
239 #endif
240
241 if (termsigs_initialized)
242 return;
243
244 /* The following code is to avoid an expensive call to
245 set_signal_handler () for each terminating_signals. Fortunately,
246 this is possible in Posix. Unfortunately, we have to call signal ()
247 on non-Posix systems for each signal in terminating_signals. */
248 #if defined (HAVE_POSIX_SIGNALS)
249 act.sa_handler = termsig_sighandler;
250 act.sa_flags = 0;
251 sigemptyset (&act.sa_mask);
252 sigemptyset (&oact.sa_mask);
253 for (i = 0; i < TERMSIGS_LENGTH; i++)
254 sigaddset (&act.sa_mask, XSIG (i));
255 for (i = 0; i < TERMSIGS_LENGTH; i++)
256 {
257 /* If we've already trapped it, don't do anything. */
258 if (signal_is_trapped (XSIG (i)))
259 continue;
260
261 sigaction (XSIG (i), &act, &oact);
262 XHANDLER(i) = oact.sa_handler;
263 XSAFLAGS(i) = oact.sa_flags;
264 /* Don't do anything with signals that are ignored at shell entry
265 if the shell is not interactive. */
266 /* XXX - should we do this for interactive shells, too? */
267 if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN)
268 {
269 sigaction (XSIG (i), &oact, &act);
270 set_signal_hard_ignored (XSIG (i));
271 }
272 #if defined (SIGPROF) && !defined (_MINIX)
273 if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
274 sigaction (XSIG (i), &oact, (struct sigaction *)NULL);
275 #endif /* SIGPROF && !_MINIX */
276 }
277
278 #else /* !HAVE_POSIX_SIGNALS */
279
280 for (i = 0; i < TERMSIGS_LENGTH; i++)
281 {
282 /* If we've already trapped it, don't do anything. */
283 if (signal_is_trapped (XSIG (i)))
284 continue;
285
286 XHANDLER(i) = signal (XSIG (i), termsig_sighandler);
287 XSAFLAGS(i) = 0;
288 /* Don't do anything with signals that are ignored at shell entry
289 if the shell is not interactive. */
290 /* XXX - should we do this for interactive shells, too? */
291 if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN)
292 {
293 signal (XSIG (i), SIG_IGN);
294 set_signal_hard_ignored (XSIG (i));
295 }
296 #ifdef SIGPROF
297 if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
298 signal (XSIG (i), XHANDLER (i));
299 #endif
300 }
301
302 #endif /* !HAVE_POSIX_SIGNALS */
303
304 termsigs_initialized = 1;
305 }
306
307 static void
308 initialize_shell_signals ()
309 {
310 if (interactive)
311 initialize_terminating_signals ();
312
313 #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
314 /* All shells use the signal mask they inherit, and pass it along
315 to child processes. Children will never block SIGCHLD, though. */
316 sigemptyset (&top_level_mask);
317 sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask);
318 # if defined (SIGCHLD)
319 sigdelset (&top_level_mask, SIGCHLD);
320 # endif
321 #endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */
322
323 /* And, some signals that are specifically ignored by the shell. */
324 set_signal_handler (SIGQUIT, SIG_IGN);
325
326 if (interactive)
327 {
328 set_signal_handler (SIGINT, sigint_sighandler);
329 get_original_signal (SIGTERM);
330 if (signal_is_hard_ignored (SIGTERM) == 0)
331 set_signal_handler (SIGTERM, sigterm_sighandler);
332 set_sigwinch_handler ();
333 }
334 }
335
336 void
337 reset_terminating_signals ()
338 {
339 register int i;
340 #if defined (HAVE_POSIX_SIGNALS)
341 struct sigaction act;
342 #endif
343
344 if (termsigs_initialized == 0)
345 return;
346
347 #if defined (HAVE_POSIX_SIGNALS)
348 act.sa_flags = 0;
349 sigemptyset (&act.sa_mask);
350 for (i = 0; i < TERMSIGS_LENGTH; i++)
351 {
352 /* Skip a signal if it's trapped or handled specially, because the
353 trap code will restore the correct value. */
354 if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
355 continue;
356
357 act.sa_handler = XHANDLER (i);
358 act.sa_flags = XSAFLAGS (i);
359 sigaction (XSIG (i), &act, (struct sigaction *) NULL);
360 }
361 #else /* !HAVE_POSIX_SIGNALS */
362 for (i = 0; i < TERMSIGS_LENGTH; i++)
363 {
364 if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
365 continue;
366
367 signal (XSIG (i), XHANDLER (i));
368 }
369 #endif /* !HAVE_POSIX_SIGNALS */
370
371 termsigs_initialized = 0;
372 }
373 #undef XSIG
374 #undef XHANDLER
375
376 /* Run some of the cleanups that should be performed when we run
377 jump_to_top_level from a builtin command context. XXX - might want to
378 also call reset_parser here. */
379 void
380 top_level_cleanup ()
381 {
382 /* Clean up string parser environment. */
383 while (parse_and_execute_level)
384 parse_and_execute_cleanup ();
385
386 #if defined (PROCESS_SUBSTITUTION)
387 unlink_fifo_list ();
388 #endif /* PROCESS_SUBSTITUTION */
389
390 run_unwind_protects ();
391 loop_level = continuing = breaking = funcnest = 0;
392 executing_list = comsub_ignore_return = return_catch_flag = 0;
393 }
394
395 /* What to do when we've been interrupted, and it is safe to handle it. */
396 void
397 throw_to_top_level ()
398 {
399 int print_newline = 0;
400
401 if (interrupt_state)
402 {
403 if (last_command_exit_value < 128)
404 last_command_exit_value = 128 + SIGINT;
405 print_newline = 1;
406 DELINTERRUPT;
407 }
408
409 if (interrupt_state)
410 return;
411
412 last_command_exit_signal = (last_command_exit_value > 128) ?
413 (last_command_exit_value - 128) : 0;
414 last_command_exit_value |= 128;
415
416 /* Run any traps set on SIGINT, mostly for interactive shells */
417 if (signal_is_trapped (SIGINT))
418 run_interrupt_trap (1);
419
420 /* Clean up string parser environment. */
421 while (parse_and_execute_level)
422 parse_and_execute_cleanup ();
423
424 if (running_trap > 0)
425 run_trap_cleanup (running_trap - 1);
426
427 #if defined (JOB_CONTROL)
428 give_terminal_to (shell_pgrp, 0);
429 #endif /* JOB_CONTROL */
430
431 #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
432 /* This needs to stay because jobs.c:make_child() uses it without resetting
433 the signal mask. */
434 sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
435 #endif
436
437 reset_parser ();
438
439 #if defined (READLINE)
440 if (interactive)
441 bashline_reset ();
442 #endif /* READLINE */
443
444 #if defined (PROCESS_SUBSTITUTION)
445 unlink_fifo_list ();
446 #endif /* PROCESS_SUBSTITUTION */
447
448 run_unwind_protects ();
449 loop_level = continuing = breaking = funcnest = 0;
450 executing_list = comsub_ignore_return = return_catch_flag = 0;
451
452 if (interactive && print_newline)
453 {
454 fflush (stdout);
455 fprintf (stderr, "\n");
456 fflush (stderr);
457 }
458
459 /* An interrupted `wait' command in a script does not exit the script. */
460 if (interactive || (interactive_shell && !shell_initialized) ||
461 (print_newline && signal_is_trapped (SIGINT)))
462 jump_to_top_level (DISCARD);
463 else
464 jump_to_top_level (EXITPROG);
465 }
466
467 /* This is just here to isolate the longjmp calls. */
468 void
469 jump_to_top_level (value)
470 int value;
471 {
472 sh_longjmp (top_level, value);
473 }
474
475 sighandler
476 termsig_sighandler (sig)
477 int sig;
478 {
479 /* If we get called twice with the same signal before handling it,
480 terminate right away. */
481 if (
482 #ifdef SIGHUP
483 sig != SIGHUP &&
484 #endif
485 #ifdef SIGINT
486 sig != SIGINT &&
487 #endif
488 #ifdef SIGDANGER
489 sig != SIGDANGER &&
490 #endif
491 #ifdef SIGPIPE
492 sig != SIGPIPE &&
493 #endif
494 #ifdef SIGALRM
495 sig != SIGALRM &&
496 #endif
497 #ifdef SIGTERM
498 sig != SIGTERM &&
499 #endif
500 #ifdef SIGXCPU
501 sig != SIGXCPU &&
502 #endif
503 #ifdef SIGXFSZ
504 sig != SIGXFSZ &&
505 #endif
506 #ifdef SIGVTALRM
507 sig != SIGVTALRM &&
508 #endif
509 #ifdef SIGLOST
510 sig != SIGLOST &&
511 #endif
512 #ifdef SIGUSR1
513 sig != SIGUSR1 &&
514 #endif
515 #ifdef SIGUSR2
516 sig != SIGUSR2 &&
517 #endif
518 sig == terminating_signal)
519 terminate_immediately = 1;
520
521 terminating_signal = sig;
522
523 /* XXX - should this also trigger when interrupt_immediately is set? */
524 if (terminate_immediately)
525 {
526 #if defined (HISTORY)
527 /* XXX - will inhibit history file being written */
528 # if defined (READLINE)
529 if (interactive_shell == 0 || interactive == 0 || (sig != SIGHUP && sig != SIGTERM) || no_line_editing || (RL_ISSTATE (RL_STATE_READCMD) == 0))
530 # endif
531 history_lines_this_session = 0;
532 #endif
533 terminate_immediately = 0;
534 termsig_handler (sig);
535 }
536
537 #if defined (READLINE)
538 /* Set the event hook so readline will call it after the signal handlers
539 finish executing, so if this interrupted character input we can get
540 quick response. If readline is active or has modified the terminal we
541 need to set this no matter what the signal is, though the check for
542 RL_STATE_TERMPREPPED is possibly redundant. */
543 if (RL_ISSTATE (RL_STATE_SIGHANDLER) || RL_ISSTATE (RL_STATE_TERMPREPPED))
544 bashline_set_event_hook ();
545 #endif
546
547 SIGRETURN (0);
548 }
549
550 void
551 termsig_handler (sig)
552 int sig;
553 {
554 static int handling_termsig = 0;
555
556 /* Simple semaphore to keep this function from being executed multiple
557 times. Since we no longer are running as a signal handler, we don't
558 block multiple occurrences of the terminating signals while running. */
559 if (handling_termsig)
560 return;
561 handling_termsig = 1;
562 terminating_signal = 0; /* keep macro from re-testing true. */
563
564 /* I don't believe this condition ever tests true. */
565 if (sig == SIGINT && signal_is_trapped (SIGINT))
566 run_interrupt_trap (0);
567
568 #if defined (HISTORY)
569 /* If we don't do something like this, the history will not be saved when
570 an interactive shell is running in a terminal window that gets closed
571 with the `close' button. We can't test for RL_STATE_READCMD because
572 readline no longer handles SIGTERM synchronously. */
573 if (interactive_shell && interactive && (sig == SIGHUP || sig == SIGTERM) && remember_on_history)
574 maybe_save_shell_history ();
575 #endif /* HISTORY */
576
577 if (this_shell_builtin == read_builtin)
578 read_tty_cleanup ();
579
580 #if defined (JOB_CONTROL)
581 if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB))))
582 hangup_all_jobs ();
583 end_job_control ();
584 #endif /* JOB_CONTROL */
585
586 #if defined (PROCESS_SUBSTITUTION)
587 unlink_fifo_list ();
588 #endif /* PROCESS_SUBSTITUTION */
589
590 /* Reset execution context */
591 loop_level = continuing = breaking = funcnest = 0;
592 executing_list = comsub_ignore_return = return_catch_flag = 0;
593
594 run_exit_trap (); /* XXX - run exit trap possibly in signal context? */
595 set_signal_handler (sig, SIG_DFL);
596 kill (getpid (), sig);
597 }
598
599 /* What we really do when SIGINT occurs. */
600 sighandler
601 sigint_sighandler (sig)
602 int sig;
603 {
604 #if defined (MUST_REINSTALL_SIGHANDLERS)
605 signal (sig, sigint_sighandler);
606 #endif
607
608 /* interrupt_state needs to be set for the stack of interrupts to work
609 right. Should it be set unconditionally? */
610 if (interrupt_state == 0)
611 ADDINTERRUPT;
612
613 /* We will get here in interactive shells with job control active; allow
614 an interactive wait to be interrupted. */
615 if (this_shell_builtin && this_shell_builtin == wait_builtin)
616 {
617 last_command_exit_value = 128 + sig;
618 wait_signal_received = sig;
619 SIGRETURN (0);
620 }
621
622 if (interrupt_immediately)
623 {
624 interrupt_immediately = 0;
625 last_command_exit_value = 128 + sig;
626 throw_to_top_level ();
627 }
628 #if defined (READLINE)
629 /* Set the event hook so readline will call it after the signal handlers
630 finish executing, so if this interrupted character input we can get
631 quick response. */
632 else if (RL_ISSTATE (RL_STATE_SIGHANDLER))
633 bashline_set_event_hook ();
634 #endif
635
636 SIGRETURN (0);
637 }
638
639 #if defined (SIGWINCH)
640 sighandler
641 sigwinch_sighandler (sig)
642 int sig;
643 {
644 #if defined (MUST_REINSTALL_SIGHANDLERS)
645 set_signal_handler (SIGWINCH, sigwinch_sighandler);
646 #endif /* MUST_REINSTALL_SIGHANDLERS */
647 sigwinch_received = 1;
648 SIGRETURN (0);
649 }
650 #endif /* SIGWINCH */
651
652 void
653 set_sigwinch_handler ()
654 {
655 #if defined (SIGWINCH)
656 old_winch = set_signal_handler (SIGWINCH, sigwinch_sighandler);
657 #endif
658 }
659
660 void
661 unset_sigwinch_handler ()
662 {
663 #if defined (SIGWINCH)
664 set_signal_handler (SIGWINCH, old_winch);
665 #endif
666 }
667
668 sighandler
669 sigterm_sighandler (sig)
670 int sig;
671 {
672 sigterm_received = 1; /* XXX - counter? */
673 SIGRETURN (0);
674 }
675
676 /* Signal functions used by the rest of the code. */
677 #if !defined (HAVE_POSIX_SIGNALS)
678
679 /* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
680 sigprocmask (operation, newset, oldset)
681 int operation, *newset, *oldset;
682 {
683 int old, new;
684
685 if (newset)
686 new = *newset;
687 else
688 new = 0;
689
690 switch (operation)
691 {
692 case SIG_BLOCK:
693 old = sigblock (new);
694 break;
695
696 case SIG_SETMASK:
697 old = sigsetmask (new);
698 break;
699
700 default:
701 internal_error (_("sigprocmask: %d: invalid operation"), operation);
702 }
703
704 if (oldset)
705 *oldset = old;
706 }
707
708 #else
709
710 #if !defined (SA_INTERRUPT)
711 # define SA_INTERRUPT 0
712 #endif
713
714 #if !defined (SA_RESTART)
715 # define SA_RESTART 0
716 #endif
717
718 SigHandler *
719 set_signal_handler (sig, handler)
720 int sig;
721 SigHandler *handler;
722 {
723 struct sigaction act, oact;
724
725 act.sa_handler = handler;
726 act.sa_flags = 0;
727
728 /* XXX - bash-4.2 */
729 /* We don't want a child death to interrupt interruptible system calls, even
730 if we take the time to reap children */
731 #if defined (SIGCHLD)
732 if (sig == SIGCHLD)
733 act.sa_flags |= SA_RESTART; /* XXX */
734 #endif
735 /* If we're installing a SIGTERM handler for interactive shells, we want
736 it to be as close to SIG_IGN as possible. */
737 if (sig == SIGTERM && handler == sigterm_sighandler)
738 act.sa_flags |= SA_RESTART; /* XXX */
739
740 sigemptyset (&act.sa_mask);
741 sigemptyset (&oact.sa_mask);
742 if (sigaction (sig, &act, &oact) == 0)
743 return (oact.sa_handler);
744 else
745 return (SIG_DFL);
746 }
747 #endif /* HAVE_POSIX_SIGNALS */