/********************************************************************** signal.c - $Author: yugui $ 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/ruby.h" #include "vm_core.h" #include #include #include #ifdef _WIN32 typedef LONG rb_atomic_t; # define ATOMIC_TEST(var) InterlockedExchange(&(var), 0) # define ATOMIC_SET(var, val) InterlockedExchange(&(var), (val)) # define ATOMIC_INC(var) InterlockedIncrement(&(var)) # define ATOMIC_DEC(var) InterlockedDecrement(&(var)) #else typedef int rb_atomic_t; # define ATOMIC_TEST(var) ((var) ? ((var) = 0, 1) : 0) # define ATOMIC_SET(var, val) ((var) = (val)) # define ATOMIC_INC(var) (++(var)) # define ATOMIC_DEC(var) (--(var)) #endif #ifdef __BEOS__ #undef SIGBUS #endif #if defined HAVE_SIGPROCMASK || defined HAVE_SIGSETMASK #define USE_TRAP_MASK 1 #else #define USE_TRAP_MASK 0 #endif #ifndef NSIG # define NSIG (_SIGMAX + 1) /* For QNX */ #endif static const struct signals { const char *signm; 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 SIGIOT {"IOT", SIGIOT}, #endif #ifdef SIGABRT {"ABRT", SIGABRT}, #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 #ifdef SIGCHLD {"CHLD", SIGCHLD}, #endif #ifdef SIGCLD {"CLD", SIGCLD}, #else # ifdef SIGCHLD {"CLD", SIGCHLD}, # endif #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 {NULL, 0} }; static int signm2signo(const char *nm) { const struct signals *sigs; for (sigs = siglist; sigs->signm; sigs++) if (strcmp(sigs->signm, nm) == 0) return sigs->signo; return 0; } static const char* signo2signm(int no) { const struct signals *sigs; for (sigs = siglist; sigs->signm; sigs++) if (sigs->signo == no) return sigs->signm; return 0; } const char * ruby_signal_name(int no) { return signo2signm(no); } /* * call-seq: * SignalException.new(sig) => signal_exception * * Construct a new SignalException object. +sig+ should be a known * signal name, or a signal number. */ static VALUE esignal_init(int argc, VALUE *argv, VALUE self) { int argnum = 1; VALUE sig = Qnil; int signo; const char *signm; if (argc > 0) { sig = rb_check_to_integer(argv[0], "to_int"); if (!NIL_P(sig)) argnum = 2; else sig = argv[0]; } if (argc < 1 || argnum < argc) { rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, 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 { signm = signo2signm(signo); if (signm) { sig = rb_sprintf("SIG%s", signm); } else { sig = rb_sprintf("SIG%u", signo); } } } else { signm = SYMBOL_P(sig) ? rb_id2name(SYM2ID(sig)) : StringValuePtr(sig); if (strncmp(signm, "SIG", 3) == 0) signm += 3; signo = signm2signo(signm); if (!signo) { rb_raise(rb_eArgError, "unsupported name `SIG%s'", signm); } sig = rb_sprintf("SIG%s", signm); } rb_call_super(1, &sig); rb_iv_set(self, "signo", INT2NUM(signo)); return self; } /* * call-seq: * signal_exception.signo => num * * Returns a signal number. */ static VALUE esignal_signo(VALUE self) { return rb_iv_get(self, "signo"); } static VALUE interrupt_init(int argc, VALUE *argv, VALUE self) { VALUE args[2]; args[0] = INT2FIX(SIGINT); rb_scan_args(argc, argv, "01", &args[1]); return rb_call_super(2, args); } void ruby_default_signal(int sig) { signal(sig, SIG_DFL); raise(sig); } /* * call-seq: * Process.kill(signal, pid, ...) => fixnum * * Sends the given signal to the specified process id(s), or to the * current process if _pid_ is zero. _signal_ may be an * integer signal number or a POSIX signal name (either with or without * a +SIG+ prefix). If _signal_ is negative (or starts * with a minus sign), kills process groups instead of * processes. Not all signals are available on all platforms. * * pid = fork do * Signal.trap("HUP") { puts "Ouch!"; exit } * # ... do some work ... * end * # ... * Process.kill("HUP", pid) * Process.wait * * produces: * * Ouch! */ VALUE rb_f_kill(int argc, VALUE *argv) { #ifndef HAS_KILLPG #define killpg(pg, sig) kill(-(pg), sig) #endif int negative = 0; int sig; int i; const char *s; rb_secure(2); if (argc < 2) rb_raise(rb_eArgError, "wrong number of arguments -- kill(sig, pid...)"); switch (TYPE(argv[0])) { case T_FIXNUM: sig = FIX2INT(argv[0]); break; case T_SYMBOL: s = rb_id2name(SYM2ID(argv[0])); if (!s) rb_raise(rb_eArgError, "bad signal"); goto str_signal; case T_STRING: s = RSTRING_PTR(argv[0]); if (s[0] == '-') { negative++; s++; } str_signal: if (strncmp("SIG", s, 3) == 0) s += 3; if((sig = signm2signo(s)) == 0) rb_raise(rb_eArgError, "unsupported name `SIG%s'", s); if (negative) sig = -sig; break; default: { VALUE str; str = rb_check_string_type(argv[0]); if (!NIL_P(str)) { s = RSTRING_PTR(str); goto str_signal; } rb_raise(rb_eArgError, "bad signal type %s", rb_obj_classname(argv[0])); } break; } if (sig < 0) { sig = -sig; for (i=1; i #endif void rb_disable_interrupt(void) { #ifndef _WIN32 sigset_t mask; sigfillset(&mask); sigdelset(&mask, SIGVTALRM); sigdelset(&mask, SIGSEGV); pthread_sigmask(SIG_SETMASK, &mask, NULL); #endif } void rb_enable_interrupt(void) { #ifndef _WIN32 sigset_t mask; sigemptyset(&mask); pthread_sigmask(SIG_SETMASK, &mask, NULL); #endif } int rb_get_next_signal(void) { int i, sig = 0; for (i=1; i 0) { rb_disable_interrupt(); { ATOMIC_DEC(signal_buff.cnt[i]); ATOMIC_DEC(signal_buff.size); } rb_enable_interrupt(); sig = i; break; } } return sig; } #ifdef SIGBUS static RETSIGTYPE sigbus(int sig) { rb_bug("Bus Error"); } #endif #ifdef SIGSEGV static int segv_received = 0; static RETSIGTYPE sigsegv(int sig SIGINFO_ARG) { #ifdef USE_SIGALTSTACK int ruby_stack_overflowed_p(const rb_thread_t *, const void *); NORETURN(void ruby_thread_stack_overflow(rb_thread_t *th)); rb_thread_t *th = GET_THREAD(); if (ruby_stack_overflowed_p(th, info->si_addr)) { ruby_thread_stack_overflow(th); } #endif if (segv_received) { fprintf(stderr, "SEGV recieved in SEGV handler\n"); exit(EXIT_FAILURE); } else { extern int ruby_disable_gc_stress; segv_received = 1; ruby_disable_gc_stress = 1; rb_bug("Segmentation fault"); } } #endif #ifdef SIGPIPE static RETSIGTYPE sigpipe(int sig) { /* do nothing */ } #endif static void signal_exec(VALUE cmd, int safe, int sig) { VALUE signum = INT2NUM(sig); rb_eval_cmd(cmd, rb_ary_new3(1, signum), safe); } void rb_trap_exit(void) { rb_vm_t *vm = GET_VM(); VALUE trap_exit = vm->trap_list[0].cmd; if (trap_exit) { vm->trap_list[0].cmd = 0; signal_exec(trap_exit, vm->trap_list[0].safe, 0); } } void rb_signal_exec(rb_thread_t *th, int sig) { rb_vm_t *vm = GET_VM(); VALUE cmd = vm->trap_list[sig].cmd; int safe = vm->trap_list[sig].safe; 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_thread_signal_raise(th, sig); break; } } else if (cmd == Qundef) { rb_thread_signal_exit(th); } else { signal_exec(cmd, safe, sig); } } struct trap_arg { #if USE_TRAP_MASK # ifdef HAVE_SIGPROCMASK sigset_t mask; # else int mask; # endif #endif int sig; sighandler_t func; VALUE cmd; }; 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 func = sighandler; break; #ifdef SIGBUS case SIGBUS: func = sigbus; break; #endif #ifdef SIGSEGV case SIGSEGV: func = (sighandler_t)sigsegv; # ifdef USE_SIGALTSTACK register_sigaltstack(); # endif break; #endif #ifdef SIGPIPE case SIGPIPE: func = sigpipe; 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_id2str(SYM2ID(*cmd)); if (!command) rb_raise(rb_eArgError, "bad handler"); } if (!NIL_P(command)) { SafeStringValue(command); /* taint check */ *cmd = command; switch (RSTRING_LEN(command)) { case 0: goto sig_ign; break; case 14: if (strncmp(RSTRING_PTR(command), "SYSTEM_DEFAULT", 14) == 0) { func = SIG_DFL; *cmd = 0; } break; case 7: if (strncmp(RSTRING_PTR(command), "SIG_IGN", 7) == 0) { sig_ign: func = SIG_IGN; *cmd = 0; } else if (strncmp(RSTRING_PTR(command), "SIG_DFL", 7) == 0) { sig_dfl: func = default_handler(sig); *cmd = 0; } else if (strncmp(RSTRING_PTR(command), "DEFAULT", 7) == 0) { goto sig_dfl; } break; case 6: if (strncmp(RSTRING_PTR(command), "IGNORE", 6) == 0) { goto sig_ign; } break; case 4: if (strncmp(RSTRING_PTR(command), "EXIT", 4) == 0) { *cmd = Qundef; } break; } } else { rb_proc_t *proc; GetProcPtr(*cmd, proc); } } return func; } static int trap_signm(VALUE vsig) { int sig = -1; const char *s; switch (TYPE(vsig)) { case T_FIXNUM: sig = FIX2INT(vsig); if (sig < 0 || sig >= NSIG) { rb_raise(rb_eArgError, "invalid signal number (%d)", sig); } break; case T_SYMBOL: s = rb_id2name(SYM2ID(vsig)); if (!s) rb_raise(rb_eArgError, "bad signal"); goto str_signal; default: s = StringValuePtr(vsig); str_signal: if (strncmp("SIG", s, 3) == 0) s += 3; sig = signm2signo(s); if (sig == 0 && strcmp(s, "EXIT") != 0) rb_raise(rb_eArgError, "unsupported signal SIG%s", s); } return sig; } static VALUE trap(struct trap_arg *arg) { sighandler_t oldfunc, func = arg->func; VALUE oldcmd, command = arg->cmd; int sig = arg->sig; rb_vm_t *vm = GET_VM(); oldfunc = ruby_signal(sig, func); oldcmd = vm->trap_list[sig].cmd; switch (oldcmd) { case 0: if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE"); else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT"); else oldcmd = Qnil; break; case Qundef: oldcmd = rb_str_new2("EXIT"); break; } vm->trap_list[sig].cmd = command; vm->trap_list[sig].safe = rb_safe_level(); /* enable at least specified signal. */ #if USE_TRAP_MASK #ifdef HAVE_SIGPROCMASK sigdelset(&arg->mask, sig); #else arg->mask &= ~sigmask(sig); #endif #endif return oldcmd; } #if USE_TRAP_MASK static VALUE trap_ensure(struct trap_arg *arg) { /* enable interrupt */ #ifdef HAVE_SIGPROCMASK sigprocmask(SIG_SETMASK, &arg->mask, NULL); #else sigsetmask(arg->mask); #endif trap_last_mask = arg->mask; return 0; } #endif void rb_trap_restore_mask(void) { #if USE_TRAP_MASK # ifdef HAVE_SIGPROCMASK sigprocmask(SIG_SETMASK, &trap_last_mask, NULL); # else sigsetmask(trap_last_mask); # endif #endif } /* * 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) { struct trap_arg arg; rb_secure(2); if (argc == 0 || argc > 2) { rb_raise(rb_eArgError, "wrong number of arguments -- trap(sig, cmd)/trap(sig){...}"); } arg.sig = trap_signm(argv[0]); if (argc == 1) { arg.cmd = rb_block_proc(); arg.func = sighandler; } else if (argc == 2) { arg.cmd = argv[1]; arg.func = trap_handler(&arg.cmd, arg.sig); } if (OBJ_TAINTED(arg.cmd)) { rb_raise(rb_eSecurityError, "Insecure: tainted signal trap"); } #if USE_TRAP_MASK /* disable interrupt */ # ifdef HAVE_SIGPROCMASK sigfillset(&arg.mask); sigprocmask(SIG_BLOCK, &arg.mask, &arg.mask); # else arg.mask = sigblock(~0); # endif return rb_ensure(trap, (VALUE)&arg, trap_ensure, (VALUE)&arg); #else return trap(&arg); #endif } /* * 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(void) { VALUE h = rb_hash_new(); const struct signals *sigs; for (sigs = siglist; sigs->signm; sigs++) { rb_hash_aset(h, rb_str_new2(sigs->signm), INT2FIX(sigs->signo)); } return h; } static void install_sighandler(int signum, sighandler_t handler) { sighandler_t old; old = ruby_signal(signum, handler); if (old != SIG_DFL) { ruby_signal(signum, old); } } #if defined(SIGCLD) || defined(SIGCHLD) static void init_sigchld(int sig) { sighandler_t oldfunc; #if USE_TRAP_MASK # ifdef HAVE_SIGPROCMASK sigset_t mask; # else int mask; # endif #endif #if USE_TRAP_MASK /* disable interrupt */ # ifdef HAVE_SIGPROCMASK sigfillset(&mask); sigprocmask(SIG_BLOCK, &mask, &mask); # else mask = sigblock(~0); # endif #endif oldfunc = ruby_signal(sig, SIG_DFL); if (oldfunc != SIG_DFL && oldfunc != SIG_IGN) { ruby_signal(sig, oldfunc); } else { GET_VM()->trap_list[sig].cmd = 0; } #if USE_TRAP_MASK #ifdef HAVE_SIGPROCMASK sigdelset(&mask, sig); sigprocmask(SIG_SETMASK, &mask, NULL); #else mask &= ~sigmask(sig); sigsetmask(mask); #endif trap_last_mask = mask; #endif } #endif void ruby_sig_finalize() { sighandler_t oldfunc; oldfunc = ruby_signal(SIGINT, SIG_IGN); if (oldfunc == sighandler) { ruby_signal(SIGINT, SIG_DFL); } } #ifdef RUBY_DEBUG_ENV int ruby_enable_coredump = 0; #endif /* * 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_method(rb_eSignal, "initialize", esignal_init, -1); rb_define_method(rb_eSignal, "signo", esignal_signo, 0); rb_alias(rb_eSignal, rb_intern("signm"), rb_intern("message")); rb_define_method(rb_eInterrupt, "initialize", interrupt_init, -1); 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 #ifdef RUBY_DEBUG_ENV if (!ruby_enable_coredump) #endif { #ifdef SIGBUS install_sighandler(SIGBUS, sigbus); #endif #ifdef SIGSEGV # ifdef USE_SIGALTSTACK register_sigaltstack(); # endif install_sighandler(SIGSEGV, (sighandler_t)sigsegv); #endif } #ifdef SIGPIPE install_sighandler(SIGPIPE, sigpipe); #endif #if defined(SIGCLD) init_sigchld(SIGCLD); #elif defined(SIGCHLD) init_sigchld(SIGCHLD); #endif }