2017-09-07 05:30:15 +03:00
/*
Unix SMB / CIFS implementation .
process model : prefork ( n client connections per process )
Copyright ( C ) Andrew Tridgell 1992 - 2005
Copyright ( C ) James J Myers 2003 < myersjj @ samba . org >
Copyright ( C ) Stefan ( metze ) Metzmacher 2004
Copyright ( C ) Andrew Bartlett 2008 < abartlet @ samba . org >
Copyright ( C ) David Disseldorp 2008 < ddiss @ sgi . com >
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/>.
*/
2018-11-22 00:35:01 +03:00
/*
* The pre - fork process model distributes the server workload amongst several
* designated worker threads ( e . g . ' prefork - worker - ldap - 0 ' ,
* ' prefork - worker - ldap - 1 ' , etc ) . The number of worker threads is controlled
* by the ' prefork children ' conf setting . The worker threads are controlled
* by a prefork master process ( e . g . ' prefork - master - ldap ' ) . The prefork master
* doesn ' t handle the server workload ( i . e . processing messages ) itself , but is
* responsible for restarting workers if they exit unexpectedly . The top - level
* samba process is responsible for restarting the master process if it exits .
*/
2017-09-07 05:30:15 +03:00
# include "includes.h"
2017-10-19 18:27:23 +03:00
# include <unistd.h>
2017-09-07 05:30:15 +03:00
# include "lib/events/events.h"
# include "lib/messaging/messaging.h"
# include "lib/socket/socket.h"
2020-11-20 17:27:17 +03:00
# include "samba/process_model.h"
2017-09-07 05:30:15 +03:00
# include "cluster/cluster.h"
# include "param/param.h"
# include "ldb_wrap.h"
# include "lib/util/tfork.h"
2018-09-14 00:45:38 +03:00
# include "lib/messaging/irpc.h"
2020-02-21 23:07:19 +03:00
# include "lib/util/util_process.h"
2020-11-26 16:21:58 +03:00
# include "server_util.h"
2017-09-07 05:30:15 +03:00
2018-09-04 22:31:22 +03:00
# define min(a, b) (((a) < (b)) ? (a) : (b))
2017-09-07 05:30:15 +03:00
NTSTATUS process_model_prefork_init ( void ) ;
2018-09-03 00:34:17 +03:00
static void prefork_new_task (
struct tevent_context * ev ,
struct loadparm_context * lp_ctx ,
const char * service_name ,
struct task_server * ( * new_task_fn ) ( struct tevent_context * ,
struct loadparm_context * lp_ctx ,
struct server_id ,
void * ,
void * ) ,
void * private_data ,
const struct service_details * service_details ,
int from_parent_fd ) ;
static void prefork_fork_worker ( struct task_server * task ,
struct tevent_context * ev ,
struct tevent_context * ev2 ,
2018-09-04 22:31:22 +03:00
struct loadparm_context * lp_ctx ,
2018-09-03 00:34:17 +03:00
const struct service_details * service_details ,
const char * service_name ,
int control_pipe [ 2 ] ,
2018-09-04 22:31:22 +03:00
unsigned restart_delay ,
2018-09-03 00:34:17 +03:00
struct process_details * pd ) ;
2018-09-04 22:31:22 +03:00
static void prefork_child_pipe_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags ,
void * private_data ) ;
2018-09-14 00:45:38 +03:00
static void setup_handlers ( struct tevent_context * ev ,
struct loadparm_context * lp_ctx ,
int from_parent_fd ) ;
2017-09-07 05:30:15 +03:00
2018-08-31 02:40:18 +03:00
/*
* State needed to restart the master process or a worker process if they
* terminate early .
*/
struct master_restart_context {
struct task_server * ( * new_task_fn ) ( struct tevent_context * ,
struct loadparm_context * lp_ctx ,
struct server_id ,
void * ,
void * ) ;
void * private_data ;
} ;
struct worker_restart_context {
unsigned int instance ;
struct task_server * task ;
struct tevent_context * ev2 ;
2018-09-03 00:34:17 +03:00
int control_pipe [ 2 ] ;
2018-08-31 02:40:18 +03:00
} ;
struct restart_context {
2018-09-04 22:31:22 +03:00
struct loadparm_context * lp_ctx ;
2018-08-31 02:40:18 +03:00
struct tfork * t ;
int from_parent_fd ;
const struct service_details * service_details ;
const char * service_name ;
2018-09-04 22:31:22 +03:00
unsigned restart_delay ;
2018-08-31 02:40:18 +03:00
struct master_restart_context * master ;
struct worker_restart_context * worker ;
} ;
2017-09-07 05:30:15 +03:00
static void sighup_signal_handler ( struct tevent_context * ev ,
struct tevent_signal * se ,
int signum , int count , void * siginfo ,
void * private_data )
{
2020-11-23 18:44:04 +03:00
reopen_logs_internal ( ) ;
2017-09-07 05:30:15 +03:00
}
static void sigterm_signal_handler ( struct tevent_context * ev ,
struct tevent_signal * se ,
int signum , int count , void * siginfo ,
void * private_data )
{
2018-11-21 13:37:26 +03:00
# ifdef HAVE_GETPGRP
2017-09-07 05:30:15 +03:00
if ( getpgrp ( ) = = getpid ( ) ) {
/*
* We ' re the process group leader , send
* SIGTERM to our process group .
*/
DBG_NOTICE ( " SIGTERM: killing children \n " ) ;
kill ( - getpgrp ( ) , SIGTERM ) ;
}
# endif
DBG_NOTICE ( " Exiting pid %d on SIGTERM \n " , getpid ( ) ) ;
2018-09-14 00:45:38 +03:00
TALLOC_FREE ( ev ) ;
2017-09-07 05:30:15 +03:00
exit ( 127 ) ;
}
/*
called when the process model is selected
*/
static void prefork_model_init ( void )
{
}
static void prefork_reload_after_fork ( void )
{
NTSTATUS status ;
ldb_wrap_fork_hook ( ) ;
/* Must be done after a fork() to reset messaging contexts. */
status = imessaging_reinit_all ( ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
smb_panic ( " Failed to re-initialise imessaging after fork " ) ;
}
2020-11-26 17:23:58 +03:00
force_check_log_size ( ) ;
2017-09-07 05:30:15 +03:00
}
2018-09-14 00:45:38 +03:00
/*
* clean up any messaging associated with the old process .
*
*/
static void irpc_cleanup (
struct loadparm_context * lp_ctx ,
struct tevent_context * ev ,
pid_t pid )
{
TALLOC_CTX * mem_ctx = talloc_new ( NULL ) ;
struct imessaging_context * msg_ctx = NULL ;
NTSTATUS status = NT_STATUS_OK ;
if ( mem_ctx = = NULL ) {
DBG_ERR ( " OOM cleaning up irpc \n " ) ;
return ;
}
msg_ctx = imessaging_client_init ( mem_ctx , lp_ctx , ev ) ;
if ( msg_ctx = = NULL ) {
DBG_ERR ( " Unable to create imessaging_context \n " ) ;
TALLOC_FREE ( mem_ctx ) ;
return ;
}
status = imessaging_process_cleanup ( msg_ctx , pid ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " imessaging_process_cleanup returned (%s) \n " ,
nt_errstr ( status ) ) ;
TALLOC_FREE ( mem_ctx ) ;
return ;
}
TALLOC_FREE ( mem_ctx ) ;
}
2017-09-07 05:30:15 +03:00
/*
2018-11-22 00:35:01 +03:00
* handle EOF on the parent - to - all - children pipe in the child , i . e .
* the parent has died and its end of the pipe has been closed .
* The child handles this by exiting as well .
*/
2017-09-07 05:30:15 +03:00
static void prefork_pipe_handler ( struct tevent_context * event_ctx ,
struct tevent_fd * fde , uint16_t flags ,
void * private_data )
{
2018-09-14 00:45:38 +03:00
struct loadparm_context * lp_ctx = NULL ;
pid_t pid ;
/*
* free the fde which removes the event and stops it firing again
*/
2017-09-07 05:30:15 +03:00
TALLOC_FREE ( fde ) ;
2018-09-14 00:45:38 +03:00
/*
* Clean up any irpc end points this process had .
*/
pid = getpid ( ) ;
lp_ctx = talloc_get_type_abort ( private_data , struct loadparm_context ) ;
irpc_cleanup ( lp_ctx , event_ctx , pid ) ;
2017-09-07 05:30:15 +03:00
DBG_NOTICE ( " Child %d exiting \n " , getpid ( ) ) ;
2018-09-14 00:45:38 +03:00
TALLOC_FREE ( event_ctx ) ;
2017-09-07 05:30:15 +03:00
exit ( 0 ) ;
}
2018-09-04 22:31:22 +03:00
/*
2018-11-22 00:35:01 +03:00
* Called by the top - level samba process to create a new prefork master process
2018-09-04 22:31:22 +03:00
*/
static void prefork_fork_master (
struct tevent_context * ev ,
struct loadparm_context * lp_ctx ,
const char * service_name ,
struct task_server * ( * new_task_fn ) ( struct tevent_context * ,
struct loadparm_context * lp_ctx ,
struct server_id ,
void * ,
void * ) ,
void * private_data ,
const struct service_details * service_details ,
unsigned restart_delay ,
int from_parent_fd )
{
pid_t pid ;
struct tfork * t = NULL ;
int i , num_children ;
struct tevent_context * ev2 ;
struct task_server * task = NULL ;
struct process_details pd = initial_process_details ;
2020-11-26 16:21:58 +03:00
struct samba_tevent_trace_state * samba_tevent_trace_state = NULL ;
2018-09-04 22:31:22 +03:00
int control_pipe [ 2 ] ;
t = tfork_create ( ) ;
if ( t = = NULL ) {
smb_panic ( " failure in tfork \n " ) ;
}
DBG_NOTICE ( " Forking [%s] pre-fork master process \n " , service_name ) ;
pid = tfork_child_pid ( t ) ;
if ( pid ! = 0 ) {
struct tevent_fd * fde = NULL ;
int fd = tfork_event_fd ( t ) ;
struct restart_context * rc = NULL ;
/* Register a pipe handler that gets called when the prefork
* master process terminates .
*/
rc = talloc_zero ( ev , struct restart_context ) ;
if ( rc = = NULL ) {
smb_panic ( " OOM allocating restart context \n " ) ;
}
rc - > t = t ;
rc - > lp_ctx = lp_ctx ;
rc - > service_name = service_name ;
rc - > service_details = service_details ;
rc - > from_parent_fd = from_parent_fd ;
rc - > restart_delay = restart_delay ;
rc - > master = talloc_zero ( rc , struct master_restart_context ) ;
if ( rc - > master = = NULL ) {
smb_panic ( " OOM allocating master restart context \n " ) ;
}
rc - > master - > new_task_fn = new_task_fn ;
rc - > master - > private_data = private_data ;
fde = tevent_add_fd (
ev , ev , fd , TEVENT_FD_READ , prefork_child_pipe_handler , rc ) ;
if ( fde = = NULL ) {
smb_panic ( " Failed to add child pipe handler, "
" after fork " ) ;
}
tevent_fd_set_auto_close ( fde ) ;
return ;
}
pid = getpid ( ) ;
2022-12-02 17:39:25 +03:00
process_set_title ( " %s[master] " , " task[%s] pre-fork master " , service_name ) ;
2018-09-04 22:31:22 +03:00
/*
* this will free all the listening sockets and all state that
* is not associated with this new connection
*/
if ( tevent_re_initialise ( ev ) ! = 0 ) {
smb_panic ( " Failed to re-initialise tevent after fork " ) ;
}
prefork_reload_after_fork ( ) ;
2018-09-14 00:45:38 +03:00
setup_handlers ( ev , lp_ctx , from_parent_fd ) ;
2018-09-04 22:31:22 +03:00
if ( service_details - > inhibit_pre_fork ) {
task = new_task_fn (
ev , lp_ctx , cluster_id ( pid , 0 ) , private_data , NULL ) ;
/*
* The task does not support pre - fork
*/
if ( task ! = NULL & & service_details - > post_fork ! = NULL ) {
service_details - > post_fork ( task , & pd ) ;
}
2023-03-01 12:48:18 +03:00
if ( task ! = NULL & & service_details - > before_loop ! = NULL ) {
service_details - > before_loop ( task ) ;
}
2018-09-04 22:31:22 +03:00
tevent_loop_wait ( ev ) ;
TALLOC_FREE ( ev ) ;
exit ( 0 ) ;
}
/*
* This is now the child code . We need a completely new event_context
* to work with
*/
ev2 = s4_event_context_init ( NULL ) ;
2020-11-26 16:21:58 +03:00
samba_tevent_trace_state = create_samba_tevent_trace_state ( ev2 ) ;
if ( samba_tevent_trace_state = = NULL ) {
TALLOC_FREE ( ev ) ;
TALLOC_FREE ( ev2 ) ;
exit ( 127 ) ;
}
tevent_set_trace_callback ( ev2 ,
samba_tevent_trace_callback ,
samba_tevent_trace_state ) ;
2018-09-04 22:31:22 +03:00
/* setup this new connection: process will bind to it's sockets etc
*
* While we can use ev for the child , which has been re - initialised
* above we must run the new task under ev2 otherwise the children would
* be listening on the sockets . Also we don ' t want the top level
* process accepting and handling requests , it ' s responsible for
* monitoring and controlling the child work processes .
*/
task = new_task_fn ( ev2 , lp_ctx , cluster_id ( pid , 0 ) , private_data , NULL ) ;
if ( task = = NULL ) {
TALLOC_FREE ( ev ) ;
TALLOC_FREE ( ev2 ) ;
2018-09-14 00:45:38 +03:00
exit ( 127 ) ;
}
/*
* Register an irpc name that can be used by the samba - tool processes
* command
*/
{
struct talloc_ctx * ctx = talloc_new ( NULL ) ;
char * name = NULL ;
if ( ctx = = NULL ) {
2023-08-07 07:54:26 +03:00
DBG_ERR ( " Out of memory \n " ) ;
2018-09-14 00:45:38 +03:00
exit ( 127 ) ;
}
name = talloc_asprintf ( ctx , " prefork-master-%s " , service_name ) ;
irpc_add_name ( task - > msg_ctx , name ) ;
TALLOC_FREE ( ctx ) ;
2018-09-04 22:31:22 +03:00
}
{
int default_children ;
default_children = lpcfg_prefork_children ( lp_ctx ) ;
num_children = lpcfg_parm_int ( lp_ctx , NULL , " prefork children " ,
service_name , default_children ) ;
}
if ( num_children = = 0 ) {
DBG_WARNING ( " Number of pre-fork children for %s is zero, "
" NO worker processes will be started for %s \n " ,
service_name , service_name ) ;
}
DBG_NOTICE ( " Forking %d %s worker processes \n " ,
num_children , service_name ) ;
2018-11-22 00:35:01 +03:00
/*
* the prefork master creates its own control pipe , so the prefork
* workers can detect if the master exits ( in which case an EOF gets
* written ) . ( Whereas from_parent_fd is the control pipe from the
* top - level process that the prefork master listens on )
*/
2018-09-04 22:31:22 +03:00
{
int ret ;
ret = pipe ( control_pipe ) ;
if ( ret ! = 0 ) {
smb_panic ( " Unable to create worker control pipe \n " ) ;
}
smb_set_close_on_exec ( control_pipe [ 0 ] ) ;
smb_set_close_on_exec ( control_pipe [ 1 ] ) ;
}
2023-03-01 12:48:18 +03:00
/*
* Note , we call this before the first
* prefork_fork_worker ( ) in order to have
* a stable order of :
* task_init ( master ) - > before_loop ( master )
* - > post_fork ( worker ) - > before_loop ( worker )
*
* Otherwise we would have different behaviors
* between the first prefork_fork_worker ( ) loop
* and restarting of died workers
*/
if ( task ! = NULL & & service_details - > before_loop ! = NULL ) {
2023-03-01 13:02:00 +03:00
struct task_server * task_copy = NULL ;
/*
* We need to use ev as parent in order to
* keep everything alive during the loop
*/
task_copy = talloc ( ev , struct task_server ) ;
if ( task_copy = = NULL ) {
TALLOC_FREE ( ev ) ;
TALLOC_FREE ( ev2 ) ;
exit ( 127 ) ;
}
* task_copy = * task ;
/*
* In order to allow the before_loop ( ) hook
* to register messages or event handlers ,
* we need to fix up task - > event_ctx
* and create a new task - > msg_ctx
*/
task_copy - > event_ctx = ev ;
task_copy - > msg_ctx = imessaging_init ( task_copy ,
task_copy - > lp_ctx ,
task_copy - > server_id ,
task_copy - > event_ctx ) ;
if ( task_copy - > msg_ctx = = NULL ) {
TALLOC_FREE ( ev ) ;
TALLOC_FREE ( ev2 ) ;
exit ( 127 ) ;
}
service_details - > before_loop ( task_copy ) ;
2023-03-01 12:48:18 +03:00
}
2018-09-04 22:31:22 +03:00
/*
* We are now free to spawn some worker processes
*/
for ( i = 0 ; i < num_children ; i + + ) {
prefork_fork_worker ( task ,
ev ,
ev2 ,
lp_ctx ,
service_details ,
service_name ,
control_pipe ,
0 ,
& pd ) ;
pd . instances + + ;
}
2023-03-01 12:47:09 +03:00
/*
* Make sure the messaging context
* used by the workers is no longer
* active on ev2 , otherwise we
* would have memory leaks , because
* we queue incoming messages
* and never process them via ev2 .
*/
imessaging_dgm_unref_ev ( ev2 ) ;
2018-09-04 22:31:22 +03:00
/* Don't listen on the sockets we just gave to the children */
tevent_loop_wait ( ev ) ;
2023-03-01 12:47:09 +03:00
imessaging_dgm_unref_ev ( ev ) ;
2018-09-04 22:31:22 +03:00
TALLOC_FREE ( ev ) ;
/* We need to keep ev2 until we're finished for the messaging to work */
TALLOC_FREE ( ev2 ) ;
exit ( 0 ) ;
}
2018-11-22 00:35:01 +03:00
2021-09-30 06:49:48 +03:00
static void prefork_restart_fn ( struct tevent_context * ev ,
struct tevent_timer * te ,
struct timeval tv ,
void * private_data ) ;
2018-11-22 00:35:01 +03:00
/*
* Restarts a child process if it exits unexpectedly
*/
2021-09-30 06:49:48 +03:00
static bool prefork_restart ( struct tevent_context * ev ,
2018-09-04 03:12:49 +03:00
struct restart_context * rc )
2021-09-30 06:49:48 +03:00
{
struct tevent_timer * te = NULL ;
if ( rc - > restart_delay > 0 ) {
DBG_ERR ( " Restarting [%s] pre-fork %s in (%d) seconds \n " ,
rc - > service_name ,
( rc - > master = = NULL ) ? " worker " : " master " ,
rc - > restart_delay ) ;
}
/*
* Always use an async timer event . If
* rc - > restart_delay is zero this is the
* same as an immediate event and will be
* called immediately we go back into the
* event loop .
*/
te = tevent_add_timer ( ev ,
ev ,
tevent_timeval_current_ofs ( rc - > restart_delay , 0 ) ,
prefork_restart_fn ,
rc ) ;
if ( te = = NULL ) {
DBG_ERR ( " tevent_add_timer fail [%s] pre-fork event %s \n " ,
rc - > service_name ,
( rc - > master = = NULL ) ? " worker " : " master " ) ;
/* Caller needs to free rc. */
return false ;
}
/* Caller must not free rc - it's in use. */
return true ;
}
static void prefork_restart_fn ( struct tevent_context * ev ,
struct tevent_timer * te ,
struct timeval tv ,
void * private_data )
2018-09-04 03:12:49 +03:00
{
2018-09-04 22:31:22 +03:00
unsigned max_backoff = 0 ;
unsigned backoff = 0 ;
unsigned default_value = 0 ;
2021-09-30 06:49:48 +03:00
struct restart_context * rc = talloc_get_type ( private_data ,
struct restart_context ) ;
unsigned restart_delay = rc - > restart_delay ;
TALLOC_FREE ( te ) ;
2018-09-04 22:31:22 +03:00
2018-11-22 00:35:01 +03:00
/*
* If the child process is constantly exiting , then restarting it can
* consume a lot of resources . In which case , we want to backoff a bit
* before respawning it
*/
2018-09-04 22:31:22 +03:00
default_value = lpcfg_prefork_backoff_increment ( rc - > lp_ctx ) ;
backoff = lpcfg_parm_int ( rc - > lp_ctx ,
NULL ,
" prefork backoff increment " ,
rc - > service_name ,
default_value ) ;
default_value = lpcfg_prefork_maximum_backoff ( rc - > lp_ctx ) ;
max_backoff = lpcfg_parm_int ( rc - > lp_ctx ,
NULL ,
" prefork maximum backoff " ,
rc - > service_name ,
default_value ) ;
restart_delay + = backoff ;
restart_delay = min ( restart_delay , max_backoff ) ;
2018-09-04 03:12:49 +03:00
if ( rc - > master ! = NULL ) {
DBG_ERR ( " Restarting [%s] pre-fork master \n " , rc - > service_name ) ;
2018-09-04 22:31:22 +03:00
prefork_fork_master ( ev ,
rc - > lp_ctx ,
rc - > service_name ,
rc - > master - > new_task_fn ,
rc - > master - > private_data ,
rc - > service_details ,
restart_delay ,
rc - > from_parent_fd ) ;
2018-09-04 03:12:49 +03:00
} else if ( rc - > worker ! = NULL ) {
struct process_details pd = initial_process_details ;
DBG_ERR ( " Restarting [%s] pre-fork worker(%d) \n " ,
rc - > service_name ,
rc - > worker - > instance ) ;
pd . instances = rc - > worker - > instance ;
prefork_fork_worker ( rc - > worker - > task ,
ev ,
rc - > worker - > ev2 ,
2018-09-04 22:31:22 +03:00
rc - > lp_ctx ,
2018-09-04 03:12:49 +03:00
rc - > service_details ,
rc - > service_name ,
rc - > worker - > control_pipe ,
2018-09-04 22:31:22 +03:00
restart_delay ,
2018-09-04 03:12:49 +03:00
& pd ) ;
}
2021-09-30 06:49:48 +03:00
/* tfork allocates tfork structures with malloc */
tfork_destroy ( & rc - > t ) ;
free ( rc - > t ) ;
TALLOC_FREE ( rc ) ;
2018-09-04 03:12:49 +03:00
}
2018-11-22 00:35:01 +03:00
2017-09-07 05:30:15 +03:00
/*
handle EOF on the child pipe in the parent , so we know when a
process terminates without using SIGCHLD or waiting on all possible pids .
We need to ensure we do not ignore SIGCHLD because we need it to
work to get a valid error code from samba_runcmd_ * ( ) .
*/
static void prefork_child_pipe_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags ,
void * private_data )
{
2018-08-31 02:40:18 +03:00
struct restart_context * rc = NULL ;
2017-09-07 05:30:15 +03:00
int status = 0 ;
pid_t pid = 0 ;
2021-09-30 06:49:48 +03:00
bool rc_inuse = false ;
2017-09-07 05:30:15 +03:00
/* free the fde which removes the event and stops it firing again */
TALLOC_FREE ( fde ) ;
/* the child has closed the pipe, assume its dead */
2018-08-31 02:40:18 +03:00
rc = talloc_get_type_abort ( private_data , struct restart_context ) ;
pid = tfork_child_pid ( rc - > t ) ;
2017-09-07 05:30:15 +03:00
errno = 0 ;
2018-09-14 00:45:38 +03:00
irpc_cleanup ( rc - > lp_ctx , ev , pid ) ;
2018-08-31 02:40:18 +03:00
status = tfork_status ( & rc - > t , false ) ;
2017-09-07 05:30:15 +03:00
if ( status = = - 1 ) {
DBG_ERR ( " Parent %d, Child %d terminated, "
" unable to get status code from tfork \n " ,
getpid ( ) , pid ) ;
2021-09-30 06:49:48 +03:00
rc_inuse = prefork_restart ( ev , rc ) ;
2017-09-07 05:30:15 +03:00
} else if ( WIFEXITED ( status ) ) {
status = WEXITSTATUS ( status ) ;
DBG_ERR ( " Parent %d, Child %d exited with status %d \n " ,
getpid ( ) , pid , status ) ;
2018-09-04 03:12:49 +03:00
if ( status ! = 0 ) {
2021-09-30 06:49:48 +03:00
rc_inuse = prefork_restart ( ev , rc ) ;
2018-09-04 03:12:49 +03:00
}
2017-09-07 05:30:15 +03:00
} else if ( WIFSIGNALED ( status ) ) {
status = WTERMSIG ( status ) ;
DBG_ERR ( " Parent %d, Child %d terminated with signal %d \n " ,
getpid ( ) , pid , status ) ;
2018-09-03 00:34:17 +03:00
if ( status = = SIGABRT | | status = = SIGBUS | | status = = SIGFPE | |
2019-12-19 01:20:30 +03:00
status = = SIGILL | | status = = SIGSYS | | status = = SIGSEGV | |
status = = SIGKILL ) {
2018-09-04 03:12:49 +03:00
2021-09-30 06:49:48 +03:00
rc_inuse = prefork_restart ( ev , rc ) ;
2018-09-03 00:34:17 +03:00
}
2017-09-07 05:30:15 +03:00
}
2021-09-30 06:49:48 +03:00
if ( ! rc_inuse ) {
/* tfork allocates tfork structures with malloc */
tfork_destroy ( & rc - > t ) ;
free ( rc - > t ) ;
TALLOC_FREE ( rc ) ;
}
2017-09-07 05:30:15 +03:00
return ;
}
/*
called when a listening socket becomes readable .
*/
static void prefork_accept_connection (
struct tevent_context * ev ,
struct loadparm_context * lp_ctx ,
struct socket_context * listen_socket ,
void ( * new_conn ) ( struct tevent_context * ,
struct loadparm_context * ,
struct socket_context * ,
struct server_id ,
void * ,
void * ) ,
void * private_data ,
void * process_context )
{
NTSTATUS status ;
struct socket_context * connected_socket ;
pid_t pid = getpid ( ) ;
/* accept an incoming connection. */
status = socket_accept ( listen_socket , & connected_socket ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
/*
* For prefork we can ignore STATUS_MORE_ENTRIES , as once a
* connection becomes available all waiting processes are
* woken , but only one gets work to process .
* AKA the thundering herd .
* In the short term this should not be an issue as the number
* of workers should be a small multiple of the number of cpus
* In the longer term socket_accept needs to implement a
* mutex / semaphore ( like apache does ) to serialise the accepts
*/
if ( ! NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
DBG_ERR ( " Worker process (%d), error in accept [%s] \n " ,
getpid ( ) , nt_errstr ( status ) ) ;
}
return ;
}
talloc_steal ( private_data , connected_socket ) ;
new_conn ( ev , lp_ctx , connected_socket ,
cluster_id ( pid , socket_get_fd ( connected_socket ) ) ,
private_data , process_context ) ;
}
2018-09-14 00:45:38 +03:00
static void setup_handlers (
struct tevent_context * ev ,
struct loadparm_context * lp_ctx ,
int from_parent_fd )
{
2017-09-07 05:30:15 +03:00
struct tevent_fd * fde = NULL ;
struct tevent_signal * se = NULL ;
fde = tevent_add_fd ( ev , ev , from_parent_fd , TEVENT_FD_READ ,
2018-09-14 00:45:38 +03:00
prefork_pipe_handler , lp_ctx ) ;
2017-09-07 05:30:15 +03:00
if ( fde = = NULL ) {
smb_panic ( " Failed to add fd handler after fork " ) ;
}
se = tevent_add_signal ( ev ,
ev ,
SIGHUP ,
0 ,
sighup_signal_handler ,
NULL ) ;
if ( se = = NULL ) {
smb_panic ( " Failed to add SIGHUP handler after fork " ) ;
}
se = tevent_add_signal ( ev ,
ev ,
SIGTERM ,
0 ,
sigterm_signal_handler ,
NULL ) ;
if ( se = = NULL ) {
smb_panic ( " Failed to add SIGTERM handler after fork " ) ;
}
}
2018-11-22 00:35:01 +03:00
/*
* Called by the prefork master to create a new prefork worker process
*/
2018-08-31 02:40:18 +03:00
static void prefork_fork_worker ( struct task_server * task ,
struct tevent_context * ev ,
struct tevent_context * ev2 ,
2018-09-04 22:31:22 +03:00
struct loadparm_context * lp_ctx ,
2018-08-31 02:40:18 +03:00
const struct service_details * service_details ,
const char * service_name ,
2018-09-03 00:34:17 +03:00
int control_pipe [ 2 ] ,
2018-09-04 22:31:22 +03:00
unsigned restart_delay ,
2018-08-31 02:40:18 +03:00
struct process_details * pd )
{
struct tfork * w = NULL ;
pid_t pid ;
w = tfork_create ( ) ;
if ( w = = NULL ) {
smb_panic ( " failure in tfork \n " ) ;
}
pid = tfork_child_pid ( w ) ;
if ( pid ! = 0 ) {
struct tevent_fd * fde = NULL ;
int fd = tfork_event_fd ( w ) ;
struct restart_context * rc = NULL ;
2018-11-22 00:35:01 +03:00
/*
* we ' re the parent ( prefork master ) , so store enough info to
* restart the worker / child if it exits unexpectedly
*/
2018-08-31 02:40:18 +03:00
rc = talloc_zero ( ev , struct restart_context ) ;
if ( rc = = NULL ) {
smb_panic ( " OOM allocating restart context \n " ) ;
}
rc - > t = w ;
2018-09-04 22:31:22 +03:00
rc - > lp_ctx = lp_ctx ;
2018-08-31 02:40:18 +03:00
rc - > service_name = service_name ;
rc - > service_details = service_details ;
2018-09-04 22:31:22 +03:00
rc - > restart_delay = restart_delay ;
2018-08-31 02:40:18 +03:00
rc - > master = NULL ;
rc - > worker = talloc_zero ( rc , struct worker_restart_context ) ;
if ( rc - > worker = = NULL ) {
smb_panic ( " OOM allocating master restart context \n " ) ;
}
rc - > worker - > ev2 = ev2 ;
rc - > worker - > instance = pd - > instances ;
rc - > worker - > task = task ;
2018-09-03 00:34:17 +03:00
rc - > worker - > control_pipe [ 0 ] = control_pipe [ 0 ] ;
rc - > worker - > control_pipe [ 1 ] = control_pipe [ 1 ] ;
2018-08-31 02:40:18 +03:00
fde = tevent_add_fd (
ev , ev , fd , TEVENT_FD_READ , prefork_child_pipe_handler , rc ) ;
if ( fde = = NULL ) {
smb_panic ( " Failed to add child pipe handler, "
" after fork " ) ;
}
tevent_fd_set_auto_close ( fde ) ;
} else {
2018-11-22 00:35:01 +03:00
/*
* we ' re the child ( prefork - worker ) . We never write to the
* control pipe , but listen on the read end in case our parent
* ( the pre - fork master ) exits
*/
2018-09-03 00:34:17 +03:00
close ( control_pipe [ 1 ] ) ;
2018-09-14 00:45:38 +03:00
setup_handlers ( ev2 , lp_ctx , control_pipe [ 0 ] ) ;
2018-11-22 00:35:01 +03:00
2018-08-31 02:40:18 +03:00
/*
* tfork uses malloc
*/
free ( w ) ;
2023-03-01 12:47:09 +03:00
imessaging_dgm_unref_ev ( ev ) ;
2018-08-31 02:40:18 +03:00
TALLOC_FREE ( ev ) ;
2022-12-02 17:39:25 +03:00
process_set_title ( " %s(%d) " ,
" task[%s] pre-forked worker(%d) " ,
2020-02-21 23:07:19 +03:00
service_name ,
pd - > instances ) ;
2022-12-02 17:39:25 +03:00
2018-08-31 02:40:18 +03:00
prefork_reload_after_fork ( ) ;
if ( service_details - > post_fork ! = NULL ) {
service_details - > post_fork ( task , pd ) ;
}
2018-09-14 00:45:38 +03:00
{
struct talloc_ctx * ctx = talloc_new ( NULL ) ;
char * name = NULL ;
if ( ctx = = NULL ) {
smb_panic ( " OOM allocating talloc context \n " ) ;
}
name = talloc_asprintf ( ctx ,
" prefork-worker-%s-%d " ,
service_name ,
pd - > instances ) ;
irpc_add_name ( task - > msg_ctx , name ) ;
TALLOC_FREE ( ctx ) ;
}
2023-03-01 12:48:18 +03:00
if ( service_details - > before_loop ! = NULL ) {
service_details - > before_loop ( task ) ;
}
2018-08-31 02:40:18 +03:00
tevent_loop_wait ( ev2 ) ;
2023-03-01 12:47:09 +03:00
imessaging_dgm_unref_ev ( ev2 ) ;
2018-08-31 02:40:18 +03:00
talloc_free ( ev2 ) ;
exit ( 0 ) ;
}
}
2017-09-07 05:30:15 +03:00
/*
* called to create a new server task
*/
static void prefork_new_task (
struct tevent_context * ev ,
struct loadparm_context * lp_ctx ,
const char * service_name ,
2018-08-23 00:35:52 +03:00
struct task_server * ( * new_task_fn ) ( struct tevent_context * ,
2017-09-07 05:30:15 +03:00
struct loadparm_context * lp_ctx ,
struct server_id , void * , void * ) ,
void * private_data ,
const struct service_details * service_details ,
int from_parent_fd )
{
2018-09-04 22:31:22 +03:00
prefork_fork_master ( ev ,
lp_ctx ,
service_name ,
new_task_fn ,
private_data ,
service_details ,
0 ,
from_parent_fd ) ;
2017-09-07 05:30:15 +03:00
}
2018-09-04 03:12:49 +03:00
/*
* called when a task terminates
*/
static void prefork_terminate_task ( struct tevent_context * ev ,
struct loadparm_context * lp_ctx ,
const char * reason ,
bool fatal ,
void * process_context )
2017-09-07 05:30:15 +03:00
{
DBG_DEBUG ( " called with reason[%s] \n " , reason ) ;
2018-09-14 00:45:38 +03:00
TALLOC_FREE ( ev ) ;
2018-09-04 03:12:49 +03:00
if ( fatal = = true ) {
exit ( 127 ) ;
} else {
exit ( 0 ) ;
}
}
/*
* called when a connection completes
*/
static void prefork_terminate_connection ( struct tevent_context * ev ,
struct loadparm_context * lp_ctx ,
const char * reason ,
void * process_context )
{
2017-09-07 05:30:15 +03:00
}
/* called to set a title of a task or connection */
static void prefork_set_title ( struct tevent_context * ev , const char * title )
{
}
static const struct model_ops prefork_ops = {
. name = " prefork " ,
. model_init = prefork_model_init ,
. accept_connection = prefork_accept_connection ,
. new_task = prefork_new_task ,
2018-09-04 03:12:49 +03:00
. terminate_task = prefork_terminate_task ,
. terminate_connection = prefork_terminate_connection ,
2017-09-07 05:30:15 +03:00
. set_title = prefork_set_title ,
} ;
/*
* initialise the prefork process model , registering ourselves with the
* process model subsystem
*/
NTSTATUS process_model_prefork_init ( void )
{
return register_process_model ( & prefork_ops ) ;
}