2014-02-24 15:43:51 +04:00
/*
* Unix SMB / CIFS implementation .
2014-05-05 10:45:52 +04:00
* Copyright ( C ) Volker Lendecke 2013 , 2014
2014-02-24 15:43:51 +04: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
* the Free Software Foundation ; either version 3 of the License , or
* ( 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
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "poll_funcs_tevent.h"
# include "tevent.h"
# include "system/select.h"
2016-01-10 00:55:53 +03:00
# include "lib/util/dlinklist.h"
2014-02-24 15:43:51 +04:00
2014-05-05 10:45:52 +04:00
/*
* A poll_watch is asked for by the engine using this library via
* funcs - > watch_new ( ) . It represents interest in " fd " becoming readable or
* writable .
*/
2014-02-24 15:43:51 +04:00
struct poll_watch {
2014-05-05 10:45:52 +04:00
struct poll_funcs_state * state ;
unsigned slot ; /* index into state->watches[] */
2014-02-24 15:43:51 +04:00
int fd ;
2014-05-05 10:45:52 +04:00
int events ;
2014-02-24 15:43:51 +04:00
void ( * callback ) ( struct poll_watch * w , int fd , short events ,
void * private_data ) ;
void * private_data ;
} ;
2014-05-05 10:45:52 +04:00
struct poll_funcs_state {
/*
* " watches " is the array of all watches that we have handed out via
* funcs - > watch_new ( ) . The " watches " array can contain NULL pointers .
*/
unsigned num_watches ;
struct poll_watch * * watches ;
/*
* " contexts is the array of tevent_contexts that serve
* " watches " . " contexts " can contain NULL pointers .
*/
unsigned num_contexts ;
struct poll_funcs_tevent_context * * contexts ;
} ;
struct poll_funcs_tevent_context {
2014-10-22 19:02:49 +04:00
struct poll_funcs_tevent_handle * handles ;
2014-05-05 10:45:52 +04:00
struct poll_funcs_state * state ;
unsigned slot ; /* index into state->contexts[] */
struct tevent_context * ev ;
struct tevent_fd * * fdes ; /* same indexes as state->watches[] */
} ;
/*
* poll_funcs_tevent_register ( ) hands out a struct poll_funcs_tevent_handle as
* a void * . poll_funcs_tevent_register allows tevent_contexts to be
* registered multiple times , and we can ' t add a tevent_fd for the same fd ' s
* multiple times . So we have to share one poll_funcs_tevent_context .
*/
struct poll_funcs_tevent_handle {
2014-10-22 19:02:49 +04:00
struct poll_funcs_tevent_handle * prev , * next ;
2014-05-05 10:45:52 +04:00
struct poll_funcs_tevent_context * ctx ;
} ;
2014-02-24 15:43:51 +04:00
static uint16_t poll_events_to_tevent ( short events )
{
uint16_t ret = 0 ;
if ( events & POLLIN ) {
ret | = TEVENT_FD_READ ;
}
if ( events & POLLOUT ) {
ret | = TEVENT_FD_WRITE ;
}
return ret ;
}
static short tevent_to_poll_events ( uint16_t flags )
{
short ret = 0 ;
if ( flags & TEVENT_FD_READ ) {
ret | = POLLIN ;
}
if ( flags & TEVENT_FD_WRITE ) {
ret | = POLLOUT ;
}
return ret ;
}
2014-05-05 10:45:52 +04:00
/*
* Find or create a free slot in state - > watches [ ]
*/
static bool poll_funcs_watch_find_slot ( struct poll_funcs_state * state ,
unsigned * slot )
{
struct poll_watch * * watches ;
unsigned i ;
for ( i = 0 ; i < state - > num_watches ; i + + ) {
if ( state - > watches [ i ] = = NULL ) {
* slot = i ;
return true ;
}
}
watches = talloc_realloc ( state , state - > watches , struct poll_watch * ,
state - > num_watches + 1 ) ;
if ( watches = = NULL ) {
return false ;
}
watches [ state - > num_watches ] = NULL ;
state - > watches = watches ;
for ( i = 0 ; i < state - > num_contexts ; i + + ) {
struct tevent_fd * * fdes ;
struct poll_funcs_tevent_context * c = state - > contexts [ i ] ;
if ( c = = NULL ) {
continue ;
}
fdes = talloc_realloc ( c , c - > fdes , struct tevent_fd * ,
state - > num_watches + 1 ) ;
if ( fdes = = NULL ) {
return false ;
}
c - > fdes = fdes ;
fdes [ state - > num_watches ] = NULL ;
}
* slot = state - > num_watches ;
state - > num_watches + = 1 ;
return true ;
}
static void poll_funcs_fde_handler ( struct tevent_context * ev ,
struct tevent_fd * fde , uint16_t flags ,
void * private_data ) ;
static int poll_watch_destructor ( struct poll_watch * w ) ;
2014-02-24 15:43:51 +04:00
static struct poll_watch * tevent_watch_new (
const struct poll_funcs * funcs , int fd , short events ,
void ( * callback ) ( struct poll_watch * w , int fd , short events ,
void * private_data ) ,
void * private_data )
{
2014-05-05 10:45:52 +04:00
struct poll_funcs_state * state = talloc_get_type_abort (
funcs - > private_data , struct poll_funcs_state ) ;
2014-02-24 15:43:51 +04:00
struct poll_watch * w ;
2014-05-05 10:45:52 +04:00
unsigned i , slot ;
2014-02-24 15:43:51 +04:00
2014-05-05 10:45:52 +04:00
if ( ! poll_funcs_watch_find_slot ( state , & slot ) ) {
2014-02-24 15:43:51 +04:00
return NULL ;
}
2014-05-05 10:45:52 +04:00
w = talloc ( state - > watches , struct poll_watch ) ;
if ( w = = NULL ) {
2014-02-24 15:43:51 +04:00
return NULL ;
}
2014-05-05 10:45:52 +04:00
w - > state = state ;
w - > slot = slot ;
w - > fd = fd ;
w - > events = poll_events_to_tevent ( events ) ;
2014-02-24 15:43:51 +04:00
w - > fd = fd ;
w - > callback = callback ;
w - > private_data = private_data ;
2014-05-05 10:45:52 +04:00
state - > watches [ slot ] = w ;
talloc_set_destructor ( w , poll_watch_destructor ) ;
for ( i = 0 ; i < state - > num_contexts ; i + + ) {
struct poll_funcs_tevent_context * c = state - > contexts [ i ] ;
if ( c = = NULL ) {
continue ;
}
c - > fdes [ slot ] = tevent_add_fd ( c - > ev , c - > fdes , w - > fd , w - > events ,
poll_funcs_fde_handler , w ) ;
if ( c - > fdes [ slot ] = = NULL ) {
goto fail ;
}
}
2014-02-24 15:43:51 +04:00
return w ;
2014-05-05 10:45:52 +04:00
fail :
TALLOC_FREE ( w ) ;
return NULL ;
2014-02-24 15:43:51 +04:00
}
2014-05-05 10:45:52 +04:00
static int poll_watch_destructor ( struct poll_watch * w )
2014-02-24 15:43:51 +04:00
{
2014-05-05 10:45:52 +04:00
struct poll_funcs_state * state = w - > state ;
unsigned slot = w - > slot ;
unsigned i ;
TALLOC_FREE ( state - > watches [ slot ] ) ;
for ( i = 0 ; i < state - > num_contexts ; i + + ) {
struct poll_funcs_tevent_context * c = state - > contexts [ i ] ;
if ( c = = NULL ) {
continue ;
}
TALLOC_FREE ( c - > fdes [ slot ] ) ;
}
2014-02-24 15:43:51 +04:00
2014-05-05 10:45:52 +04:00
return 0 ;
2014-02-24 15:43:51 +04:00
}
static void tevent_watch_update ( struct poll_watch * w , short events )
{
2014-05-05 10:45:52 +04:00
struct poll_funcs_state * state = w - > state ;
unsigned slot = w - > slot ;
unsigned i ;
w - > events = poll_events_to_tevent ( events ) ;
for ( i = 0 ; i < state - > num_contexts ; i + + ) {
struct poll_funcs_tevent_context * c = state - > contexts [ i ] ;
if ( c = = NULL ) {
continue ;
}
tevent_fd_set_flags ( c - > fdes [ slot ] , w - > events ) ;
}
2014-02-24 15:43:51 +04:00
}
static short tevent_watch_get_events ( struct poll_watch * w )
{
2014-05-05 10:45:52 +04:00
return tevent_to_poll_events ( w - > events ) ;
2014-02-24 15:43:51 +04:00
}
static void tevent_watch_free ( struct poll_watch * w )
{
TALLOC_FREE ( w ) ;
}
static struct poll_timeout * tevent_timeout_new (
const struct poll_funcs * funcs , const struct timeval * tv ,
void ( * callback ) ( struct poll_timeout * t , void * private_data ) ,
void * private_data )
{
/* not implemented yet */
return NULL ;
}
static void tevent_timeout_update ( struct poll_timeout * t ,
const struct timespec * ts )
{
return ;
}
static void tevent_timeout_free ( struct poll_timeout * t )
{
return ;
}
2014-05-05 10:45:52 +04:00
static int poll_funcs_state_destructor ( struct poll_funcs_state * state ) ;
struct poll_funcs * poll_funcs_init_tevent ( TALLOC_CTX * mem_ctx )
2014-02-24 15:43:51 +04:00
{
2014-05-05 10:45:52 +04:00
struct poll_funcs * f ;
struct poll_funcs_state * state ;
f = talloc ( mem_ctx , struct poll_funcs ) ;
if ( f = = NULL ) {
return NULL ;
}
state = talloc_zero ( f , struct poll_funcs_state ) ;
if ( state = = NULL ) {
TALLOC_FREE ( f ) ;
return NULL ;
}
talloc_set_destructor ( state , poll_funcs_state_destructor ) ;
2014-02-24 15:43:51 +04:00
f - > watch_new = tevent_watch_new ;
f - > watch_update = tevent_watch_update ;
f - > watch_get_events = tevent_watch_get_events ;
f - > watch_free = tevent_watch_free ;
f - > timeout_new = tevent_timeout_new ;
f - > timeout_update = tevent_timeout_update ;
f - > timeout_free = tevent_timeout_free ;
2014-05-05 10:45:52 +04:00
f - > private_data = state ;
return f ;
}
static int poll_funcs_state_destructor ( struct poll_funcs_state * state )
{
unsigned i ;
/*
* Make sure the watches are cleared before the contexts . The watches
* have destructors attached to them that clean up the fde ' s
*/
for ( i = 0 ; i < state - > num_watches ; i + + ) {
TALLOC_FREE ( state - > watches [ i ] ) ;
}
return 0 ;
}
/*
* Find or create a free slot in state - > contexts [ ]
*/
static bool poll_funcs_context_slot_find ( struct poll_funcs_state * state ,
struct tevent_context * ev ,
unsigned * slot )
{
struct poll_funcs_tevent_context * * contexts ;
unsigned i ;
for ( i = 0 ; i < state - > num_contexts ; i + + ) {
struct poll_funcs_tevent_context * ctx = state - > contexts [ i ] ;
if ( ( ctx = = NULL ) | | ( ctx - > ev = = ev ) ) {
* slot = i ;
return true ;
}
}
contexts = talloc_realloc ( state , state - > contexts ,
struct poll_funcs_tevent_context * ,
state - > num_contexts + 1 ) ;
if ( contexts = = NULL ) {
return false ;
}
state - > contexts = contexts ;
state - > contexts [ state - > num_contexts ] = NULL ;
* slot = state - > num_contexts ;
state - > num_contexts + = 1 ;
return true ;
}
static int poll_funcs_tevent_context_destructor (
struct poll_funcs_tevent_context * ctx ) ;
static struct poll_funcs_tevent_context * poll_funcs_tevent_context_new (
TALLOC_CTX * mem_ctx , struct poll_funcs_state * state ,
struct tevent_context * ev , unsigned slot )
{
struct poll_funcs_tevent_context * ctx ;
unsigned i ;
ctx = talloc ( mem_ctx , struct poll_funcs_tevent_context ) ;
if ( ctx = = NULL ) {
return NULL ;
}
2014-10-22 19:02:49 +04:00
ctx - > handles = NULL ;
2014-05-05 10:45:52 +04:00
ctx - > state = state ;
ctx - > ev = ev ;
ctx - > slot = slot ;
ctx - > fdes = talloc_array ( ctx , struct tevent_fd * , state - > num_watches ) ;
if ( ctx - > fdes = = NULL ) {
goto fail ;
}
for ( i = 0 ; i < state - > num_watches ; i + + ) {
struct poll_watch * w = state - > watches [ i ] ;
if ( w = = NULL ) {
ctx - > fdes [ i ] = NULL ;
continue ;
}
ctx - > fdes [ i ] = tevent_add_fd ( ev , ctx - > fdes , w - > fd , w - > events ,
poll_funcs_fde_handler , w ) ;
if ( ctx - > fdes [ i ] = = NULL ) {
goto fail ;
}
}
talloc_set_destructor ( ctx , poll_funcs_tevent_context_destructor ) ;
return ctx ;
fail :
TALLOC_FREE ( ctx ) ;
return NULL ;
}
static int poll_funcs_tevent_context_destructor (
struct poll_funcs_tevent_context * ctx )
{
2014-10-22 19:02:49 +04:00
struct poll_funcs_tevent_handle * h ;
2014-05-05 10:45:52 +04:00
ctx - > state - > contexts [ ctx - > slot ] = NULL ;
2014-10-22 19:02:49 +04:00
for ( h = ctx - > handles ; h ! = NULL ; h = h - > next ) {
h - > ctx = NULL ;
}
2014-05-05 10:45:52 +04:00
return 0 ;
}
static void poll_funcs_fde_handler ( struct tevent_context * ev ,
struct tevent_fd * fde , uint16_t flags ,
void * private_data )
{
struct poll_watch * w = talloc_get_type_abort (
private_data , struct poll_watch ) ;
short events = tevent_to_poll_events ( flags ) ;
w - > callback ( w , w - > fd , events , w - > private_data ) ;
}
static int poll_funcs_tevent_handle_destructor (
struct poll_funcs_tevent_handle * handle ) ;
void * poll_funcs_tevent_register ( TALLOC_CTX * mem_ctx , struct poll_funcs * f ,
struct tevent_context * ev )
{
struct poll_funcs_state * state = talloc_get_type_abort (
f - > private_data , struct poll_funcs_state ) ;
struct poll_funcs_tevent_handle * handle ;
unsigned slot ;
handle = talloc ( mem_ctx , struct poll_funcs_tevent_handle ) ;
if ( handle = = NULL ) {
return NULL ;
}
if ( ! poll_funcs_context_slot_find ( state , ev , & slot ) ) {
goto fail ;
}
if ( state - > contexts [ slot ] = = NULL ) {
state - > contexts [ slot ] = poll_funcs_tevent_context_new (
state - > contexts , state , ev , slot ) ;
if ( state - > contexts [ slot ] = = NULL ) {
goto fail ;
}
}
handle - > ctx = state - > contexts [ slot ] ;
2014-10-22 19:02:49 +04:00
DLIST_ADD ( handle - > ctx - > handles , handle ) ;
2014-05-05 10:45:52 +04:00
talloc_set_destructor ( handle , poll_funcs_tevent_handle_destructor ) ;
return handle ;
fail :
TALLOC_FREE ( handle ) ;
return NULL ;
}
static int poll_funcs_tevent_handle_destructor (
struct poll_funcs_tevent_handle * handle )
{
2014-10-22 19:02:49 +04:00
if ( handle - > ctx = = NULL ) {
return 0 ;
}
if ( handle - > ctx - > handles = = NULL ) {
2014-05-05 10:45:52 +04:00
abort ( ) ;
}
2014-10-22 19:02:49 +04:00
DLIST_REMOVE ( handle - > ctx - > handles , handle ) ;
if ( handle - > ctx - > handles = = NULL ) {
TALLOC_FREE ( handle - > ctx ) ;
2014-05-05 10:45:52 +04:00
}
return 0 ;
2014-02-24 15:43:51 +04:00
}