/********************************************************************** signal.c - $Author$ $Date$ created at: Tue Dec 20 10:13:44 JST 1994 Copyright (C) 1993-2003 Yukihiro Matsumoto Copyright (C) 2000 Network Applied Communication Laboratory, Inc. Copyright (C) 2000 Information-technology Promotion Agency, Japan **********************************************************************/ #include "ruby.h" #include "rubysig.h" #include #include #ifdef __BEOS__ #undef SIGBUS #endif #ifndef NSIG # ifdef DJGPP # define NSIG SIGMAX # else # define NSIG (_SIGMAX + 1) /* For QNX */ # endif #endif static struct signals { 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(nm) const char *nm; { struct signals *sigs; for (sigs = siglist; sigs->signm; sigs++) if (strcmp(sigs->signm, nm) == 0) return sigs->signo; return 0; } static char* signo2signm(no) int no; { struct signals *sigs; for (sigs = siglist; sigs->signm; sigs++) if (sigs->signo == no) return sigs->signm; return 0; } const char * ruby_signal_name(no) 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(argc, argv, self) int argc; VALUE *argv; VALUE self; { int argnum = 1; VALUE sig = Qnil; int signo; const char *signm; char tmpnm[(sizeof(int)*CHAR_BIT)/3+4]; if (argc > 0) { sig = argv[0]; if (FIXNUM_P(sig)) argnum = 2; } if (argc < 1 || argnum < argc) { rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, argnum); } if (argnum == 2) { signo = FIX2INT(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) { snprintf(tmpnm, sizeof(tmpnm), "SIG%s", signm); } else { snprintf(tmpnm, sizeof(tmpnm), "SIG%u", signo); } sig = rb_str_new2(signm = tmpnm); } } 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); } if (SYMBOL_P(sig)) { sig = rb_str_new2(signm); } } rb_call_super(1, &sig); rb_iv_set(self, "signo", INT2NUM(signo)); return self; } static VALUE interrupt_init(self, mesg) VALUE self, mesg; { VALUE argv[2]; argv[0] = INT2FIX(SIGINT); argv[1] = mesg; return rb_call_super(2, argv); } void ruby_default_signal(sig) int sig; { #ifndef MACOS_UNUSE_SIGNAL extern rb_pid_t getpid _((void)); signal(sig, SIG_DFL); kill(getpid(), sig); #endif } /* * 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(argc, argv) int argc; VALUE *argv; { int negative = 0; int sig; int i; 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(argv[0])->ptr; 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(str)->ptr; 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= NSIG) { rb_bug("trap_handler: Bad signal %d", sig); } #if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL) if (!is_ruby_native_thread() && !rb_trap_accept_nativethreads[sig]) { sigsend_to_ruby_thread(sig); return; } #endif #if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL) if (rb_trap_accept_nativethreads[sig]) { ruby_nativethread_signal(sig, sighandler); } else { ruby_signal(sig, sighandler); } #endif if (trap_list[sig].cmd == 0 && ATOMIC_TEST(rb_trap_immediate)) { IN_MAIN_CONTEXT(signal_exec, sig); ATOMIC_SET(rb_trap_immediate, 1); } else { ATOMIC_INC(rb_trap_pending); ATOMIC_INC(trap_pending_list[sig]); #ifdef _WIN32 rb_w32_interrupted(); #endif } } #ifdef SIGBUS static RETSIGTYPE sigbus _((int)); static RETSIGTYPE sigbus(sig) int sig; { #if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL) if (!is_ruby_native_thread() && !rb_trap_accept_nativethreads[sig]) { sigsend_to_ruby_thread(sig); return; } #endif rb_bug("Bus Error"); } #endif #ifdef SIGSEGV static RETSIGTYPE sigsegv _((int)); static RETSIGTYPE sigsegv(sig) int sig; { #if defined(HAVE_NATIVETHREAD) && defined(HAVE_NATIVETHREAD_KILL) if (!is_ruby_native_thread() && !rb_trap_accept_nativethreads[sig]) { sigsend_to_ruby_thread(sig); return; } #endif rb_bug("Segmentation fault"); } #endif #ifdef SIGPIPE static RETSIGTYPE sigpipe _((int)); static RETSIGTYPE sigpipe(sig) int sig; { /* do nothing */ } #endif void rb_trap_exit() { #ifndef MACOS_UNUSE_SIGNAL if (trap_list[0].cmd) { VALUE trap_exit = trap_list[0].cmd; trap_list[0].cmd = 0; rb_eval_cmd(trap_exit, rb_ary_new3(1, INT2FIX(0)), trap_list[0].safe); } #endif } void rb_trap_exec() { #ifndef MACOS_UNUSE_SIGNAL int i; for (i=0; icmd; if (NIL_P(command)) { func = SIG_IGN; } else if (TYPE(command) == T_STRING) { SafeStringValue(command); /* taint check */ if (RSTRING(command)->len == 0) { func = SIG_IGN; } else if (RSTRING(command)->len == 7) { if (strncmp(RSTRING(command)->ptr, "SIG_IGN", 7) == 0) { func = SIG_IGN; } else if (strncmp(RSTRING(command)->ptr, "SIG_DFL", 7) == 0) { func = SIG_DFL; } else if (strncmp(RSTRING(command)->ptr, "DEFAULT", 7) == 0) { func = SIG_DFL; } } else if (RSTRING(command)->len == 6) { if (strncmp(RSTRING(command)->ptr, "IGNORE", 6) == 0) { func = SIG_IGN; } } else if (RSTRING(command)->len == 4) { if (strncmp(RSTRING(command)->ptr, "EXIT", 4) == 0) { func = sigexit; } } } if (func == SIG_IGN || func == SIG_DFL) { command = 0; } switch (TYPE(arg->sig)) { case T_FIXNUM: sig = FIX2INT(arg->sig); break; case T_SYMBOL: s = rb_id2name(SYM2ID(arg->sig)); if (!s) rb_raise(rb_eArgError, "bad signal"); goto str_signal; case T_STRING: s = RSTRING(arg->sig)->ptr; 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); } if (sig < 0 || sig >= NSIG) { rb_raise(rb_eArgError, "invalid signal number (%d)", sig); } #if defined(HAVE_SETITIMER) if (sig == SIGVTALRM) { rb_raise(rb_eArgError, "SIGVTALRM reserved for Thread; can't set handler"); } #endif if (func == SIG_DFL) { 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 = sigsegv; break; #endif #ifdef SIGPIPE case SIGPIPE: func = sigpipe; break; #endif } } oldfunc = ruby_signal(sig, func); oldcmd = trap_list[sig].cmd; if (!oldcmd) { if (oldfunc == SIG_IGN) oldcmd = rb_str_new2("IGNORE"); else if (oldfunc == sighandler) oldcmd = rb_str_new2("DEFAULT"); else oldcmd = Qnil; } trap_list[sig].cmd = command; trap_list[sig].safe = ruby_safe_level; /* enable at least specified signal. */ #ifndef _WIN32 #ifdef HAVE_SIGPROCMASK sigdelset(&arg->mask, sig); #else arg->mask &= ~sigmask(sig); #endif #endif return oldcmd; } #ifndef _WIN32 static VALUE trap_ensure(arg) 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() { #ifndef _WIN32 # ifdef HAVE_SIGPROCMASK sigprocmask(SIG_SETMASK, &trap_last_mask, NULL); # else sigsetmask(trap_last_mask); # endif #endif } /* * call-seq: * Signal.trap( signal, proc ) => 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 operating system's default handler * will be invoked. If the command is ``EXIT'', the script will be * terminated by the signal. 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(argc, argv) 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 = argv[0]; if (argc == 1) { arg.cmd = rb_block_proc(); } else if (argc == 2) { arg.cmd = argv[1]; } if (OBJ_TAINTED(arg.cmd)) { rb_raise(rb_eSecurityError, "Insecure: tainted signal trap"); } #ifndef _WIN32 /* 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 #=> {"ABRT"=>6, "ALRM"=>14, "BUS"=>7, "CHLD"=>17, "CLD"=>17, "CONT"=>18, "FPE"=>8, "HUP"=>1, "ILL"=>4, "INT"=>2, "IO"=>29, "IOT"=>6, "KILL"=>9, "PIPE"=>13, "POLL"=>29, "PROF"=>27, "PWR"=>30, "QUIT"=>3, "SEGV"=>11, "STOP"=>19, "SYS"=>31, "TERM"=>15, "TRAP"=>5, "TSTP"=>20, "TTIN"=>21, "TTOU"=>22, "URG"=>23, "USR1"=>10, "USR2"=>12, "VTALRM"=>26, "WINCH"=>28, "XCPU"=>24, "XFSZ"=>25} */ static VALUE sig_list() { VALUE h = rb_hash_new(); 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(signum, handler) int signum; sighandler_t handler; { sighandler_t old; old = ruby_signal(signum, handler); if (old != SIG_DFL) { ruby_signal(signum, old); } } #if 0 /* * If you write a handler which works on any native thread * (even if the thread is NOT a ruby's one), please enable * this function and use it to install the handler, instead * of `install_sighandler()'. */ #ifdef HAVE_NATIVETHREAD static void install_nativethread_sighandler(signum, handler) int signum; sighandler_t handler; { sighandler_t old; int old_st; old_st = rb_trap_accept_nativethreads[signum]; old = ruby_nativethread_signal(signum, handler); if (old != SIG_DFL) { if (old_st) { ruby_nativethread_signal(signum, old); } else { ruby_signal(signum, old); } } } #endif #endif static void init_sigchld(sig) int sig; { sighandler_t oldfunc; #ifndef _WIN32 # ifdef HAVE_SIGPROCMASK sigset_t mask; # else int mask; # endif #endif #ifndef _WIN32 /* 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 { trap_list[sig].cmd = 0; } #ifndef _WIN32 #ifdef HAVE_SIGPROCMASK sigdelset(&mask, sig); sigprocmask(SIG_SETMASK, &mask, NULL); #else mask &= ~sigmask(sig); sigsetmask(mask); #endif trap_last_mask = mask; #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() { #ifndef MACOS_UNUSE_SIGNAL 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_attr(rb_eSignal, rb_intern("signo"), 1, 0, 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 SIGBUS install_sighandler(SIGBUS, sigbus); #endif #ifdef SIGSEGV install_sighandler(SIGSEGV, sigsegv); #endif #ifdef SIGPIPE install_sighandler(SIGPIPE, sigpipe); #endif #if defined(SIGCLD) init_sigchld(SIGCLD); #elif defined(SIGCHLD) init_sigchld(SIGCHLD); #endif #endif /* MACOS_UNUSE_SIGNAL */ }