2014-07-22 18:51:38 +04:00
/*
Infrastructure for event context wrappers
Copyright ( C ) Stefan Metzmacher 2014
* * 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"
# ifdef HAVE_PTHREAD
# include "system/threads.h"
# endif
2019-01-08 17:25:22 +03:00
# define TEVENT_DEPRECATED 1
2014-07-22 18:51:38 +04:00
# include "tevent.h"
# include "tevent_internal.h"
# include "tevent_util.h"
static int tevent_wrapper_glue_context_init ( struct tevent_context * ev )
{
tevent_abort ( ev , " tevent_wrapper_glue_context_init() called " ) ;
errno = ENOSYS ;
return - 1 ;
}
static struct tevent_fd * tevent_wrapper_glue_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 tevent_wrapper_glue * glue = ev - > wrapper . glue ;
struct tevent_fd * fde = NULL ;
if ( glue - > destroyed ) {
tevent_abort ( ev , " add_fd wrapper use after free " ) ;
return NULL ;
}
if ( glue - > main_ev = = NULL ) {
errno = EINVAL ;
return NULL ;
}
fde = _tevent_add_fd ( glue - > main_ev , mem_ctx , fd , flags ,
handler , private_data ,
handler_name , location ) ;
if ( fde = = NULL ) {
return NULL ;
}
fde - > wrapper = glue ;
return fde ;
}
static struct tevent_timer * tevent_wrapper_glue_add_timer ( struct tevent_context * ev ,
TALLOC_CTX * mem_ctx ,
struct timeval next_event ,
tevent_timer_handler_t handler ,
void * private_data ,
const char * handler_name ,
const char * location )
{
struct tevent_wrapper_glue * glue = ev - > wrapper . glue ;
struct tevent_timer * te = NULL ;
if ( glue - > destroyed ) {
tevent_abort ( ev , " add_timer wrapper use after free " ) ;
return NULL ;
}
if ( glue - > main_ev = = NULL ) {
errno = EINVAL ;
return NULL ;
}
te = _tevent_add_timer ( glue - > main_ev , mem_ctx , next_event ,
handler , private_data ,
handler_name , location ) ;
if ( te = = NULL ) {
return NULL ;
}
te - > wrapper = glue ;
return te ;
}
static void tevent_wrapper_glue_schedule_immediate ( struct tevent_immediate * im ,
struct tevent_context * ev ,
tevent_immediate_handler_t handler ,
void * private_data ,
const char * handler_name ,
const char * location )
{
struct tevent_wrapper_glue * glue = ev - > wrapper . glue ;
if ( glue - > destroyed ) {
tevent_abort ( ev , " scheduke_immediate wrapper use after free " ) ;
return ;
}
if ( glue - > main_ev = = NULL ) {
tevent_abort ( ev , location ) ;
errno = EINVAL ;
return ;
}
_tevent_schedule_immediate ( im , glue - > main_ev ,
handler , private_data ,
handler_name , location ) ;
im - > wrapper = glue ;
return ;
}
static struct tevent_signal * tevent_wrapper_glue_add_signal ( struct tevent_context * ev ,
TALLOC_CTX * mem_ctx ,
int signum , int sa_flags ,
tevent_signal_handler_t handler ,
void * private_data ,
const char * handler_name ,
const char * location )
{
struct tevent_wrapper_glue * glue = ev - > wrapper . glue ;
struct tevent_signal * se = NULL ;
if ( glue - > destroyed ) {
tevent_abort ( ev , " add_signal wrapper use after free " ) ;
return NULL ;
}
if ( glue - > main_ev = = NULL ) {
errno = EINVAL ;
return NULL ;
}
se = _tevent_add_signal ( glue - > main_ev , mem_ctx ,
signum , sa_flags ,
handler , private_data ,
handler_name , location ) ;
if ( se = = NULL ) {
return NULL ;
}
se - > wrapper = glue ;
return se ;
}
static int tevent_wrapper_glue_loop_once ( struct tevent_context * ev , const char * location )
{
tevent_abort ( ev , " tevent_wrapper_glue_loop_once() called " ) ;
errno = ENOSYS ;
return - 1 ;
}
static int tevent_wrapper_glue_loop_wait ( struct tevent_context * ev , const char * location )
{
tevent_abort ( ev , " tevent_wrapper_glue_loop_wait() called " ) ;
errno = ENOSYS ;
return - 1 ;
}
static const struct tevent_ops tevent_wrapper_glue_ops = {
. context_init = tevent_wrapper_glue_context_init ,
. add_fd = tevent_wrapper_glue_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_wrapper_glue_add_timer ,
. schedule_immediate = tevent_wrapper_glue_schedule_immediate ,
. add_signal = tevent_wrapper_glue_add_signal ,
. loop_once = tevent_wrapper_glue_loop_once ,
. loop_wait = tevent_wrapper_glue_loop_wait ,
} ;
static int tevent_wrapper_context_destructor ( struct tevent_context * wrap_ev )
{
struct tevent_wrapper_glue * glue = wrap_ev - > wrapper . glue ;
struct tevent_context * main_ev = NULL ;
struct tevent_fd * fd = NULL , * fn = NULL ;
struct tevent_timer * te = NULL , * tn = NULL ;
struct tevent_immediate * ie = NULL , * in = NULL ;
struct tevent_signal * se = NULL , * sn = NULL ;
# ifdef HAVE_PTHREAD
struct tevent_threaded_context * tctx = NULL , * tctxn = NULL ;
# endif
if ( glue = = NULL ) {
tevent_abort ( wrap_ev ,
" tevent_wrapper_context_destructor() active on main " ) ;
2019-05-24 16:01:30 +03:00
/* static checker support, return below is never reached */
return - 1 ;
2014-07-22 18:51:38 +04:00
}
if ( glue - > destroyed & & glue - > busy ) {
tevent_common_check_double_free ( wrap_ev ,
" tevent_context wrapper double free " ) ;
}
glue - > destroyed = true ;
if ( glue - > busy ) {
return - 1 ;
}
main_ev = glue - > main_ev ;
if ( main_ev = = NULL ) {
return 0 ;
}
2022-11-12 00:25:34 +03:00
TEVENT_DEBUG ( wrap_ev , TEVENT_DEBUG_TRACE ,
2014-07-22 18:51:38 +04:00
" Destroying wrapper context %p \" %s \" \n " ,
wrap_ev , talloc_get_name ( glue - > private_state ) ) ;
glue - > main_ev = NULL ;
DLIST_REMOVE ( main_ev - > wrapper . list , glue ) ;
# ifdef HAVE_PTHREAD
for ( tctx = main_ev - > threaded_contexts ; tctx ! = NULL ; tctx = tctxn ) {
int ret ;
tctxn = tctx - > next ;
if ( tctx - > event_ctx ! = glue - > wrap_ev ) {
continue ;
}
ret = pthread_mutex_lock ( & tctx - > event_ctx_mutex ) ;
if ( ret ! = 0 ) {
abort ( ) ;
}
/*
* Indicate to the thread that the tevent_context is
* gone . The counterpart of this is in
* _tevent_threaded_schedule_immediate , there we read
* this under the threaded_context ' s mutex .
*/
tctx - > event_ctx = NULL ;
ret = pthread_mutex_unlock ( & tctx - > event_ctx_mutex ) ;
if ( ret ! = 0 ) {
abort ( ) ;
}
DLIST_REMOVE ( main_ev - > threaded_contexts , tctx ) ;
}
# endif
for ( fd = main_ev - > fd_events ; fd ; fd = fn ) {
fn = fd - > next ;
if ( fd - > wrapper ! = glue ) {
continue ;
}
tevent_fd_set_flags ( fd , 0 ) ;
fd - > wrapper = NULL ;
fd - > event_ctx = NULL ;
DLIST_REMOVE ( main_ev - > fd_events , fd ) ;
}
for ( te = main_ev - > timer_events ; te ; te = tn ) {
tn = te - > next ;
if ( te - > wrapper ! = glue ) {
continue ;
}
te - > wrapper = NULL ;
te - > event_ctx = NULL ;
if ( main_ev - > last_zero_timer = = te ) {
main_ev - > last_zero_timer = DLIST_PREV ( te ) ;
}
DLIST_REMOVE ( main_ev - > timer_events , te ) ;
}
for ( ie = main_ev - > immediate_events ; ie ; ie = in ) {
in = ie - > next ;
if ( ie - > wrapper ! = glue ) {
continue ;
}
ie - > wrapper = NULL ;
ie - > event_ctx = NULL ;
ie - > cancel_fn = NULL ;
DLIST_REMOVE ( main_ev - > immediate_events , ie ) ;
}
for ( se = main_ev - > signal_events ; se ; se = sn ) {
sn = se - > next ;
if ( se - > wrapper ! = glue ) {
continue ;
}
se - > wrapper = NULL ;
tevent_cleanup_pending_signal_handlers ( se ) ;
}
return 0 ;
}
struct tevent_context * _tevent_context_wrapper_create ( struct tevent_context * main_ev ,
TALLOC_CTX * mem_ctx ,
const struct tevent_wrapper_ops * ops ,
void * pstate ,
size_t psize ,
const char * type ,
const char * location )
{
void * * ppstate = ( void * * ) pstate ;
struct tevent_context * ev = NULL ;
if ( main_ev - > wrapper . glue ! = NULL ) {
/*
* stacking of wrappers is not supported
*/
tevent_debug ( main_ev - > wrapper . glue - > main_ev , TEVENT_DEBUG_FATAL ,
" %s: %s() stacking not allowed \n " ,
__func__ , location ) ;
errno = EINVAL ;
return NULL ;
}
if ( main_ev - > nesting . allowed ) {
/*
* wrappers conflict with nesting
*/
2018-07-17 13:59:50 +03:00
tevent_debug ( main_ev , TEVENT_DEBUG_FATAL ,
2014-07-22 18:51:38 +04:00
" %s: %s() conflicts with nesting \n " ,
__func__ , location ) ;
errno = EINVAL ;
return NULL ;
}
ev = talloc_zero ( mem_ctx , struct tevent_context ) ;
if ( ev = = NULL ) {
return NULL ;
}
ev - > ops = & tevent_wrapper_glue_ops ;
ev - > wrapper . glue = talloc_zero ( ev , struct tevent_wrapper_glue ) ;
if ( ev - > wrapper . glue = = NULL ) {
talloc_free ( ev ) ;
return NULL ;
}
talloc_set_destructor ( ev , tevent_wrapper_context_destructor ) ;
ev - > wrapper . glue - > wrap_ev = ev ;
ev - > wrapper . glue - > main_ev = main_ev ;
ev - > wrapper . glue - > ops = ops ;
2018-07-14 11:55:02 +03:00
ev - > wrapper . glue - > private_state = talloc_zero_size ( ev - > wrapper . glue , psize ) ;
2014-07-22 18:51:38 +04:00
if ( ev - > wrapper . glue - > private_state = = NULL ) {
talloc_free ( ev ) ;
return NULL ;
}
talloc_set_name_const ( ev - > wrapper . glue - > private_state , type ) ;
DLIST_ADD_END ( main_ev - > wrapper . list , ev - > wrapper . glue ) ;
* ppstate = ev - > wrapper . glue - > private_state ;
return ev ;
}
bool tevent_context_is_wrapper ( struct tevent_context * ev )
{
if ( ev - > wrapper . glue ! = NULL ) {
return true ;
}
return false ;
}
_PRIVATE_
struct tevent_context * tevent_wrapper_main_ev ( struct tevent_context * ev )
{
if ( ev = = NULL ) {
return NULL ;
}
if ( ev - > wrapper . glue = = NULL ) {
return ev ;
}
return ev - > wrapper . glue - > main_ev ;
}
/*
* 32 stack elements should be more than enough
*
* e . g . Samba uses just 8 elements for [ un ] become_ { root , user } ( )
*/
# define TEVENT_WRAPPER_STACK_SIZE 32
static struct tevent_wrapper_stack {
const void * ev_ptr ;
const struct tevent_wrapper_glue * wrapper ;
} wrapper_stack [ TEVENT_WRAPPER_STACK_SIZE ] ;
static size_t wrapper_stack_idx ;
_PRIVATE_
void tevent_wrapper_push_use_internal ( struct tevent_context * ev ,
struct tevent_wrapper_glue * wrapper )
{
/*
* ev and wrapper need to belong together !
* It ' s also fine to only have a raw ev
* without a wrapper .
*/
if ( unlikely ( ev - > wrapper . glue ! = wrapper ) ) {
tevent_abort ( ev , " tevent_wrapper_push_use_internal() invalid arguments " ) ;
return ;
}
if ( wrapper ! = NULL ) {
if ( unlikely ( wrapper - > busy ) ) {
tevent_abort ( ev , " wrapper already busy! " ) ;
return ;
}
wrapper - > busy = true ;
}
if ( unlikely ( wrapper_stack_idx > = TEVENT_WRAPPER_STACK_SIZE ) ) {
tevent_abort ( ev , " TEVENT_WRAPPER_STACK_SIZE overflow " ) ;
return ;
}
wrapper_stack [ wrapper_stack_idx ] = ( struct tevent_wrapper_stack ) {
. ev_ptr = ev ,
. wrapper = wrapper ,
} ;
wrapper_stack_idx + + ;
}
_PRIVATE_
void tevent_wrapper_pop_use_internal ( const struct tevent_context * __ev_ptr ,
struct tevent_wrapper_glue * wrapper )
{
struct tevent_context * main_ev = NULL ;
/*
* Note that __ev_ptr might a a stale pointer and should not
* be touched , we just compare the pointer value in order
* to enforce the stack order .
*/
if ( wrapper ! = NULL ) {
main_ev = wrapper - > main_ev ;
}
if ( unlikely ( wrapper_stack_idx = = 0 ) ) {
tevent_abort ( main_ev , " tevent_wrapper stack already empty " ) ;
return ;
}
wrapper_stack_idx - - ;
if ( wrapper ! = NULL ) {
wrapper - > busy = false ;
}
if ( wrapper_stack [ wrapper_stack_idx ] . ev_ptr ! = __ev_ptr ) {
tevent_abort ( main_ev , " tevent_wrapper_pop_use mismatch ev! " ) ;
return ;
}
if ( wrapper_stack [ wrapper_stack_idx ] . wrapper ! = wrapper ) {
tevent_abort ( main_ev , " tevent_wrapper_pop_use mismatch wrap! " ) ;
return ;
}
if ( wrapper = = NULL ) {
return ;
}
if ( wrapper - > destroyed ) {
/*
* Notice that we can ' t use TALLOC_FREE ( )
* here because wrapper is a talloc child
* of wrapper - > wrap_ev .
*/
talloc_free ( wrapper - > wrap_ev ) ;
}
}
bool _tevent_context_push_use ( struct tevent_context * ev ,
const char * location )
{
bool ok ;
if ( ev - > wrapper . glue = = NULL ) {
tevent_wrapper_push_use_internal ( ev , NULL ) ;
return true ;
}
if ( ev - > wrapper . glue - > main_ev = = NULL ) {
return false ;
}
tevent_wrapper_push_use_internal ( ev , ev - > wrapper . glue ) ;
ok = ev - > wrapper . glue - > ops - > before_use ( ev - > wrapper . glue - > wrap_ev ,
ev - > wrapper . glue - > private_state ,
ev - > wrapper . glue - > main_ev ,
location ) ;
if ( ! ok ) {
tevent_wrapper_pop_use_internal ( ev , ev - > wrapper . glue ) ;
return false ;
}
return true ;
}
void _tevent_context_pop_use ( struct tevent_context * ev ,
const char * location )
{
tevent_wrapper_pop_use_internal ( ev , ev - > wrapper . glue ) ;
if ( ev - > wrapper . glue = = NULL ) {
return ;
}
if ( ev - > wrapper . glue - > main_ev = = NULL ) {
return ;
}
ev - > wrapper . glue - > ops - > after_use ( ev - > wrapper . glue - > wrap_ev ,
ev - > wrapper . glue - > private_state ,
ev - > wrapper . glue - > main_ev ,
location ) ;
}
bool tevent_context_same_loop ( struct tevent_context * ev1 ,
struct tevent_context * ev2 )
{
struct tevent_context * main_ev1 = tevent_wrapper_main_ev ( ev1 ) ;
struct tevent_context * main_ev2 = tevent_wrapper_main_ev ( ev2 ) ;
if ( main_ev1 = = NULL ) {
return false ;
}
if ( main_ev1 = = main_ev2 ) {
return true ;
}
return false ;
}