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