2011-02-09 17:28:10 +03:00
/*
Unix SMB / CIFS implementation .
main select loop and event handling
Copyright ( C ) Andrew Tridgell 2003 - 2005
Copyright ( C ) Stefan Metzmacher 2005 - 2009
* * NOTE ! The following LGPL license applies to the tevent
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 3 of the License , or ( at your option ) any later version .
This library 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include "replace.h"
# include "system/filesys.h"
# include "system/select.h"
# include "tevent.h"
# include "tevent_util.h"
# include "tevent_internal.h"
struct poll_event_context {
2012-06-07 06:26:02 +04:00
/* a pointer back to the generic event_context */
struct tevent_context * ev ;
2012-07-29 15:05:36 +04:00
/*
* A DLIST for fresh fde ' s
*/
struct tevent_fd * fresh ;
2011-02-09 17:28:10 +03:00
/*
* These two arrays are maintained together .
*/
struct pollfd * fds ;
2012-07-29 15:05:36 +04:00
struct tevent_fd * * fdes ;
unsigned num_fds ;
2011-02-09 17:28:10 +03:00
/* information for exiting from the event loop */
int exit_code ;
} ;
/*
create a select_event_context structure .
*/
static int poll_event_context_init ( struct tevent_context * ev )
{
struct poll_event_context * poll_ev ;
poll_ev = talloc_zero ( ev , struct poll_event_context ) ;
if ( poll_ev = = NULL ) {
return - 1 ;
}
2012-06-07 06:26:02 +04:00
poll_ev - > ev = ev ;
2011-02-09 17:28:10 +03:00
ev - > additional_data = poll_ev ;
return 0 ;
}
/*
destroy an fd_event
*/
static int poll_event_fd_destructor ( struct tevent_fd * fde )
{
struct tevent_context * ev = fde - > event_ctx ;
struct poll_event_context * poll_ev = NULL ;
2011-03-02 17:22:09 +03:00
uint64_t del_idx = fde - > additional_flags ;
2011-02-09 17:28:10 +03:00
if ( ev = = NULL ) {
goto done ;
}
poll_ev = talloc_get_type_abort (
ev - > additional_data , struct poll_event_context ) ;
2012-07-29 15:05:36 +04:00
poll_ev - > fdes [ del_idx ] = NULL ;
2011-02-09 17:28:10 +03:00
done :
return tevent_common_fd_destructor ( fde ) ;
}
2012-07-29 15:05:36 +04:00
static int poll_fresh_fde_destructor ( struct tevent_fd * fde )
{
struct poll_event_context * poll_ev = talloc_get_type_abort (
fde - > event_ctx - > additional_data , struct poll_event_context ) ;
DLIST_REMOVE ( poll_ev - > fresh , fde ) ;
return 0 ;
}
2011-02-09 17:28:10 +03:00
/*
add a fd based event
return NULL on failure ( memory allocation error )
*/
static struct tevent_fd * poll_event_add_fd ( struct tevent_context * ev ,
TALLOC_CTX * mem_ctx ,
int fd , uint16_t flags ,
tevent_fd_handler_t handler ,
void * private_data ,
const char * handler_name ,
const char * location )
{
struct poll_event_context * poll_ev = talloc_get_type_abort (
ev - > additional_data , struct poll_event_context ) ;
struct tevent_fd * fde ;
2012-07-29 15:05:36 +04:00
if ( fd < 0 ) {
2011-02-09 17:28:10 +03:00
return NULL ;
}
2012-07-29 15:05:36 +04:00
fde = talloc ( mem_ctx ? mem_ctx : ev , struct tevent_fd ) ;
if ( fde = = NULL ) {
return NULL ;
2011-02-09 17:28:10 +03:00
}
2012-07-29 15:05:36 +04:00
fde - > event_ctx = ev ;
fde - > fd = fd ;
fde - > flags = flags ;
fde - > handler = handler ;
fde - > close_fn = NULL ;
fde - > private_data = private_data ;
fde - > handler_name = handler_name ;
fde - > location = location ;
fde - > additional_flags = 0 ;
fde - > additional_data = NULL ;
DLIST_ADD ( poll_ev - > fresh , fde ) ;
talloc_set_destructor ( fde , poll_fresh_fde_destructor ) ;
2011-02-09 17:28:10 +03:00
2012-07-29 15:05:36 +04:00
/*
* poll_event_loop_poll will take care of the rest in
* poll_event_setup_fresh
*/
2011-02-09 17:28:10 +03:00
return fde ;
}
/*
set the fd event flags
*/
static void poll_event_set_fd_flags ( struct tevent_fd * fde , uint16_t flags )
{
struct poll_event_context * poll_ev = talloc_get_type_abort (
fde - > event_ctx - > additional_data , struct poll_event_context ) ;
2011-03-02 17:22:09 +03:00
uint64_t idx = fde - > additional_flags ;
2011-02-09 17:28:10 +03:00
uint16_t pollflags = 0 ;
if ( flags & TEVENT_FD_READ ) {
pollflags | = ( POLLIN | POLLHUP ) ;
}
if ( flags & TEVENT_FD_WRITE ) {
pollflags | = ( POLLOUT ) ;
}
poll_ev - > fds [ idx ] . events = pollflags ;
fde - > flags = flags ;
}
2012-07-29 15:05:36 +04:00
static bool poll_event_setup_fresh ( struct tevent_context * ev ,
struct poll_event_context * poll_ev )
{
struct tevent_fd * fde , * next ;
unsigned num_fresh , num_fds ;
if ( poll_ev - > fresh = = NULL ) {
return true ;
}
num_fresh = 0 ;
for ( fde = poll_ev - > fresh ; fde ; fde = fde - > next ) {
num_fresh + = 1 ;
}
num_fds = poll_ev - > num_fds + num_fresh ;
/*
* We check the length of fdes here . It is the last one
* enlarged , so if the realloc for poll_fd - > fdes fails ,
* poll_fd - > fds will have at least the size of poll_fd - > fdes
*/
if ( num_fds > = talloc_array_length ( poll_ev - > fdes ) ) {
struct pollfd * tmp_fds ;
struct tevent_fd * * tmp_fdes ;
unsigned array_length ;
array_length = ( num_fds + 15 ) & ~ 15 ; /* round up to 16 */
tmp_fds = talloc_realloc (
poll_ev , poll_ev - > fds , struct pollfd , array_length ) ;
if ( tmp_fds = = NULL ) {
return false ;
}
poll_ev - > fds = tmp_fds ;
tmp_fdes = talloc_realloc (
poll_ev , poll_ev - > fdes , struct tevent_fd * ,
array_length ) ;
if ( tmp_fdes = = NULL ) {
return false ;
}
poll_ev - > fdes = tmp_fdes ;
}
for ( fde = poll_ev - > fresh ; fde ; fde = next ) {
struct pollfd * pfd ;
pfd = & poll_ev - > fds [ poll_ev - > num_fds ] ;
pfd - > fd = fde - > fd ;
pfd - > events = 0 ;
pfd - > revents = 0 ;
if ( fde - > flags & TEVENT_FD_READ ) {
pfd - > events | = ( POLLIN | POLLHUP ) ;
}
if ( fde - > flags & TEVENT_FD_WRITE ) {
pfd - > events | = ( POLLOUT ) ;
}
fde - > additional_flags = poll_ev - > num_fds ;
poll_ev - > fdes [ poll_ev - > num_fds ] = fde ;
next = fde - > next ;
DLIST_REMOVE ( poll_ev - > fresh , fde ) ;
DLIST_ADD ( ev - > fd_events , fde ) ;
talloc_set_destructor ( fde , poll_event_fd_destructor ) ;
poll_ev - > num_fds + = 1 ;
}
return true ;
}
2011-02-09 17:28:10 +03:00
/*
2012-04-09 02:40:38 +04:00
event loop handling using poll ( )
2011-02-09 17:28:10 +03:00
*/
static int poll_event_loop_poll ( struct tevent_context * ev ,
struct timeval * tvalp )
{
struct poll_event_context * poll_ev = talloc_get_type_abort (
ev - > additional_data , struct poll_event_context ) ;
int pollrtn ;
int timeout = - 1 ;
2012-07-29 15:05:36 +04:00
unsigned i ;
2011-02-09 17:28:10 +03:00
if ( ev - > signal_events & & tevent_common_check_signal ( ev ) ) {
return 0 ;
}
if ( tvalp ! = NULL ) {
timeout = tvalp - > tv_sec * 1000 ;
timeout + = ( tvalp - > tv_usec + 999 ) / 1000 ;
}
2012-07-29 15:05:36 +04:00
if ( ! poll_event_setup_fresh ( ev , poll_ev ) ) {
return - 1 ;
}
2012-06-05 10:00:07 +04:00
tevent_trace_point_callback ( poll_ev - > ev , TEVENT_TRACE_BEFORE_WAIT ) ;
2011-02-09 17:28:10 +03:00
pollrtn = poll ( poll_ev - > fds , poll_ev - > num_fds , timeout ) ;
2012-06-05 10:00:07 +04:00
tevent_trace_point_callback ( poll_ev - > ev , TEVENT_TRACE_AFTER_WAIT ) ;
2011-02-09 17:28:10 +03:00
if ( pollrtn = = - 1 & & errno = = EINTR & & ev - > signal_events ) {
tevent_common_check_signal ( ev ) ;
return 0 ;
}
if ( pollrtn = = 0 & & tvalp ) {
/* we don't care about a possible delay here */
tevent_common_loop_timer_delay ( ev ) ;
return 0 ;
}
2011-07-28 16:09:13 +04:00
if ( pollrtn < = 0 ) {
/*
* No fd ' s ready
*/
return 0 ;
}
/* at least one file descriptor is ready - check
which ones and call the handler , being careful to allow
the handler to remove itself when called */
2012-07-29 15:05:36 +04:00
for ( i = 0 ; i < poll_ev - > num_fds ; i + + ) {
2011-07-28 16:09:13 +04:00
struct pollfd * pfd ;
2012-07-29 15:05:36 +04:00
struct tevent_fd * fde ;
2011-07-28 16:09:13 +04:00
uint16_t flags = 0 ;
2012-07-29 15:05:36 +04:00
fde = poll_ev - > fdes [ i ] ;
if ( fde = = NULL ) {
/*
* This fde was talloc_free ( ) ' ed . Delete it
* from the arrays
*/
poll_ev - > num_fds - = 1 ;
poll_ev - > fds [ i ] = poll_ev - > fds [ poll_ev - > num_fds ] ;
poll_ev - > fdes [ i ] = poll_ev - > fdes [ poll_ev - > num_fds ] ;
if ( poll_ev - > fdes [ i ] ! = NULL ) {
poll_ev - > fdes [ i ] - > additional_flags = i ;
}
continue ;
}
pfd = & poll_ev - > fds [ i ] ;
2011-07-28 16:09:13 +04:00
if ( pfd - > revents & ( POLLHUP | POLLERR ) ) {
/* If we only wait for TEVENT_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 & TEVENT_FD_READ ) ) {
TEVENT_FD_NOT_WRITEABLE ( fde ) ;
continue ;
2011-02-09 17:28:10 +03:00
}
2011-07-28 16:09:13 +04:00
flags | = TEVENT_FD_READ ;
}
if ( pfd - > revents & POLLIN ) {
flags | = TEVENT_FD_READ ;
}
if ( pfd - > revents & POLLOUT ) {
flags | = TEVENT_FD_WRITE ;
}
if ( flags ! = 0 ) {
fde - > handler ( ev , fde , flags , fde - > private_data ) ;
break ;
2011-02-09 17:28:10 +03:00
}
}
return 0 ;
}
/*
do a single event loop using the events defined in ev
*/
static int poll_event_loop_once ( struct tevent_context * ev ,
const char * location )
{
struct timeval tval ;
if ( ev - > signal_events & &
tevent_common_check_signal ( ev ) ) {
return 0 ;
}
if ( ev - > immediate_events & &
tevent_common_loop_immediate ( ev ) ) {
return 0 ;
}
tval = tevent_common_loop_timer_delay ( ev ) ;
if ( tevent_timeval_is_zero ( & tval ) ) {
return 0 ;
}
return poll_event_loop_poll ( ev , & tval ) ;
}
static const struct tevent_ops poll_event_ops = {
. context_init = poll_event_context_init ,
. add_fd = poll_event_add_fd ,
. set_fd_close_fn = tevent_common_fd_set_close_fn ,
. get_fd_flags = tevent_common_fd_get_flags ,
. set_fd_flags = poll_event_set_fd_flags ,
. add_timer = tevent_common_add_timer ,
. schedule_immediate = tevent_common_schedule_immediate ,
. add_signal = tevent_common_add_signal ,
. loop_once = poll_event_loop_once ,
. loop_wait = tevent_common_loop_wait ,
} ;
_PRIVATE_ bool tevent_poll_init ( void )
{
return tevent_register_backend ( " poll " , & poll_event_ops ) ;
}