/** \file signal.c The library for various signal related issues */ #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_SIGINFO_H #include #endif #include "common.h" #include "fallback.h" #include "util.h" #include "wutil.h" #include "signal.h" #include "event.h" #include "reader.h" #include "proc.h" /** Struct describing an entry for the lookup table used to convert between signal names and signal ids, etc. */ struct lookup_entry { /** Signal id */ int signal; /** Signal name */ const wchar_t *name; /** Signal description */ const wchar_t *desc; }; /** The number of signal blocks in place. Increased by signal_block, decreased by signal_unblock. */ static int block_count=0; /** Lookup table used to convert between signal names and signal ids, etc. */ static const struct lookup_entry lookup[] = { #ifdef SIGHUP { SIGHUP, L"SIGHUP", N_( L"Terminal hung up" ) } , #endif #ifdef SIGINT { SIGINT, L"SIGINT", N_( L"Quit request from job control (^C)" ) } , #endif #ifdef SIGQUIT { SIGQUIT, L"SIGQUIT", N_( L"Quit request from job control with core dump (^\\)" ) } , #endif #ifdef SIGILL { SIGILL, L"SIGILL", N_( L"Illegal instruction" ) } , #endif #ifdef SIGTRAP { SIGTRAP, L"SIGTRAP", N_( L"Trace or breakpoint trap" ) } , #endif #ifdef SIGABRT { SIGABRT, L"SIGABRT", N_( L"Abort" ) } , #endif #ifdef SIGBUS { SIGBUS, L"SIGBUS", N_( L"Misaligned address error" ) } , #endif #ifdef SIGFPE { SIGFPE, L"SIGFPE", N_( L"Floating point exception" ) } , #endif #ifdef SIGKILL { SIGKILL, L"SIGKILL", N_( L"Forced quit" ) } , #endif #ifdef SIGUSR1 { SIGUSR1, L"SIGUSR1", N_( L"User defined signal 1" ) } , #endif #ifdef SIGUSR2 { SIGUSR2, L"SIGUSR2", N_( L"User defined signal 2" ) } , #endif #ifdef SIGSEGV { SIGSEGV, L"SIGSEGV", N_( L"Address boundary error" ) } , #endif #ifdef SIGPIPE { SIGPIPE, L"SIGPIPE", N_( L"Broken pipe" ) } , #endif #ifdef SIGALRM { SIGALRM, L"SIGALRM", N_( L"Timer expired" ) } , #endif #ifdef SIGTERM { SIGTERM, L"SIGTERM", N_( L"Polite quit request" ) } , #endif #ifdef SIGCHLD { SIGCHLD, L"SIGCHLD", N_( L"Child process status changed" ) } , #endif #ifdef SIGCONT { SIGCONT, L"SIGCONT", N_( L"Continue previously stopped process" ) } , #endif #ifdef SIGSTOP { SIGSTOP, L"SIGSTOP", N_( L"Forced stop" ) } , #endif #ifdef SIGTSTP { SIGTSTP, L"SIGTSTP", N_( L"Stop request from job control (^Z)" ) } , #endif #ifdef SIGTTIN { SIGTTIN, L"SIGTTIN", N_( L"Stop from terminal input" ) } , #endif #ifdef SIGTTOU { SIGTTOU, L"SIGTTOU", N_( L"Stop from terminal output" ) } , #endif #ifdef SIGURG { SIGURG, L"SIGURG", N_( L"Urgent socket condition" ) } , #endif #ifdef SIGXCPY { SIGXCPU, L"SIGXCPU", N_( L"CPU time limit exceeded" ) } , #endif #ifdef SIGXFSZ { SIGXFSZ, L"SIGXFSZ", N_( L"File size limit exceeded" ) } , #endif #ifdef SIGVTALRM { SIGVTALRM, L"SIGVTALRM", N_( L"Virtual timer expired" ) } , #endif #ifdef SIGPROF { SIGPROF, L"SIGPROF", N_( L"Profiling timer expired" ) } , #endif #ifdef SIGWINCH { SIGWINCH, L"SIGWINCH", N_( L"Window size change" ) } , #endif #ifdef SIGWIND { SIGWIND, L"SIGWIND", N_( L"Window size change" ) } , #endif #ifdef SIGIO { SIGIO, L"SIGIO", N_( L"I/O on asynchronous file descriptor is possible" ) } , #endif #ifdef SIGPWR { SIGPWR, L"SIGPWR", N_( L"Power failure" ) } , #endif #ifdef SIGSYS { SIGSYS, L"SIGSYS", N_( L"Bad system call" ) } , #endif #ifdef SIGINFO { SIGINFO, L"SIGINFO", N_( L"Information request" ) } , #endif #ifdef SIGSTKFLT { SIGSTKFLT, L"SISTKFLT", N_( L"Stack fault" ) } , #endif #ifdef SIGEMT { SIGEMT, L"SIGEMT", N_( L"Emulator trap" ) } , #endif #ifdef SIGIOT { SIGIOT, L"SIGIOT", N_( L"Abort (Alias for SIGABRT)" ) } , #endif #ifdef SIGUNUSED { SIGUNUSED, L"SIGUNUSED", N_( L"Unused signal" ) } , #endif { 0, 0, 0 } } ; /** Test if \c name is a string describing the signal named \c canonical. */ static int match_signal_name( const wchar_t *canonical, const wchar_t *name ) { if( wcsncasecmp( name, L"sig", 3 )==0) name +=3; return wcscasecmp( canonical+3,name ) == 0; } int wcs2sig( const wchar_t *str ) { int i, res; wchar_t *end=0; for( i=0; lookup[i].desc ; i++ ) { if( match_signal_name( lookup[i].name, str) ) { return lookup[i].signal; } } errno=0; res = wcstol( str, &end, 10 ); if( !errno && res>=0 && !*end ) return res; return -1; } const wchar_t *sig2wcs( int sig ) { int i; for( i=0; lookup[i].desc ; i++ ) { if( lookup[i].signal == sig ) { return lookup[i].name; } } return _(L"Unknown"); } const wchar_t *signal_get_desc( int sig ) { int i; for( i=0; lookup[i].desc ; i++ ) { if( lookup[i].signal == sig ) { return _(lookup[i].desc); } } return _(L"Unknown"); } /** Standard signal handler */ static void default_handler(int signal, siginfo_t *info, void *context) { event_t e = event_t::signal_event(signal); if( event_get( &e, 0 ) ) { event_fire( &e ); } } /** Respond to a winch signal by checking the terminal size */ static void handle_winch( int sig, siginfo_t *info, void *context ) { common_handle_winch( sig ); default_handler( sig, 0, 0 ); } /** Respond to a hup signal by exiting, unless it is caught by a shellscript function, in which case we do nothing. */ static void handle_hup( int sig, siginfo_t *info, void *context ) { event_t e = event_t::signal_event(SIGHUP); if( event_get( &e, 0 ) ) { default_handler( sig, 0, 0 ); } else { reader_exit( 1, 1 ); } } /** Interactive mode ^C handler. Respond to int signal by setting interrupted-flag and stopping all loops and conditionals. */ static void handle_int( int sig, siginfo_t *info, void *context ) { reader_handle_int( sig ); default_handler( sig, info, context); } /** sigchld handler. Does notification and calls the handler in proc.c */ static void handle_chld( int sig, siginfo_t *info, void *context ) { job_handle_signal( sig, info, context ); default_handler( sig, info, context); } void signal_reset_handlers() { int i; struct sigaction act; sigemptyset( & act.sa_mask ); act.sa_flags=0; act.sa_handler=SIG_DFL; for( i=0; lookup[i].desc ; i++ ) { sigaction( lookup[i].signal, &act, 0); } } /** Sets appropriate signal handlers. */ void signal_set_handlers() { struct sigaction act; if( get_is_interactive() == -1 ) return; sigemptyset( & act.sa_mask ); act.sa_flags=SA_SIGINFO; act.sa_sigaction = &default_handler; /* First reset everything to a use default_handler, a function whose sole action is to fire of an event */ sigaction( SIGINT, &act, 0); sigaction( SIGQUIT, &act, 0); sigaction( SIGTSTP, &act, 0); sigaction( SIGTTIN, &act, 0); sigaction( SIGTTOU, &act, 0); sigaction( SIGCHLD, &act, 0); /* Ignore sigpipe, it is generated if fishd dies, but we can recover. */ sigaction( SIGPIPE, &act, 0); if( get_is_interactive() ) { /* Interactive mode. Ignore interactive signals. We are a shell, we know whats best for the user. ;-) */ act.sa_handler=SIG_IGN; sigaction( SIGINT, &act, 0); sigaction( SIGQUIT, &act, 0); sigaction( SIGTSTP, &act, 0); sigaction( SIGTTIN, &act, 0); sigaction( SIGTTOU, &act, 0); act.sa_sigaction = &handle_int; act.sa_flags = SA_SIGINFO; if( sigaction( SIGINT, &act, 0) ) { wperror( L"sigaction" ); FATAL_EXIT(); } act.sa_sigaction = &handle_chld; act.sa_flags = SA_SIGINFO; if( sigaction( SIGCHLD, &act, 0) ) { wperror( L"sigaction" ); FATAL_EXIT(); } #ifdef SIGWINCH act.sa_flags = SA_SIGINFO; act.sa_sigaction= &handle_winch; if( sigaction( SIGWINCH, &act, 0 ) ) { wperror( L"sigaction" ); FATAL_EXIT(); } #endif act.sa_flags = SA_SIGINFO; act.sa_sigaction= &handle_hup; if( sigaction( SIGHUP, &act, 0 ) ) { wperror( L"sigaction" ); FATAL_EXIT(); } } else { /* Non-interactive. Ignore interrupt, check exit status of processes to determine result instead. */ act.sa_handler=SIG_IGN; sigaction( SIGINT, &act, 0); sigaction( SIGQUIT, &act, 0); act.sa_handler=SIG_DFL; act.sa_sigaction = &handle_chld; act.sa_flags = SA_SIGINFO; if( sigaction( SIGCHLD, &act, 0) ) { wperror( L"sigaction" ); exit(1); } } } void signal_handle( int sig, int do_handle ) { struct sigaction act; /* These should always be handled */ if( (sig == SIGINT) || (sig == SIGQUIT) || (sig == SIGTSTP) || (sig == SIGTTIN) || (sig == SIGTTOU) || (sig == SIGCHLD) ) return; sigemptyset( &act.sa_mask ); if( do_handle ) { act.sa_flags = SA_SIGINFO; act.sa_sigaction = &default_handler; } else { act.sa_flags = 0; act.sa_handler = SIG_DFL; } sigaction( sig, &act, 0); } void signal_block() { ASSERT_IS_MAIN_THREAD(); sigset_t chldset; if( !block_count ) { sigfillset( &chldset ); sigprocmask(SIG_BLOCK, &chldset, 0); } block_count++; // debug( 0, L"signal block level increased to %d", block_count ); } void signal_unblock() { ASSERT_IS_MAIN_THREAD(); sigset_t chldset; block_count--; if( block_count < 0 ) { debug( 0, _( L"Signal block mismatch" ) ); bugreport(); FATAL_EXIT(); } if( !block_count ) { sigfillset( &chldset ); sigprocmask(SIG_UNBLOCK, &chldset, 0); } // debug( 0, L"signal block level decreased to %d", block_count ); } int signal_is_blocked() { return !!block_count; }