1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/signal.c
kosaki cb92817e7c * signal.c (rb_f_kill): remove rb_thread_polling() because this
has no good effect and makes meaningless 100ms delay. 1)
  when sending signal to another process, waiting has just silly.
  2) when sending signal to current process, 100ms is often not
  enough time to wait. It depend on kernel behavior. And,
  rb_thread_polling() doesn't make sense anyway. When rb_thread_alone()
  is true, it doesn't wait at all and Process.kill() users don't
  expect threading changes Process.kill() behavior. [Bug #7560]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38380 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-14 15:11:59 +00:00

1148 lines
24 KiB
C

/**********************************************************************
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>
#include "ruby_atomic.h"
#include "eval_intern.h"
#if defined(__native_client__) && defined(NACL_NEWLIB)
# include "nacl/signal.h"
#endif
#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
#if defined(__BEOS__) || defined(__HAIKU__)
#undef SIGBUS
#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;
}
/*
* call-seq:
* Signal.signame(signo) -> string
*
* convert signal number to signal name
*
* 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));
return rb_str_new_cstr(signame);
}
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];
}
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 {
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) if _pid_ is positive.
* If _pid_ is zero _signal_ is sent to all processes whose group ID is equal
* to the group ID of the process. _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;
volatile VALUE str;
const char *s;
rb_secure(2);
rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
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]);
str_signal:
if (s[0] == '-') {
negative++;
s++;
}
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:
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);
}
}
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 USE_SIGALTSTACK
/* alternate stack for SIGSEGV */
void
rb_register_sigaltstack(rb_thread_t *th)
{
stack_t newSS, oldSS;
if (!th->altstack)
rb_bug("rb_register_sigaltstack: th->altstack not initialized\n");
newSS.ss_sp = th->altstack;
newSS.ss_size = ALT_STACK_SIZE;
newSS.ss_flags = 0;
sigaltstack(&newSS, &oldSS); /* ignore error. */
}
#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
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
#ifdef SIGBUS
|| signum == SIGBUS
#endif
)
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);
rb_thread_wakeup_timer_thread();
#if !defined(BSD_SIGNAL) && !defined(POSIX_SIGNAL)
ruby_signal(sig, sighandler);
#endif
}
int
rb_signal_buff_size(void)
{
return signal_buff.size;
}
#if HAVE_PTHREAD_H
#include <pthread.h>
#endif
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;
}
#ifdef SIGBUS
static RETSIGTYPE
sigbus(int sig SIGINFO_ARG)
{
/*
* Mac OS X makes KERN_PROTECTION_FAILURE when thread touch guard page.
* and it's delivered as SIGBUS instaed of SIGSEGV to userland. It's crazy
* wrong IMHO. but anyway we have to care it. Sigh.
*/
#if defined __APPLE__ && defined 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
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");
abort();
}
else {
extern int ruby_disable_gc_stress;
segv_received = 1;
ruby_disable_gc_stress = 1;
rb_bug("Segmentation fault");
}
}
#endif
static void
signal_exec(VALUE cmd, int safe, int sig)
{
rb_thread_t *cur_th = GET_THREAD();
volatile unsigned long old_interrupt_mask = cur_th->interrupt_mask;
int state;
cur_th->interrupt_mask |= TRAP_INTERRUPT_MASK;
TH_PUSH_TAG(cur_th);
if ((state = EXEC_TAG()) == 0) {
VALUE signum = INT2NUM(sig);
rb_eval_cmd(cmd, rb_ary_new3(1, signum), safe);
}
TH_POP_TAG();
cur_th = GET_THREAD();
cur_th->interrupt_mask = old_interrupt_mask;
if (state) {
/* XXX: should be replaced with rb_threadptr_async_errinfo_enque() */
JUMP_TAG(state);
}
}
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);
}
}
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 = (sighandler_t)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 = SIG_IGN;
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);
(void)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(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[sig].cmd must be changed
* atomically. In current implementation, we only need to don't call
* RUBY_VM_CHECK_INTS().
*/
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();
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)
{
int sig;
sighandler_t func;
VALUE cmd;
rb_secure(2);
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 (OBJ_TAINTED(cmd)) {
rb_raise(rb_eSecurityError, "Insecure: tainted signal trap");
}
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(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;
/* At this time, there is no subthread. Then sigmask guarantee atomics. */
rb_disable_interrupt();
old = ruby_signal(signum, handler);
/* signal handler should be inherited during exec. */
if (old != SIG_DFL) {
ruby_signal(signum, old);
}
rb_enable_interrupt();
}
#if defined(SIGCLD) || defined(SIGCHLD)
static void
init_sigchld(int sig)
{
sighandler_t oldfunc;
rb_disable_interrupt();
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;
}
rb_enable_interrupt();
}
#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;
#ifndef RUBY_DEBUG_ENV
#define 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_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("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
if (!ruby_enable_coredump) {
#ifdef SIGBUS
install_sighandler(SIGBUS, (sighandler_t)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, SIG_IGN);
#endif
#if defined(SIGCLD)
init_sigchld(SIGCLD);
#elif defined(SIGCHLD)
init_sigchld(SIGCHLD);
#endif
}