/signal.c
C | 1142 lines | 896 code | 102 blank | 144 comment | 129 complexity | a4f7d2d1c98100cc74027b63089b0590 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
- /**********************************************************************
- 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 "vm_core.h"
- #include <signal.h>
- #include <stdio.h>
- #include <errno.h>
- #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
- #if defined(__BEOS__) || defined(__HAIKU__)
- #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_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;
- 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");
- }
- /* :nodoc: */
- 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
- *
- * <em>produces:</em>
- *
- * Ouch!
- *
- * If _signal_ is an integer but wrong for signal,
- * <code>Errno::EINVAL</code> or +RangeError+ will be raised.
- * Otherwise unless _signal_ is a +String+ or a +Symbol+, and a known
- * signal name, +ArgumentError+ will be raised.
- *
- * Also, <code>Errno::ESRCH</code> or +RangeError+ for invalid _pid_,
- * <code>Errno::EPERM</code> when failed because of no privilege,
- * will be raised. In these cases, signals may have been sent to
- * preceding processes.
- */
- 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 (%d for at least 2)", argc);
- 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 {
- rb_atomic_t cnt[RUBY_NSIG];
- rb_atomic_t size;
- } signal_buff;
- #ifdef __dietlibc__
- #define sighandler_t sh_t
- #endif
- typedef RETSIGTYPE (*sighandler_t)(int);
- #ifdef USE_SIGALTSTACK
- typedef void ruby_sigaction_t(int, siginfo_t*, void*);
- #define SIGINFO_ARG , siginfo_t *info, void *ctx
- #else
- typedef RETSIGTYPE ruby_sigaction_t(int);
- #define SIGINFO_ARG
- #endif
- #ifdef POSIX_SIGNAL
- #ifdef USE_SIGALTSTACK
- #ifdef SIGSTKSZ
- #define ALT_STACK_SIZE (SIGSTKSZ*2)
- #else
- #define ALT_STACK_SIZE (4*1024)
- #endif
- /* alternate stack for SIGSEGV */
- void
- rb_register_sigaltstack(rb_thread_t *th)
- {
- stack_t newSS, oldSS;
- if (th->altstack) return;
- newSS.ss_sp = th->altstack = malloc(ALT_STACK_SIZE);
- if (newSS.ss_sp == NULL)
- /* should handle error */
- rb_bug("rb_register_sigaltstack. malloc error\n");
- newSS.ss_size = ALT_STACK_SIZE;
- newSS.ss_flags = 0;
- sigaltstack(&newSS, &oldSS); /* ignore error. */
- }
- #endif
- 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 = (ruby_sigaction_t*)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
- #if defined(SA_ONSTACK) && defined(USE_SIGALTSTACK)
- if (signum == SIGSEGV)
- sigact.sa_flags |= SA_ONSTACK;
- #endif
- if (sigaction(signum, &sigact, &old) < 0) {
- if (errno != 0 && errno != EINVAL) {
- rb_bug_errno("sigaction", errno);
- }
- }
- 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)
- {
- ATOMIC_INC(signal_buff.cnt[sig]);
- ATOMIC_INC(signal_buff.size);
- #if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL)
- ruby_signal(sig, sighandler);
- #endif
- }
- int
- rb_signal_buff_size(void)
- {
- return signal_buff.size;
- }
- #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)
- {
- #if USE_TRAP_MASK
- sigset_t mask;
- sigfillset(&mask);
- sigdelset(&mask, SIGVTALRM);
- sigdelset(&mask, SIGSEGV);
- pthread_sigmask(SIG_SETMASK, &mask, NULL);
- #endif
- }
- void
- rb_enable_interrupt(void)
- {
- #if USE_TRAP_MASK
- 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<RUBY_NSIG; i++) {
- if (signal_buff.cnt[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 received 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_threadptr_signal_raise(th, sig);
- break;
- }
- }
- else if (cmd == Qundef) {
- rb_threadptr_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
- rb_register_sigaltstack(GET_THREAD());
- # 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 */
- pthread_sigmask(SIG_SETMASK, &arg->mask, NULL);
- trap_last_mask = arg->mask;
- return 0;
- }
- #endif
- void
- rb_trap_restore_mask(void)
- {
- #if USE_TRAP_MASK
- pthread_sigmask(SIG_SETMASK, &trap_last_mask, NULL);
- #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 < 1 || argc > 2) {
- rb_raise(rb_eArgError, "wrong number of arguments (%d for 1..2)", argc);
- }
- arg.sig = trap_signm(argv[0]);
- if (argc == 1) {
- arg.cmd = rb_block_proc();
- arg.func = sighandler;
- }
- else {
- 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 */
- sigfillset(&arg.mask);
- pthread_sigmask(SIG_BLOCK, &arg.mask, &arg.mask);
- 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 */
- sigfillset(&mask);
- pthread_sigmask(SIG_BLOCK, &mask, &mask);
- #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
- sigdelset(&mask, sig);
- pthread_sigmask(SIG_SETMASK, &mask, NULL);
- trap_last_mask = mask;
- #endif
- }
- #endif
- void
- ruby_sig_finalize(void)
- {
- 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
- rb_register_sigaltstack(GET_THREAD());
- # 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
- }