2006-02-04 01:19:41 +03:00
/*
Unix SMB / CIFS implementation .
Timed event library .
Copyright ( C ) Andrew Tridgell 1992 - 1998
2011-07-04 12:52:47 +04:00
Copyright ( C ) Volker Lendecke 2005 - 2007
2006-02-04 01:19:41 +03:00
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 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2006-02-04 01:19:41 +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 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-02-04 01:19:41 +03:00
*/
# include "includes.h"
2011-05-10 16:55:45 +04:00
# include "lib/tevent/tevent_internal.h"
2010-10-01 12:08:15 +04:00
# include "../lib/util/select.h"
2011-02-03 18:34:07 +03:00
# include "system/select.h"
2006-02-04 01:19:41 +03:00
2011-02-03 18:34:07 +03: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 ) {
2011-06-07 05:44:43 +04:00
state = talloc_zero ( ev , struct tevent_poll_private ) ;
2011-02-03 18:34:07 +03:00
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 ) {
2012-06-11 08:53:20 +04:00
if ( fde - > flags & ( TEVENT_FD_READ | TEVENT_FD_WRITE ) ) {
2011-02-03 18:34:07 +03:00
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 ) {
2011-06-07 05:10:15 +04:00
state - > pollfd_idx = talloc_realloc (
2011-02-03 18:34:07 +03:00
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 ;
2012-02-09 06:16:55 +04:00
if ( talloc_array_length ( fds ) < num_pollfds + num_fds ) {
2011-06-07 05:10:15 +04:00
fds = talloc_realloc ( mem_ctx , fds , struct pollfd ,
2012-02-09 06:16:55 +04:00
num_pollfds + num_fds ) ;
2011-02-03 18:34:07 +03:00
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 ;
2012-06-11 08:53:20 +04:00
if ( ( fde - > flags & ( TEVENT_FD_READ | TEVENT_FD_WRITE ) ) = = 0 ) {
2011-02-03 18:34:07 +03:00
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 ;
2012-06-11 08:53:20 +04:00
if ( fde - > flags & TEVENT_FD_READ ) {
2011-02-03 18:34:07 +03:00
pfd - > events | = ( POLLIN | POLLHUP ) ;
}
2012-06-11 08:53:20 +04:00
if ( fde - > flags & TEVENT_FD_WRITE ) {
2011-02-03 18:34:07 +03:00
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 15:37:42 +04:00
* ptimeout = MIN ( * ptimeout , INT_MAX ) ;
2011-02-03 18:34:07 +03: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 ;
2012-06-11 08:53:20 +04:00
if ( ( fde - > flags & ( TEVENT_FD_READ | TEVENT_FD_WRITE ) ) = = 0 ) {
2011-08-08 20:49:06 +04:00
continue ;
}
2011-02-03 18:34:07 +03:00
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 ;
}
2011-06-03 23:55:19 +04:00
if ( pfd - > revents & ( POLLHUP | POLLERR ) ) {
/* If we only wait for EVENT_FD_WRITE, we
should not tell the event handler about it ,
and remove the writable flag , as we only
report errors when waiting for read events
to match the select behavior . */
if ( ! ( fde - > flags & EVENT_FD_READ ) ) {
EVENT_FD_NOT_WRITEABLE ( fde ) ;
continue ;
}
flags | = EVENT_FD_READ ;
}
if ( pfd - > revents & POLLIN ) {
2011-02-03 18:34:07 +03:00
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 15:59:14 +03:00
2009-01-04 21:29:12 +03:00
struct timeval * get_timed_events_timeout ( struct tevent_context * ev ,
2007-01-17 15:59:14 +03:00
struct timeval * to_ret )
2006-02-04 01:19:41 +03:00
{
struct timeval now ;
2009-05-17 13:43:54 +04:00
if ( ( ev - > timer_events = = NULL ) & & ( ev - > immediate_events = = NULL ) ) {
2006-04-14 07:55:42 +04:00
return NULL ;
2006-02-04 01:19:41 +03:00
}
2009-05-16 17:42:53 +04:00
if ( ev - > immediate_events ! = NULL ) {
* to_ret = timeval_zero ( ) ;
return to_ret ;
}
2006-02-04 01:19:41 +03:00
now = timeval_current ( ) ;
2009-01-04 21:29:12 +03:00
* to_ret = timeval_until ( & now , & ev - > timer_events - > next_event ) ;
2006-02-04 01:19:41 +03: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 17:55:19 +04:00
2009-03-12 11:33:58 +03:00
static int s3_event_loop_once ( struct tevent_context * ev , const char * location )
2007-05-16 17:02:53 +04:00
{
2011-02-03 18:34:07 +03:00
struct tevent_poll_private * state ;
int timeout ;
int num_pfds ;
2011-01-24 10:47:38 +03:00
int ret ;
2007-05-16 17:02:53 +04:00
2011-02-03 18:34:07 +03:00
timeout = INT_MAX ;
2007-05-16 17:02:53 +04:00
2011-02-03 18:34:07 +03:00
state = tevent_get_poll_private ( ev ) ;
if ( state = = NULL ) {
errno = ENOMEM ;
return - 1 ;
}
2007-05-16 17:02:53 +04:00
2011-02-03 18:34:07 +03:00
if ( run_events_poll ( ev , 0 , NULL , 0 ) ) {
2009-01-20 03:58:04 +03:00
return 0 ;
}
2011-02-03 18:34:07 +03:00
num_pfds = 0 ;
if ( ! event_add_to_poll_args ( ev , state ,
& state - > pfds , & num_pfds , & timeout ) ) {
2007-05-16 17:02:53 +04:00
return - 1 ;
}
2012-02-09 06:16:55 +04:00
ret = poll ( state - > pfds , num_pfds , timeout ) ;
2007-05-16 17:02:53 +04:00
if ( ret = = - 1 & & errno ! = EINTR ) {
2009-01-08 21:45:13 +03:00
tevent_debug ( ev , TEVENT_DEBUG_FATAL ,
2011-02-03 18:34:07 +03:00
" poll() failed: %d:%s \n " ,
2009-01-08 21:45:13 +03:00
errno , strerror ( errno ) ) ;
2007-05-16 17:02:53 +04:00
return - 1 ;
}
2011-02-03 18:34:07 +03:00
run_events_poll ( ev , ret , state - > pfds , num_pfds ) ;
2007-05-16 17:02:53 +04:00
return 0 ;
}
2009-01-04 21:29:12 +03:00
static int s3_event_context_init ( struct tevent_context * ev )
2007-01-17 15:59:14 +03:00
{
2009-01-04 21:29:12 +03:00
return 0 ;
2007-01-17 15:59:14 +03:00
}
2009-01-04 21:29:12 +03:00
void dump_event_list ( struct tevent_context * ev )
2007-06-21 17:03:27 +04:00
{
2009-01-04 21:29:12 +03:00
struct tevent_timer * te ;
struct tevent_fd * fe ;
2007-06-21 17:03:27 +04:00
struct timeval evt , now ;
2009-01-04 21:29:12 +03:00
if ( ! ev ) {
2007-06-21 17:03:27 +04:00
return ;
}
now = timeval_current ( ) ;
DEBUG ( 10 , ( " dump_event_list: \n " ) ) ;
2009-01-04 21:29:12 +03:00
for ( te = ev - > timer_events ; te ; te = te - > next ) {
2007-06-21 17:03:27 +04:00
2009-01-04 21:29:12 +03:00
evt = timeval_until ( & now , & te - > next_event ) ;
2007-06-21 17:03:27 +04:00
2009-01-04 21:29:12 +03:00
DEBUGADD ( 10 , ( " Timed Event \" %s \" %p handled in %d seconds (at %s) \n " ,
te - > handler_name ,
te ,
2007-09-20 15:02:27 +04:00
( int ) evt . tv_sec ,
2009-01-04 21:29:12 +03:00
http_timestring ( talloc_tos ( ) , te - > next_event . tv_sec ) ) ) ;
2007-06-21 17:03:27 +04:00
}
2007-06-21 19:12:51 +04:00
2009-01-05 21:47:59 +03:00
for ( fe = ev - > fd_events ; fe ; fe = fe - > next ) {
2007-06-21 19:12:51 +04:00
2009-01-04 21:29:12 +03:00
DEBUGADD ( 10 , ( " FD Event %d %p, flags: 0x%04x \n " ,
2007-06-21 19:12:51 +04:00
fe - > fd ,
2009-01-04 21:29:12 +03:00
fe ,
2007-06-21 19:12:51 +04:00
fe - > flags ) ) ;
}
2007-06-21 17:03:27 +04:00
}
2009-01-04 21:29:12 +03:00
static const struct tevent_ops s3_event_ops = {
2009-03-16 17:06:52 +03: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 21:29:12 +03: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 17:16:51 +03: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 16:39:16 +04:00
samba_level = 11 ;
2009-01-08 17:16:51 +03:00
break ;
} ;
2012-08-09 12:34:28 +04:00
if ( CHECK_DEBUGLVL ( samba_level ) ) {
if ( vasprintf ( & s , fmt , ap ) = = - 1 ) {
return ;
}
DEBUG ( samba_level , ( " s3_event: %s " , s ) ) ;
free ( s ) ;
2009-03-03 19:50:35 +03:00
}
2009-01-08 17:16:51 +03:00
}
2009-01-04 21:29:12 +03:00
struct tevent_context * s3_tevent_context_init ( TALLOC_CTX * mem_ctx )
{
2009-01-08 17:16:51 +03:00
struct tevent_context * ev ;
2009-01-04 21:29:12 +03:00
s3_tevent_init ( ) ;
2009-01-08 17:16:51 +03:00
ev = tevent_context_init_byname ( mem_ctx , " s3 " ) ;
if ( ev ) {
tevent_set_debug ( ev , s3_event_debug , NULL ) ;
}
return ev ;
2009-01-04 21:29:12 +03:00
}
2009-01-08 17:16:51 +03:00
2011-07-04 12:52:47 +04:00
struct idle_event {
2012-06-15 15:52:35 +04:00
struct tevent_timer * te ;
2011-07-04 12:52:47 +04:00
struct timeval interval ;
char * name ;
bool ( * handler ) ( const struct timeval * now , void * private_data ) ;
void * private_data ;
} ;
2012-06-11 08:53:20 +04:00
static void smbd_idle_event_handler ( struct tevent_context * ctx ,
2012-06-15 15:52:35 +04:00
struct tevent_timer * te ,
2011-07-04 12:52:47 +04:00
struct timeval now ,
void * private_data )
{
struct idle_event * event =
talloc_get_type_abort ( private_data , struct idle_event ) ;
TALLOC_FREE ( event - > te ) ;
DEBUG ( 10 , ( " smbd_idle_event_handler: %s %p called \n " ,
event - > name , event - > te ) ) ;
if ( ! event - > handler ( & now , event - > private_data ) ) {
DEBUG ( 10 , ( " smbd_idle_event_handler: %s %p stopped \n " ,
event - > name , event - > te ) ) ;
/* Don't repeat, delete ourselves */
TALLOC_FREE ( event ) ;
return ;
}
DEBUG ( 10 , ( " smbd_idle_event_handler: %s %p rescheduled \n " ,
event - > name , event - > te ) ) ;
2012-06-11 08:53:20 +04:00
event - > te = tevent_add_timer ( ctx , event ,
timeval_sum ( & now , & event - > interval ) ,
smbd_idle_event_handler , event ) ;
2011-07-04 12:52:47 +04:00
/* We can't do much but fail here. */
SMB_ASSERT ( event - > te ! = NULL ) ;
}
2012-06-11 08:53:20 +04:00
struct idle_event * event_add_idle ( struct tevent_context * event_ctx ,
2011-07-04 12:52:47 +04:00
TALLOC_CTX * mem_ctx ,
struct timeval interval ,
const char * name ,
bool ( * handler ) ( const struct timeval * now ,
void * private_data ) ,
void * private_data )
{
struct idle_event * result ;
struct timeval now = timeval_current ( ) ;
result = talloc ( mem_ctx , struct idle_event ) ;
if ( result = = NULL ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
return NULL ;
}
result - > interval = interval ;
result - > handler = handler ;
result - > private_data = private_data ;
if ( ! ( result - > name = talloc_asprintf ( result , " idle_evt(%s) " , name ) ) ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
TALLOC_FREE ( result ) ;
return NULL ;
}
2012-06-11 08:53:20 +04:00
result - > te = tevent_add_timer ( event_ctx , result ,
timeval_sum ( & now , & interval ) ,
smbd_idle_event_handler , result ) ;
2011-07-04 12:52:47 +04:00
if ( result - > te = = NULL ) {
DEBUG ( 0 , ( " event_add_timed failed \n " ) ) ;
TALLOC_FREE ( result ) ;
return NULL ;
}
DEBUG ( 10 , ( " event_add_idle: %s %p \n " , result - > name , result - > te ) ) ;
return result ;
}