2008-02-04 09:59:16 +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/>.
*/
# include "includes.h"
# include "lib/events/events.h"
2008-09-16 17:16:31 +04:00
# include "../tdb/include/tdb.h"
2008-02-04 09:59:16 +03:00
# include "lib/socket/socket.h"
# include "smbd/process_model.h"
# include "param/secrets.h"
# include "system/filesys.h"
# include "cluster/cluster.h"
# include "param/param.h"
# ifdef HAVE_SETPROCTITLE
# ifdef HAVE_SETPROCTITLE_H
# include <setproctitle.h>
# endif
# else
# define setproctitle none_setproctitle
static int none_setproctitle ( const char * fmt , . . . ) PRINTF_ATTRIBUTE ( 1 , 2 ) ;
static int none_setproctitle ( const char * fmt , . . . )
{
return 0 ;
}
# endif
/*
called when the process model is selected
*/
static void prefork_model_init ( struct event_context * ev )
{
signal ( SIGCHLD , SIG_IGN ) ;
}
2008-02-05 11:16:57 +03:00
static void prefork_reload_after_fork ( void )
{
/* tdb needs special fork handling */
if ( tdb_reopen_all ( 1 ) = = - 1 ) {
DEBUG ( 0 , ( " prefork_reload_after_fork: tdb_reopen_all failed. \n " ) ) ;
}
/* Ensure that the forked children do not expose identical random streams */
set_need_random_reseed ( ) ;
}
2008-02-04 09:59:16 +03:00
/*
called when a listening socket becomes readable .
*/
static void prefork_accept_connection ( struct event_context * ev ,
2008-02-05 06:51:01 +03:00
struct loadparm_context * lp_ctx ,
struct socket_context * listen_socket ,
2008-02-04 09:59:16 +03:00
void ( * new_conn ) ( struct event_context * ,
struct loadparm_context * , struct socket_context * ,
struct server_id , void * ) ,
void * private )
{
NTSTATUS status ;
2008-02-05 06:51:01 +03:00
struct socket_context * connected_socket ;
2008-02-04 09:59:16 +03:00
pid_t pid = getpid ( ) ;
/* accept an incoming connection. */
2008-02-05 06:51:01 +03:00
status = socket_accept ( listen_socket , & connected_socket ) ;
2008-02-04 09:59:16 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return ;
}
2008-02-05 06:51:01 +03:00
talloc_steal ( private , connected_socket ) ;
2008-02-04 09:59:16 +03:00
2008-02-05 06:51:01 +03:00
new_conn ( ev , lp_ctx , connected_socket , cluster_id ( pid , socket_get_fd ( connected_socket ) ) , private ) ;
2008-02-04 09:59:16 +03:00
}
/*
called to create a new server task
*/
static void prefork_new_task ( struct event_context * ev ,
struct loadparm_context * lp_ctx ,
const char * service_name ,
void ( * new_task_fn ) ( struct event_context * , struct loadparm_context * lp_ctx , struct server_id , void * ) ,
void * private )
{
pid_t pid ;
int i , num_children ;
struct event_context * ev2 , * ev_parent ;
pid = fork ( ) ;
if ( pid ! = 0 ) {
/* parent or error code ... go back to the event loop */
return ;
}
pid = getpid ( ) ;
/* This is now the child code. We need a completely new event_context to work with */
2008-06-14 21:00:53 +04:00
ev2 = s4_event_context_init ( NULL ) ;
2008-02-04 09:59:16 +03:00
/* the service has given us a private pointer that
encapsulates the context it needs for this new connection -
everything else will be freed */
talloc_steal ( ev2 , private ) ;
/* this will free all the listening sockets and all state that
is not associated with this new connection */
talloc_free ( ev ) ;
setproctitle ( " task %s server_id[%d] " , service_name , pid ) ;
2008-02-05 11:16:57 +03:00
prefork_reload_after_fork ( ) ;
2008-02-04 09:59:16 +03:00
/* setup this new connection: process will bind to it's sockets etc */
new_task_fn ( ev2 , lp_ctx , cluster_id ( pid , 0 ) , private ) ;
num_children = lp_parm_int ( lp_ctx , NULL , " prefork children " , service_name , 0 ) ;
if ( num_children = = 0 ) {
/* We don't want any kids hanging around for this one,
* let the parent do all the work */
event_loop_wait ( ev2 ) ;
talloc_free ( ev2 ) ;
exit ( 0 ) ;
}
/* We are now free to spawn some child proccesses */
for ( i = 0 ; i < num_children ; i + + ) {
pid = fork ( ) ;
if ( pid > 0 ) {
continue ;
} else if ( pid = = - 1 ) {
return ;
} else {
pid = getpid ( ) ;
setproctitle ( " task %s server_id[%d] " , service_name , pid ) ;
2008-02-05 11:16:57 +03:00
prefork_reload_after_fork ( ) ;
2008-02-04 09:59:16 +03:00
/* we can't return to the top level here, as that event context is gone,
so we now process events in the new event context until there are no
more to process */
event_loop_wait ( ev2 ) ;
talloc_free ( ev2 ) ;
exit ( 0 ) ;
}
}
/* Don't listen on the sockets we just gave to the children */
talloc_free ( ev2 ) ;
/* But we need a events system to handle reaping children */
2008-06-14 21:00:53 +04:00
ev_parent = s4_event_context_init ( NULL ) ;
2008-02-04 09:59:16 +03:00
/* TODO: Handle some events... */
/* we can't return to the top level here, as that event context is gone,
so we now process events in the new event context until there are no
more to process */
event_loop_wait ( ev_parent ) ;
talloc_free ( ev_parent ) ;
exit ( 0 ) ;
}
/* called when a task goes down */
2008-09-30 05:20:46 +04:00
_NORETURN_ static void prefork_terminate ( struct event_context * ev , struct loadparm_context * lp_ctx , const char * reason )
2008-02-04 09:59:16 +03:00
{
DEBUG ( 2 , ( " prefork_terminate: reason[%s] \n " , reason ) ) ;
}
/* called to set a title of a task or connection */
static void prefork_set_title ( struct event_context * ev , const char * title )
{
if ( title ) {
setproctitle ( " %s " , title ) ;
} else {
setproctitle ( NULL ) ;
}
}
static const struct model_ops prefork_ops = {
. name = " prefork " ,
. model_init = prefork_model_init ,
. accept_connection = prefork_accept_connection ,
. new_task = prefork_new_task ,
. terminate = prefork_terminate ,
. 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 ) ;
}