diff options
Diffstat (limited to 'trunk/signal.c')
-rw-r--r-- | trunk/signal.c | 1128 |
1 files changed, 1128 insertions, 0 deletions
diff --git a/trunk/signal.c b/trunk/signal.c new file mode 100644 index 0000000000..b110e16cb7 --- /dev/null +++ b/trunk/signal.c @@ -0,0 +1,1128 @@ +/********************************************************************** + + 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/ruby.h" +#include "ruby/signal.h" +#include "ruby/node.h" +#include "vm_core.h" +#include <signal.h> +#include <stdio.h> + +#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 +# ifdef DJGPP +# define NSIG SIGMAX +# else +# define NSIG (_SIGMAX + 1) /* For QNX */ +# endif +#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) +{ +#ifndef MACOS_UNUSE_SIGNAL + signal(sig, SIG_DFL); + raise(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 + * + * <em>produces:</em> + * + * 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<argc; i++) { + if (killpg(NUM2PIDT(argv[i]), sig) < 0) + rb_sys_fail(0); + } + } + else { + for (i=1; i<argc; i++) { + if (kill(NUM2PIDT(argv[i]), sig) < 0) + rb_sys_fail(0); + } + } + rb_thread_polling(); + return INT2FIX(i-1); +} + +static struct { + VALUE cmd; + int safe; +} trap_list[NSIG]; +static rb_atomic_t trap_pending_list[NSIG]; +#if 0 +static char rb_trap_accept_nativethreads[NSIG]; +#endif +rb_atomic_t rb_trap_pending; +rb_atomic_t rb_trap_immediate; +int rb_prohibit_interrupt = 1; + +VALUE +rb_get_trap_cmd(int sig) +{ + return trap_list[sig].cmd; +} + +void +rb_gc_mark_trap_list(void) +{ +#ifndef MACOS_UNUSE_SIGNAL + int i; + + for (i=0; i<NSIG; i++) { + if (trap_list[i].cmd) + rb_gc_mark(trap_list[i].cmd); + } +#endif /* MACOS_UNUSE_SIGNAL */ +} + +#ifdef __dietlibc__ +#define sighandler_t sh_t +#endif + +typedef RETSIGTYPE (*sighandler_t)(int); + +#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 SA_SIGINFO + sigact.sa_sigaction = (void (*)(int, siginfo_t*, void*))handler; + sigact.sa_flags = SA_SIGINFO; +#else + sigact.sa_handler = handler; + sigact.sa_flags = 0; +#endif + +#ifdef SA_NOCLDWAIT + if (signum == SIGCHLD && handler == SIG_IGN) + sigact.sa_flags |= SA_NOCLDWAIT; +#endif + sigaction(signum, &sigact, &old); + return old.sa_handler; +} + +sighandler_t +posix_signal(int signum, sighandler_t handler) +{ + return ruby_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 RETSIGTYPE +sighandler(int sig) +{ + rb_vm_t *vm = GET_VM(); /* fix me for Multi-VM */ + ATOMIC_INC(vm->signal_buff[sig]); + ATOMIC_INC(vm->buffered_signal_size); + +#if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL) + ruby_signal(sig, sighandler); +#endif +} + +#if USE_TRAP_MASK +# ifdef HAVE_SIGPROCMASK +static sigset_t trap_last_mask; +# else +static int trap_last_mask; +# endif +#endif + +#if HAVE_PTHREAD_H +#include <pthread.h> +#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(rb_vm_t *vm) +{ + int i, sig = 0; + + for (i=1; i<RUBY_NSIG; i++) { + if (vm->signal_buff[i] > 0) { + rb_disable_interrupt(); + { + ATOMIC_DEC(vm->signal_buff[i]); + ATOMIC_DEC(vm->buffered_signal_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) +{ + 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 sig) +{ + rb_proc_t *proc; + VALUE signum = INT2FIX(sig); + + if (TYPE(cmd) == T_STRING) { + rb_eval_cmd(cmd, rb_ary_new3(1, signum), trap_list[sig].safe); + return; + } + GetProcPtr(cmd, proc); + vm_invoke_proc(GET_THREAD(), proc, proc->block.self, 1, &signum, 0); +} + +void +rb_trap_exit(void) +{ +#ifndef MACOS_UNUSE_SIGNAL + if (trap_list[0].cmd) { + VALUE trap_exit = trap_list[0].cmd; + + trap_list[0].cmd = 0; + signal_exec(trap_exit, 0); + } +#endif +} + +void +rb_signal_exec(rb_thread_t *th, int sig) +{ + VALUE cmd = rb_get_trap_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_thread_signal_raise(th, sig); + break; + } + } + else if (cmd == Qundef) { + rb_thread_signal_exit(th); + } + else { + signal_exec(cmd, sig); + } +} + +void +rb_trap_exec(void) +{ +#ifndef MACOS_UNUSE_SIGNAL + int i; + + for (i=0; i<NSIG; i++) { + if (trap_pending_list[i]) { + trap_pending_list[i] = 0; + rb_signal_exec(GET_THREAD(), i); + } + } +#endif /* MACOS_UNUSE_SIGNAL */ + rb_trap_pending = 0; +} + +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 = sigsegv; + 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)) { + 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; + + oldfunc = ruby_signal(sig, func); + oldcmd = 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; + } + + trap_list[sig].cmd = command; + 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 { + 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) +{ +#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_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 + 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 */ +} |