Permalink
Cannot retrieve contributors at this time
1655 lines (1498 sloc)
36.5 KB
| /********************************************************************** | |
| signal.c - | |
| $Author$ | |
| created at: Tue Dec 20 10:13:44 JST 1994 | |
| Copyright (C) 1993-2007 Yukihiro Matsumoto | |
| Copyright (C) 2000 Network Applied Communication Laboratory, Inc. | |
| Copyright (C) 2000 Information-technology Promotion Agency, Japan | |
| **********************************************************************/ | |
| #include "ruby/internal/config.h" | |
| #include <errno.h> | |
| #include <signal.h> | |
| #include <stdio.h> | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> | |
| #endif | |
| #ifdef HAVE_SYS_UIO_H | |
| # include <sys/uio.h> | |
| #endif | |
| #ifdef HAVE_UCONTEXT_H | |
| # include <ucontext.h> | |
| #endif | |
| #ifdef HAVE_PTHREAD_H | |
| # include <pthread.h> | |
| #endif | |
| #include "debug_counter.h" | |
| #include "eval_intern.h" | |
| #include "internal.h" | |
| #include "internal/eval.h" | |
| #include "internal/sanitizers.h" | |
| #include "internal/signal.h" | |
| #include "internal/string.h" | |
| #include "internal/thread.h" | |
| #include "ruby_atomic.h" | |
| #include "vm_core.h" | |
| #include "ractor_core.h" | |
| #ifdef NEED_RUBY_ATOMIC_OPS | |
| rb_atomic_t | |
| ruby_atomic_exchange(rb_atomic_t *ptr, rb_atomic_t val) | |
| { | |
| rb_atomic_t old = *ptr; | |
| *ptr = val; | |
| return old; | |
| } | |
| rb_atomic_t | |
| ruby_atomic_compare_and_swap(rb_atomic_t *ptr, rb_atomic_t cmp, | |
| rb_atomic_t newval) | |
| { | |
| rb_atomic_t old = *ptr; | |
| if (old == cmp) { | |
| *ptr = newval; | |
| } | |
| return old; | |
| } | |
| #endif | |
| #define FOREACH_SIGNAL(sig, offset) \ | |
| for (sig = siglist + (offset); sig < siglist + numberof(siglist); ++sig) | |
| enum { LONGEST_SIGNAME = 7 }; /* MIGRATE and RETRACT */ | |
| static const struct signals { | |
| char signm[LONGEST_SIGNAME + 1]; | |
| int signo; | |
| } siglist [] = { | |
| {"EXIT", 0}, | |
| #ifdef SIGHUP | |
| {"HUP", SIGHUP}, | |
| #endif | |
| {"INT", SIGINT}, | |
| #ifdef SIGQUIT | |
| {"QUIT", SIGQUIT}, | |
| #endif | |
| #ifdef SIGILL | |
| {"ILL", SIGILL}, | |
| #endif | |
| #ifdef SIGTRAP | |
| {"TRAP", SIGTRAP}, | |
| #endif | |
| #ifdef SIGABRT | |
| {"ABRT", SIGABRT}, | |
| #endif | |
| #ifdef SIGIOT | |
| {"IOT", SIGIOT}, | |
| #endif | |
| #ifdef SIGEMT | |
| {"EMT", SIGEMT}, | |
| #endif | |
| #ifdef SIGFPE | |
| {"FPE", SIGFPE}, | |
| #endif | |
| #ifdef SIGKILL | |
| {"KILL", SIGKILL}, | |
| #endif | |
| #ifdef SIGBUS | |
| {"BUS", SIGBUS}, | |
| #endif | |
| #ifdef SIGSEGV | |
| {"SEGV", SIGSEGV}, | |
| #endif | |
| #ifdef SIGSYS | |
| {"SYS", SIGSYS}, | |
| #endif | |
| #ifdef SIGPIPE | |
| {"PIPE", SIGPIPE}, | |
| #endif | |
| #ifdef SIGALRM | |
| {"ALRM", SIGALRM}, | |
| #endif | |
| #ifdef SIGTERM | |
| {"TERM", SIGTERM}, | |
| #endif | |
| #ifdef SIGURG | |
| {"URG", SIGURG}, | |
| #endif | |
| #ifdef SIGSTOP | |
| {"STOP", SIGSTOP}, | |
| #endif | |
| #ifdef SIGTSTP | |
| {"TSTP", SIGTSTP}, | |
| #endif | |
| #ifdef SIGCONT | |
| {"CONT", SIGCONT}, | |
| #endif | |
| #if RUBY_SIGCHLD | |
| {"CHLD", RUBY_SIGCHLD }, | |
| {"CLD", RUBY_SIGCHLD }, | |
| #endif | |
| #ifdef SIGTTIN | |
| {"TTIN", SIGTTIN}, | |
| #endif | |
| #ifdef SIGTTOU | |
| {"TTOU", SIGTTOU}, | |
| #endif | |
| #ifdef SIGIO | |
| {"IO", SIGIO}, | |
| #endif | |
| #ifdef SIGXCPU | |
| {"XCPU", SIGXCPU}, | |
| #endif | |
| #ifdef SIGXFSZ | |
| {"XFSZ", SIGXFSZ}, | |
| #endif | |
| #ifdef SIGVTALRM | |
| {"VTALRM", SIGVTALRM}, | |
| #endif | |
| #ifdef SIGPROF | |
| {"PROF", SIGPROF}, | |
| #endif | |
| #ifdef SIGWINCH | |
| {"WINCH", SIGWINCH}, | |
| #endif | |
| #ifdef SIGUSR1 | |
| {"USR1", SIGUSR1}, | |
| #endif | |
| #ifdef SIGUSR2 | |
| {"USR2", SIGUSR2}, | |
| #endif | |
| #ifdef SIGLOST | |
| {"LOST", SIGLOST}, | |
| #endif | |
| #ifdef SIGMSG | |
| {"MSG", SIGMSG}, | |
| #endif | |
| #ifdef SIGPWR | |
| {"PWR", SIGPWR}, | |
| #endif | |
| #ifdef SIGPOLL | |
| {"POLL", SIGPOLL}, | |
| #endif | |
| #ifdef SIGDANGER | |
| {"DANGER", SIGDANGER}, | |
| #endif | |
| #ifdef SIGMIGRATE | |
| {"MIGRATE", SIGMIGRATE}, | |
| #endif | |
| #ifdef SIGPRE | |
| {"PRE", SIGPRE}, | |
| #endif | |
| #ifdef SIGGRANT | |
| {"GRANT", SIGGRANT}, | |
| #endif | |
| #ifdef SIGRETRACT | |
| {"RETRACT", SIGRETRACT}, | |
| #endif | |
| #ifdef SIGSOUND | |
| {"SOUND", SIGSOUND}, | |
| #endif | |
| #ifdef SIGINFO | |
| {"INFO", SIGINFO}, | |
| #endif | |
| }; | |
| static const char signame_prefix[] = "SIG"; | |
| static const int signame_prefix_len = 3; | |
| static int | |
| signm2signo(VALUE *sig_ptr, int negative, int exit, int *prefix_ptr) | |
| { | |
| const struct signals *sigs; | |
| VALUE vsig = *sig_ptr; | |
| const char *nm; | |
| long len, nmlen; | |
| int prefix = 0; | |
| if (RB_SYMBOL_P(vsig)) { | |
| *sig_ptr = vsig = rb_sym2str(vsig); | |
| } | |
| else if (!RB_TYPE_P(vsig, T_STRING)) { | |
| VALUE str = rb_check_string_type(vsig); | |
| if (NIL_P(str)) { | |
| rb_raise(rb_eArgError, "bad signal type %s", | |
| rb_obj_classname(vsig)); | |
| } | |
| *sig_ptr = vsig = str; | |
| } | |
| rb_must_asciicompat(vsig); | |
| RSTRING_GETMEM(vsig, nm, len); | |
| if (memchr(nm, '\0', len)) { | |
| rb_raise(rb_eArgError, "signal name with null byte"); | |
| } | |
| if (len > 0 && nm[0] == '-') { | |
| if (!negative) | |
| rb_raise(rb_eArgError, "negative signal name: % "PRIsVALUE, vsig); | |
| prefix = 1; | |
| } | |
| else { | |
| negative = 0; | |
| } | |
| if (len >= prefix + signame_prefix_len) { | |
| if (memcmp(nm + prefix, signame_prefix, signame_prefix_len) == 0) | |
| prefix += signame_prefix_len; | |
| } | |
| if (len <= (long)prefix) { | |
| goto unsupported; | |
| } | |
| if (prefix_ptr) *prefix_ptr = prefix; | |
| nmlen = len - prefix; | |
| nm += prefix; | |
| if (nmlen > LONGEST_SIGNAME) goto unsupported; | |
| FOREACH_SIGNAL(sigs, !exit) { | |
| if (memcmp(sigs->signm, nm, nmlen) == 0 && | |
| sigs->signm[nmlen] == '\0') { | |
| return negative ? -sigs->signo : sigs->signo; | |
| } | |
| } | |
| unsupported: | |
| if (prefix == signame_prefix_len) { | |
| prefix = 0; | |
| } | |
| else if (prefix > signame_prefix_len) { | |
| prefix -= signame_prefix_len; | |
| len -= prefix; | |
| vsig = rb_str_subseq(vsig, prefix, len); | |
| prefix = 0; | |
| } | |
| else { | |
| len -= prefix; | |
| vsig = rb_str_subseq(vsig, prefix, len); | |
| prefix = signame_prefix_len; | |
| } | |
| rb_raise(rb_eArgError, "unsupported signal `%.*s%"PRIsVALUE"'", | |
| prefix, signame_prefix, vsig); | |
| UNREACHABLE_RETURN(0); | |
| } | |
| static const char* | |
| signo2signm(int no) | |
| { | |
| const struct signals *sigs; | |
| FOREACH_SIGNAL(sigs, 0) { | |
| if (sigs->signo == no) | |
| return sigs->signm; | |
| } | |
| return 0; | |
| } | |
| /* | |
| * call-seq: | |
| * Signal.signame(signo) -> string or nil | |
| * | |
| * Convert signal number to signal name. | |
| * Returns +nil+ if the signo is an invalid signal number. | |
| * | |
| * Signal.trap("INT") { |signo| puts Signal.signame(signo) } | |
| * Process.kill("INT", 0) | |
| * | |
| * <em>produces:</em> | |
| * | |
| * INT | |
| */ | |
| static VALUE | |
| sig_signame(VALUE recv, VALUE signo) | |
| { | |
| const char *signame = signo2signm(NUM2INT(signo)); | |
| if (!signame) return Qnil; | |
| return rb_str_new_cstr(signame); | |
| } | |
| const char * | |
| ruby_signal_name(int no) | |
| { | |
| return signo2signm(no); | |
| } | |
| static VALUE | |
| rb_signo2signm(int signo) | |
| { | |
| const char *const signm = signo2signm(signo); | |
| if (signm) { | |
| return rb_sprintf("SIG%s", signm); | |
| } | |
| else { | |
| return rb_sprintf("SIG%u", signo); | |
| } | |
| } | |
| /* | |
| * call-seq: | |
| * SignalException.new(sig_name) -> signal_exception | |
| * SignalException.new(sig_number [, name]) -> signal_exception | |
| * | |
| * Construct a new SignalException object. +sig_name+ should be a known | |
| * signal name. | |
| */ | |
| static VALUE | |
| esignal_init(int argc, VALUE *argv, VALUE self) | |
| { | |
| int argnum = 1; | |
| VALUE sig = Qnil; | |
| int signo; | |
| if (argc > 0) { | |
| sig = rb_check_to_integer(argv[0], "to_int"); | |
| if (!NIL_P(sig)) argnum = 2; | |
| else sig = argv[0]; | |
| } | |
| rb_check_arity(argc, 1, argnum); | |
| if (argnum == 2) { | |
| signo = NUM2INT(sig); | |
| if (signo < 0 || signo > NSIG) { | |
| rb_raise(rb_eArgError, "invalid signal number (%d)", signo); | |
| } | |
| if (argc > 1) { | |
| sig = argv[1]; | |
| } | |
| else { | |
| sig = rb_signo2signm(signo); | |
| } | |
| } | |
| else { | |
| int prefix; | |
| signo = signm2signo(&sig, FALSE, FALSE, &prefix); | |
| if (prefix != signame_prefix_len) { | |
| sig = rb_str_append(rb_str_new_cstr("SIG"), sig); | |
| } | |
| } | |
| rb_call_super(1, &sig); | |
| rb_ivar_set(self, id_signo, INT2NUM(signo)); | |
| return self; | |
| } | |
| /* | |
| * call-seq: | |
| * signal_exception.signo -> num | |
| * | |
| * Returns a signal number. | |
| */ | |
| static VALUE | |
| esignal_signo(VALUE self) | |
| { | |
| return rb_ivar_get(self, id_signo); | |
| } | |
| /* :nodoc: */ | |
| static VALUE | |
| interrupt_init(int argc, VALUE *argv, VALUE self) | |
| { | |
| VALUE args[2]; | |
| args[0] = INT2FIX(SIGINT); | |
| args[1] = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil; | |
| return rb_call_super(2, args); | |
| } | |
| void rb_malloc_info_show_results(void); /* gc.c */ | |
| void | |
| ruby_default_signal(int sig) | |
| { | |
| #if USE_DEBUG_COUNTER | |
| rb_debug_counter_show_results("killed by signal."); | |
| #endif | |
| rb_malloc_info_show_results(); | |
| signal(sig, SIG_DFL); | |
| raise(sig); | |
| } | |
| static void sighandler(int sig); | |
| static int signal_ignored(int sig); | |
| static void signal_enque(int sig); | |
| VALUE | |
| rb_f_kill(int argc, const VALUE *argv) | |
| { | |
| #ifndef HAVE_KILLPG | |
| #define killpg(pg, sig) kill(-(pg), (sig)) | |
| #endif | |
| int sig; | |
| int i; | |
| VALUE str; | |
| rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS); | |
| if (FIXNUM_P(argv[0])) { | |
| sig = FIX2INT(argv[0]); | |
| } | |
| else { | |
| str = argv[0]; | |
| sig = signm2signo(&str, TRUE, FALSE, NULL); | |
| } | |
| if (argc <= 1) return INT2FIX(0); | |
| if (sig < 0) { | |
| sig = -sig; | |
| for (i=1; i<argc; i++) { | |
| if (killpg(NUM2PIDT(argv[i]), sig) < 0) | |
| rb_sys_fail(0); | |
| } | |
| } | |
| else { | |
| const rb_pid_t self = (GET_THREAD() == GET_VM()->ractor.main_thread) ? getpid() : -1; | |
| int wakeup = 0; | |
| for (i=1; i<argc; i++) { | |
| rb_pid_t pid = NUM2PIDT(argv[i]); | |
| if ((sig != 0) && (self != -1) && (pid == self)) { | |
| int t; | |
| /* | |
| * When target pid is self, many caller assume signal will be | |
| * delivered immediately and synchronously. | |
| */ | |
| switch (sig) { | |
| case SIGSEGV: | |
| #ifdef SIGBUS | |
| case SIGBUS: | |
| #endif | |
| #ifdef SIGKILL | |
| case SIGKILL: | |
| #endif | |
| #ifdef SIGILL | |
| case SIGILL: | |
| #endif | |
| #ifdef SIGFPE | |
| case SIGFPE: | |
| #endif | |
| #ifdef SIGSTOP | |
| case SIGSTOP: | |
| #endif | |
| kill(pid, sig); | |
| break; | |
| default: | |
| t = signal_ignored(sig); | |
| if (t) { | |
| if (t < 0 && kill(pid, sig)) | |
| rb_sys_fail(0); | |
| break; | |
| } | |
| signal_enque(sig); | |
| wakeup = 1; | |
| } | |
| } | |
| else if (kill(pid, sig) < 0) { | |
| rb_sys_fail(0); | |
| } | |
| } | |
| if (wakeup) { | |
| rb_threadptr_check_signal(GET_VM()->ractor.main_thread); | |
| } | |
| } | |
| rb_thread_execute_interrupts(rb_thread_current()); | |
| return INT2FIX(i-1); | |
| } | |
| static struct { | |
| rb_atomic_t cnt[RUBY_NSIG]; | |
| rb_atomic_t size; | |
| } signal_buff; | |
| #if RUBY_SIGCHLD | |
| volatile unsigned int ruby_nocldwait; | |
| #endif | |
| #define sighandler_t ruby_sighandler_t | |
| #ifdef USE_SIGALTSTACK | |
| typedef void ruby_sigaction_t(int, siginfo_t*, void*); | |
| #define SIGINFO_ARG , siginfo_t *info, void *ctx | |
| #define SIGINFO_CTX ctx | |
| #else | |
| typedef void ruby_sigaction_t(int); | |
| #define SIGINFO_ARG | |
| #define SIGINFO_CTX 0 | |
| #endif | |
| #ifdef USE_SIGALTSTACK | |
| /* XXX: BSD_vfprintf() uses >1500B stack and x86-64 need >5KiB stack. */ | |
| #define RUBY_SIGALTSTACK_SIZE (16*1024) | |
| static int | |
| rb_sigaltstack_size(void) | |
| { | |
| int size = RUBY_SIGALTSTACK_SIZE; | |
| #ifdef MINSIGSTKSZ | |
| { | |
| int minsigstksz = (int)MINSIGSTKSZ; | |
| if (size < minsigstksz) | |
| size = minsigstksz; | |
| } | |
| #endif | |
| #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) | |
| { | |
| int pagesize; | |
| pagesize = (int)sysconf(_SC_PAGE_SIZE); | |
| if (size < pagesize) | |
| size = pagesize; | |
| } | |
| #endif | |
| return size; | |
| } | |
| static int rb_sigaltstack_size_value = 0; | |
| void * | |
| rb_allocate_sigaltstack(void) | |
| { | |
| void *altstack; | |
| if (!rb_sigaltstack_size_value) { | |
| rb_sigaltstack_size_value = rb_sigaltstack_size(); | |
| } | |
| altstack = malloc(rb_sigaltstack_size_value); | |
| if (!altstack) rb_memerror(); | |
| return altstack; | |
| } | |
| /* alternate stack for SIGSEGV */ | |
| void * | |
| rb_register_sigaltstack(void *altstack) | |
| { | |
| stack_t newSS, oldSS; | |
| newSS.ss_size = rb_sigaltstack_size_value; | |
| newSS.ss_sp = altstack; | |
| newSS.ss_flags = 0; | |
| sigaltstack(&newSS, &oldSS); /* ignore error. */ | |
| return newSS.ss_sp; | |
| } | |
| #endif /* USE_SIGALTSTACK */ | |
| #ifdef POSIX_SIGNAL | |
| static sighandler_t | |
| ruby_signal(int signum, sighandler_t handler) | |
| { | |
| struct sigaction sigact, old; | |
| #if 0 | |
| rb_trap_accept_nativethreads[signum] = 0; | |
| #endif | |
| sigemptyset(&sigact.sa_mask); | |
| #ifdef USE_SIGALTSTACK | |
| if (handler == SIG_IGN || handler == SIG_DFL) { | |
| sigact.sa_handler = handler; | |
| sigact.sa_flags = 0; | |
| } | |
| else { | |
| sigact.sa_sigaction = (ruby_sigaction_t*)handler; | |
| sigact.sa_flags = SA_SIGINFO; | |
| } | |
| #else | |
| sigact.sa_handler = handler; | |
| sigact.sa_flags = 0; | |
| #endif | |
| switch (signum) { | |
| #if RUBY_SIGCHLD | |
| case RUBY_SIGCHLD: | |
| if (handler == SIG_IGN) { | |
| ruby_nocldwait = 1; | |
| # ifdef USE_SIGALTSTACK | |
| if (sigact.sa_flags & SA_SIGINFO) { | |
| sigact.sa_sigaction = (ruby_sigaction_t*)sighandler; | |
| } | |
| else { | |
| sigact.sa_handler = sighandler; | |
| } | |
| # else | |
| sigact.sa_handler = handler; | |
| sigact.sa_flags = 0; | |
| # endif | |
| } | |
| else { | |
| ruby_nocldwait = 0; | |
| } | |
| break; | |
| #endif | |
| #if defined(SA_ONSTACK) && defined(USE_SIGALTSTACK) | |
| case SIGSEGV: | |
| #ifdef SIGBUS | |
| case SIGBUS: | |
| #endif | |
| sigact.sa_flags |= SA_ONSTACK; | |
| break; | |
| #endif | |
| } | |
| (void)VALGRIND_MAKE_MEM_DEFINED(&old, sizeof(old)); | |
| if (sigaction(signum, &sigact, &old) < 0) { | |
| return SIG_ERR; | |
| } | |
| if (old.sa_flags & SA_SIGINFO) | |
| handler = (sighandler_t)old.sa_sigaction; | |
| else | |
| handler = old.sa_handler; | |
| ASSUME(handler != SIG_ERR); | |
| return handler; | |
| } | |
| sighandler_t | |
| posix_signal(int signum, sighandler_t handler) | |
| { | |
| return ruby_signal(signum, handler); | |
| } | |
| #elif defined _WIN32 | |
| static inline sighandler_t | |
| ruby_signal(int signum, sighandler_t handler) | |
| { | |
| if (signum == SIGKILL) { | |
| errno = EINVAL; | |
| return SIG_ERR; | |
| } | |
| return signal(signum, handler); | |
| } | |
| #else /* !POSIX_SIGNAL */ | |
| #define ruby_signal(sig,handler) (/* rb_trap_accept_nativethreads[(sig)] = 0,*/ signal((sig),(handler))) | |
| #if 0 /* def HAVE_NATIVETHREAD */ | |
| static sighandler_t | |
| ruby_nativethread_signal(int signum, sighandler_t handler) | |
| { | |
| sighandler_t old; | |
| old = signal(signum, handler); | |
| rb_trap_accept_nativethreads[signum] = 1; | |
| return old; | |
| } | |
| #endif | |
| #endif | |
| static int | |
| signal_ignored(int sig) | |
| { | |
| sighandler_t func; | |
| #ifdef POSIX_SIGNAL | |
| struct sigaction old; | |
| (void)VALGRIND_MAKE_MEM_DEFINED(&old, sizeof(old)); | |
| if (sigaction(sig, NULL, &old) < 0) return FALSE; | |
| func = old.sa_handler; | |
| #else | |
| sighandler_t old = signal(sig, SIG_DFL); | |
| signal(sig, old); | |
| func = old; | |
| #endif | |
| if (func == SIG_IGN) return 1; | |
| return func == sighandler ? 0 : -1; | |
| } | |
| static void | |
| signal_enque(int sig) | |
| { | |
| ATOMIC_INC(signal_buff.cnt[sig]); | |
| ATOMIC_INC(signal_buff.size); | |
| } | |
| #if RUBY_SIGCHLD | |
| static rb_atomic_t sigchld_hit; | |
| /* destructive getter than simple predicate */ | |
| # define GET_SIGCHLD_HIT() ATOMIC_EXCHANGE(sigchld_hit, 0) | |
| #else | |
| # define GET_SIGCHLD_HIT() 0 | |
| #endif | |
| static void | |
| sighandler(int sig) | |
| { | |
| int old_errnum = errno; | |
| /* the VM always needs to handle SIGCHLD for rb_waitpid */ | |
| if (sig == RUBY_SIGCHLD) { | |
| #if RUBY_SIGCHLD | |
| rb_vm_t *vm = GET_VM(); | |
| ATOMIC_EXCHANGE(sigchld_hit, 1); | |
| /* avoid spurious wakeup in main thread if and only if nobody uses trap(:CHLD) */ | |
| if (vm && ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig])) { | |
| signal_enque(sig); | |
| } | |
| #endif | |
| } | |
| else { | |
| signal_enque(sig); | |
| } | |
| rb_thread_wakeup_timer_thread(sig); | |
| #if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL) | |
| ruby_signal(sig, sighandler); | |
| #endif | |
| errno = old_errnum; | |
| } | |
| int | |
| rb_signal_buff_size(void) | |
| { | |
| return signal_buff.size; | |
| } | |
| static void | |
| rb_disable_interrupt(void) | |
| { | |
| #ifdef HAVE_PTHREAD_SIGMASK | |
| sigset_t mask; | |
| sigfillset(&mask); | |
| pthread_sigmask(SIG_SETMASK, &mask, NULL); | |
| #endif | |
| } | |
| static void | |
| rb_enable_interrupt(void) | |
| { | |
| #ifdef HAVE_PTHREAD_SIGMASK | |
| sigset_t mask; | |
| sigemptyset(&mask); | |
| pthread_sigmask(SIG_SETMASK, &mask, NULL); | |
| #endif | |
| } | |
| int | |
| rb_get_next_signal(void) | |
| { | |
| int i, sig = 0; | |
| if (signal_buff.size != 0) { | |
| for (i=1; i<RUBY_NSIG; i++) { | |
| if (signal_buff.cnt[i] > 0) { | |
| ATOMIC_DEC(signal_buff.cnt[i]); | |
| ATOMIC_DEC(signal_buff.size); | |
| sig = i; | |
| break; | |
| } | |
| } | |
| } | |
| return sig; | |
| } | |
| #if defined SIGSEGV || defined SIGBUS || defined SIGILL || defined SIGFPE | |
| static const char *received_signal; | |
| # define clear_received_signal() (void)(ruby_disable_gc = 0, received_signal = 0) | |
| #else | |
| # define clear_received_signal() ((void)0) | |
| #endif | |
| #if defined(USE_SIGALTSTACK) || defined(_WIN32) | |
| NORETURN(void rb_ec_stack_overflow(rb_execution_context_t *ec, int crit)); | |
| # if defined __HAIKU__ | |
| # define USE_UCONTEXT_REG 1 | |
| # elif !(defined(HAVE_UCONTEXT_H) && (defined __i386__ || defined __x86_64__ || defined __amd64__)) | |
| # elif defined __linux__ | |
| # define USE_UCONTEXT_REG 1 | |
| # elif defined __APPLE__ | |
| # define USE_UCONTEXT_REG 1 | |
| # elif defined __FreeBSD__ | |
| # define USE_UCONTEXT_REG 1 | |
| # endif | |
| #if defined(HAVE_PTHREAD_SIGMASK) | |
| # define ruby_sigunmask pthread_sigmask | |
| #elif defined(HAVE_SIGPROCMASK) | |
| # define ruby_sigunmask sigprocmask | |
| #endif | |
| static void | |
| reset_sigmask(int sig) | |
| { | |
| #if defined(ruby_sigunmask) | |
| sigset_t mask; | |
| #endif | |
| clear_received_signal(); | |
| #if defined(ruby_sigunmask) | |
| sigemptyset(&mask); | |
| sigaddset(&mask, sig); | |
| if (ruby_sigunmask(SIG_UNBLOCK, &mask, NULL)) { | |
| rb_bug_errno(STRINGIZE(ruby_sigunmask)":unblock", errno); | |
| } | |
| #endif | |
| } | |
| # ifdef USE_UCONTEXT_REG | |
| static void | |
| check_stack_overflow(int sig, const uintptr_t addr, const ucontext_t *ctx) | |
| { | |
| const DEFINE_MCONTEXT_PTR(mctx, ctx); | |
| # if defined __linux__ | |
| # if defined REG_RSP | |
| const greg_t sp = mctx->gregs[REG_RSP]; | |
| const greg_t bp = mctx->gregs[REG_RBP]; | |
| # else | |
| const greg_t sp = mctx->gregs[REG_ESP]; | |
| const greg_t bp = mctx->gregs[REG_EBP]; | |
| # endif | |
| # elif defined __APPLE__ | |
| # if __DARWIN_UNIX03 | |
| # define MCTX_SS_REG(reg) __ss.__##reg | |
| # else | |
| # define MCTX_SS_REG(reg) ss.reg | |
| # endif | |
| # if defined(__LP64__) | |
| const uintptr_t sp = mctx->MCTX_SS_REG(rsp); | |
| const uintptr_t bp = mctx->MCTX_SS_REG(rbp); | |
| # else | |
| const uintptr_t sp = mctx->MCTX_SS_REG(esp); | |
| const uintptr_t bp = mctx->MCTX_SS_REG(ebp); | |
| # endif | |
| # elif defined __FreeBSD__ | |
| # if defined(__amd64__) | |
| const __register_t sp = mctx->mc_rsp; | |
| const __register_t bp = mctx->mc_rbp; | |
| # else | |
| const __register_t sp = mctx->mc_esp; | |
| const __register_t bp = mctx->mc_ebp; | |
| # endif | |
| # elif defined __HAIKU__ | |
| # if defined(__amd64__) | |
| const unsigned long sp = mctx->rsp; | |
| const unsigned long bp = mctx->rbp; | |
| # else | |
| const unsigned long sp = mctx->esp; | |
| const unsigned long bp = mctx->ebp; | |
| # endif | |
| # endif | |
| enum {pagesize = 4096}; | |
| const uintptr_t sp_page = (uintptr_t)sp / pagesize; | |
| const uintptr_t bp_page = (uintptr_t)bp / pagesize; | |
| const uintptr_t fault_page = addr / pagesize; | |
| /* SP in ucontext is not decremented yet when `push` failed, so | |
| * the fault page can be the next. */ | |
| if (sp_page == fault_page || sp_page == fault_page + 1 || | |
| (sp_page <= fault_page && fault_page <= bp_page)) { | |
| rb_execution_context_t *ec = GET_EC(); | |
| int crit = FALSE; | |
| int uplevel = roomof(pagesize, sizeof(*ec->tag)) / 2; /* XXX: heuristic */ | |
| while ((uintptr_t)ec->tag->buf / pagesize <= fault_page + 1) { | |
| /* drop the last tag if it is close to the fault, | |
| * otherwise it can cause stack overflow again at the same | |
| * place. */ | |
| if ((crit = (!ec->tag->prev || !--uplevel)) != FALSE) break; | |
| ec->tag = ec->tag->prev; | |
| } | |
| reset_sigmask(sig); | |
| rb_ec_stack_overflow(ec, crit); | |
| } | |
| } | |
| # else | |
| static void | |
| check_stack_overflow(int sig, const void *addr) | |
| { | |
| int ruby_stack_overflowed_p(const rb_thread_t *, const void *); | |
| rb_thread_t *th = GET_THREAD(); | |
| if (ruby_stack_overflowed_p(th, addr)) { | |
| reset_sigmask(sig); | |
| rb_ec_stack_overflow(th->ec, FALSE); | |
| } | |
| } | |
| # endif | |
| # ifdef _WIN32 | |
| # define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, 0) | |
| # else | |
| # define FAULT_ADDRESS info->si_addr | |
| # ifdef USE_UCONTEXT_REG | |
| # define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, (uintptr_t)FAULT_ADDRESS, ctx) | |
| # else | |
| # define CHECK_STACK_OVERFLOW() check_stack_overflow(sig, FAULT_ADDRESS) | |
| # endif | |
| # define MESSAGE_FAULT_ADDRESS " at %p", FAULT_ADDRESS | |
| # endif | |
| #else | |
| # define CHECK_STACK_OVERFLOW() (void)0 | |
| #endif | |
| #ifndef MESSAGE_FAULT_ADDRESS | |
| # define MESSAGE_FAULT_ADDRESS | |
| #endif | |
| #if defined SIGSEGV || defined SIGBUS || defined SIGILL || defined SIGFPE | |
| NOINLINE(static void check_reserved_signal_(const char *name, size_t name_len)); | |
| /* noinine to reduce stack usage in signal handers */ | |
| #define check_reserved_signal(name) check_reserved_signal_(name, sizeof(name)-1) | |
| #ifdef SIGBUS | |
| static sighandler_t default_sigbus_handler; | |
| NORETURN(static ruby_sigaction_t sigbus); | |
| static void | |
| sigbus(int sig SIGINFO_ARG) | |
| { | |
| check_reserved_signal("BUS"); | |
| /* | |
| * Mac OS X makes KERN_PROTECTION_FAILURE when thread touch guard page. | |
| * and it's delivered as SIGBUS instead of SIGSEGV to userland. It's crazy | |
| * wrong IMHO. but anyway we have to care it. Sigh. | |
| */ | |
| /* Seems Linux also delivers SIGBUS. */ | |
| #if defined __APPLE__ || defined __linux__ | |
| CHECK_STACK_OVERFLOW(); | |
| #endif | |
| rb_bug_for_fatal_signal(default_sigbus_handler, sig, SIGINFO_CTX, "Bus Error" MESSAGE_FAULT_ADDRESS); | |
| } | |
| #endif | |
| #ifdef SIGSEGV | |
| static sighandler_t default_sigsegv_handler; | |
| NORETURN(static ruby_sigaction_t sigsegv); | |
| static void | |
| sigsegv(int sig SIGINFO_ARG) | |
| { | |
| check_reserved_signal("SEGV"); | |
| CHECK_STACK_OVERFLOW(); | |
| rb_bug_for_fatal_signal(default_sigsegv_handler, sig, SIGINFO_CTX, "Segmentation fault" MESSAGE_FAULT_ADDRESS); | |
| } | |
| #endif | |
| #ifdef SIGILL | |
| static sighandler_t default_sigill_handler; | |
| NORETURN(static ruby_sigaction_t sigill); | |
| static void | |
| sigill(int sig SIGINFO_ARG) | |
| { | |
| check_reserved_signal("ILL"); | |
| #if defined __APPLE__ || defined __linux__ | |
| CHECK_STACK_OVERFLOW(); | |
| #endif | |
| rb_bug_for_fatal_signal(default_sigill_handler, sig, SIGINFO_CTX, "Illegal instruction" MESSAGE_FAULT_ADDRESS); | |
| } | |
| #endif | |
| #ifndef __sun | |
| NORETURN(static void ruby_abort(void)); | |
| #endif | |
| static void | |
| ruby_abort(void) | |
| { | |
| #ifdef __sun | |
| /* Solaris's abort() is async signal unsafe. Of course, it is not | |
| * POSIX compliant. | |
| */ | |
| raise(SIGABRT); | |
| #else | |
| abort(); | |
| #endif | |
| } | |
| static void | |
| check_reserved_signal_(const char *name, size_t name_len) | |
| { | |
| const char *prev = ATOMIC_PTR_EXCHANGE(received_signal, name); | |
| if (prev) { | |
| ssize_t RB_UNUSED_VAR(err); | |
| #define NOZ(name, str) name[sizeof(str)-1] = str | |
| static const char NOZ(msg1, " received in "); | |
| static const char NOZ(msg2, " handler\n"); | |
| #ifdef HAVE_WRITEV | |
| struct iovec iov[4]; | |
| iov[0].iov_base = (void *)name; | |
| iov[0].iov_len = name_len; | |
| iov[1].iov_base = (void *)msg1; | |
| iov[1].iov_len = sizeof(msg1); | |
| iov[2].iov_base = (void *)prev; | |
| iov[2].iov_len = strlen(prev); | |
| iov[3].iov_base = (void *)msg2; | |
| iov[3].iov_len = sizeof(msg2); | |
| err = writev(2, iov, 4); | |
| #else | |
| err = write(2, name, name_len); | |
| err = write(2, msg1, sizeof(msg1)); | |
| err = write(2, prev, strlen(prev)); | |
| err = write(2, msg2, sizeof(msg2)); | |
| #endif | |
| ruby_abort(); | |
| } | |
| ruby_disable_gc = 1; | |
| } | |
| #endif | |
| #if defined SIGPIPE || defined SIGSYS | |
| static void | |
| sig_do_nothing(int sig) | |
| { | |
| } | |
| #endif | |
| static int | |
| signal_exec(VALUE cmd, int sig) | |
| { | |
| rb_execution_context_t *ec = GET_EC(); | |
| volatile rb_atomic_t old_interrupt_mask = ec->interrupt_mask; | |
| enum ruby_tag_type state; | |
| /* | |
| * workaround the following race: | |
| * 1. signal_enque queues signal for execution | |
| * 2. user calls trap(sig, "IGNORE"), setting SIG_IGN | |
| * 3. rb_signal_exec runs on queued signal | |
| */ | |
| if (IMMEDIATE_P(cmd)) | |
| return FALSE; | |
| ec->interrupt_mask |= TRAP_INTERRUPT_MASK; | |
| EC_PUSH_TAG(ec); | |
| if ((state = EC_EXEC_TAG()) == TAG_NONE) { | |
| VALUE signum = INT2NUM(sig); | |
| rb_eval_cmd_kw(cmd, rb_ary_new3(1, signum), RB_NO_KEYWORDS); | |
| } | |
| EC_POP_TAG(); | |
| ec = GET_EC(); | |
| ec->interrupt_mask = old_interrupt_mask; | |
| if (state) { | |
| /* XXX: should be replaced with rb_threadptr_pending_interrupt_enque() */ | |
| EC_JUMP_TAG(ec, state); | |
| } | |
| return TRUE; | |
| } | |
| void | |
| rb_vm_trap_exit(rb_vm_t *vm) | |
| { | |
| VALUE trap_exit = vm->trap_list.cmd[0]; | |
| if (trap_exit) { | |
| vm->trap_list.cmd[0] = 0; | |
| signal_exec(trap_exit, 0); | |
| } | |
| } | |
| void ruby_waitpid_all(rb_vm_t *); /* process.c */ | |
| void | |
| ruby_sigchld_handler(rb_vm_t *vm) | |
| { | |
| if (SIGCHLD_LOSSY || GET_SIGCHLD_HIT()) { | |
| ruby_waitpid_all(vm); | |
| } | |
| } | |
| /* returns true if a trap handler was run, false otherwise */ | |
| int | |
| rb_signal_exec(rb_thread_t *th, int sig) | |
| { | |
| rb_vm_t *vm = GET_VM(); | |
| VALUE cmd = vm->trap_list.cmd[sig]; | |
| if (cmd == 0) { | |
| switch (sig) { | |
| case SIGINT: | |
| rb_interrupt(); | |
| break; | |
| #ifdef SIGHUP | |
| case SIGHUP: | |
| #endif | |
| #ifdef SIGQUIT | |
| case SIGQUIT: | |
| #endif | |
| #ifdef SIGTERM | |
| case SIGTERM: | |
| #endif | |
| #ifdef SIGALRM | |
| case SIGALRM: | |
| #endif | |
| #ifdef SIGUSR1 | |
| case SIGUSR1: | |
| #endif | |
| #ifdef SIGUSR2 | |
| case SIGUSR2: | |
| #endif | |
| rb_threadptr_signal_raise(th, sig); | |
| break; | |
| } | |
| } | |
| else if (cmd == Qundef) { | |
| rb_threadptr_signal_exit(th); | |
| } | |
| else { | |
| return signal_exec(cmd, sig); | |
| } | |
| return FALSE; | |
| } | |
| static sighandler_t | |
| default_handler(int sig) | |
| { | |
| sighandler_t func; | |
| switch (sig) { | |
| case SIGINT: | |
| #ifdef SIGHUP | |
| case SIGHUP: | |
| #endif | |
| #ifdef SIGQUIT | |
| case SIGQUIT: | |
| #endif | |
| #ifdef SIGTERM | |
| case SIGTERM: | |
| #endif | |
| #ifdef SIGALRM | |
| case SIGALRM: | |
| #endif | |
| #ifdef SIGUSR1 | |
| case SIGUSR1: | |
| #endif | |
| #ifdef SIGUSR2 | |
| case SIGUSR2: | |
| #endif | |
| #if RUBY_SIGCHLD | |
| case RUBY_SIGCHLD: | |
| #endif | |
| func = sighandler; | |
| break; | |
| #ifdef SIGBUS | |
| case SIGBUS: | |
| func = (sighandler_t)sigbus; | |
| break; | |
| #endif | |
| #ifdef SIGSEGV | |
| case SIGSEGV: | |
| func = (sighandler_t)sigsegv; | |
| break; | |
| #endif | |
| #ifdef SIGPIPE | |
| case SIGPIPE: | |
| func = sig_do_nothing; | |
| break; | |
| #endif | |
| #ifdef SIGSYS | |
| case SIGSYS: | |
| func = sig_do_nothing; | |
| break; | |
| #endif | |
| default: | |
| func = SIG_DFL; | |
| break; | |
| } | |
| return func; | |
| } | |
| static sighandler_t | |
| trap_handler(VALUE *cmd, int sig) | |
| { | |
| sighandler_t func = sighandler; | |
| VALUE command; | |
| if (NIL_P(*cmd)) { | |
| func = SIG_IGN; | |
| } | |
| else { | |
| command = rb_check_string_type(*cmd); | |
| if (NIL_P(command) && SYMBOL_P(*cmd)) { | |
| command = rb_sym2str(*cmd); | |
| if (!command) rb_raise(rb_eArgError, "bad handler"); | |
| } | |
| if (!NIL_P(command)) { | |
| const char *cptr; | |
| long len; | |
| StringValue(command); | |
| *cmd = command; | |
| RSTRING_GETMEM(command, cptr, len); | |
| switch (len) { | |
| sig_ign: | |
| func = SIG_IGN; | |
| *cmd = Qtrue; | |
| break; | |
| sig_dfl: | |
| func = default_handler(sig); | |
| *cmd = 0; | |
| break; | |
| case 0: | |
| goto sig_ign; | |
| break; | |
| case 14: | |
| if (memcmp(cptr, "SYSTEM_DEFAULT", 14) == 0) { | |
| if (sig == RUBY_SIGCHLD) { | |
| goto sig_dfl; | |
| } | |
| func = SIG_DFL; | |
| *cmd = 0; | |
| } | |
| break; | |
| case 7: | |
| if (memcmp(cptr, "SIG_IGN", 7) == 0) { | |
| goto sig_ign; | |
| } | |
| else if (memcmp(cptr, "SIG_DFL", 7) == 0) { | |
| goto sig_dfl; | |
| } | |
| else if (memcmp(cptr, "DEFAULT", 7) == 0) { | |
| goto sig_dfl; | |
| } | |
| break; | |
| case 6: | |
| if (memcmp(cptr, "IGNORE", 6) == 0) { | |
| goto sig_ign; | |
| } | |
| break; | |
| case 4: | |
| if (memcmp(cptr, "EXIT", 4) == 0) { | |
| *cmd = Qundef; | |
| } | |
| break; | |
| } | |
| } | |
| else { | |
| rb_proc_t *proc; | |
| GetProcPtr(*cmd, proc); | |
| (void)proc; | |
| } | |
| } | |
| return func; | |
| } | |
| static int | |
| trap_signm(VALUE vsig) | |
| { | |
| int sig = -1; | |
| if (FIXNUM_P(vsig)) { | |
| sig = FIX2INT(vsig); | |
| if (sig < 0 || sig >= NSIG) { | |
| rb_raise(rb_eArgError, "invalid signal number (%d)", sig); | |
| } | |
| } | |
| else { | |
| sig = signm2signo(&vsig, FALSE, TRUE, NULL); | |
| } | |
| return sig; | |
| } | |
| static VALUE | |
| trap(int sig, sighandler_t func, VALUE command) | |
| { | |
| sighandler_t oldfunc; | |
| VALUE oldcmd; | |
| rb_vm_t *vm = GET_VM(); | |
| /* | |
| * Be careful. ruby_signal() and trap_list.cmd[sig] must be changed | |
| * atomically. In current implementation, we only need to don't call | |
| * RUBY_VM_CHECK_INTS(). | |
| */ | |
| if (sig == 0) { | |
| oldfunc = SIG_ERR; | |
| } | |
| else { | |
| oldfunc = ruby_signal(sig, func); | |
| if (oldfunc == SIG_ERR) rb_sys_fail_str(rb_signo2signm(sig)); | |
| } | |
| oldcmd = vm->trap_list.cmd[sig]; | |
| switch (oldcmd) { | |
| case 0: | |
| case Qtrue: | |
| if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE"); | |
| else if (oldfunc == SIG_DFL) oldcmd = rb_str_new2("SYSTEM_DEFAULT"); | |
| else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT"); | |
| else oldcmd = Qnil; | |
| break; | |
| case Qnil: | |
| break; | |
| case Qundef: | |
| oldcmd = rb_str_new2("EXIT"); | |
| break; | |
| } | |
| ACCESS_ONCE(VALUE, vm->trap_list.cmd[sig]) = command; | |
| return oldcmd; | |
| } | |
| static int | |
| reserved_signal_p(int signo) | |
| { | |
| /* Synchronous signal can't deliver to main thread */ | |
| #ifdef SIGSEGV | |
| if (signo == SIGSEGV) | |
| return 1; | |
| #endif | |
| #ifdef SIGBUS | |
| if (signo == SIGBUS) | |
| return 1; | |
| #endif | |
| #ifdef SIGILL | |
| if (signo == SIGILL) | |
| return 1; | |
| #endif | |
| #ifdef SIGFPE | |
| if (signo == SIGFPE) | |
| return 1; | |
| #endif | |
| /* used ubf internal see thread_pthread.c. */ | |
| #ifdef SIGVTALRM | |
| if (signo == SIGVTALRM) | |
| return 1; | |
| #endif | |
| return 0; | |
| } | |
| /* | |
| * call-seq: | |
| * Signal.trap( signal, command ) -> obj | |
| * Signal.trap( signal ) {| | block } -> obj | |
| * | |
| * Specifies the handling of signals. The first parameter is a signal | |
| * name (a string such as ``SIGALRM'', ``SIGUSR1'', and so on) or a | |
| * signal number. The characters ``SIG'' may be omitted from the | |
| * signal name. The command or block specifies code to be run when the | |
| * signal is raised. | |
| * If the command is the string ``IGNORE'' or ``SIG_IGN'', the signal | |
| * will be ignored. | |
| * If the command is ``DEFAULT'' or ``SIG_DFL'', the Ruby's default handler | |
| * will be invoked. | |
| * If the command is ``EXIT'', the script will be terminated by the signal. | |
| * If the command is ``SYSTEM_DEFAULT'', the operating system's default | |
| * handler will be invoked. | |
| * Otherwise, the given command or block will be run. | |
| * The special signal name ``EXIT'' or signal number zero will be | |
| * invoked just prior to program termination. | |
| * trap returns the previous handler for the given signal. | |
| * | |
| * Signal.trap(0, proc { puts "Terminating: #{$$}" }) | |
| * Signal.trap("CLD") { puts "Child died" } | |
| * fork && Process.wait | |
| * | |
| * produces: | |
| * Terminating: 27461 | |
| * Child died | |
| * Terminating: 27460 | |
| */ | |
| static VALUE | |
| sig_trap(int argc, VALUE *argv, VALUE _) | |
| { | |
| int sig; | |
| sighandler_t func; | |
| VALUE cmd; | |
| rb_check_arity(argc, 1, 2); | |
| sig = trap_signm(argv[0]); | |
| if (reserved_signal_p(sig)) { | |
| const char *name = signo2signm(sig); | |
| if (name) | |
| rb_raise(rb_eArgError, "can't trap reserved signal: SIG%s", name); | |
| else | |
| rb_raise(rb_eArgError, "can't trap reserved signal: %d", sig); | |
| } | |
| if (argc == 1) { | |
| cmd = rb_block_proc(); | |
| func = sighandler; | |
| } | |
| else { | |
| cmd = argv[1]; | |
| func = trap_handler(&cmd, sig); | |
| } | |
| if (rb_obj_is_proc(cmd) && | |
| !rb_ractor_main_p() && !rb_ractor_shareable_p(cmd)) { | |
| cmd = rb_proc_isolate(cmd); | |
| } | |
| return trap(sig, func, cmd); | |
| } | |
| /* | |
| * call-seq: | |
| * Signal.list -> a_hash | |
| * | |
| * Returns a list of signal names mapped to the corresponding | |
| * underlying signal numbers. | |
| * | |
| * Signal.list #=> {"EXIT"=>0, "HUP"=>1, "INT"=>2, "QUIT"=>3, "ILL"=>4, "TRAP"=>5, "IOT"=>6, "ABRT"=>6, "FPE"=>8, "KILL"=>9, "BUS"=>7, "SEGV"=>11, "SYS"=>31, "PIPE"=>13, "ALRM"=>14, "TERM"=>15, "URG"=>23, "STOP"=>19, "TSTP"=>20, "CONT"=>18, "CHLD"=>17, "CLD"=>17, "TTIN"=>21, "TTOU"=>22, "IO"=>29, "XCPU"=>24, "XFSZ"=>25, "VTALRM"=>26, "PROF"=>27, "WINCH"=>28, "USR1"=>10, "USR2"=>12, "PWR"=>30, "POLL"=>29} | |
| */ | |
| static VALUE | |
| sig_list(VALUE _) | |
| { | |
| VALUE h = rb_hash_new(); | |
| const struct signals *sigs; | |
| FOREACH_SIGNAL(sigs, 0) { | |
| rb_hash_aset(h, rb_fstring_cstr(sigs->signm), INT2FIX(sigs->signo)); | |
| } | |
| return h; | |
| } | |
| #define INSTALL_SIGHANDLER(cond, signame, signum) do { \ | |
| static const char failed[] = "failed to install "signame" handler"; \ | |
| if (!(cond)) break; \ | |
| if (reserved_signal_p(signum)) rb_bug(failed); \ | |
| perror(failed); \ | |
| } while (0) | |
| static int | |
| install_sighandler_core(int signum, sighandler_t handler, sighandler_t *old_handler) | |
| { | |
| sighandler_t old; | |
| old = ruby_signal(signum, handler); | |
| if (old == SIG_ERR) return -1; | |
| if (old_handler) { | |
| *old_handler = (old == SIG_DFL || old == SIG_IGN) ? 0 : old; | |
| } | |
| else { | |
| /* signal handler should be inherited during exec. */ | |
| if (old != SIG_DFL) { | |
| ruby_signal(signum, old); | |
| } | |
| } | |
| return 0; | |
| } | |
| # define install_sighandler(signum, handler) \ | |
| INSTALL_SIGHANDLER(install_sighandler_core(signum, handler, NULL), #signum, signum) | |
| # define force_install_sighandler(signum, handler, old_handler) \ | |
| INSTALL_SIGHANDLER(install_sighandler_core(signum, handler, old_handler), #signum, signum) | |
| #if RUBY_SIGCHLD | |
| static int | |
| init_sigchld(int sig) | |
| { | |
| sighandler_t oldfunc; | |
| sighandler_t func = sighandler; | |
| oldfunc = ruby_signal(sig, SIG_DFL); | |
| if (oldfunc == SIG_ERR) return -1; | |
| ruby_signal(sig, func); | |
| ACCESS_ONCE(VALUE, GET_VM()->trap_list.cmd[sig]) = 0; | |
| return 0; | |
| } | |
| # define init_sigchld(signum) \ | |
| INSTALL_SIGHANDLER(init_sigchld(signum), #signum, signum) | |
| #endif | |
| void | |
| ruby_sig_finalize(void) | |
| { | |
| sighandler_t oldfunc; | |
| oldfunc = ruby_signal(SIGINT, SIG_IGN); | |
| if (oldfunc == sighandler) { | |
| ruby_signal(SIGINT, SIG_DFL); | |
| } | |
| } | |
| int ruby_enable_coredump = 0; | |
| /* | |
| * Many operating systems allow signals to be sent to running | |
| * processes. Some signals have a defined effect on the process, while | |
| * others may be trapped at the code level and acted upon. For | |
| * example, your process may trap the USR1 signal and use it to toggle | |
| * debugging, and may use TERM to initiate a controlled shutdown. | |
| * | |
| * pid = fork do | |
| * Signal.trap("USR1") do | |
| * $debug = !$debug | |
| * puts "Debug now: #$debug" | |
| * end | |
| * Signal.trap("TERM") do | |
| * puts "Terminating..." | |
| * shutdown() | |
| * end | |
| * # . . . do some work . . . | |
| * end | |
| * | |
| * Process.detach(pid) | |
| * | |
| * # Controlling program: | |
| * Process.kill("USR1", pid) | |
| * # ... | |
| * Process.kill("USR1", pid) | |
| * # ... | |
| * Process.kill("TERM", pid) | |
| * | |
| * produces: | |
| * Debug now: true | |
| * Debug now: false | |
| * Terminating... | |
| * | |
| * The list of available signal names and their interpretation is | |
| * system dependent. Signal delivery semantics may also vary between | |
| * systems; in particular signal delivery may not always be reliable. | |
| */ | |
| void | |
| Init_signal(void) | |
| { | |
| VALUE mSignal = rb_define_module("Signal"); | |
| rb_define_global_function("trap", sig_trap, -1); | |
| rb_define_module_function(mSignal, "trap", sig_trap, -1); | |
| rb_define_module_function(mSignal, "list", sig_list, 0); | |
| rb_define_module_function(mSignal, "signame", sig_signame, 1); | |
| rb_define_method(rb_eSignal, "initialize", esignal_init, -1); | |
| rb_define_method(rb_eSignal, "signo", esignal_signo, 0); | |
| rb_alias(rb_eSignal, rb_intern_const("signm"), rb_intern_const("message")); | |
| rb_define_method(rb_eInterrupt, "initialize", interrupt_init, -1); | |
| /* At this time, there is no subthread. Then sigmask guarantee atomics. */ | |
| rb_disable_interrupt(); | |
| install_sighandler(SIGINT, sighandler); | |
| #ifdef SIGHUP | |
| install_sighandler(SIGHUP, sighandler); | |
| #endif | |
| #ifdef SIGQUIT | |
| install_sighandler(SIGQUIT, sighandler); | |
| #endif | |
| #ifdef SIGTERM | |
| install_sighandler(SIGTERM, sighandler); | |
| #endif | |
| #ifdef SIGALRM | |
| install_sighandler(SIGALRM, sighandler); | |
| #endif | |
| #ifdef SIGUSR1 | |
| install_sighandler(SIGUSR1, sighandler); | |
| #endif | |
| #ifdef SIGUSR2 | |
| install_sighandler(SIGUSR2, sighandler); | |
| #endif | |
| if (!ruby_enable_coredump) { | |
| #ifdef SIGBUS | |
| force_install_sighandler(SIGBUS, (sighandler_t)sigbus, &default_sigbus_handler); | |
| #endif | |
| #ifdef SIGILL | |
| force_install_sighandler(SIGILL, (sighandler_t)sigill, &default_sigill_handler); | |
| #endif | |
| #ifdef SIGSEGV | |
| RB_ALTSTACK_INIT(GET_VM()->main_altstack, rb_allocate_sigaltstack()); | |
| force_install_sighandler(SIGSEGV, (sighandler_t)sigsegv, &default_sigsegv_handler); | |
| #endif | |
| } | |
| #ifdef SIGPIPE | |
| install_sighandler(SIGPIPE, sig_do_nothing); | |
| #endif | |
| #ifdef SIGSYS | |
| install_sighandler(SIGSYS, sig_do_nothing); | |
| #endif | |
| #if RUBY_SIGCHLD | |
| init_sigchld(RUBY_SIGCHLD); | |
| #endif | |
| rb_enable_interrupt(); | |
| } | |
| #if defined(HAVE_GRANTPT) | |
| extern int grantpt(int); | |
| #else | |
| static int | |
| fake_grantfd(int masterfd) | |
| { | |
| errno = ENOSYS; | |
| return -1; | |
| } | |
| #define grantpt(fd) fake_grantfd(fd) | |
| #endif | |
| int | |
| rb_grantpt(int masterfd) | |
| { | |
| if (RUBY_SIGCHLD) { | |
| rb_vm_t *vm = GET_VM(); | |
| int ret, e; | |
| /* | |
| * Prevent waitpid calls from Ruby by taking waitpid_lock. | |
| * Pedantically, grantpt(3) is undefined if a non-default | |
| * SIGCHLD handler is defined, but preventing conflicting | |
| * waitpid calls ought to be sufficient. | |
| * | |
| * We could install the default sighandler temporarily, but that | |
| * could cause SIGCHLD to be missed by other threads. Blocking | |
| * SIGCHLD won't work here, either, unless we stop and restart | |
| * timer-thread (as only timer-thread sees SIGCHLD), but that | |
| * seems like overkill. | |
| */ | |
| rb_nativethread_lock_lock(&vm->waitpid_lock); | |
| { | |
| ret = grantpt(masterfd); /* may spawn `pt_chown' and wait on it */ | |
| if (ret < 0) e = errno; | |
| } | |
| rb_nativethread_lock_unlock(&vm->waitpid_lock); | |
| if (ret < 0) errno = e; | |
| return ret; | |
| } | |
| else { | |
| return grantpt(masterfd); | |
| } | |
| } |