2007-01-21 11:23:14 +03:00
/*
Unix SMB / CIFS implementation .
common events code for signal events
Copyright ( C ) Andrew Tridgell 2007
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2007-01-21 11:23:14 +03:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2007-01-21 11:23:14 +03:00
*/
2008-04-25 01:28:30 +04:00
# include <signal.h>
# include "replace.h"
2007-01-21 11:23:14 +03:00
# include "system/filesys.h"
# include "system/select.h"
2008-04-25 01:28:30 +04:00
# include "events.h"
# include "events_internal.h"
2008-06-17 04:21:46 +04:00
# include "events_util.h"
2007-01-21 11:23:14 +03:00
# define NUM_SIGNALS 64
2007-01-21 13:32:39 +03:00
/* maximum number of SA_SIGINFO signals to hold in the queue */
# define SA_INFO_QUEUE_COUNT 10
2007-01-22 00:02:24 +03:00
struct sigcounter {
uint32_t count ;
uint32_t seen ;
} ;
# define SIG_INCREMENT(s) (s).count++
# define SIG_SEEN(s, n) (s).seen += (n)
# define SIG_PENDING(s) ((s).seen != (s).count)
2007-01-21 11:23:14 +03:00
/*
the poor design of signals means that this table must be static global
*/
2007-01-23 03:06:33 +03:00
static struct sig_state {
2008-07-24 05:48:27 +04:00
struct signal_event * sig_handlers [ NUM_SIGNALS + 1 ] ;
struct sigaction * oldact [ NUM_SIGNALS + 1 ] ;
struct sigcounter signal_count [ NUM_SIGNALS + 1 ] ;
2007-01-22 00:02:24 +03:00
struct sigcounter got_signal ;
2007-01-21 11:23:14 +03:00
int pipe_hack [ 2 ] ;
2007-01-21 13:32:39 +03:00
# ifdef SA_SIGINFO
/* with SA_SIGINFO we get quite a lot of info per signal */
2008-07-24 05:48:27 +04:00
siginfo_t * sig_info [ NUM_SIGNALS + 1 ] ;
struct sigcounter sig_blocked [ NUM_SIGNALS + 1 ] ;
2007-01-21 13:32:39 +03:00
# endif
2007-01-23 03:06:33 +03:00
} * sig_state ;
2007-01-21 11:23:14 +03:00
2007-01-22 00:02:24 +03:00
/*
return number of sigcounter events not processed yet
*/
static uint32_t sig_count ( struct sigcounter s )
{
if ( s . count > = s . seen ) {
return s . count - s . seen ;
}
return 1 + ( 0xFFFFFFFF & ~ ( s . seen - s . count ) ) ;
}
2007-01-21 11:23:14 +03:00
/*
signal handler - redirects to registered signals
*/
static void signal_handler ( int signum )
{
char c = 0 ;
2007-01-23 03:06:33 +03:00
SIG_INCREMENT ( sig_state - > signal_count [ signum ] ) ;
SIG_INCREMENT ( sig_state - > got_signal ) ;
2007-01-21 11:23:14 +03:00
/* doesn't matter if this pipe overflows */
2007-01-23 03:06:33 +03:00
write ( sig_state - > pipe_hack [ 1 ] , & c , 1 ) ;
2007-01-21 11:23:14 +03:00
}
2007-01-21 13:32:39 +03:00
# ifdef SA_SIGINFO
/*
signal handler with SA_SIGINFO - redirects to registered signals
*/
static void signal_handler_info ( int signum , siginfo_t * info , void * uctx )
{
2007-01-23 03:06:33 +03:00
uint32_t count = sig_count ( sig_state - > signal_count [ signum ] ) ;
sig_state - > sig_info [ signum ] [ count ] = * info ;
2007-01-21 13:32:39 +03:00
signal_handler ( signum ) ;
/* handle SA_SIGINFO */
2007-01-22 00:02:24 +03:00
if ( count + 1 = = SA_INFO_QUEUE_COUNT ) {
2007-01-21 13:32:39 +03:00
/* we've filled the info array - block this signal until
these ones are delivered */
sigset_t set ;
sigemptyset ( & set ) ;
sigaddset ( & set , signum ) ;
sigprocmask ( SIG_BLOCK , & set , NULL ) ;
2007-01-23 03:06:33 +03:00
SIG_INCREMENT ( sig_state - > sig_blocked [ signum ] ) ;
2007-01-21 13:32:39 +03:00
}
}
# endif
2007-01-21 11:23:14 +03:00
/*
destroy a signal event
*/
static int signal_event_destructor ( struct signal_event * se )
{
se - > event_ctx - > num_signal_handlers - - ;
2007-01-23 03:06:33 +03:00
DLIST_REMOVE ( sig_state - > sig_handlers [ se - > signum ] , se ) ;
if ( sig_state - > sig_handlers [ se - > signum ] = = NULL ) {
2007-01-21 13:32:39 +03:00
/* restore old handler, if any */
2007-01-23 03:06:33 +03:00
sigaction ( se - > signum , sig_state - > oldact [ se - > signum ] , NULL ) ;
sig_state - > oldact [ se - > signum ] = NULL ;
2007-01-22 01:12:19 +03:00
# ifdef SA_SIGINFO
if ( se - > sa_flags & SA_SIGINFO ) {
2007-01-23 03:06:33 +03:00
talloc_free ( sig_state - > sig_info [ se - > signum ] ) ;
sig_state - > sig_info [ se - > signum ] = NULL ;
2007-01-22 01:12:19 +03:00
}
# endif
2007-01-21 11:23:14 +03:00
}
return 0 ;
}
/*
this is part of the pipe hack needed to avoid the signal race condition
*/
static void signal_pipe_handler ( struct event_context * ev , struct fd_event * fde ,
uint16_t flags , void * private )
{
char c [ 16 ] ;
/* its non-blocking, doesn't matter if we read too much */
2007-01-23 03:06:33 +03:00
read ( sig_state - > pipe_hack [ 0 ] , c , sizeof ( c ) ) ;
2007-01-21 11:23:14 +03:00
}
/*
add a signal event
return NULL on failure ( memory allocation error )
*/
struct signal_event * common_event_add_signal ( struct event_context * ev ,
2007-01-21 13:32:39 +03:00
TALLOC_CTX * mem_ctx ,
int signum ,
int sa_flags ,
event_signal_handler_t handler ,
void * private_data )
2007-01-21 11:23:14 +03:00
{
struct signal_event * se ;
if ( signum > = NUM_SIGNALS ) {
return NULL ;
}
2007-01-23 03:06:33 +03:00
/* the sig_state needs to be on a global context as it can last across
multiple event contexts */
if ( sig_state = = NULL ) {
sig_state = talloc_zero ( talloc_autofree_context ( ) , struct sig_state ) ;
if ( sig_state = = NULL ) {
return NULL ;
}
}
2007-01-21 11:23:14 +03:00
se = talloc ( mem_ctx ? mem_ctx : ev , struct signal_event ) ;
if ( se = = NULL ) return NULL ;
se - > event_ctx = ev ;
se - > handler = handler ;
se - > private_data = private_data ;
se - > signum = signum ;
2007-01-21 13:32:39 +03:00
se - > sa_flags = sa_flags ;
2007-01-24 01:28:21 +03:00
/* Ensure, no matter the destruction order, that we always have a handle on the global sig_state */
if ( ! talloc_reference ( se , sig_state ) ) {
return NULL ;
}
2007-01-21 11:23:14 +03:00
2007-01-21 13:32:39 +03:00
/* only install a signal handler if not already installed */
2007-01-23 03:06:33 +03:00
if ( sig_state - > sig_handlers [ signum ] = = NULL ) {
2007-01-21 13:32:39 +03:00
struct sigaction act ;
ZERO_STRUCT ( act ) ;
act . sa_handler = signal_handler ;
act . sa_flags = sa_flags ;
# ifdef SA_SIGINFO
if ( sa_flags & SA_SIGINFO ) {
act . sa_handler = NULL ;
act . sa_sigaction = signal_handler_info ;
2007-01-23 03:06:33 +03:00
if ( sig_state - > sig_info [ signum ] = = NULL ) {
sig_state - > sig_info [ signum ] = talloc_array ( sig_state , siginfo_t , SA_INFO_QUEUE_COUNT ) ;
if ( sig_state - > sig_info [ signum ] = = NULL ) {
2007-01-22 01:12:19 +03:00
talloc_free ( se ) ;
return NULL ;
}
}
2007-01-21 13:32:39 +03:00
}
# endif
2007-01-23 03:06:33 +03:00
sig_state - > oldact [ signum ] = talloc ( sig_state , struct sigaction ) ;
if ( sig_state - > oldact [ signum ] = = NULL ) {
2007-01-22 01:12:19 +03:00
talloc_free ( se ) ;
return NULL ;
}
2007-01-23 03:06:33 +03:00
if ( sigaction ( signum , & act , sig_state - > oldact [ signum ] ) = = - 1 ) {
2007-01-21 13:32:39 +03:00
talloc_free ( se ) ;
return NULL ;
}
2007-01-21 11:23:14 +03:00
}
2007-01-23 03:06:33 +03:00
DLIST_ADD ( sig_state - > sig_handlers [ signum ] , se ) ;
2007-01-21 11:23:14 +03:00
talloc_set_destructor ( se , signal_event_destructor ) ;
2007-01-21 13:32:39 +03:00
/* we need to setup the pipe hack handler if not already
setup */
2007-01-21 11:23:14 +03:00
if ( ev - > pipe_fde = = NULL ) {
2007-01-23 03:06:33 +03:00
if ( sig_state - > pipe_hack [ 0 ] = = 0 & &
sig_state - > pipe_hack [ 1 ] = = 0 ) {
pipe ( sig_state - > pipe_hack ) ;
2008-06-14 19:23:31 +04:00
ev_set_blocking ( sig_state - > pipe_hack [ 0 ] , false ) ;
ev_set_blocking ( sig_state - > pipe_hack [ 1 ] , false ) ;
2007-01-21 11:23:14 +03:00
}
2007-01-23 03:06:33 +03:00
ev - > pipe_fde = event_add_fd ( ev , ev , sig_state - > pipe_hack [ 0 ] ,
2007-01-21 11:23:14 +03:00
EVENT_FD_READ , signal_pipe_handler , NULL ) ;
}
ev - > num_signal_handlers + + ;
return se ;
}
/*
check if a signal is pending
return ! = 0 if a signal was pending
*/
int common_event_check_signal ( struct event_context * ev )
{
int i ;
2007-01-23 03:06:33 +03:00
if ( ! sig_state | | ! SIG_PENDING ( sig_state - > got_signal ) ) {
2007-01-21 11:23:14 +03:00
return 0 ;
}
for ( i = 0 ; i < NUM_SIGNALS + 1 ; i + + ) {
2007-01-21 20:58:56 +03:00
struct signal_event * se , * next ;
2007-01-23 03:06:33 +03:00
struct sigcounter counter = sig_state - > signal_count [ i ] ;
2007-01-22 02:37:32 +03:00
uint32_t count = sig_count ( counter ) ;
2007-01-21 20:58:56 +03:00
if ( count = = 0 ) {
continue ;
}
2007-01-23 03:06:33 +03:00
for ( se = sig_state - > sig_handlers [ i ] ; se ; se = next ) {
2007-01-21 20:58:56 +03:00
next = se - > next ;
2007-01-21 13:32:39 +03:00
# ifdef SA_SIGINFO
2007-01-21 20:58:56 +03:00
if ( se - > sa_flags & SA_SIGINFO ) {
int j ;
for ( j = 0 ; j < count ; j + + ) {
2007-01-22 02:37:32 +03:00
/* note the use of the sig_info array as a
ring buffer */
2008-03-10 14:46:17 +03:00
int ofs = ( ( count - 1 ) + j ) % SA_INFO_QUEUE_COUNT ;
2007-01-21 20:58:56 +03:00
se - > handler ( ev , se , i , 1 ,
2007-01-23 03:06:33 +03:00
( void * ) & sig_state - > sig_info [ i ] [ ofs ] ,
2007-01-21 20:58:56 +03:00
se - > private_data ) ;
2007-01-21 13:32:39 +03:00
}
2007-01-23 03:06:33 +03:00
if ( SIG_PENDING ( sig_state - > sig_blocked [ i ] ) ) {
2007-01-21 20:58:56 +03:00
/* we'd filled the queue, unblock the
signal now */
sigset_t set ;
sigemptyset ( & set ) ;
sigaddset ( & set , i ) ;
2007-01-23 03:06:33 +03:00
SIG_SEEN ( sig_state - > sig_blocked [ i ] ,
sig_count ( sig_state - > sig_blocked [ i ] ) ) ;
2007-01-21 20:58:56 +03:00
sigprocmask ( SIG_UNBLOCK , & set , NULL ) ;
2007-01-21 13:32:39 +03:00
}
2007-01-22 01:14:05 +03:00
if ( se - > sa_flags & SA_RESETHAND ) {
talloc_free ( se ) ;
}
2007-01-21 20:58:56 +03:00
continue ;
}
# endif
se - > handler ( ev , se , i , count , NULL , se - > private_data ) ;
if ( se - > sa_flags & SA_RESETHAND ) {
talloc_free ( se ) ;
2007-01-21 11:23:14 +03:00
}
}
2007-01-23 03:06:33 +03:00
SIG_SEEN ( sig_state - > signal_count [ i ] , count ) ;
SIG_SEEN ( sig_state - > got_signal , count ) ;
2007-01-21 11:23:14 +03:00
}
return 1 ;
}