2003-08-13 05:53:07 +04:00
/*
Unix SMB / CIFS implementation .
thread model : standard ( 1 thread per client connection )
Copyright ( C ) Andrew Tridgell 2003
Copyright ( C ) James J Myers 2003 < myersjj @ samba . org >
2004-07-14 01:04:56 +04:00
Copyright ( C ) Stefan ( metze ) Metzmacher 2004
2003-08-13 05:53:07 +04:00
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 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2004-11-02 10:29:10 +03:00
# include "dlinklist.h"
# include "smb_server/smb_server.h"
# include "process_model.h"
2003-08-13 05:53:07 +04:00
# include "pthread.h"
2003-12-04 14:01:58 +03:00
# ifdef HAVE_BACKTRACE
2003-08-13 05:53:07 +04:00
# include "execinfo.h"
2003-12-04 14:01:58 +03:00
# endif
2003-08-13 05:53:07 +04:00
2004-07-14 01:04:56 +04:00
static void * thread_connection_fn ( void * thread_parm )
2003-08-13 05:53:07 +04:00
{
struct event_context * ev = thread_parm ;
/* wait for action */
event_loop_wait ( ev ) ;
2004-07-14 01:04:56 +04:00
2003-08-13 05:53:07 +04:00
#if 0
pthread_cleanup_pop ( 1 ) ; /* will invoke terminate_mt_connection() */
# endif
return NULL ;
}
2004-07-14 01:04:56 +04:00
static int thread_get_id ( struct smbsrv_request * req )
2003-08-13 05:53:07 +04:00
{
return ( int ) pthread_self ( ) ;
}
/*
called when a listening socket becomes readable
*/
2004-07-14 01:04:56 +04:00
static void thread_accept_connection ( struct event_context * ev , struct fd_event * srv_fde ,
2004-05-25 21:24:24 +04:00
time_t t , uint16_t flags )
2004-07-14 01:04:56 +04:00
{
2004-09-20 16:31:07 +04:00
NTSTATUS status ;
struct socket_context * sock ;
int rc ;
2003-08-13 05:53:07 +04:00
pthread_t thread_id ;
pthread_attr_t thread_attr ;
2004-07-14 01:04:56 +04:00
struct server_socket * server_socket = srv_fde - > private ;
struct server_connection * conn ;
/* accept an incoming connection. */
2004-10-28 11:55:33 +04:00
status = socket_accept ( server_socket - > socket , & sock ) ;
2004-09-20 16:31:07 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " accept_connection_single: accept: %s \n " ,
nt_errstr ( status ) ) ) ;
2003-08-13 05:53:07 +04:00
return ;
}
/* create new detached thread for this connection. The new
thread gets a new event_context with a single fd_event for
receiving from the new socket . We set that thread running
with the main event loop , then return . When we return the
main event_context is continued .
*/
2004-07-14 01:04:56 +04:00
2004-10-17 14:04:49 +04:00
ev = event_context_init ( server_socket ) ;
2004-07-14 01:04:56 +04:00
if ( ! ev ) {
DEBUG ( 0 , ( " thread_accept_connection: failed to create event_context! \n " ) ) ;
2004-09-20 16:31:07 +04:00
socket_destroy ( sock ) ;
2004-07-14 01:04:56 +04:00
return ;
2003-12-13 13:58:48 +03:00
}
2004-10-17 06:55:47 +04:00
conn = server_setup_connection ( ev , server_socket , sock , t , pthread_self ( ) ) ;
2004-07-14 01:04:56 +04:00
if ( ! conn ) {
2004-09-20 16:31:07 +04:00
DEBUG ( 0 , ( " server_setup_connection(ev, server_socket, sock, t) failed \n " ) ) ;
event_context_destroy ( ev ) ;
socket_destroy ( sock ) ;
2004-07-14 01:04:56 +04:00
return ;
}
2004-10-17 14:04:49 +04:00
talloc_steal ( conn , ev ) ;
2004-09-26 07:05:04 +04:00
talloc_steal ( conn , sock ) ;
2004-07-14 01:04:56 +04:00
/* TODO: is this MUTEX_LOCK in the right place here?
* - - metze
*/
2003-12-13 13:58:48 +03:00
MUTEX_LOCK_BY_ID ( MUTEX_SMBD ) ;
2004-07-14 01:04:56 +04:00
DLIST_ADD ( server_socket - > connection_list , conn ) ;
2003-08-13 05:53:07 +04:00
MUTEX_UNLOCK_BY_ID ( MUTEX_SMBD ) ;
pthread_attr_init ( & thread_attr ) ;
pthread_attr_setdetachstate ( & thread_attr , PTHREAD_CREATE_DETACHED ) ;
2004-07-14 01:04:56 +04:00
rc = pthread_create ( & thread_id , & thread_attr , thread_connection_fn , ev ) ;
2003-08-13 05:53:07 +04:00
pthread_attr_destroy ( & thread_attr ) ;
if ( rc = = 0 ) {
DEBUG ( 4 , ( " accept_connection_thread: created thread_id=%lu for fd=%d \n " ,
2004-09-20 16:31:07 +04:00
( unsigned long int ) thread_id , socket_get_fd ( sock ) ) ) ;
2003-08-13 05:53:07 +04:00
} else {
2004-09-20 16:31:07 +04:00
DEBUG ( 0 , ( " accept_connection_thread: thread create failed for fd=%d, rc=%d \n " , socket_get_fd ( sock ) , rc ) ) ;
event_context_destroy ( ev ) ;
socket_destroy ( sock ) ;
return ;
2003-08-13 05:53:07 +04:00
}
}
/* called when a SMB connection goes down */
2004-07-14 01:04:56 +04:00
static void thread_terminate_connection ( struct server_connection * conn , const char * reason )
2003-08-13 05:53:07 +04:00
{
2004-07-14 01:04:56 +04:00
DEBUG ( 0 , ( " thread_terminate_connection: reason[%s] \n " , reason ) ) ;
2004-07-15 13:43:32 +04:00
if ( conn ) {
2004-09-26 07:50:24 +04:00
talloc_free ( conn ) ;
2004-07-15 13:43:32 +04:00
}
2003-12-14 02:25:15 +03:00
/* terminate this thread */
pthread_exit ( NULL ) ; /* thread cleanup routine will do actual cleanup */
}
2003-08-13 05:53:07 +04:00
/*
mutex init function for thread model
*/
2003-08-15 21:13:41 +04:00
static int thread_mutex_init ( smb_mutex_t * mutex , const char * name )
2003-08-13 05:53:07 +04:00
{
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER ;
mutex - > mutex = memdup ( & m , sizeof ( m ) ) ;
if ( ! mutex - > mutex ) {
errno = ENOMEM ;
return - 1 ;
}
return pthread_mutex_init ( ( pthread_mutex_t * ) mutex - > mutex , NULL ) ;
}
/*
mutex destroy function for thread model
*/
2003-08-15 21:13:41 +04:00
static int thread_mutex_destroy ( smb_mutex_t * mutex , const char * name )
2003-08-13 05:53:07 +04:00
{
return pthread_mutex_destroy ( ( pthread_mutex_t * ) mutex - > mutex ) ;
}
static void mutex_start_timer ( struct timeval * tp1 )
{
gettimeofday ( tp1 , NULL ) ;
}
static double mutex_end_timer ( struct timeval tp1 )
{
struct timeval tp2 ;
gettimeofday ( & tp2 , NULL ) ;
return ( ( tp2 . tv_sec - tp1 . tv_sec ) +
( tp2 . tv_usec - tp1 . tv_usec ) * 1.0e-6 ) ;
}
/*
mutex lock function for thread model
*/
2003-08-15 21:13:41 +04:00
static int thread_mutex_lock ( smb_mutex_t * mutexP , const char * name )
2003-08-13 05:53:07 +04:00
{
pthread_mutex_t * mutex = ( pthread_mutex_t * ) mutexP - > mutex ;
int rc ;
double t ;
struct timeval tp1 ;
/* Test below is ONLY for debugging */
if ( ( rc = pthread_mutex_trylock ( mutex ) ) ) {
if ( rc = = EBUSY ) {
mutex_start_timer ( & tp1 ) ;
printf ( " mutex lock: thread %d, lock %s not available \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name ) ;
2003-08-13 05:53:07 +04:00
print_suspicious_usage ( " mutex_lock " , name ) ;
pthread_mutex_lock ( mutex ) ;
t = mutex_end_timer ( tp1 ) ;
printf ( " mutex lock: thread %d, lock %s now available, waited %g seconds \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name , t ) ;
2003-08-13 05:53:07 +04:00
return 0 ;
}
printf ( " mutex lock: thread %d, lock %s failed rc=%d \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name , rc ) ;
2003-08-13 05:53:07 +04:00
SMB_ASSERT ( errno = = 0 ) ; /* force error */
}
return 0 ;
}
/*
mutex unlock for thread model
*/
2003-08-15 21:13:41 +04:00
static int thread_mutex_unlock ( smb_mutex_t * mutex , const char * name )
2003-08-13 05:53:07 +04:00
{
return pthread_mutex_unlock ( ( pthread_mutex_t * ) mutex - > mutex ) ;
}
/*****************************************************************
Read / write lock routines .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
rwlock init function for thread model
*/
2003-12-16 06:27:36 +03:00
static int thread_rwlock_init ( smb_rwlock_t * rwlock , const char * name )
2003-08-13 05:53:07 +04:00
{
pthread_rwlock_t m = PTHREAD_RWLOCK_INITIALIZER ;
rwlock - > rwlock = memdup ( & m , sizeof ( m ) ) ;
if ( ! rwlock - > rwlock ) {
errno = ENOMEM ;
return - 1 ;
}
return pthread_rwlock_init ( ( pthread_rwlock_t * ) rwlock - > rwlock , NULL ) ;
}
/*
rwlock destroy function for thread model
*/
2003-12-16 06:27:36 +03:00
static int thread_rwlock_destroy ( smb_rwlock_t * rwlock , const char * name )
2003-08-13 05:53:07 +04:00
{
return pthread_rwlock_destroy ( ( pthread_rwlock_t * ) rwlock - > rwlock ) ;
}
/*
rwlock lock for read function for thread model
*/
2003-12-16 06:27:36 +03:00
static int thread_rwlock_lock_read ( smb_rwlock_t * rwlockP , const char * name )
2003-08-13 05:53:07 +04:00
{
pthread_rwlock_t * rwlock = ( pthread_rwlock_t * ) rwlockP - > rwlock ;
int rc ;
double t ;
struct timeval tp1 ;
/* Test below is ONLY for debugging */
if ( ( rc = pthread_rwlock_tryrdlock ( rwlock ) ) ) {
if ( rc = = EBUSY ) {
mutex_start_timer ( & tp1 ) ;
printf ( " rwlock lock_read: thread %d, lock %s not available \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name ) ;
2003-08-13 05:53:07 +04:00
print_suspicious_usage ( " rwlock_lock_read " , name ) ;
pthread_rwlock_rdlock ( rwlock ) ;
t = mutex_end_timer ( tp1 ) ;
printf ( " rwlock lock_read: thread %d, lock %s now available, waited %g seconds \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name , t ) ;
2003-08-13 05:53:07 +04:00
return 0 ;
}
printf ( " rwlock lock_read: thread %d, lock %s failed rc=%d \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name , rc ) ;
2003-08-13 05:53:07 +04:00
SMB_ASSERT ( errno = = 0 ) ; /* force error */
}
return 0 ;
}
/*
rwlock lock for write function for thread model
*/
2003-12-16 06:27:36 +03:00
static int thread_rwlock_lock_write ( smb_rwlock_t * rwlockP , const char * name )
2003-08-13 05:53:07 +04:00
{
pthread_rwlock_t * rwlock = ( pthread_rwlock_t * ) rwlockP - > rwlock ;
int rc ;
double t ;
struct timeval tp1 ;
/* Test below is ONLY for debugging */
if ( ( rc = pthread_rwlock_trywrlock ( rwlock ) ) ) {
if ( rc = = EBUSY ) {
mutex_start_timer ( & tp1 ) ;
printf ( " rwlock lock_write: thread %d, lock %s not available \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name ) ;
2003-08-13 05:53:07 +04:00
print_suspicious_usage ( " rwlock_lock_write " , name ) ;
pthread_rwlock_wrlock ( rwlock ) ;
t = mutex_end_timer ( tp1 ) ;
printf ( " rwlock lock_write: thread %d, lock %s now available, waited %g seconds \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name , t ) ;
2003-08-13 05:53:07 +04:00
return 0 ;
}
printf ( " rwlock lock_write: thread %d, lock %s failed rc=%d \n " ,
2004-05-25 20:24:13 +04:00
( uint32_t ) pthread_self ( ) , name , rc ) ;
2003-08-13 05:53:07 +04:00
SMB_ASSERT ( errno = = 0 ) ; /* force error */
}
return 0 ;
}
/*
rwlock unlock for thread model
*/
2003-12-16 06:27:36 +03:00
static int thread_rwlock_unlock ( smb_rwlock_t * rwlock , const char * name )
2003-08-13 05:53:07 +04:00
{
return pthread_rwlock_unlock ( ( pthread_rwlock_t * ) rwlock - > rwlock ) ;
}
/*****************************************************************
Log suspicious usage ( primarily for possible thread - unsafe behavior .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void thread_log_suspicious_usage ( const char * from , const char * info )
{
DEBUG ( 1 , ( " log_suspicious_usage: from %s info='%s' \n " , from , info ) ) ;
2003-12-04 14:01:58 +03:00
# ifdef HAVE_BACKTRACE
2004-01-22 05:28:17 +03:00
{
void * addresses [ 10 ] ;
int num_addresses = backtrace ( addresses , 8 ) ;
char * * bt_symbols = backtrace_symbols ( addresses , num_addresses ) ;
int i ;
if ( bt_symbols ) {
for ( i = 0 ; i < num_addresses ; i + + ) {
DEBUG ( 1 , ( " log_suspicious_usage: %s%s \n " , DEBUGTAB ( 1 ) , bt_symbols [ i ] ) ) ;
}
free ( bt_symbols ) ;
}
2003-08-13 05:53:07 +04:00
}
2003-12-04 14:01:58 +03:00
# endif
2003-08-13 05:53:07 +04:00
}
/*****************************************************************
Log suspicious usage to stdout ( primarily for possible thread - unsafe behavior .
Used in mutex code where DEBUG calls would cause recursion .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void thread_print_suspicious_usage ( const char * from , const char * info )
{
printf ( " log_suspicious_usage: from %s info='%s' \n " , from , info ) ;
2003-12-04 14:01:58 +03:00
# ifdef HAVE_BACKTRACE
2004-01-22 05:28:17 +03:00
{
void * addresses [ 10 ] ;
int num_addresses = backtrace ( addresses , 8 ) ;
char * * bt_symbols = backtrace_symbols ( addresses , num_addresses ) ;
int i ;
if ( bt_symbols ) {
for ( i = 0 ; i < num_addresses ; i + + ) {
printf ( " log_suspicious_usage: %s%s \n " , DEBUGTAB ( 1 ) , bt_symbols [ i ] ) ;
}
free ( bt_symbols ) ;
}
2003-08-13 05:53:07 +04:00
}
2003-12-04 14:01:58 +03:00
# endif
2003-08-13 05:53:07 +04:00
}
2004-05-25 20:24:13 +04:00
static uint32_t thread_get_task_id ( void )
2003-08-13 05:53:07 +04:00
{
2004-05-25 20:24:13 +04:00
return ( uint32_t ) pthread_self ( ) ;
2003-08-13 05:53:07 +04:00
}
2003-12-04 14:01:58 +03:00
static void thread_log_task_id ( int fd )
{
char * s ;
2004-05-25 20:24:13 +04:00
asprintf ( & s , " thread %u: " , ( uint32_t ) pthread_self ( ) ) ;
2003-12-04 14:01:58 +03:00
write ( fd , s , strlen ( s ) ) ;
free ( s ) ;
}
2003-08-13 05:53:07 +04:00
/****************************************************************************
catch serious errors
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void thread_sig_fault ( int sig )
{
DEBUG ( 0 , ( " =============================================================== \n " ) ) ;
2004-01-28 15:47:52 +03:00
DEBUG ( 0 , ( " TERMINAL ERROR: Recursive signal %d in thread %lu (%s) \n " , sig , ( unsigned long int ) pthread_self ( ) , SAMBA_VERSION_STRING ) ) ;
2003-08-13 05:53:07 +04:00
DEBUG ( 0 , ( " =============================================================== \n " ) ) ;
exit ( 1 ) ; /* kill the whole server for now */
}
/*******************************************************************
setup our recursive fault handlers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void thread_fault_setup ( void )
{
# ifdef SIGSEGV
CatchSignal ( SIGSEGV , SIGNAL_CAST thread_sig_fault ) ;
# endif
# ifdef SIGBUS
CatchSignal ( SIGBUS , SIGNAL_CAST thread_sig_fault ) ;
# endif
2004-01-22 05:28:17 +03:00
# ifdef SIGABRT
CatchSignal ( SIGABRT , SIGNAL_CAST thread_sig_fault ) ;
# endif
2003-08-13 05:53:07 +04:00
}
/*******************************************************************
report a fault in a thread
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void thread_fault_handler ( int sig )
{
static int counter ;
/* try to catch recursive faults */
thread_fault_setup ( ) ;
counter + + ; /* count number of faults that have occurred */
DEBUG ( 0 , ( " =============================================================== \n " ) ) ;
2004-01-28 15:47:52 +03:00
DEBUG ( 0 , ( " INTERNAL ERROR: Signal %d in thread %lu (%s) \n " , sig , ( unsigned long int ) pthread_self ( ) , SAMBA_VERSION_STRING ) ) ;
2003-08-13 05:53:07 +04:00
DEBUG ( 0 , ( " Please read the file BUGS.txt in the distribution \n " ) ) ;
DEBUG ( 0 , ( " =============================================================== \n " ) ) ;
2003-12-04 14:01:58 +03:00
# ifdef HAVE_BACKTRACE
2004-01-22 05:28:17 +03:00
{
void * addresses [ 10 ] ;
int num_addresses = backtrace ( addresses , 8 ) ;
char * * bt_symbols = backtrace_symbols ( addresses , num_addresses ) ;
int i ;
if ( bt_symbols ) {
for ( i = 0 ; i < num_addresses ; i + + ) {
DEBUG ( 1 , ( " fault_report: %s%s \n " , DEBUGTAB ( 1 ) , bt_symbols [ i ] ) ) ;
}
free ( bt_symbols ) ;
}
2003-08-13 05:53:07 +04:00
}
2003-12-04 14:01:58 +03:00
# endif
2003-08-13 05:53:07 +04:00
pthread_exit ( NULL ) ; /* terminate failing thread only */
}
/*
called when the process model is selected
*/
2004-07-14 01:04:56 +04:00
static void thread_model_startup ( void )
2003-08-13 05:53:07 +04:00
{
struct mutex_ops m_ops ;
struct debug_ops d_ops ;
ZERO_STRUCT ( m_ops ) ;
ZERO_STRUCT ( d_ops ) ;
smbd_process_init ( ) ;
/* register mutex/rwlock handlers */
m_ops . mutex_init = thread_mutex_init ;
m_ops . mutex_lock = thread_mutex_lock ;
m_ops . mutex_unlock = thread_mutex_unlock ;
m_ops . mutex_destroy = thread_mutex_destroy ;
m_ops . rwlock_init = thread_rwlock_init ;
m_ops . rwlock_lock_write = thread_rwlock_lock_write ;
m_ops . rwlock_lock_read = thread_rwlock_lock_read ;
m_ops . rwlock_unlock = thread_rwlock_unlock ;
m_ops . rwlock_destroy = thread_rwlock_destroy ;
register_mutex_handlers ( " thread " , & m_ops ) ;
register_fault_handler ( " thread " , thread_fault_handler ) ;
d_ops . log_suspicious_usage = thread_log_suspicious_usage ;
d_ops . print_suspicious_usage = thread_print_suspicious_usage ;
d_ops . get_task_id = thread_get_task_id ;
2003-12-04 14:01:58 +03:00
d_ops . log_task_id = thread_log_task_id ;
2003-08-13 05:53:07 +04:00
register_debug_handlers ( " thread " , & d_ops ) ;
}
2004-07-14 01:04:56 +04:00
static void thread_exit_server ( struct server_context * srv_ctx , const char * reason )
2004-02-02 16:43:03 +03:00
{
DEBUG ( 1 , ( " thread_exit_server: reason[%s] \n " , reason ) ) ;
}
2003-08-13 05:53:07 +04:00
/*
initialise the thread process model , registering ourselves with the model subsystem
*/
2004-02-02 16:43:03 +03:00
NTSTATUS process_model_thread_init ( void )
2003-08-13 05:53:07 +04:00
{
2004-02-02 16:43:03 +03:00
NTSTATUS ret ;
2003-08-13 05:53:07 +04:00
struct model_ops ops ;
ZERO_STRUCT ( ops ) ;
2004-02-02 16:43:03 +03:00
/* fill in our name */
ops . name = " thread " ;
2003-08-13 05:53:07 +04:00
/* fill in all the operations */
2004-07-14 01:04:56 +04:00
ops . model_startup = thread_model_startup ;
ops . accept_connection = thread_accept_connection ;
ops . terminate_connection = thread_terminate_connection ;
2004-02-02 16:43:03 +03:00
ops . exit_server = thread_exit_server ;
2004-07-14 01:04:56 +04:00
ops . get_id = thread_get_id ;
2004-02-02 16:43:03 +03:00
/* register ourselves with the PROCESS_MODEL subsystem. */
ret = register_backend ( " process_model " , & ops ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
DEBUG ( 0 , ( " Failed to register process_model 'thread'! \n " ) ) ;
return ret ;
}
return ret ;
2003-08-13 05:53:07 +04:00
}