2012-03-23 16:43:39 +04:00
/*
Unix SMB / CIFS implementation .
Regular background jobs as forked helpers
Copyright ( C ) Volker Lendecke 2012
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 "lib/util/tevent_ntstatus.h"
# include "lib/async_req/async_sock.h"
# include "include/messages.h"
# include "background.h"
struct background_job_state {
struct tevent_context * ev ;
struct messaging_context * msg ;
uint32_t * trigger_msgs ;
size_t num_trigger_msgs ;
bool parent_longlived ;
int ( * fn ) ( void * private_data ) ;
void * private_data ;
struct tevent_req * wakeup_req ;
int pipe_fd ;
} ;
static int background_job_state_destructor ( struct background_job_state * s ) ;
static void background_job_waited ( struct tevent_req * subreq ) ;
static void background_job_done ( struct tevent_req * subreq ) ;
static void background_job_trigger (
struct messaging_context * msg , void * private_data , uint32_t msg_type ,
struct server_id server_id , DATA_BLOB * data ) ;
struct tevent_req * background_job_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct messaging_context * msg ,
uint32_t * trigger_msgs ,
size_t num_trigger_msgs ,
time_t initial_wait_sec ,
int ( * fn ) ( void * private_data ) ,
void * private_data )
{
struct tevent_req * req , * subreq ;
struct background_job_state * state ;
size_t i ;
req = tevent_req_create ( mem_ctx , & state ,
struct background_job_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > msg = msg ;
if ( num_trigger_msgs ! = 0 ) {
state - > trigger_msgs = ( uint32_t * ) talloc_memdup (
state , trigger_msgs ,
sizeof ( uint32_t ) * num_trigger_msgs ) ;
if ( tevent_req_nomem ( state - > trigger_msgs , req ) ) {
return tevent_req_post ( req , ev ) ;
}
state - > num_trigger_msgs = num_trigger_msgs ;
}
state - > fn = fn ;
state - > private_data = private_data ;
state - > pipe_fd = - 1 ;
talloc_set_destructor ( state , background_job_state_destructor ) ;
for ( i = 0 ; i < num_trigger_msgs ; i + + ) {
NTSTATUS status ;
status = messaging_register ( msg , state , trigger_msgs [ i ] ,
background_job_trigger ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
}
subreq = tevent_wakeup_send (
state , state - > ev , timeval_current_ofs ( initial_wait_sec , 0 ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , background_job_waited , req ) ;
state - > wakeup_req = subreq ;
return req ;
}
static int background_job_state_destructor ( struct background_job_state * state )
{
size_t i ;
if ( state - > pipe_fd ! = - 1 ) {
close ( state - > pipe_fd ) ;
state - > pipe_fd = - 1 ;
}
for ( i = 0 ; i < state - > num_trigger_msgs ; i + + ) {
messaging_deregister ( state - > msg , state - > trigger_msgs [ i ] ,
state ) ;
}
return 0 ;
}
static void background_job_trigger (
struct messaging_context * msg , void * private_data , uint32_t msg_type ,
struct server_id server_id , DATA_BLOB * data )
{
struct background_job_state * state = talloc_get_type_abort (
private_data , struct background_job_state ) ;
2012-04-19 13:15:46 +04:00
if ( state - > wakeup_req = = NULL ) {
return ;
}
if ( ! tevent_req_set_endtime ( state - > wakeup_req , state - > ev ,
timeval_zero ( ) ) ) {
DEBUG ( 10 , ( " tevent_req_set_endtime failed \n " ) ) ;
2012-03-23 16:43:39 +04:00
}
}
static void background_job_waited ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct background_job_state * state = tevent_req_data (
req , struct background_job_state ) ;
int fds [ 2 ] ;
int res ;
bool ret ;
ret = tevent_wakeup_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
state - > wakeup_req = NULL ;
if ( ! ret ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
res = pipe ( fds ) ;
if ( res = = - 1 ) {
tevent_req_nterror ( req , map_nt_error_from_unix ( errno ) ) ;
return ;
}
res = fork ( ) ;
if ( res = = - 1 ) {
int err = errno ;
close ( fds [ 0 ] ) ;
close ( fds [ 1 ] ) ;
tevent_req_nterror ( req , map_nt_error_from_unix ( err ) ) ;
return ;
}
if ( res = = 0 ) {
/* child */
NTSTATUS status ;
ssize_t written ;
close ( fds [ 0 ] ) ;
status = reinit_after_fork ( state - > msg , state - > ev , true ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
res = state - > fn ( state - > private_data ) ;
} else {
res = - 1 ;
}
written = write ( fds [ 1 ] , & res , sizeof ( res ) ) ;
if ( written = = - 1 ) {
_exit ( 1 ) ;
}
_exit ( 0 ) ;
}
/* parent */
close ( fds [ 1 ] ) ;
state - > pipe_fd = fds [ 0 ] ;
subreq = read_packet_send ( state , state - > ev , state - > pipe_fd ,
sizeof ( int ) , NULL , NULL ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , background_job_done , req ) ;
}
static void background_job_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct background_job_state * state = tevent_req_data (
req , struct background_job_state ) ;
ssize_t ret ;
uint8_t * buf ;
int err ;
int wait_secs ;
ret = read_packet_recv ( subreq , talloc_tos ( ) , & buf , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_nterror ( req , map_nt_error_from_unix ( err ) ) ;
return ;
}
close ( state - > pipe_fd ) ;
state - > pipe_fd = - 1 ;
memcpy ( & wait_secs , buf , sizeof ( wait_secs ) ) ;
if ( wait_secs = = - 1 ) {
tevent_req_done ( req ) ;
return ;
}
subreq = tevent_wakeup_send (
state , state - > ev , timeval_current_ofs ( wait_secs , 0 ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , background_job_waited , req ) ;
state - > wakeup_req = subreq ;
}
NTSTATUS background_job_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
}