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()
|
int main()
|
||||||
{
|
{
|
||||||
sigset_t set, old_set;
|
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);
|
sigemptyset(&set);
|
||||||
sigprocmask(SIG_BLOCK, &set, &old_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 init_signal_handlers PROTO((void));
|
||||||
static void keyboard_interrupt PROTO((ptr tc));
|
static void keyboard_interrupt PROTO((ptr tc));
|
||||||
|
|
||||||
|
static void (*register_modified_signal)(int);
|
||||||
|
|
||||||
ptr S_get_scheme_arg(tc, n) ptr tc; iptr n; {
|
ptr S_get_scheme_arg(tc, n) ptr tc; iptr n; {
|
||||||
|
|
||||||
if (n <= asm_arg_reg_cnt) return REGARG(tc, 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);
|
do_error(ERROR_NONCONTINUABLE_INTERRUPT,"","",Snil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sscheme_register_signal_registerer(void (*registerer)(int)) {
|
||||||
|
register_modified_signal = registerer;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
ptr S_dequeue_scheme_signals(UNUSED ptr tc) {
|
ptr S_dequeue_scheme_signals(UNUSED ptr tc) {
|
||||||
return Snil;
|
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() {
|
static void init_signal_handlers() {
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
|
|
||||||
|
if (register_modified_signal == NULL)
|
||||||
|
register_modified_signal = no_op_register;
|
||||||
|
|
||||||
sigemptyset(&act.sa_mask);
|
sigemptyset(&act.sa_mask);
|
||||||
|
|
||||||
/* drop pending keyboard interrupts */
|
/* drop pending keyboard interrupts */
|
||||||
act.sa_flags = 0;
|
act.sa_flags = 0;
|
||||||
act.sa_handler = SIG_IGN;
|
act.sa_handler = SIG_IGN;
|
||||||
sigaction(SIGINT, &act, (struct sigaction *)0);
|
SIGACTION(SIGINT, &act, (struct sigaction *)0);
|
||||||
|
|
||||||
/* ignore broken pipe signals */
|
/* ignore broken pipe signals */
|
||||||
act.sa_flags = 0;
|
act.sa_flags = 0;
|
||||||
act.sa_handler = SIG_IGN;
|
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 */
|
/* set up to catch SIGINT w/no system call restart */
|
||||||
#ifdef SA_INTERRUPT
|
#ifdef SA_INTERRUPT
|
||||||
|
@ -748,7 +762,7 @@ static void init_signal_handlers() {
|
||||||
act.sa_flags = SA_SIGINFO;
|
act.sa_flags = SA_SIGINFO;
|
||||||
#endif /* SA_INTERRUPT */
|
#endif /* SA_INTERRUPT */
|
||||||
act.sa_sigaction = handle_signal;
|
act.sa_sigaction = handle_signal;
|
||||||
sigaction(SIGINT, &act, (struct sigaction *)0);
|
SIGACTION(SIGINT, &act, (struct sigaction *)0);
|
||||||
#ifdef BSDI
|
#ifdef BSDI
|
||||||
siginterrupt(SIGINT, 1);
|
siginterrupt(SIGINT, 1);
|
||||||
#endif
|
#endif
|
||||||
|
@ -760,14 +774,14 @@ static void init_signal_handlers() {
|
||||||
act.sa_flags |= SA_RESTART;
|
act.sa_flags |= SA_RESTART;
|
||||||
#endif /* SA_RESTART */
|
#endif /* SA_RESTART */
|
||||||
#ifdef SIGQUIT
|
#ifdef SIGQUIT
|
||||||
sigaction(SIGQUIT, &act, (struct sigaction *)0);
|
SIGACTION(SIGQUIT, &act, (struct sigaction *)0);
|
||||||
#endif /* SIGQUIT */
|
#endif /* SIGQUIT */
|
||||||
sigaction(SIGILL, &act, (struct sigaction *)0);
|
SIGACTION(SIGILL, &act, (struct sigaction *)0);
|
||||||
sigaction(SIGFPE, &act, (struct sigaction *)0);
|
SIGACTION(SIGFPE, &act, (struct sigaction *)0);
|
||||||
#ifdef SIGBUS
|
#ifdef SIGBUS
|
||||||
sigaction(SIGBUS, &act, (struct sigaction *)0);
|
SIGACTION(SIGBUS, &act, (struct sigaction *)0);
|
||||||
#endif /* SIGBUS */
|
#endif /* SIGBUS */
|
||||||
sigaction(SIGSEGV, &act, (struct sigaction *)0);
|
SIGACTION(SIGSEGV, &act, (struct sigaction *)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* WIN32 */
|
#endif /* WIN32 */
|
||||||
|
|
|
@ -445,6 +445,7 @@
|
||||||
(export "int" "Sscheme_script" "(const char *, int, const char *[])")
|
(export "int" "Sscheme_script" "(const char *, int, const char *[])")
|
||||||
(export "int" "Sscheme_program" "(const char *, int, const char *[])")
|
(export "int" "Sscheme_program" "(const char *, int, const char *[])")
|
||||||
(export "void" "Sscheme_deinit" "(void)")
|
(export "void" "Sscheme_deinit" "(void)")
|
||||||
|
(export "void" "Sscheme_register_signal_registerer" "(void (*f)(int))")
|
||||||
|
|
||||||
(when-feature pthreads
|
(when-feature pthreads
|
||||||
(nl) (comment "Thread support.")
|
(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
|
Called by GC when it has to give up, maybe due to running out of memory
|
||||||
during a collection. */
|
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);
|
GC2_EXTERN void GC_dump(void);
|
||||||
/*
|
/*
|
||||||
Dumps memory state info to stderr. */
|
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;
|
GC_Out_Of_Memory_Proc GC_out_of_memory;
|
||||||
void (*GC_report_out_of_memory)(void);
|
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)
|
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
|
# ifdef NEED_SIGSTACK
|
||||||
act.sa_flags |= SA_ONSTACK;
|
act.sa_flags |= SA_ONSTACK;
|
||||||
# endif
|
# endif
|
||||||
|
if (GC_report_signal_handle_modify)
|
||||||
|
GC_report_signal_handle_modify(USE_SIGACTON_SIGNAL_KIND);
|
||||||
sigaction(USE_SIGACTON_SIGNAL_KIND, &act, &oact);
|
sigaction(USE_SIGACTON_SIGNAL_KIND, &act, &oact);
|
||||||
# ifdef USE_ANOTHER_SIGACTON_SIGNAL_KIND
|
# 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);
|
sigaction(USE_ANOTHER_SIGACTON_SIGNAL_KIND, &act, &oact);
|
||||||
# endif
|
# 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
|
void scheme_set_stack_base(void *base, int no_auto_statics) XFORM_SKIP_PROC
|
||||||
{
|
{
|
||||||
#ifdef MZ_PRECISE_GC
|
#ifdef MZ_PRECISE_GC
|
||||||
|
GC_report_signal_handle_modify = rktio_will_modify_os_signal_handler;
|
||||||
GC_init_type_tags(_scheme_last_type_,
|
GC_init_type_tags(_scheme_last_type_,
|
||||||
scheme_pair_type, scheme_mutable_pair_type, scheme_weak_box_type,
|
scheme_pair_type, scheme_mutable_pair_type, scheme_weak_box_type,
|
||||||
scheme_ephemeron_type, scheme_rt_weak_array,
|
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);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = 0;
|
sa.sa_flags = 0;
|
||||||
sa.sa_handler = (proc ? proc : SIG_IGN);
|
sa.sa_handler = (proc ? proc : SIG_IGN);
|
||||||
|
rktio_will_modify_os_signal_handler(sig_id);
|
||||||
sigaction(sig_id, &sa, NULL);
|
sigaction(sig_id, &sa, NULL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1655,6 +1657,7 @@ static void install_w_xor_x_handler()
|
||||||
sigaddset(&act.sa_mask, SIGINT);
|
sigaddset(&act.sa_mask, SIGINT);
|
||||||
sigaddset(&act.sa_mask, SIGCHLD);
|
sigaddset(&act.sa_mask, SIGCHLD);
|
||||||
act.sa_flags = SA_SIGINFO;
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
rktio_will_modify_os_signal_handler(SIG_W_XOR_X);
|
||||||
sigaction(SIG_W_XOR_X, &act, &oact);
|
sigaction(SIG_W_XOR_X, &act, &oact);
|
||||||
previous_fault_handler = oact.sa_sigaction;
|
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);
|
rktio_set_dll_procs(ba->dll_open, ba->dll_find_object, ba->dll_close);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Sscheme_register_signal_registerer(rktio_will_modify_os_signal_handler);
|
||||||
|
|
||||||
Sscheme_init(NULL);
|
Sscheme_init(NULL);
|
||||||
|
|
||||||
if ((ba->argc == 4) && !strcmp(ba->argv[0], "--cross-server"))
|
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);
|
RKTIO_EXTERN void rktio_install_os_signal_handler(rktio_t *rktio);
|
||||||
/* Installs OS-level handlers for SIGINT, SIGTERM, and SIGHUP (or
|
/* 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
|
the signal for reporting via `rktio_poll_os_signal`. Only one
|
||||||
`rktio` can be registered this way at a time. This function must
|
`rktio` can be registered this way at a time. This function must
|
||||||
not be called in two threads at the same time. */
|
not be called in two threads at the same time. */
|
||||||
|
@ -1016,6 +1016,17 @@ enum {
|
||||||
RKTIO_NUM_OS_SIGNALS
|
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 */
|
/* Time and date */
|
||||||
|
|
||||||
|
|
|
@ -416,9 +416,11 @@ char *rktio_strndup(char *s, intptr_t len);
|
||||||
|
|
||||||
#ifdef RKTIO_SYSTEM_UNIX
|
#ifdef RKTIO_SYSTEM_UNIX
|
||||||
void rktio_set_signal_handler(int sig_id, void (*proc)(int));
|
void rktio_set_signal_handler(int sig_id, void (*proc)(int));
|
||||||
|
void rktio_restore_modified_signal_handlers();
|
||||||
#endif
|
#endif
|
||||||
void rktio_forget_os_signal_handler(rktio_t *rktio);
|
void rktio_forget_os_signal_handler(rktio_t *rktio);
|
||||||
|
|
||||||
|
|
||||||
#ifdef RKTIO_SYSTEM_WINDOWS
|
#ifdef RKTIO_SYSTEM_WINDOWS
|
||||||
int rktio_system_time_is_dst(SYSTEMTIME *st, TIME_ZONE_INFORMATION *_tz);
|
int rktio_system_time_is_dst(SYSTEMTIME *st, TIME_ZONE_INFORMATION *_tz);
|
||||||
#endif
|
#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_close_fds_after_fork(close_after_len, 0, 1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rktio_restore_modified_signal_handlers();
|
||||||
|
|
||||||
/* Set real CWD: */
|
/* Set real CWD: */
|
||||||
if (!rktio_set_current_directory(rktio, current_directory)) {
|
if (!rktio_set_current_directory(rktio, current_directory)) {
|
||||||
fprintf(stderr, "racket: chdir failed to: %s\n", 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);
|
sigemptyset(&sa.sa_mask);
|
||||||
sa.sa_flags = 0;
|
sa.sa_flags = 0;
|
||||||
sa.sa_handler = proc;
|
sa.sa_handler = proc;
|
||||||
|
rktio_will_modify_os_signal_handler(sig_id);
|
||||||
sigaction(sig_id, &sa, NULL);
|
sigaction(sig_id, &sa, NULL);
|
||||||
}
|
}
|
||||||
#endif
|
#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