2005-10-25 01:26:25 +10:00
/** \file event.c
2005-10-06 08:37:08 +10:00
2005-10-25 01:26:25 +10:00
Functions for handling event triggers
2005-10-06 08:37:08 +10:00
*/
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <unistd.h>
# include <termios.h>
# include <signal.h>
# include <string.h>
# include "config.h"
# include "util.h"
# include "function.h"
# include "proc.h"
# include "parser.h"
# include "common.h"
# include "event.h"
# include "signal.h"
2006-01-04 22:51:02 +10:00
# include "translate.h"
2005-10-06 08:37:08 +10:00
/**
Number of signals that can be queued before an overflow occurs
*/
# define SIG_UNHANDLED_MAX 64
/**
This struct contains a list of generated signals waiting to be
dispatched
*/
typedef struct
{
2005-10-25 01:26:25 +10:00
/**
Number of delivered signals
*/
2005-10-06 08:37:08 +10:00
int count ;
2005-10-25 01:26:25 +10:00
/**
Whether signals have been skipped
*/
2005-10-06 08:37:08 +10:00
int overflow ;
2005-10-25 01:26:25 +10:00
/**
Array of signal events
*/
2005-10-06 08:37:08 +10:00
int signal [ SIG_UNHANDLED_MAX ] ;
}
signal_list_t ;
2005-10-25 01:26:25 +10:00
/**
2005-10-06 08:37:08 +10:00
The signal event list . Actually two separate lists . One which is
active , which is the one that new events is written to . The inactive
one contains the events that are currently beeing performed .
*/
static signal_list_t sig_list [ 2 ] ;
/**
The index of sig_list that is the list of signals currently written to
*/
static int active_list = 0 ;
/**
List of event handlers
*/
static array_list_t * events ;
/**
List of event handlers that should be removed
*/
static array_list_t * killme ;
2005-12-12 08:21:01 +10:00
/**
List of events that have been sent but have not yet been delivered because they are blocked .
*/
static array_list_t * blocked ;
2005-10-06 08:37:08 +10:00
/**
Tests if one event instance matches the definition of a event
class . If the class defines a function name , that will also be a
match criterion .
*/
static int event_match ( event_t * class , event_t * instance )
{
if ( class - > function_name & & instance - > function_name )
{
if ( wcscmp ( class - > function_name , instance - > function_name ) ! = 0 )
return 0 ;
}
if ( class - > type = = EVENT_ANY )
return 1 ;
if ( class - > type ! = instance - > type )
return 0 ;
switch ( class - > type )
{
case EVENT_SIGNAL :
2005-10-12 05:31:16 +10:00
if ( class - > param1 . signal = = EVENT_ANY_SIGNAL )
2005-10-06 08:37:08 +10:00
return 1 ;
2005-10-12 05:31:16 +10:00
return class - > param1 . signal = = instance - > param1 . signal ;
2005-10-06 08:37:08 +10:00
case EVENT_VARIABLE :
2005-10-12 05:31:16 +10:00
return wcscmp ( instance - > param1 . variable ,
class - > param1 . variable ) = = 0 ;
2005-10-06 08:37:08 +10:00
case EVENT_EXIT :
2005-10-12 05:31:16 +10:00
if ( class - > param1 . pid = = EVENT_ANY_PID )
2005-10-06 08:37:08 +10:00
return 1 ;
2005-10-12 05:31:16 +10:00
return class - > param1 . pid = = instance - > param1 . pid ;
2005-10-15 10:51:26 +10:00
case EVENT_JOB_ID :
return class - > param1 . job_id = = instance - > param1 . job_id ;
2005-10-06 08:37:08 +10:00
}
/**
This should never be reached
*/
return 0 ;
}
/**
Create an identical copy of an event . Use deep copying , i . e . make
duplicates of any strings used as well .
*/
2005-12-12 08:21:01 +10:00
static event_t * event_copy ( event_t * event , int copy_arguments )
2005-10-06 08:37:08 +10:00
{
event_t * e = malloc ( sizeof ( event_t ) ) ;
if ( ! e )
die_mem ( ) ;
memcpy ( e , event , sizeof ( event_t ) ) ;
if ( e - > function_name )
e - > function_name = wcsdup ( e - > function_name ) ;
if ( e - > type = = EVENT_VARIABLE )
2005-10-12 05:31:16 +10:00
e - > param1 . variable = wcsdup ( e - > param1 . variable ) ;
2005-10-06 08:37:08 +10:00
2005-12-12 08:21:01 +10:00
al_init ( & e - > arguments ) ;
if ( copy_arguments )
{
int i ;
for ( i = 0 ; i < al_get_count ( & event - > arguments ) ; i + + )
{
al_push ( & e - > arguments , wcsdup ( ( wchar_t * ) al_get ( & event - > arguments , i ) ) ) ;
}
}
2005-10-06 08:37:08 +10:00
return e ;
}
2005-12-12 08:21:01 +10:00
static int event_is_blocked ( event_t * e )
{
block_t * block ;
event_block_t * eb ;
for ( block = current_block ; block ; block = block - > outer )
{
for ( eb = block - > first_event_block ; eb ; eb = eb - > next )
{
if ( eb - > type & ( 1 < < EVENT_ANY ) )
return 1 ;
if ( eb - > type & ( 1 < < e - > type ) )
return 1 ;
}
}
for ( eb = global_event_block ; eb ; eb = eb - > next )
{
if ( eb - > type & ( 1 < < EVENT_ANY ) )
return 1 ;
if ( eb - > type & ( 1 < < e - > type ) )
return 1 ;
return 1 ;
}
return 0 ;
}
2005-10-06 08:37:08 +10:00
void event_add_handler ( event_t * event )
{
2005-12-12 08:21:01 +10:00
event_t * e ;
e = event_copy ( event , 0 ) ;
2005-10-06 08:37:08 +10:00
if ( ! events )
events = al_new ( ) ;
2005-10-06 21:54:16 +10:00
if ( e - > type = = EVENT_SIGNAL )
{
2005-10-12 05:31:16 +10:00
signal_handle ( e - > param1 . signal , 1 ) ;
2005-10-06 21:54:16 +10:00
}
2005-10-06 08:37:08 +10:00
al_push ( events , e ) ;
}
void event_remove ( event_t * criterion )
{
int i ;
2005-10-06 21:54:16 +10:00
array_list_t * new_list = 0 ;
event_t e ;
2005-10-06 08:37:08 +10:00
/*
Because of concurrency issues , env_remove does not actually free
2005-10-06 21:54:16 +10:00
any events - instead it simply moves all events that should be
removed from the event list to the killme list .
2005-10-06 08:37:08 +10:00
*/
if ( ! events )
return ;
for ( i = 0 ; i < al_get_count ( events ) ; i + + )
{
event_t * n = ( event_t * ) al_get ( events , i ) ;
if ( event_match ( criterion , n ) )
{
if ( ! killme )
killme = al_new ( ) ;
al_push ( killme , n ) ;
2005-10-06 21:54:16 +10:00
/*
If this event was a signal handler and no other handler handles
the specified signal type , do not handle that type of signal any
more .
*/
if ( n - > type = = EVENT_SIGNAL )
{
e . type = EVENT_SIGNAL ;
2005-10-12 05:31:16 +10:00
e . param1 . signal = n - > param1 . signal ;
2005-10-06 21:54:16 +10:00
e . function_name = 0 ;
if ( event_get ( & e , 0 ) = = 1 )
{
2005-10-12 05:31:16 +10:00
signal_handle ( e . param1 . signal , 0 ) ;
2005-10-06 21:54:16 +10:00
}
}
2005-10-06 08:37:08 +10:00
}
else
{
2005-10-06 21:54:16 +10:00
if ( ! new_list )
new_list = al_new ( ) ;
2005-10-06 08:37:08 +10:00
al_push ( new_list , n ) ;
}
}
al_destroy ( events ) ;
2005-10-06 21:54:16 +10:00
free ( events ) ;
2005-10-06 08:37:08 +10:00
events = new_list ;
}
2005-10-06 21:54:16 +10:00
int event_get ( event_t * criterion , array_list_t * out )
2005-10-06 08:37:08 +10:00
{
int i ;
2005-10-06 21:54:16 +10:00
int found = 0 ;
2005-10-06 08:37:08 +10:00
if ( ! events )
2005-10-06 21:54:16 +10:00
return 0 ;
2005-10-06 08:37:08 +10:00
for ( i = 0 ; i < al_get_count ( events ) ; i + + )
{
event_t * n = ( event_t * ) al_get ( events , i ) ;
if ( event_match ( criterion , n ) )
2005-10-06 21:54:16 +10:00
{
found + + ;
if ( out )
al_push ( out , n ) ;
}
2005-10-06 08:37:08 +10:00
}
2005-10-06 21:54:16 +10:00
return found ;
2005-10-06 08:37:08 +10:00
}
/**
Free all events in the kill list
*/
static void event_free_kills ( )
{
int i ;
if ( ! killme )
return ;
for ( i = 0 ; i < al_get_count ( killme ) ; i + + )
{
2005-10-06 21:54:16 +10:00
event_t * roadkill = ( event_t * ) al_get ( killme , i ) ;
2005-10-06 08:37:08 +10:00
event_free ( roadkill ) ;
}
al_truncate ( killme , 0 ) ;
}
/**
Test if the specified event is waiting to be killed
*/
static int event_is_killed ( event_t * e )
{
int i ;
if ( ! killme )
return 0 ;
for ( i = 0 ; i < al_get_count ( killme ) ; i + + )
{
event_t * roadkill = ( event_t * ) al_get ( events , i ) ;
if ( roadkill = = e )
return 1 ;
}
return 0 ;
}
/**
Perform the specified event . Since almost all event firings will
not match a single event handler , we make sureto optimize the ' no
matches ' path . This means that nothing is allocated / initialized
unless that is needed .
*/
2005-12-12 08:21:01 +10:00
static void event_fire_internal ( event_t * event )
2005-10-06 08:37:08 +10:00
{
int i , j ;
string_buffer_t * b = 0 ;
array_list_t * fire = 0 ;
2005-12-04 05:46:18 +10:00
int was_subshell = is_subshell ;
2005-10-06 08:37:08 +10:00
/*
First we free all events that have been removed
*/
event_free_kills ( ) ;
2005-10-06 21:54:16 +10:00
if ( ! events )
return ;
2005-10-06 08:37:08 +10:00
/*
Then we iterate over all events , adding events that should be
fired to a second list . We need to do this in a separate step
since an event handler might call event_remove or
event_add_handler , which will change the contents of the \ c
events list .
*/
for ( i = 0 ; i < al_get_count ( events ) ; i + + )
{
event_t * criterion = ( event_t * ) al_get ( events , i ) ;
2005-10-17 23:24:12 +10:00
2005-10-06 08:37:08 +10:00
/*
Check if this event is a match
*/
if ( event_match ( criterion , event ) )
{
if ( ! fire )
fire = al_new ( ) ;
al_push ( fire , criterion ) ;
}
}
2005-10-15 10:51:26 +10:00
2005-10-06 08:37:08 +10:00
/*
No matches . Time to return .
*/
if ( ! fire )
return ;
/*
Iterate over our list of matching events
*/
for ( i = 0 ; i < al_get_count ( fire ) ; i + + )
{
event_t * criterion = ( event_t * ) al_get ( fire , i ) ;
/*
Check if this event has been removed , if so , dont fire it
*/
if ( event_is_killed ( criterion ) )
continue ;
/*
Fire event
*/
if ( ! b )
b = sb_new ( ) ;
else
sb_clear ( b ) ;
sb_append ( b , criterion - > function_name ) ;
2005-12-12 08:21:01 +10:00
for ( j = 0 ; j < al_get_count ( & event - > arguments ) ; j + + )
2005-10-06 08:37:08 +10:00
{
2005-12-12 08:21:01 +10:00
wchar_t * arg_esc = escape ( ( wchar_t * ) al_get ( & event - > arguments , j ) , 0 ) ;
2005-10-06 08:37:08 +10:00
sb_append ( b , L " " ) ;
sb_append ( b , arg_esc ) ;
free ( arg_esc ) ;
}
2005-10-12 05:23:43 +10:00
// debug( 1, L"Event handler fires command '%ls'", (wchar_t *)b->buff );
2005-10-06 08:37:08 +10:00
2005-12-04 05:46:18 +10:00
/*
Event handlers are not part of the main flow of code , so
they are marked as non - interactive and as a subshell
*/
2005-10-12 05:23:43 +10:00
is_subshell = 1 ;
2005-10-06 08:37:08 +10:00
eval ( ( wchar_t * ) b - > buff , 0 , TOP ) ;
2005-10-12 05:23:43 +10:00
2005-10-06 08:37:08 +10:00
}
2005-12-04 05:46:18 +10:00
/*
Restore interactivity flags
*/
is_subshell = was_subshell ;
2005-10-06 08:37:08 +10:00
if ( b )
{
sb_destroy ( b ) ;
free ( b ) ;
}
if ( fire )
{
al_destroy ( fire ) ;
free ( fire ) ;
}
/*
Free killed events
*/
event_free_kills ( ) ;
2005-10-12 05:23:43 +10:00
2005-10-06 08:37:08 +10:00
}
/**
2005-10-15 10:51:26 +10:00
Handle all pending signal events
2005-10-06 08:37:08 +10:00
*/
2005-12-12 08:21:01 +10:00
static void event_fire_delayed ( )
2005-10-06 08:37:08 +10:00
{
2005-12-12 08:21:01 +10:00
int i ;
if ( blocked & & is_event = = 1 )
{
array_list_t * new_blocked = 0 ;
for ( i = 0 ; i < al_get_count ( blocked ) ; i + + )
{
event_t * e = ( event_t * ) al_get ( blocked , i ) ;
if ( event_is_blocked ( e ) )
{
if ( ! new_blocked )
new_blocked = al_new ( ) ;
al_push ( new_blocked , e ) ;
}
else
{
event_fire_internal ( e ) ;
event_free ( e ) ;
}
}
al_destroy ( blocked ) ;
free ( blocked ) ;
blocked = new_blocked ;
}
2005-10-06 08:37:08 +10:00
while ( sig_list [ active_list ] . count > 0 )
{
signal_list_t * lst ;
event_t e ;
2005-12-12 08:21:01 +10:00
al_init ( & e . arguments ) ;
2005-10-06 08:37:08 +10:00
2005-10-14 21:40:33 +10:00
/*
Switch signal lists
*/
2005-10-06 08:37:08 +10:00
sig_list [ 1 - active_list ] . count = 0 ;
sig_list [ 1 - active_list ] . overflow = 0 ;
active_list = 1 - active_list ;
2005-10-14 21:40:33 +10:00
/*
Set up
*/
2005-10-06 08:37:08 +10:00
e . type = EVENT_SIGNAL ;
e . function_name = 0 ;
lst = & sig_list [ 1 - active_list ] ;
if ( lst - > overflow )
{
2006-01-04 22:51:02 +10:00
debug ( 0 , _ ( L " Signal list overflow. Signals have been ignored. " ) ) ;
2005-10-06 08:37:08 +10:00
}
2005-10-14 21:40:33 +10:00
/*
Send all signals in our private list
*/
2005-10-06 08:37:08 +10:00
for ( i = 0 ; i < lst - > count ; i + + )
{
2005-10-12 05:31:16 +10:00
e . param1 . signal = lst - > signal [ i ] ;
2005-12-12 08:21:01 +10:00
al_set ( & e . arguments , 0 , sig2wcs ( e . param1 . signal ) ) ;
if ( event_is_blocked ( & e ) )
{
if ( ! blocked )
blocked = al_new ( ) ;
al_push ( blocked , event_copy ( & e , 1 ) ) ;
}
else
{
event_fire_internal ( & e ) ;
}
2005-10-14 21:40:33 +10:00
}
2005-12-12 08:21:01 +10:00
al_destroy ( & e . arguments ) ;
2005-10-06 08:37:08 +10:00
}
}
2005-12-12 08:21:01 +10:00
void event_fire ( event_t * event )
2005-10-06 08:37:08 +10:00
{
2005-10-12 05:23:43 +10:00
is_event + + ;
2005-10-06 08:37:08 +10:00
if ( event & & ( event - > type = = EVENT_SIGNAL ) )
{
/*
This means we are in a signal handler . We must be very
careful not do do anything that could cause a memory
allocation or something else that might be illegal in a
signal handler .
*/
if ( sig_list [ active_list ] . count < SIG_UNHANDLED_MAX )
2005-10-12 05:31:16 +10:00
sig_list [ active_list ] . signal [ sig_list [ active_list ] . count + + ] = event - > param1 . signal ;
2005-10-06 08:37:08 +10:00
else
sig_list [ active_list ] . overflow = 1 ;
2005-10-12 05:23:43 +10:00
}
2005-10-06 08:37:08 +10:00
else
{
2005-10-12 05:23:43 +10:00
2005-12-12 08:21:01 +10:00
event_fire_delayed ( ) ;
2005-10-06 08:37:08 +10:00
if ( event )
2005-12-12 08:21:01 +10:00
{
if ( event_is_blocked ( event ) )
{
if ( ! blocked )
blocked = al_new ( ) ;
al_push ( blocked , event_copy ( event , 1 ) ) ;
}
else
{
event_fire_internal ( event ) ;
}
}
2005-10-12 05:23:43 +10:00
2005-10-06 08:37:08 +10:00
}
2005-12-12 09:30:01 +10:00
is_event - - ;
2005-10-06 08:37:08 +10:00
}
void event_init ( )
{
sig_list [ active_list ] . count = 0 ;
}
void event_destroy ( )
{
if ( events )
{
al_foreach ( events , ( void ( * ) ( const void * ) ) & event_free ) ;
al_destroy ( events ) ;
2005-10-06 21:54:16 +10:00
free ( events ) ;
2005-10-06 08:37:08 +10:00
events = 0 ;
}
if ( killme )
{
al_foreach ( killme , ( void ( * ) ( const void * ) ) & event_free ) ;
al_destroy ( killme ) ;
2005-10-06 21:54:16 +10:00
free ( killme ) ;
2005-10-06 08:37:08 +10:00
killme = 0 ;
}
}
void event_free ( event_t * e )
{
2005-12-12 08:21:01 +10:00
/*
When apropriate , we clear the argument vector
*/
al_foreach ( & e - > arguments , ( void ( * ) ( const void * ) ) & free ) ;
al_destroy ( & e - > arguments ) ;
2005-10-06 21:54:16 +10:00
free ( ( void * ) e - > function_name ) ;
2005-10-06 08:37:08 +10:00
if ( e - > type = = EVENT_VARIABLE )
2005-10-12 05:31:16 +10:00
free ( ( void * ) e - > param1 . variable ) ;
2005-10-06 08:37:08 +10:00
free ( e ) ;
}