2011-04-21 00:28:57 +04:00
/*
Unix SMB / CIFS implementation .
Common server globals
Copyright ( C ) Simo Sorce < idra @ samba . org > 2011
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 "includes.h"
# include "system/time.h"
# include "system/shmem.h"
# include "system/filesys.h"
# include "server_prefork.h"
# include "../lib/util/util.h"
2011-05-04 02:55:25 +04:00
# include "../lib/util/tevent_unix.h"
2011-04-21 00:28:57 +04:00
struct prefork_pool {
2011-05-10 16:39:14 +04:00
int listen_fd_size ;
int * listen_fds ;
2011-04-21 00:28:57 +04:00
int lock_fd ;
prefork_main_fn_t * main_fn ;
void * private_data ;
int pool_size ;
struct pf_worker_data * pool ;
2011-05-06 01:56:31 +04:00
int allowed_clients ;
2011-05-20 07:56:02 +04:00
prefork_sigchld_fn_t * sigchld_fn ;
void * sigchld_data ;
2011-04-21 00:28:57 +04:00
} ;
2011-05-20 07:56:02 +04:00
static bool prefork_setup_sigchld_handler ( struct tevent_context * ev_ctx ,
struct prefork_pool * pfp ) ;
static int prefork_pool_destructor ( struct prefork_pool * pfp )
2011-04-21 00:28:57 +04:00
{
munmap ( pfp - > pool , pfp - > pool_size * sizeof ( struct pf_worker_data ) ) ;
return 0 ;
}
2011-07-18 18:39:50 +04:00
bool prefork_create_pool ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev_ctx ,
struct messaging_context * msg_ctx ,
2011-05-10 16:39:14 +04:00
int listen_fd_size , int * listen_fds ,
2011-04-21 00:28:57 +04:00
int min_children , int max_children ,
prefork_main_fn_t * main_fn , void * private_data ,
struct prefork_pool * * pf_pool )
{
struct prefork_pool * pfp ;
pid_t pid ;
time_t now = time ( NULL ) ;
size_t data_size ;
int ret ;
int i ;
2011-05-20 07:56:02 +04:00
bool ok ;
2011-04-21 00:28:57 +04:00
2011-05-20 07:56:02 +04:00
pfp = talloc_zero ( mem_ctx , struct prefork_pool ) ;
2011-04-21 00:28:57 +04:00
if ( ! pfp ) {
DEBUG ( 1 , ( " Out of memory! \n " ) ) ;
return false ;
}
2011-05-10 16:39:14 +04:00
pfp - > listen_fd_size = listen_fd_size ;
pfp - > listen_fds = talloc_array ( pfp , int , listen_fd_size ) ;
if ( ! pfp - > listen_fds ) {
DEBUG ( 1 , ( " Out of memory! \n " ) ) ;
return false ;
}
for ( i = 0 ; i < listen_fd_size ; i + + ) {
pfp - > listen_fds [ i ] = listen_fds [ i ] ;
}
2011-04-21 00:28:57 +04:00
pfp - > main_fn = main_fn ;
pfp - > private_data = private_data ;
pfp - > lock_fd = create_unlink_tmp ( NULL ) ;
if ( pfp - > lock_fd = = - 1 ) {
DEBUG ( 1 , ( " Failed to create prefork lock fd! \n " ) ) ;
talloc_free ( pfp ) ;
return false ;
}
pfp - > pool_size = max_children ;
data_size = sizeof ( struct pf_worker_data ) * max_children ;
pfp - > pool = mmap ( NULL , data_size , PROT_READ | PROT_WRITE ,
MAP_SHARED | MAP_ANONYMOUS , - 1 , 0 ) ;
if ( pfp - > pool = = MAP_FAILED ) {
DEBUG ( 1 , ( " Failed to mmap memory for prefork pool! \n " ) ) ;
talloc_free ( pfp ) ;
return false ;
}
talloc_set_destructor ( pfp , prefork_pool_destructor ) ;
for ( i = 0 ; i < min_children ; i + + ) {
2011-05-06 01:56:31 +04:00
pfp - > pool [ i ] . allowed_clients = 1 ;
pfp - > pool [ i ] . started = now ;
2011-04-21 00:28:57 +04:00
pid = sys_fork ( ) ;
switch ( pid ) {
case - 1 :
DEBUG ( 1 , ( " Failed to prefork child n. %d ! \n " , i ) ) ;
break ;
case 0 : /* THE CHILD */
pfp - > pool [ i ] . status = PF_WORKER_IDLE ;
2011-07-18 18:39:50 +04:00
ret = pfp - > main_fn ( ev_ctx , msg_ctx ,
& pfp - > pool [ i ] ,
2011-05-10 16:39:14 +04:00
pfp - > listen_fd_size ,
pfp - > listen_fds ,
pfp - > lock_fd ,
2011-04-21 00:28:57 +04:00
pfp - > private_data ) ;
exit ( ret ) ;
default : /* THE PARENT */
pfp - > pool [ i ] . pid = pid ;
break ;
}
}
2011-05-20 07:56:02 +04:00
ok = prefork_setup_sigchld_handler ( ev_ctx , pfp ) ;
if ( ! ok ) {
DEBUG ( 1 , ( " Failed to setup SIGCHLD Handler! \n " ) ) ;
talloc_free ( pfp ) ;
return false ;
}
2011-04-21 00:28:57 +04:00
* pf_pool = pfp ;
return true ;
}
2011-05-09 12:51:47 +04:00
/* Provide the new max children number in new_max
* ( must be larger than current max ) .
* Returns : 0 if all fine
* ENOSPC if mremap fails to expand
* EINVAL if new_max is invalid
*/
int prefork_expand_pool ( struct prefork_pool * pfp , int new_max )
{
struct pf_worker_data * pool ;
size_t old_size ;
size_t new_size ;
if ( new_max < = pfp - > pool_size ) {
return EINVAL ;
}
old_size = sizeof ( struct pf_worker_data ) * pfp - > pool_size ;
new_size = sizeof ( struct pf_worker_data ) * new_max ;
pool = mremap ( pfp - > pool , old_size , new_size , 0 ) ;
if ( pool = = MAP_FAILED ) {
DEBUG ( 3 , ( " Failed to mremap memory for prefork pool! \n " ) ) ;
return ENOSPC ;
}
memset ( & pool [ pfp - > pool_size ] , 0 , new_size - old_size ) ;
pfp - > pool_size = new_max ;
return 0 ;
}
2011-04-21 00:28:57 +04:00
int prefork_add_children ( struct tevent_context * ev_ctx ,
2011-07-18 18:39:50 +04:00
struct messaging_context * msg_ctx ,
2011-04-21 00:28:57 +04:00
struct prefork_pool * pfp ,
int num_children )
{
pid_t pid ;
time_t now = time ( NULL ) ;
int ret ;
int i , j ;
for ( i = 0 , j = 0 ; i < pfp - > pool_size & & j < num_children ; i + + ) {
if ( pfp - > pool [ i ] . status ! = PF_WORKER_NONE ) {
continue ;
}
2011-05-06 01:56:31 +04:00
pfp - > pool [ i ] . allowed_clients = 1 ;
pfp - > pool [ i ] . started = now ;
2011-04-21 00:28:57 +04:00
pid = sys_fork ( ) ;
switch ( pid ) {
case - 1 :
DEBUG ( 1 , ( " Failed to prefork child n. %d ! \n " , j ) ) ;
break ;
case 0 : /* THE CHILD */
pfp - > pool [ i ] . status = PF_WORKER_IDLE ;
2011-07-18 18:39:50 +04:00
ret = pfp - > main_fn ( ev_ctx , msg_ctx ,
& pfp - > pool [ i ] ,
2011-05-10 16:39:14 +04:00
pfp - > listen_fd_size ,
pfp - > listen_fds ,
pfp - > lock_fd ,
2011-04-21 00:28:57 +04:00
pfp - > private_data ) ;
pfp - > pool [ i ] . status = PF_WORKER_EXITING ;
exit ( ret ) ;
default : /* THE PARENT */
pfp - > pool [ i ] . pid = pid ;
j + + ;
break ;
}
}
DEBUG ( 5 , ( " Added %d children! \n " , j ) ) ;
return j ;
}
struct prefork_oldest {
int num ;
time_t started ;
} ;
/* sort in inverse order */
static int prefork_sort_oldest ( const void * ap , const void * bp )
{
struct prefork_oldest * a = ( struct prefork_oldest * ) ap ;
struct prefork_oldest * b = ( struct prefork_oldest * ) bp ;
if ( a - > started = = b - > started ) {
return 0 ;
}
if ( a - > started < b - > started ) {
return 1 ;
}
return - 1 ;
}
int prefork_retire_children ( struct prefork_pool * pfp ,
int num_children , time_t age_limit )
{
time_t now = time ( NULL ) ;
struct prefork_oldest * oldest ;
int i , j ;
oldest = talloc_array ( pfp , struct prefork_oldest , pfp - > pool_size ) ;
if ( ! oldest ) {
return - 1 ;
}
for ( i = 0 ; i < pfp - > pool_size ; i + + ) {
oldest [ i ] . num = i ;
if ( pfp - > pool [ i ] . status = = PF_WORKER_IDLE ) {
oldest [ i ] . started = pfp - > pool [ i ] . started ;
} else {
oldest [ i ] . started = now ;
}
}
qsort ( oldest , pfp - > pool_size ,
sizeof ( struct prefork_oldest ) ,
prefork_sort_oldest ) ;
for ( i = 0 , j = 0 ; i < pfp - > pool_size & & j < num_children ; i + + ) {
if ( pfp - > pool [ i ] . status = = PF_WORKER_IDLE & &
pfp - > pool [ i ] . started < = age_limit ) {
/* tell the child it's time to give up */
DEBUG ( 5 , ( " Retiring pid %d! \n " , pfp - > pool [ i ] . pid ) ) ;
pfp - > pool [ i ] . cmds = PF_SRV_MSG_EXIT ;
kill ( pfp - > pool [ i ] . pid , SIGHUP ) ;
j + + ;
}
}
return j ;
}
int prefork_count_active_children ( struct prefork_pool * pfp , int * total )
{
int i , a , t ;
a = 0 ;
t = 0 ;
for ( i = 0 ; i < pfp - > pool_size ; i + + ) {
if ( pfp - > pool [ i ] . status = = PF_WORKER_NONE ) {
continue ;
}
t + + ;
if ( pfp - > pool [ i ] . num_clients = = 0 ) {
continue ;
}
a + + ;
}
* total = t ;
return a ;
}
2011-05-20 07:56:02 +04:00
static void prefork_cleanup_loop ( struct prefork_pool * pfp )
2011-04-21 00:28:57 +04:00
{
2011-05-16 18:46:35 +04:00
int status ;
pid_t pid ;
2011-04-21 00:28:57 +04:00
int i ;
2011-05-16 18:46:35 +04:00
/* TODO: should we use a process group id wait instead of looping ? */
2011-04-21 00:28:57 +04:00
for ( i = 0 ; i < pfp - > pool_size ; i + + ) {
2011-05-16 18:46:35 +04:00
if ( pfp - > pool [ i ] . status = = PF_WORKER_NONE | |
pfp - > pool [ i ] . pid = = 0 ) {
continue ;
}
pid = sys_waitpid ( pfp - > pool [ i ] . pid , & status , WNOHANG ) ;
if ( pid > 0 ) {
2011-04-21 00:28:57 +04:00
if ( pfp - > pool [ i ] . status ! = PF_WORKER_EXITING ) {
2011-05-16 18:46:35 +04:00
DEBUG ( 3 , ( " Child (%d) terminated abnormally: "
" %d \n " , ( int ) pid , status ) ) ;
} else {
DEBUG ( 10 , ( " Child (%d) terminated with status: "
" %d \n " , ( int ) pid , status ) ) ;
2011-04-21 00:28:57 +04:00
}
/* reset all fields,
* this makes status = PF_WORK_NONE */
memset ( & pfp - > pool [ i ] , 0 ,
sizeof ( struct pf_worker_data ) ) ;
}
}
}
2011-05-06 01:56:31 +04:00
void prefork_increase_allowed_clients ( struct prefork_pool * pfp , int max )
{
int i ;
for ( i = 0 ; i < pfp - > pool_size ; i + + ) {
if ( pfp - > pool [ i ] . status = = PF_WORKER_NONE ) {
continue ;
}
if ( pfp - > pool [ i ] . allowed_clients < max ) {
pfp - > pool [ i ] . allowed_clients + + ;
}
}
}
void prefork_reset_allowed_clients ( struct prefork_pool * pfp )
{
int i ;
for ( i = 0 ; i < pfp - > pool_size ; i + + ) {
pfp - > pool [ i ] . allowed_clients = 1 ;
}
}
2011-05-09 16:49:50 +04:00
void prefork_send_signal_to_all ( struct prefork_pool * pfp , int signal_num )
{
int i ;
for ( i = 0 ; i < pfp - > pool_size ; i + + ) {
if ( pfp - > pool [ i ] . status = = PF_WORKER_NONE ) {
continue ;
}
kill ( pfp - > pool [ i ] . pid , signal_num ) ;
}
}
2011-05-20 07:56:02 +04:00
static void prefork_sigchld_handler ( struct tevent_context * ev_ctx ,
struct tevent_signal * se ,
int signum , int count ,
void * siginfo , void * pvt )
{
struct prefork_pool * pfp ;
pfp = talloc_get_type_abort ( pvt , struct prefork_pool ) ;
/* run the cleanup function to make sure all dead children are
* properly and timely retired . */
prefork_cleanup_loop ( pfp ) ;
if ( pfp - > sigchld_fn ) {
pfp - > sigchld_fn ( ev_ctx , pfp , pfp - > sigchld_data ) ;
}
}
static bool prefork_setup_sigchld_handler ( struct tevent_context * ev_ctx ,
struct prefork_pool * pfp )
{
struct tevent_signal * se ;
se = tevent_add_signal ( ev_ctx , pfp , SIGCHLD , 0 ,
prefork_sigchld_handler , pfp ) ;
if ( ! se ) {
DEBUG ( 0 , ( " Failed to setup SIGCHLD handler! \n " ) ) ;
return false ;
}
return true ;
}
void prefork_set_sigchld_callback ( struct prefork_pool * pfp ,
prefork_sigchld_fn_t * sigchld_fn ,
void * private_data )
{
pfp - > sigchld_fn = sigchld_fn ;
pfp - > sigchld_data = private_data ;
}
2011-05-09 16:49:50 +04:00
2011-04-21 00:28:57 +04:00
/* ==== Functions used by children ==== */
static SIG_ATOMIC_T pf_alarm ;
static void pf_alarm_cb ( int signum )
{
pf_alarm = 1 ;
}
/*
* Parameters :
* pf - the worker shared data structure
* lock_fd - the file descriptor used for locking
* timeout - expressed in seconds :
* - 1 never timeouts ,
* 0 timeouts immediately
* N seconds before timing out
*
* Returns values :
* negative errno on fatal error
* 0 on success to acquire lock
* - 1 on timeout / lock held by other
* - 2 on server msg to terminate
* ERRNO on other errors
*/
static int prefork_grab_lock ( struct pf_worker_data * pf ,
int lock_fd , int timeout )
{
struct flock lock ;
int op ;
int ret ;
if ( pf - > cmds = = PF_SRV_MSG_EXIT ) {
return - 2 ;
}
pf_alarm = 0 ;
if ( timeout > 0 ) {
CatchSignal ( SIGALRM , pf_alarm_cb ) ;
alarm ( timeout ) ;
}
if ( timeout = = 0 ) {
op = F_SETLK ;
} else {
op = F_SETLKW ;
}
ret = 0 ;
do {
ZERO_STRUCT ( lock ) ;
lock . l_type = F_WRLCK ;
lock . l_whence = SEEK_SET ;
ret = fcntl ( lock_fd , op , & lock ) ;
if ( ret = = 0 ) break ;
ret = errno ;
if ( pf - > cmds = = PF_SRV_MSG_EXIT ) {
ret = - 2 ;
goto done ;
}
switch ( ret ) {
case EINTR :
break ;
case EACCES :
case EAGAIN :
/* lock held by other proc */
ret = - 1 ;
goto done ;
default :
goto done ;
}
if ( pf_alarm = = 1 ) {
/* timed out */
ret = - 1 ;
goto done ;
}
} while ( timeout ! = 0 ) ;
if ( ret ! = 0 ) {
/* We have the Lock */
pf - > status = PF_WORKER_ACCEPTING ;
}
done :
if ( timeout > 0 ) {
alarm ( 0 ) ;
CatchSignal ( SIGALRM , SIG_IGN ) ;
}
if ( ret > 0 ) {
DEBUG ( 1 , ( " Failed to get lock (%d, %s)! \n " ,
ret , strerror ( ret ) ) ) ;
}
return ret ;
}
/*
* Parameters :
* pf - the worker shared data structure
* lock_fd - the file descriptor used for locking
* timeout - expressed in seconds :
* - 1 never timeouts ,
* 0 timeouts immediately
* N seconds before timing out
*
* Returns values :
* negative errno on fatal error
* 0 on success to release lock
* - 1 on timeout
* ERRNO on error
*/
static int prefork_release_lock ( struct pf_worker_data * pf ,
int lock_fd , int timeout )
{
struct flock lock ;
int op ;
int ret ;
pf_alarm = 0 ;
if ( timeout > 0 ) {
CatchSignal ( SIGALRM , pf_alarm_cb ) ;
alarm ( timeout ) ;
}
if ( timeout = = 0 ) {
op = F_SETLK ;
} else {
op = F_SETLKW ;
}
do {
ZERO_STRUCT ( lock ) ;
lock . l_type = F_UNLCK ;
lock . l_whence = SEEK_SET ;
ret = fcntl ( lock_fd , op , & lock ) ;
if ( ret = = 0 ) break ;
ret = errno ;
if ( ret ! = EINTR ) {
goto done ;
}
if ( pf_alarm = = 1 ) {
/* timed out */
ret = - 1 ;
goto done ;
}
} while ( timeout ! = 0 ) ;
done :
if ( timeout > 0 ) {
alarm ( 0 ) ;
CatchSignal ( SIGALRM , SIG_IGN ) ;
}
if ( ret > 0 ) {
DEBUG ( 1 , ( " Failed to release lock (%d, %s)! \n " ,
ret , strerror ( ret ) ) ) ;
}
return ret ;
}
2011-05-04 02:55:25 +04:00
/* ==== async code ==== */
# define PF_ASYNC_LOCK_GRAB 0x01
# define PF_ASYNC_LOCK_RELEASE 0x02
# define PF_ASYNC_ACTION_MASK 0x03
# define PF_ASYNC_LOCK_DONE 0x04
struct pf_lock_state {
struct pf_worker_data * pf ;
int lock_fd ;
int flags ;
} ;
static void prefork_lock_handler ( struct tevent_context * ev ,
struct tevent_timer * te ,
struct timeval curtime , void * pvt ) ;
static struct tevent_req * prefork_lock_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct pf_worker_data * pf ,
int lock_fd , int action )
{
struct tevent_req * req ;
struct pf_lock_state * state ;
req = tevent_req_create ( mem_ctx , & state , struct pf_lock_state ) ;
if ( ! req ) {
return NULL ;
}
state - > pf = pf ;
state - > lock_fd = lock_fd ;
state - > flags = action ;
/* try once immediately */
prefork_lock_handler ( ev , NULL , tevent_timeval_zero ( ) , req ) ;
if ( state - > flags & PF_ASYNC_LOCK_DONE ) {
tevent_req_post ( req , ev ) ;
}
return req ;
}
static void prefork_lock_handler ( struct tevent_context * ev ,
struct tevent_timer * te ,
struct timeval curtime , void * pvt )
{
struct tevent_req * req ;
struct pf_lock_state * state ;
2011-05-10 17:08:21 +04:00
struct timeval tv ;
int timeout = 0 ;
2011-05-04 02:55:25 +04:00
int ret ;
req = talloc_get_type_abort ( pvt , struct tevent_req ) ;
state = tevent_req_data ( req , struct pf_lock_state ) ;
2011-05-10 17:08:21 +04:00
if ( state - > pf - > num_clients > 0 ) {
timeout = 1 ;
}
2011-05-04 02:55:25 +04:00
switch ( state - > flags & PF_ASYNC_ACTION_MASK ) {
case PF_ASYNC_LOCK_GRAB :
2011-05-10 17:08:21 +04:00
ret = prefork_grab_lock ( state - > pf , state - > lock_fd , timeout ) ;
2011-05-04 02:55:25 +04:00
break ;
case PF_ASYNC_LOCK_RELEASE :
2011-05-10 17:08:21 +04:00
ret = prefork_release_lock ( state - > pf , state - > lock_fd , timeout ) ;
2011-05-04 02:55:25 +04:00
break ;
default :
ret = EINVAL ;
break ;
}
switch ( ret ) {
case 0 :
state - > flags | = PF_ASYNC_LOCK_DONE ;
tevent_req_done ( req ) ;
return ;
case - 1 :
2011-05-10 17:08:21 +04:00
if ( timeout ) {
tv = tevent_timeval_zero ( ) ;
} else {
tv = tevent_timeval_current_ofs ( 0 , 100000 ) ;
}
te = tevent_add_timer ( ev , state , tv ,
2011-05-04 02:55:25 +04:00
prefork_lock_handler , req ) ;
tevent_req_nomem ( te , req ) ;
return ;
case - 2 :
/* server tells us to stop */
state - > flags | = PF_ASYNC_LOCK_DONE ;
tevent_req_error ( req , - 2 ) ;
return ;
default :
state - > flags | = PF_ASYNC_LOCK_DONE ;
tevent_req_error ( req , ret ) ;
return ;
}
}
static int prefork_lock_recv ( struct tevent_req * req )
{
int ret ;
if ( ! tevent_req_is_unix_error ( req , & ret ) ) {
ret = 0 ;
}
tevent_req_received ( req ) ;
return ret ;
}
struct pf_listen_state {
struct tevent_context * ev ;
struct pf_worker_data * pf ;
2011-05-10 16:39:14 +04:00
int listen_fd_size ;
int * listen_fds ;
2011-05-04 02:55:25 +04:00
int lock_fd ;
struct sockaddr * addr ;
socklen_t * addrlen ;
int accept_fd ;
int error ;
} ;
static void prefork_listen_lock_done ( struct tevent_req * subreq ) ;
static void prefork_listen_accept_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags , void * pvt ) ;
static void prefork_listen_release_done ( struct tevent_req * subreq ) ;
struct tevent_req * prefork_listen_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct pf_worker_data * pf ,
2011-05-10 16:39:14 +04:00
int listen_fd_size ,
int * listen_fds ,
int lock_fd ,
2011-05-04 02:55:25 +04:00
struct sockaddr * addr ,
socklen_t * addrlen )
{
struct tevent_req * req , * subreq ;
struct pf_listen_state * state ;
req = tevent_req_create ( mem_ctx , & state , struct pf_listen_state ) ;
if ( ! req ) {
return NULL ;
}
state - > ev = ev ;
state - > pf = pf ;
state - > lock_fd = lock_fd ;
2011-05-10 16:39:14 +04:00
state - > listen_fd_size = listen_fd_size ;
state - > listen_fds = listen_fds ;
2011-05-04 02:55:25 +04:00
state - > addr = addr ;
state - > addrlen = addrlen ;
state - > accept_fd = - 1 ;
state - > error = 0 ;
subreq = prefork_lock_send ( state , state - > ev , state - > pf ,
state - > lock_fd , PF_ASYNC_LOCK_GRAB ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , prefork_listen_lock_done , req ) ;
return req ;
}
2011-05-10 16:39:14 +04:00
struct pf_listen_ctx {
TALLOC_CTX * fde_ctx ;
struct tevent_req * req ;
int listen_fd ;
} ;
2011-05-04 02:55:25 +04:00
static void prefork_listen_lock_done ( struct tevent_req * subreq )
{
struct tevent_req * req ;
struct pf_listen_state * state ;
2011-05-10 16:39:14 +04:00
struct pf_listen_ctx * ctx ;
2011-05-04 02:55:25 +04:00
struct tevent_fd * fde ;
2011-05-10 16:39:14 +04:00
TALLOC_CTX * fde_ctx ;
2011-05-04 02:55:25 +04:00
int ret ;
2011-05-10 16:39:14 +04:00
int i ;
2011-05-04 02:55:25 +04:00
req = tevent_req_callback_data ( subreq , struct tevent_req ) ;
state = tevent_req_data ( req , struct pf_listen_state ) ;
ret = prefork_lock_recv ( subreq ) ;
if ( ret ! = 0 ) {
tevent_req_error ( req , ret ) ;
return ;
}
2011-05-10 16:39:14 +04:00
fde_ctx = talloc_new ( state ) ;
if ( tevent_req_nomem ( fde_ctx , req ) ) {
return ;
}
2011-05-04 02:55:25 +04:00
/* next step, accept */
2011-05-10 16:39:14 +04:00
for ( i = 0 ; i < state - > listen_fd_size ; i + + ) {
ctx = talloc ( fde_ctx , struct pf_listen_ctx ) ;
if ( tevent_req_nomem ( ctx , req ) ) {
return ;
}
ctx - > fde_ctx = fde_ctx ;
ctx - > req = req ;
ctx - > listen_fd = state - > listen_fds [ i ] ;
fde = tevent_add_fd ( state - > ev , fde_ctx ,
ctx - > listen_fd , TEVENT_FD_READ ,
prefork_listen_accept_handler , ctx ) ;
if ( tevent_req_nomem ( fde , req ) ) {
return ;
}
}
2011-05-04 02:55:25 +04:00
}
static void prefork_listen_accept_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags , void * pvt )
{
struct pf_listen_state * state ;
struct tevent_req * req , * subreq ;
2011-05-10 16:39:14 +04:00
struct pf_listen_ctx * ctx ;
2011-05-04 02:55:25 +04:00
int err = 0 ;
int sd = - 1 ;
2011-05-10 16:39:14 +04:00
ctx = talloc_get_type_abort ( pvt , struct pf_listen_ctx ) ;
state = tevent_req_data ( ctx - > req , struct pf_listen_state ) ;
2011-05-04 02:55:25 +04:00
2011-05-10 16:39:14 +04:00
sd = accept ( ctx - > listen_fd , state - > addr , state - > addrlen ) ;
2011-05-04 02:55:25 +04:00
if ( sd = = - 1 ) {
if ( errno = = EINTR ) {
/* keep trying */
return ;
}
err = errno ;
DEBUG ( 6 , ( " Accept failed! (%d, %s) \n " , err , strerror ( err ) ) ) ;
}
2011-05-10 16:39:14 +04:00
/* do not track the listen fds anymore */
req = ctx - > req ;
talloc_free ( ctx - > fde_ctx ) ;
ctx = NULL ;
2011-05-04 02:55:25 +04:00
if ( err ) {
tevent_req_error ( req , err ) ;
return ;
}
state - > accept_fd = sd ;
/* release lock now */
subreq = prefork_lock_send ( state , state - > ev , state - > pf ,
state - > lock_fd , PF_ASYNC_LOCK_RELEASE ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , prefork_listen_release_done , req ) ;
}
static void prefork_listen_release_done ( struct tevent_req * subreq )
{
struct tevent_req * req ;
int ret ;
req = tevent_req_callback_data ( subreq , struct tevent_req ) ;
ret = prefork_lock_recv ( subreq ) ;
if ( ret ! = 0 ) {
tevent_req_error ( req , ret ) ;
return ;
}
tevent_req_done ( req ) ;
}
int prefork_listen_recv ( struct tevent_req * req , int * fd )
{
struct pf_listen_state * state ;
int ret ;
state = tevent_req_data ( req , struct pf_listen_state ) ;
if ( tevent_req_is_unix_error ( req , & ret ) ) {
if ( state - > accept_fd ! = - 1 ) {
close ( state - > accept_fd ) ;
}
} else {
* fd = state - > accept_fd ;
ret = 0 ;
state - > pf - > status = PF_WORKER_BUSY ;
state - > pf - > num_clients + + ;
}
tevent_req_received ( req ) ;
return ret ;
}