2005-10-06 17:30:50 +10:00
/** \file signal.c
The library for various signal related issues
*/
# include "config.h"
2006-02-28 23:17:16 +10:00
2005-10-06 17:30:50 +10:00
# include <stdlib.h>
# include <stdio.h>
# include <sys/types.h>
# include <signal.h>
# include <dirent.h>
# include <unistd.h>
2005-10-06 21:54:16 +10:00
# include <errno.h>
2005-10-06 17:30:50 +10:00
2006-07-31 06:26:59 +10:00
# ifdef HAVE_SIGINFO_H
# include <siginfo.h>
# endif
2005-10-06 17:30:50 +10:00
# include "common.h"
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2005-10-06 21:54:16 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2005-10-06 21:54:16 +10:00
# include "wutil.h"
2005-10-06 17:30:50 +10:00
# include "signal.h"
# include "event.h"
# include "reader.h"
# include "proc.h"
2006-07-20 08:55:49 +10:00
2005-10-06 17:30:50 +10:00
/**
Struct describing an entry for the lookup table used to convert
between signal names and signal ids , etc .
*/
struct lookup_entry
{
2012-11-18 16:30:30 -08:00
/**
Signal id
*/
int signal ;
/**
Signal name
*/
const wchar_t * name ;
/**
Signal description
*/
const wchar_t * desc ;
2005-10-06 17:30:50 +10:00
} ;
2006-06-20 10:50:10 +10:00
/**
The number of signal blocks in place . Increased by signal_block , decreased by signal_unblock .
*/
2006-02-10 01:50:20 +10:00
static int block_count = 0 ;
2005-10-06 17:30:50 +10:00
/**
Lookup table used to convert between signal names and signal ids ,
etc .
*/
2010-10-08 08:43:57 +08:00
static const struct lookup_entry lookup [ ] =
2005-10-06 17:30:50 +10:00
{
2006-05-15 02:39:36 +10:00
# ifdef SIGHUP
2012-11-18 16:30:30 -08:00
{
SIGHUP ,
L " SIGHUP " ,
N_ ( L " Terminal hung up " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGINT
2012-11-18 16:30:30 -08:00
{
SIGINT ,
L " SIGINT " ,
N_ ( L " Quit request from job control (^C) " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGQUIT
2012-11-18 16:30:30 -08:00
{
SIGQUIT ,
L " SIGQUIT " ,
N_ ( L " Quit request from job control with core dump (^ \\ ) " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGILL
2012-11-18 16:30:30 -08:00
{
SIGILL ,
L " SIGILL " ,
N_ ( L " Illegal instruction " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGTRAP
2012-11-18 16:30:30 -08:00
{
SIGTRAP ,
L " SIGTRAP " ,
N_ ( L " Trace or breakpoint trap " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGABRT
2012-11-18 16:30:30 -08:00
{
SIGABRT ,
L " SIGABRT " ,
N_ ( L " Abort " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGBUS
2012-11-18 16:30:30 -08:00
{
SIGBUS ,
L " SIGBUS " ,
N_ ( L " Misaligned address error " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGFPE
2012-11-18 16:30:30 -08:00
{
SIGFPE ,
L " SIGFPE " ,
N_ ( L " Floating point exception " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGKILL
2012-11-18 16:30:30 -08:00
{
SIGKILL ,
L " SIGKILL " ,
N_ ( L " Forced quit " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGUSR1
2012-11-18 16:30:30 -08:00
{
SIGUSR1 ,
L " SIGUSR1 " ,
N_ ( L " User defined signal 1 " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGUSR2
2012-11-18 16:30:30 -08:00
{
SIGUSR2 , L " SIGUSR2 " ,
N_ ( L " User defined signal 2 " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGSEGV
2012-11-18 16:30:30 -08:00
{
SIGSEGV ,
L " SIGSEGV " ,
N_ ( L " Address boundary error " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGPIPE
2012-11-18 16:30:30 -08:00
{
SIGPIPE ,
L " SIGPIPE " ,
N_ ( L " Broken pipe " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGALRM
2012-11-18 16:30:30 -08:00
{
SIGALRM ,
L " SIGALRM " ,
N_ ( L " Timer expired " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGTERM
2012-11-18 16:30:30 -08:00
{
SIGTERM ,
L " SIGTERM " ,
N_ ( L " Polite quit request " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGCHLD
2012-11-18 16:30:30 -08:00
{
SIGCHLD ,
L " SIGCHLD " ,
N_ ( L " Child process status changed " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGCONT
2012-11-18 16:30:30 -08:00
{
SIGCONT ,
L " SIGCONT " ,
N_ ( L " Continue previously stopped process " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGSTOP
2012-11-18 16:30:30 -08:00
{
SIGSTOP ,
L " SIGSTOP " ,
N_ ( L " Forced stop " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGTSTP
2012-11-18 16:30:30 -08:00
{
SIGTSTP ,
L " SIGTSTP " ,
N_ ( L " Stop request from job control (^Z) " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGTTIN
2012-11-18 16:30:30 -08:00
{
SIGTTIN ,
L " SIGTTIN " ,
N_ ( L " Stop from terminal input " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGTTOU
2012-11-18 16:30:30 -08:00
{
SIGTTOU ,
L " SIGTTOU " ,
N_ ( L " Stop from terminal output " )
}
,
2006-05-15 02:39:36 +10:00
# endif
# ifdef SIGURG
2012-11-18 16:30:30 -08:00
{
SIGURG ,
L " SIGURG " ,
N_ ( L " Urgent socket condition " )
}
,
2006-05-15 02:39:36 +10:00
# endif
2012-06-10 03:36:02 -07:00
# ifdef SIGXCPU
2012-11-18 16:30:30 -08:00
{
SIGXCPU ,
L " SIGXCPU " ,
N_ ( L " CPU time limit exceeded " )
}
,
2006-05-15 02:39:36 +10:00
# endif
2006-01-19 04:40:46 +10:00
# ifdef SIGXFSZ
2012-11-18 16:30:30 -08:00
{
SIGXFSZ ,
L " SIGXFSZ " ,
N_ ( L " File size limit exceeded " )
}
,
2006-01-19 04:40:46 +10:00
# endif
2006-05-15 02:39:36 +10:00
# ifdef SIGVTALRM
2012-11-18 16:30:30 -08:00
{
SIGVTALRM ,
L " SIGVTALRM " ,
N_ ( L " Virtual timer expired " )
}
,
2006-05-15 02:39:36 +10:00
# endif
2006-01-19 04:40:46 +10:00
# ifdef SIGPROF
2012-11-18 16:30:30 -08:00
{
SIGPROF ,
L " SIGPROF " ,
N_ ( L " Profiling timer expired " )
}
,
2006-01-19 04:40:46 +10:00
# endif
# ifdef SIGWINCH
2012-11-18 16:30:30 -08:00
{
SIGWINCH ,
L " SIGWINCH " ,
N_ ( L " Window size change " )
}
,
2006-01-19 04:40:46 +10:00
# endif
# ifdef SIGWIND
2012-11-18 16:30:30 -08:00
{
SIGWIND ,
L " SIGWIND " ,
N_ ( L " Window size change " )
}
,
2006-01-19 04:40:46 +10:00
# endif
2006-08-01 10:25:50 +10:00
# ifdef SIGIO
2012-11-18 16:30:30 -08:00
{
SIGIO ,
L " SIGIO " ,
N_ ( L " I/O on asynchronous file descriptor is possible " )
}
,
2006-05-15 02:39:36 +10:00
# endif
2005-10-06 17:30:50 +10:00
# ifdef SIGPWR
2012-11-18 16:30:30 -08:00
{
SIGPWR ,
L " SIGPWR " ,
N_ ( L " Power failure " )
}
,
2005-10-06 17:30:50 +10:00
# endif
2006-01-19 04:40:46 +10:00
# ifdef SIGSYS
2012-11-18 16:30:30 -08:00
{
SIGSYS ,
L " SIGSYS " ,
N_ ( L " Bad system call " )
}
,
2006-01-19 04:40:46 +10:00
# endif
# ifdef SIGINFO
2012-11-18 16:30:30 -08:00
{
SIGINFO ,
L " SIGINFO " ,
N_ ( L " Information request " )
}
,
2006-01-19 04:40:46 +10:00
# endif
# ifdef SIGSTKFLT
2012-11-18 16:30:30 -08:00
{
SIGSTKFLT ,
L " SISTKFLT " ,
N_ ( L " Stack fault " )
}
,
2006-01-19 04:40:46 +10:00
# endif
# ifdef SIGEMT
2012-11-18 16:30:30 -08:00
{
SIGEMT ,
L " SIGEMT " ,
N_ ( L " Emulator trap " )
}
,
2006-01-19 04:40:46 +10:00
# endif
# ifdef SIGIOT
2012-11-18 16:30:30 -08:00
{
SIGIOT ,
L " SIGIOT " ,
N_ ( L " Abort (Alias for SIGABRT) " )
}
,
2006-01-19 04:40:46 +10:00
# endif
# ifdef SIGUNUSED
2012-11-18 16:30:30 -08:00
{
SIGUNUSED ,
L " SIGUNUSED " ,
N_ ( L " Unused signal " )
}
,
# endif
{
0 ,
0 ,
0
}
2005-10-06 17:30:50 +10:00
}
2012-11-18 16:30:30 -08:00
;
2005-10-06 17:30:50 +10:00
/**
2012-11-18 16:30:30 -08:00
Test if \ c name is a string describing the signal named \ c canonical .
2005-10-06 17:30:50 +10:00
*/
2012-11-18 16:30:30 -08:00
static int match_signal_name ( const wchar_t * canonical ,
const wchar_t * name )
2005-10-06 17:30:50 +10:00
{
2012-11-18 16:30:30 -08:00
if ( wcsncasecmp ( name , L " sig " , 3 ) = = 0 )
name + = 3 ;
2005-10-06 17:30:50 +10:00
2012-11-18 16:30:30 -08:00
return wcscasecmp ( canonical + 3 , name ) = = 0 ;
2005-10-06 17:30:50 +10:00
}
2012-11-18 16:30:30 -08:00
int wcs2sig ( const wchar_t * str )
2005-10-06 17:30:50 +10:00
{
2012-11-18 16:30:30 -08:00
int i ;
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 ;
int res = fish_wcstoi ( str , & end , 10 ) ;
if ( ! errno & & res > = 0 & & ! * end )
return res ;
return - 1 ;
2005-10-06 17:30:50 +10:00
}
2012-11-18 16:30:30 -08:00
const wchar_t * sig2wcs ( int sig )
2005-10-06 17:30:50 +10:00
{
2012-11-18 16:30:30 -08:00
int i ;
2006-05-15 02:39:36 +10:00
2012-11-18 16:30:30 -08:00
for ( i = 0 ; lookup [ i ] . desc ; i + + )
{
if ( lookup [ i ] . signal = = sig )
{
return lookup [ i ] . name ;
}
}
2006-05-15 02:39:36 +10:00
2012-11-18 16:30:30 -08:00
return _ ( L " Unknown " ) ;
2005-10-06 17:30:50 +10:00
}
2012-11-18 16:30:30 -08:00
const wchar_t * signal_get_desc ( int sig )
2005-10-06 17:30:50 +10:00
{
2012-11-18 16:30:30 -08:00
int i ;
2006-05-15 02:39:36 +10:00
2012-11-18 16:30:30 -08:00
for ( i = 0 ; lookup [ i ] . desc ; i + + )
{
if ( lookup [ i ] . signal = = sig )
{
return _ ( lookup [ i ] . desc ) ;
}
}
2006-05-15 02:39:36 +10:00
2012-11-18 16:30:30 -08:00
return _ ( L " Unknown " ) ;
2005-10-06 17:30:50 +10:00
}
2006-01-24 06:40:14 +10:00
/**
Standard signal handler
*/
2005-10-06 17:30:50 +10:00
static void default_handler ( int signal , siginfo_t * info , void * context )
{
2012-06-04 14:20:01 -07:00
if ( event_is_signal_observed ( signal ) )
{
2012-11-18 16:30:30 -08:00
event_fire_signal ( signal ) ;
}
2005-10-06 17:30:50 +10:00
}
/**
Respond to a winch signal by checking the terminal size
*/
2012-11-18 16:30:30 -08:00
static void handle_winch ( int sig , siginfo_t * info , void * context )
2005-10-06 17:30:50 +10:00
{
2012-11-18 16:30:30 -08:00
common_handle_winch ( sig ) ;
default_handler ( sig , 0 , 0 ) ;
2005-10-06 17:30:50 +10:00
}
2006-05-14 20:16:23 +10:00
/**
2007-10-02 20:09:37 +10:00
Respond to a hup signal by exiting , unless it is caught by a
2007-04-17 06:10:53 +10:00
shellscript function , in which case we do nothing .
2006-05-14 20:16:23 +10:00
*/
2012-11-18 16:30:30 -08:00
static void handle_hup ( int sig , siginfo_t * info , void * context )
2006-05-14 20:16:23 +10:00
{
2012-11-18 16:30:30 -08:00
if ( event_is_signal_observed ( SIGHUP ) )
{
default_handler ( sig , 0 , 0 ) ;
}
else
{
reader_exit ( 1 , 1 ) ;
}
2006-05-14 20:16:23 +10:00
}
2012-11-18 02:16:14 -08:00
/** Handle sigterm. The only thing we do is restore the front process ID, then die. */
2012-11-18 16:30:30 -08:00
static void handle_term ( int sig , siginfo_t * info , void * context )
2012-11-18 02:16:14 -08:00
{
restore_term_foreground_process_group ( ) ;
signal ( SIGTERM , SIG_DFL ) ;
raise ( SIGTERM ) ;
}
2005-10-06 17:30:50 +10:00
/**
Interactive mode ^ C handler . Respond to int signal by setting
interrupted - flag and stopping all loops and conditionals .
*/
2012-11-18 16:30:30 -08:00
static void handle_int ( int sig , siginfo_t * info , void * context )
2005-10-06 17:30:50 +10:00
{
2012-11-18 16:30:30 -08:00
reader_handle_int ( sig ) ;
default_handler ( sig , info , context ) ;
2005-10-06 17:30:50 +10:00
}
2005-10-06 21:54:16 +10:00
/**
sigchld handler . Does notification and calls the handler in proc . c
*/
2012-11-18 16:30:30 -08:00
static void handle_chld ( int sig , siginfo_t * info , void * context )
2005-10-06 21:54:16 +10:00
{
2012-11-18 16:30:30 -08:00
job_handle_signal ( sig , info , context ) ;
default_handler ( sig , info , context ) ;
2005-10-06 21:54:16 +10:00
}
void signal_reset_handlers ( )
{
2012-11-18 16:30:30 -08:00
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 ) ;
}
2005-10-06 21:54:16 +10:00
}
2005-10-06 17:30:50 +10:00
/**
Sets appropriate signal handlers .
*/
void signal_set_handlers ( )
{
2012-11-18 16:30:30 -08:00
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 ( ) ;
}
2007-01-22 01:01:14 +10:00
# ifdef SIGWINCH
2012-11-18 16:30:30 -08:00
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 ( ) ;
}
// SIGTERM restores the terminal controlling process before dying
act . sa_flags = SA_SIGINFO ;
act . sa_sigaction = & handle_term ;
if ( sigaction ( SIGTERM , & 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_without_destructors ( 1 ) ;
}
}
2005-10-06 17:30:50 +10:00
}
2012-11-18 16:30:30 -08:00
void signal_handle ( int sig , int do_handle )
2005-10-06 21:54:16 +10:00
{
2012-11-18 16:30:30 -08:00
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 ) ;
2005-10-06 21:54:16 +10:00
}
2012-08-15 00:57:56 -07:00
void get_signals_with_handlers ( sigset_t * set )
{
sigemptyset ( set ) ;
2012-11-18 16:30:30 -08:00
for ( int i = 0 ; lookup [ i ] . desc ; i + + )
{
2012-08-15 00:57:56 -07:00
struct sigaction act = { } ;
2012-11-18 16:30:30 -08:00
sigaction ( lookup [ i ] . signal , NULL , & act ) ;
2012-08-15 00:57:56 -07:00
if ( act . sa_handler ! = SIG_DFL )
sigaddset ( set , lookup [ i ] . signal ) ;
2012-11-18 16:30:30 -08:00
}
2012-08-15 00:57:56 -07:00
}
2005-10-14 21:40:33 +10:00
void signal_block ( )
{
2012-02-18 23:26:39 -08:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-18 16:30:30 -08:00
sigset_t chldset ;
if ( ! block_count )
{
sigfillset ( & chldset ) ;
VOMIT_ON_FAILURE ( pthread_sigmask ( SIG_BLOCK , & chldset , NULL ) ) ;
}
block_count + + ;
2006-10-30 07:09:11 +10:00
// debug( 0, L"signal block level increased to %d", block_count );
2005-10-14 21:40:33 +10:00
}
void signal_unblock ( )
{
2012-02-18 23:26:39 -08:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-18 16:30:30 -08:00
sigset_t chldset ;
block_count - - ;
if ( block_count < 0 )
{
debug ( 0 , _ ( L " Signal block mismatch " ) ) ;
bugreport ( ) ;
FATAL_EXIT ( ) ;
}
if ( ! block_count )
{
sigfillset ( & chldset ) ;
VOMIT_ON_FAILURE ( pthread_sigmask ( SIG_UNBLOCK , & chldset , 0 ) ) ;
}
2006-10-30 07:09:11 +10:00
// debug( 0, L"signal block level decreased to %d", block_count );
}
2006-05-15 02:39:36 +10:00
2012-12-04 16:00:35 -08:00
bool signal_is_blocked ( )
2006-10-30 07:09:11 +10:00
{
2012-11-18 16:30:30 -08:00
return ! ! block_count ;
2005-10-14 21:40:33 +10:00
}
2006-10-30 07:09:11 +10:00