2006-02-03 22:19:41 +00:00
/*
Unix SMB / CIFS implementation .
Timed event library .
Copyright ( C ) Andrew Tridgell 1992 - 1998
Copyright ( C ) Volker Lendecke 2005
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-09 19:25:36 +00:00
the Free Software Foundation ; either version 3 of the License , or
2006-02-03 22:19:41 +00: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 00:52:41 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-02-03 22:19:41 +00:00
*/
# include "includes.h"
2011-05-10 14:55:45 +02:00
# include "lib/tevent/tevent_internal.h"
2010-10-01 10:08:15 +02:00
# include "../lib/util/select.h"
2011-02-03 16:34:07 +01:00
# include "system/select.h"
2006-02-03 22:19:41 +00:00
2011-02-03 16:34:07 +01:00
struct tevent_poll_private {
/*
* Index from file descriptor into the pollfd array
*/
int * pollfd_idx ;
/*
* Cache for s3_event_loop_once to avoid reallocs
*/
struct pollfd * pfds ;
} ;
static struct tevent_poll_private * tevent_get_poll_private (
struct tevent_context * ev )
{
struct tevent_poll_private * state ;
state = ( struct tevent_poll_private * ) ev - > additional_data ;
if ( state = = NULL ) {
state = TALLOC_ZERO_P ( ev , struct tevent_poll_private ) ;
ev - > additional_data = ( void * ) state ;
if ( state = = NULL ) {
DEBUG ( 10 , ( " talloc failed \n " ) ) ;
}
}
return state ;
}
static void count_fds ( struct tevent_context * ev ,
int * pnum_fds , int * pmax_fd )
{
struct tevent_fd * fde ;
int num_fds = 0 ;
int max_fd = 0 ;
for ( fde = ev - > fd_events ; fde ! = NULL ; fde = fde - > next ) {
if ( fde - > flags & ( EVENT_FD_READ | EVENT_FD_WRITE ) ) {
num_fds + = 1 ;
if ( fde - > fd > max_fd ) {
max_fd = fde - > fd ;
}
}
}
* pnum_fds = num_fds ;
* pmax_fd = max_fd ;
}
bool event_add_to_poll_args ( struct tevent_context * ev , TALLOC_CTX * mem_ctx ,
struct pollfd * * pfds , int * pnum_pfds ,
int * ptimeout )
{
struct tevent_poll_private * state ;
struct tevent_fd * fde ;
int i , num_fds , max_fd , num_pollfds , idx_len ;
struct pollfd * fds ;
struct timeval now , diff ;
int timeout ;
state = tevent_get_poll_private ( ev ) ;
if ( state = = NULL ) {
return false ;
}
count_fds ( ev , & num_fds , & max_fd ) ;
idx_len = max_fd + 1 ;
if ( talloc_array_length ( state - > pollfd_idx ) < idx_len ) {
state - > pollfd_idx = TALLOC_REALLOC_ARRAY (
state , state - > pollfd_idx , int , idx_len ) ;
if ( state - > pollfd_idx = = NULL ) {
DEBUG ( 10 , ( " talloc_realloc failed \n " ) ) ;
return false ;
}
}
fds = * pfds ;
num_pollfds = * pnum_pfds ;
/*
* The + 1 is for the sys_poll calling convention . It expects
* an array 1 longer for the signal pipe
*/
if ( talloc_array_length ( fds ) < num_pollfds + num_fds + 1 ) {
fds = TALLOC_REALLOC_ARRAY ( mem_ctx , fds , struct pollfd ,
num_pollfds + num_fds + 1 ) ;
if ( fds = = NULL ) {
DEBUG ( 10 , ( " talloc_realloc failed \n " ) ) ;
return false ;
}
}
memset ( & fds [ num_pollfds ] , 0 , sizeof ( struct pollfd ) * num_fds ) ;
/*
* This needs tuning . We need to cope with multiple fde ' s for a file
* descriptor . The problem is that we need to re - use pollfd_idx across
* calls for efficiency . One way would be a direct bitmask that might
* be initialized quicker , but our bitmap_init implementation is
* pretty heavy - weight as well .
*/
for ( i = 0 ; i < idx_len ; i + + ) {
state - > pollfd_idx [ i ] = - 1 ;
}
for ( fde = ev - > fd_events ; fde ; fde = fde - > next ) {
struct pollfd * pfd ;
if ( ( fde - > flags & ( EVENT_FD_READ | EVENT_FD_WRITE ) ) = = 0 ) {
continue ;
}
if ( state - > pollfd_idx [ fde - > fd ] = = - 1 ) {
/*
* We haven ' t seen this fd yet . Allocate a new pollfd .
*/
state - > pollfd_idx [ fde - > fd ] = num_pollfds ;
pfd = & fds [ num_pollfds ] ;
num_pollfds + = 1 ;
} else {
/*
* We have already seen this fd . OR in the flags .
*/
pfd = & fds [ state - > pollfd_idx [ fde - > fd ] ] ;
}
pfd - > fd = fde - > fd ;
if ( fde - > flags & EVENT_FD_READ ) {
pfd - > events | = ( POLLIN | POLLHUP ) ;
}
if ( fde - > flags & EVENT_FD_WRITE ) {
pfd - > events | = POLLOUT ;
}
}
* pfds = fds ;
* pnum_pfds = num_pollfds ;
if ( ev - > immediate_events ! = NULL ) {
* ptimeout = 0 ;
return true ;
}
if ( ev - > timer_events = = NULL ) {
2011-04-11 13:37:42 +02:00
* ptimeout = MIN ( * ptimeout , INT_MAX ) ;
2011-02-03 16:34:07 +01:00
return true ;
}
now = timeval_current ( ) ;
diff = timeval_until ( & now , & ev - > timer_events - > next_event ) ;
timeout = timeval_to_msec ( diff ) ;
if ( timeout < * ptimeout ) {
* ptimeout = timeout ;
}
return true ;
}
bool run_events_poll ( struct tevent_context * ev , int pollrtn ,
struct pollfd * pfds , int num_pfds )
{
struct tevent_poll_private * state ;
int * pollfd_idx ;
struct tevent_fd * fde ;
struct timeval now ;
if ( ev - > signal_events & &
tevent_common_check_signal ( ev ) ) {
return true ;
}
if ( ev - > immediate_events & &
tevent_common_loop_immediate ( ev ) ) {
return true ;
}
GetTimeOfDay ( & now ) ;
if ( ( ev - > timer_events ! = NULL )
& & ( timeval_compare ( & now , & ev - > timer_events - > next_event ) > = 0 ) ) {
/* this older events system did not auto-free timed
events on running them , and had a race condition
where the event could be called twice if the
talloc_free of the te happened after the callback
made a call which invoked the event loop . To avoid
this while still allowing old code which frees the
te , we need to create a temporary context which
will be used to ensure the te is freed . We also
remove the te from the timed event list before we
call the handler , to ensure we can ' t loop */
struct tevent_timer * te = ev - > timer_events ;
TALLOC_CTX * tmp_ctx = talloc_new ( ev ) ;
DEBUG ( 10 , ( " Running timed event \" %s \" %p \n " ,
ev - > timer_events - > handler_name , ev - > timer_events ) ) ;
DLIST_REMOVE ( ev - > timer_events , te ) ;
talloc_steal ( tmp_ctx , te ) ;
te - > handler ( ev , te , now , te - > private_data ) ;
talloc_free ( tmp_ctx ) ;
return true ;
}
if ( pollrtn < = 0 ) {
/*
* No fd ready
*/
return false ;
}
state = ( struct tevent_poll_private * ) ev - > additional_data ;
pollfd_idx = state - > pollfd_idx ;
for ( fde = ev - > fd_events ; fde ; fde = fde - > next ) {
struct pollfd * pfd ;
uint16 flags = 0 ;
if ( pollfd_idx [ fde - > fd ] > = num_pfds ) {
DEBUG ( 1 , ( " internal error: pollfd_idx[fde->fd] (%d) "
" >= num_pfds (%d) \n " , pollfd_idx [ fde - > fd ] ,
num_pfds ) ) ;
return false ;
}
pfd = & pfds [ pollfd_idx [ fde - > fd ] ] ;
if ( pfd - > fd ! = fde - > fd ) {
DEBUG ( 1 , ( " internal error: pfd->fd (%d) "
" != fde->fd (%d) \n " , pollfd_idx [ fde - > fd ] ,
num_pfds ) ) ;
return false ;
}
if ( pfd - > revents & ( POLLIN | POLLHUP | POLLERR ) ) {
flags | = EVENT_FD_READ ;
}
if ( pfd - > revents & POLLOUT ) {
flags | = EVENT_FD_WRITE ;
}
if ( flags & fde - > flags ) {
DLIST_DEMOTE ( ev - > fd_events , fde , struct tevent_fd ) ;
fde - > handler ( ev , fde , flags , fde - > private_data ) ;
return true ;
}
}
return false ;
}
2007-01-17 12:59:14 +00:00
2009-01-04 19:29:12 +01:00
struct timeval * get_timed_events_timeout ( struct tevent_context * ev ,
2007-01-17 12:59:14 +00:00
struct timeval * to_ret )
2006-02-03 22:19:41 +00:00
{
struct timeval now ;
2009-05-17 11:43:54 +02:00
if ( ( ev - > timer_events = = NULL ) & & ( ev - > immediate_events = = NULL ) ) {
2006-04-14 03:55:42 +00:00
return NULL ;
2006-02-03 22:19:41 +00:00
}
2009-05-16 15:42:53 +02:00
if ( ev - > immediate_events ! = NULL ) {
* to_ret = timeval_zero ( ) ;
return to_ret ;
}
2006-02-03 22:19:41 +00:00
now = timeval_current ( ) ;
2009-01-04 19:29:12 +01:00
* to_ret = timeval_until ( & now , & ev - > timer_events - > next_event ) ;
2006-02-03 22:19:41 +00:00
DEBUG ( 10 , ( " timed_events_timeout: %d/%d \n " , ( int ) to_ret - > tv_sec ,
( int ) to_ret - > tv_usec ) ) ;
return to_ret ;
}
2006-09-13 13:55:19 +00:00
2009-03-12 09:33:58 +01:00
static int s3_event_loop_once ( struct tevent_context * ev , const char * location )
2007-05-16 13:02:53 +00:00
{
2011-02-03 16:34:07 +01:00
struct tevent_poll_private * state ;
int timeout ;
int num_pfds ;
2011-01-24 08:47:38 +01:00
int ret ;
2007-05-16 13:02:53 +00:00
2011-02-03 16:34:07 +01:00
timeout = INT_MAX ;
2007-05-16 13:02:53 +00:00
2011-02-03 16:34:07 +01:00
state = tevent_get_poll_private ( ev ) ;
if ( state = = NULL ) {
errno = ENOMEM ;
return - 1 ;
}
2007-05-16 13:02:53 +00:00
2011-02-03 16:34:07 +01:00
if ( run_events_poll ( ev , 0 , NULL , 0 ) ) {
2009-01-20 01:58:04 +01:00
return 0 ;
}
2011-02-03 16:34:07 +01:00
num_pfds = 0 ;
if ( ! event_add_to_poll_args ( ev , state ,
& state - > pfds , & num_pfds , & timeout ) ) {
2007-05-16 13:02:53 +00:00
return - 1 ;
}
2011-02-03 16:34:07 +01:00
ret = sys_poll ( state - > pfds , num_pfds , timeout ) ;
2007-05-16 13:02:53 +00:00
if ( ret = = - 1 & & errno ! = EINTR ) {
2009-01-08 19:45:13 +01:00
tevent_debug ( ev , TEVENT_DEBUG_FATAL ,
2011-02-03 16:34:07 +01:00
" poll() failed: %d:%s \n " ,
2009-01-08 19:45:13 +01:00
errno , strerror ( errno ) ) ;
2007-05-16 13:02:53 +00:00
return - 1 ;
}
2011-02-03 16:34:07 +01:00
run_events_poll ( ev , ret , state - > pfds , num_pfds ) ;
2007-05-16 13:02:53 +00:00
return 0 ;
}
2009-01-04 19:29:12 +01:00
static int s3_event_context_init ( struct tevent_context * ev )
2007-01-17 12:59:14 +00:00
{
2009-01-04 19:29:12 +01:00
return 0 ;
2007-01-17 12:59:14 +00:00
}
2009-01-04 19:29:12 +01:00
void dump_event_list ( struct tevent_context * ev )
2007-06-21 13:03:27 +00:00
{
2009-01-04 19:29:12 +01:00
struct tevent_timer * te ;
struct tevent_fd * fe ;
2007-06-21 13:03:27 +00:00
struct timeval evt , now ;
2009-01-04 19:29:12 +01:00
if ( ! ev ) {
2007-06-21 13:03:27 +00:00
return ;
}
now = timeval_current ( ) ;
DEBUG ( 10 , ( " dump_event_list: \n " ) ) ;
2009-01-04 19:29:12 +01:00
for ( te = ev - > timer_events ; te ; te = te - > next ) {
2007-06-21 13:03:27 +00:00
2009-01-04 19:29:12 +01:00
evt = timeval_until ( & now , & te - > next_event ) ;
2007-06-21 13:03:27 +00:00
2009-01-04 19:29:12 +01:00
DEBUGADD ( 10 , ( " Timed Event \" %s \" %p handled in %d seconds (at %s) \n " ,
te - > handler_name ,
te ,
2007-09-20 11:02:27 +00:00
( int ) evt . tv_sec ,
2009-01-04 19:29:12 +01:00
http_timestring ( talloc_tos ( ) , te - > next_event . tv_sec ) ) ) ;
2007-06-21 13:03:27 +00:00
}
2007-06-21 15:12:51 +00:00
2009-01-05 19:47:59 +01:00
for ( fe = ev - > fd_events ; fe ; fe = fe - > next ) {
2007-06-21 15:12:51 +00:00
2009-01-04 19:29:12 +01:00
DEBUGADD ( 10 , ( " FD Event %d %p, flags: 0x%04x \n " ,
2007-06-21 15:12:51 +00:00
fe - > fd ,
2009-01-04 19:29:12 +01:00
fe ,
2007-06-21 15:12:51 +00:00
fe - > flags ) ) ;
}
2007-06-21 13:03:27 +00:00
}
2009-01-04 19:29:12 +01:00
static const struct tevent_ops s3_event_ops = {
2009-03-16 15:06:52 +01:00
. context_init = s3_event_context_init ,
. add_fd = tevent_common_add_fd ,
. set_fd_close_fn = tevent_common_fd_set_close_fn ,
. get_fd_flags = tevent_common_fd_get_flags ,
. set_fd_flags = tevent_common_fd_set_flags ,
. add_timer = tevent_common_add_timer ,
. schedule_immediate = tevent_common_schedule_immediate ,
. add_signal = tevent_common_add_signal ,
. loop_once = s3_event_loop_once ,
. loop_wait = tevent_common_loop_wait ,
2009-01-04 19:29:12 +01:00
} ;
static bool s3_tevent_init ( void )
{
static bool initialized ;
if ( initialized ) {
return true ;
}
initialized = tevent_register_backend ( " s3 " , & s3_event_ops ) ;
tevent_set_default_backend ( " s3 " ) ;
return initialized ;
}
2009-01-08 15:16:51 +01:00
/*
this is used to catch debug messages from events
*/
static void s3_event_debug ( void * context , enum tevent_debug_level level ,
const char * fmt , va_list ap ) PRINTF_ATTRIBUTE ( 3 , 0 ) ;
static void s3_event_debug ( void * context , enum tevent_debug_level level ,
const char * fmt , va_list ap )
{
int samba_level = - 1 ;
char * s = NULL ;
switch ( level ) {
case TEVENT_DEBUG_FATAL :
samba_level = 0 ;
break ;
case TEVENT_DEBUG_ERROR :
samba_level = 1 ;
break ;
case TEVENT_DEBUG_WARNING :
samba_level = 2 ;
break ;
case TEVENT_DEBUG_TRACE :
2009-07-05 14:39:16 +02:00
samba_level = 11 ;
2009-01-08 15:16:51 +01:00
break ;
} ;
2009-03-03 08:50:35 -08:00
if ( vasprintf ( & s , fmt , ap ) = = - 1 ) {
return ;
}
2009-01-08 15:16:51 +01:00
DEBUG ( samba_level , ( " s3_event: %s " , s ) ) ;
free ( s ) ;
}
2009-01-04 19:29:12 +01:00
struct tevent_context * s3_tevent_context_init ( TALLOC_CTX * mem_ctx )
{
2009-01-08 15:16:51 +01:00
struct tevent_context * ev ;
2009-01-04 19:29:12 +01:00
s3_tevent_init ( ) ;
2009-01-08 15:16:51 +01:00
ev = tevent_context_init_byname ( mem_ctx , " s3 " ) ;
if ( ev ) {
tevent_set_debug ( ev , s3_event_debug , NULL ) ;
}
return ev ;
2009-01-04 19:29:12 +01:00
}
2009-01-08 15:16:51 +01:00