Unix: restore all signal handlers after creating a subprocess fork
For each signal handler that is changed, save its state, and restore the state in a child process after a `fork` and before an `execve`. Also save and restore the signal mask. This change requires cooperation from various subsystems, which often takes the form of a callback registered with the subsystem to be called before adjusts a signal handler. Closes #3609
This commit is contained in:
parent
2ca6f7a5d6
commit
2cbaeb7865
|
@ -7,6 +7,16 @@
|
|||
int main()
|
||||
{
|
||||
sigset_t set, old_set;
|
||||
struct sigaction sa;
|
||||
int i;
|
||||
|
||||
/* SIGPROF tends to be near the end of the range of signal IDs */
|
||||
for (i = 0; i < SIGPROF; i++) {
|
||||
sigaction(i, NULL, &sa);
|
||||
if (sa.sa_handler != SIG_DFL)
|
||||
return 1;
|
||||
}
|
||||
|
||||
sigemptyset(&set);
|
||||
sigprocmask(SIG_BLOCK, &set, &old_set);
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ static void handle_call_error PROTO((ptr tc, iptr type, ptr x));
|
|||
static void init_signal_handlers PROTO((void));
|
||||
static void keyboard_interrupt PROTO((ptr tc));
|
||||
|
||||
static void (*register_modified_signal)(int);
|
||||
|
||||
ptr S_get_scheme_arg(tc, n) ptr tc; iptr n; {
|
||||
|
||||
if (n <= asm_arg_reg_cnt) return REGARG(tc, n);
|
||||
|
@ -533,6 +535,10 @@ void S_noncontinuable_interrupt() {
|
|||
do_error(ERROR_NONCONTINUABLE_INTERRUPT,"","",Snil);
|
||||
}
|
||||
|
||||
void Sscheme_register_signal_registerer(void (*registerer)(int)) {
|
||||
register_modified_signal = registerer;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
ptr S_dequeue_scheme_signals(UNUSED ptr tc) {
|
||||
return Snil;
|
||||
|
@ -726,20 +732,28 @@ static void handle_signal(INT sig, UNUSED siginfo_t *si, UNUSED void *data) {
|
|||
}
|
||||
}
|
||||
|
||||
static void no_op_register(UNUSED int sigid) {
|
||||
}
|
||||
|
||||
#define SIGACTION(id, act_p, old_p) (register_modified_signal(id), sigaction(id, act_p, old_p))
|
||||
|
||||
static void init_signal_handlers() {
|
||||
struct sigaction act;
|
||||
|
||||
if (register_modified_signal == NULL)
|
||||
register_modified_signal = no_op_register;
|
||||
|
||||
sigemptyset(&act.sa_mask);
|
||||
|
||||
/* drop pending keyboard interrupts */
|
||||
act.sa_flags = 0;
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGINT, &act, (struct sigaction *)0);
|
||||
SIGACTION(SIGINT, &act, (struct sigaction *)0);
|
||||
|
||||
/* ignore broken pipe signals */
|
||||
act.sa_flags = 0;
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, (struct sigaction *)0);
|
||||
SIGACTION(SIGPIPE, &act, (struct sigaction *)0);
|
||||
|
||||
/* set up to catch SIGINT w/no system call restart */
|
||||
#ifdef SA_INTERRUPT
|
||||
|
@ -748,7 +762,7 @@ static void init_signal_handlers() {
|
|||
act.sa_flags = SA_SIGINFO;
|
||||
#endif /* SA_INTERRUPT */
|
||||
act.sa_sigaction = handle_signal;
|
||||
sigaction(SIGINT, &act, (struct sigaction *)0);
|
||||
SIGACTION(SIGINT, &act, (struct sigaction *)0);
|
||||
#ifdef BSDI
|
||||
siginterrupt(SIGINT, 1);
|
||||
#endif
|
||||
|
@ -760,14 +774,14 @@ static void init_signal_handlers() {
|
|||
act.sa_flags |= SA_RESTART;
|
||||
#endif /* SA_RESTART */
|
||||
#ifdef SIGQUIT
|
||||
sigaction(SIGQUIT, &act, (struct sigaction *)0);
|
||||
SIGACTION(SIGQUIT, &act, (struct sigaction *)0);
|
||||
#endif /* SIGQUIT */
|
||||
sigaction(SIGILL, &act, (struct sigaction *)0);
|
||||
sigaction(SIGFPE, &act, (struct sigaction *)0);
|
||||
SIGACTION(SIGILL, &act, (struct sigaction *)0);
|
||||
SIGACTION(SIGFPE, &act, (struct sigaction *)0);
|
||||
#ifdef SIGBUS
|
||||
sigaction(SIGBUS, &act, (struct sigaction *)0);
|
||||
SIGACTION(SIGBUS, &act, (struct sigaction *)0);
|
||||
#endif /* SIGBUS */
|
||||
sigaction(SIGSEGV, &act, (struct sigaction *)0);
|
||||
SIGACTION(SIGSEGV, &act, (struct sigaction *)0);
|
||||
}
|
||||
|
||||
#endif /* WIN32 */
|
||||
|
|
|
@ -445,6 +445,7 @@
|
|||
(export "int" "Sscheme_script" "(const char *, int, const char *[])")
|
||||
(export "int" "Sscheme_program" "(const char *, int, const char *[])")
|
||||
(export "void" "Sscheme_deinit" "(void)")
|
||||
(export "void" "Sscheme_register_signal_registerer" "(void (*f)(int))")
|
||||
|
||||
(when-feature pthreads
|
||||
(nl) (comment "Thread support.")
|
||||
|
|
|
@ -139,6 +139,10 @@ GC2_EXTERN void (*GC_report_out_of_memory)(void);
|
|||
Called by GC when it has to give up, maybe due to running out of memory
|
||||
during a collection. */
|
||||
|
||||
GC2_EXTERN void (*GC_report_signal_handle_modify)(int);
|
||||
/*
|
||||
Called by GC just before it changes an OS signal handler. */
|
||||
|
||||
GC2_EXTERN void GC_dump(void);
|
||||
/*
|
||||
Dumps memory state info to stderr. */
|
||||
|
|
|
@ -185,6 +185,7 @@ extern double scheme_get_inexact_milliseconds(void);
|
|||
|
||||
GC_Out_Of_Memory_Proc GC_out_of_memory;
|
||||
void (*GC_report_out_of_memory)(void);
|
||||
void (*GC_report_signal_handle_modify)(int);
|
||||
|
||||
GC_Out_Of_Memory_Proc GC_get_out_of_memory(void)
|
||||
{
|
||||
|
|
|
@ -247,8 +247,12 @@ static void initialize_signal_handler(GCTYPE *gc)
|
|||
# ifdef NEED_SIGSTACK
|
||||
act.sa_flags |= SA_ONSTACK;
|
||||
# endif
|
||||
if (GC_report_signal_handle_modify)
|
||||
GC_report_signal_handle_modify(USE_SIGACTON_SIGNAL_KIND);
|
||||
sigaction(USE_SIGACTON_SIGNAL_KIND, &act, &oact);
|
||||
# ifdef USE_ANOTHER_SIGACTON_SIGNAL_KIND
|
||||
if (GC_report_signal_handle_modify)
|
||||
GC_report_signal_handle_modify(USE_ANOTHER_SIGACTON_SIGNAL_KIND);
|
||||
sigaction(USE_ANOTHER_SIGACTON_SIGNAL_KIND, &act, &oact);
|
||||
# endif
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ extern MZGC_DLLIMPORT void GC_init();
|
|||
void scheme_set_stack_base(void *base, int no_auto_statics) XFORM_SKIP_PROC
|
||||
{
|
||||
#ifdef MZ_PRECISE_GC
|
||||
GC_report_signal_handle_modify = rktio_will_modify_os_signal_handler;
|
||||
GC_init_type_tags(_scheme_last_type_,
|
||||
scheme_pair_type, scheme_mutable_pair_type, scheme_weak_box_type,
|
||||
scheme_ephemeron_type, scheme_rt_weak_array,
|
||||
|
@ -464,6 +465,7 @@ void scheme_set_signal_handler(int sig_id, Scheme_Signal_Handler_Proc proc) XFOR
|
|||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = (proc ? proc : SIG_IGN);
|
||||
rktio_will_modify_os_signal_handler(sig_id);
|
||||
sigaction(sig_id, &sa, NULL);
|
||||
#endif
|
||||
}
|
||||
|
@ -1655,6 +1657,7 @@ static void install_w_xor_x_handler()
|
|||
sigaddset(&act.sa_mask, SIGINT);
|
||||
sigaddset(&act.sa_mask, SIGCHLD);
|
||||
act.sa_flags = SA_SIGINFO;
|
||||
rktio_will_modify_os_signal_handler(SIG_W_XOR_X);
|
||||
sigaction(SIG_W_XOR_X, &act, &oact);
|
||||
previous_fault_handler = oact.sa_sigaction;
|
||||
}
|
||||
|
|
|
@ -109,6 +109,8 @@ void racket_boot(racket_boot_arguments_t *ba)
|
|||
rktio_set_dll_procs(ba->dll_open, ba->dll_find_object, ba->dll_close);
|
||||
#endif
|
||||
|
||||
Sscheme_register_signal_registerer(rktio_will_modify_os_signal_handler);
|
||||
|
||||
Sscheme_init(NULL);
|
||||
|
||||
if ((ba->argc == 4) && !strcmp(ba->argv[0], "--cross-server"))
|
||||
|
|
|
@ -1001,7 +1001,7 @@ RKTIO_EXTERN void rktio_flush_signals_received(rktio_t *rktio);
|
|||
|
||||
RKTIO_EXTERN void rktio_install_os_signal_handler(rktio_t *rktio);
|
||||
/* Installs OS-level handlers for SIGINT, SIGTERM, and SIGHUP (or
|
||||
Ctl-C on Windows) to signal the handle of `rktio` and also record
|
||||
Ctl-C on Windows) to signal the handle of `rktio` and also records
|
||||
the signal for reporting via `rktio_poll_os_signal`. Only one
|
||||
`rktio` can be registered this way at a time. This function must
|
||||
not be called in two threads at the same time. */
|
||||
|
@ -1016,6 +1016,17 @@ enum {
|
|||
RKTIO_NUM_OS_SIGNALS
|
||||
};
|
||||
|
||||
RKTIO_EXTERN void rktio_will_modify_os_signal_handler(int sig_id);
|
||||
/* Registers with rktio that an operating-system signal handler is
|
||||
about to be modified within the process but outside of rktio, where
|
||||
`sig_id` is a signal identifier --- such as SIGINT or SIGTERM. This
|
||||
notification allows rktio to record the current signal disposition
|
||||
so that it can be restored after forking a new Unix process.
|
||||
Signal registrations should happen only before multiple threads use
|
||||
rktio, and registration of the signal can happen before any
|
||||
`rktio_init` call. On the first `rktio_will_modify_os_signal_handler`
|
||||
call, the signal mask is also recorded to be restored in a fork. */
|
||||
|
||||
/*************************************************/
|
||||
/* Time and date */
|
||||
|
||||
|
|
|
@ -416,9 +416,11 @@ char *rktio_strndup(char *s, intptr_t len);
|
|||
|
||||
#ifdef RKTIO_SYSTEM_UNIX
|
||||
void rktio_set_signal_handler(int sig_id, void (*proc)(int));
|
||||
void rktio_restore_modified_signal_handlers();
|
||||
#endif
|
||||
void rktio_forget_os_signal_handler(rktio_t *rktio);
|
||||
|
||||
|
||||
#ifdef RKTIO_SYSTEM_WINDOWS
|
||||
int rktio_system_time_is_dst(SYSTEMTIME *st, TIME_ZONE_INFORMATION *_tz);
|
||||
#endif
|
||||
|
|
|
@ -1526,6 +1526,8 @@ rktio_process_result_t *rktio_process(rktio_t *rktio,
|
|||
rktio_close_fds_after_fork(close_after_len, 0, 1, 2);
|
||||
}
|
||||
|
||||
rktio_restore_modified_signal_handlers();
|
||||
|
||||
/* Set real CWD: */
|
||||
if (!rktio_set_current_directory(rktio, current_directory)) {
|
||||
fprintf(stderr, "racket: chdir failed to: %s\n", current_directory);
|
||||
|
|
|
@ -103,6 +103,58 @@ void rktio_set_signal_handler(int sig_id, void (*proc)(int))
|
|||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = proc;
|
||||
rktio_will_modify_os_signal_handler(sig_id);
|
||||
sigaction(sig_id, &sa, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*========================================================================*/
|
||||
/* Signal handler original state */
|
||||
/*========================================================================*/
|
||||
|
||||
typedef struct signal_handler_saved_disposition {
|
||||
int sig_id;
|
||||
struct signal_handler_saved_disposition *next;
|
||||
#if defined(RKTIO_SYSTEM_UNIX)
|
||||
struct sigaction sa;
|
||||
#endif
|
||||
} signal_handler_saved_disposition;
|
||||
|
||||
static signal_handler_saved_disposition *saved_dispositions;
|
||||
static sigset_t initial_procmask;
|
||||
|
||||
void rktio_will_modify_os_signal_handler(int sig_id) {
|
||||
signal_handler_saved_disposition *saved;
|
||||
|
||||
#ifdef RKTIO_SYSTEM_UNIX
|
||||
if (saved_dispositions == NULL)
|
||||
sigprocmask(SIG_SETMASK, NULL, &initial_procmask);
|
||||
#endif
|
||||
|
||||
for (saved = saved_dispositions; saved; saved = saved->next)
|
||||
if (saved->sig_id == sig_id)
|
||||
return;
|
||||
|
||||
saved = malloc(sizeof(signal_handler_saved_disposition));
|
||||
saved->next = saved_dispositions;
|
||||
saved->sig_id = sig_id;
|
||||
saved_dispositions = saved;
|
||||
|
||||
#if defined(RKTIO_SYSTEM_UNIX)
|
||||
sigaction(sig_id, NULL, &saved->sa);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef RKTIO_SYSTEM_UNIX
|
||||
/* called in a child thread after `fork */
|
||||
void rktio_restore_modified_signal_handlers() {
|
||||
if (saved_dispositions) {
|
||||
signal_handler_saved_disposition *saved;
|
||||
|
||||
for (saved = saved_dispositions; saved; saved = saved->next)
|
||||
sigaction(saved->sig_id, &saved->sa, NULL);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &initial_procmask, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue
Block a user