2016-07-12 16:33:59 +03:00
/*
* Unix SMB / CIFS implementation .
* Wait for process death
* Copyright ( C ) Volker Lendecke 2016
*
* 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/>.
*/
2024-04-04 13:31:05 +03:00
# include "includes.h"
2016-07-12 16:33:59 +03:00
# include "serverid.h"
# include "server_id_watch.h"
2024-04-04 13:31:05 +03:00
# include "lib/util/server_id.h"
2016-07-12 16:33:59 +03:00
# include "lib/util/tevent_unix.h"
2024-04-04 13:31:05 +03:00
# include "lib/util/util_file.h"
2016-07-12 16:33:59 +03:00
struct server_id_watch_state {
struct tevent_context * ev ;
struct server_id pid ;
2024-04-25 16:24:57 +03:00
struct timeval start ;
2024-04-04 13:31:05 +03:00
struct timeval warn ;
bool debug ;
2016-07-12 16:33:59 +03:00
} ;
static void server_id_watch_waited ( struct tevent_req * subreq ) ;
struct tevent_req * server_id_watch_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct server_id pid )
{
struct tevent_req * req , * subreq ;
struct server_id_watch_state * state ;
2024-04-25 16:24:57 +03:00
struct timeval next ;
2016-07-12 16:33:59 +03:00
req = tevent_req_create ( mem_ctx , & state , struct server_id_watch_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > pid = pid ;
2024-04-25 16:24:57 +03:00
state - > start = tevent_timeval_current ( ) ;
2024-04-04 13:31:05 +03:00
state - > warn = tevent_timeval_add ( & state - > start , 10 , 0 ) ;
state - > debug = lp_parm_bool ( GLOBAL_SECTION_SNUM ,
" serverid watch " ,
" debug " ,
CHECK_DEBUGLVL ( DBGLVL_DEBUG ) ) ;
2016-07-12 16:33:59 +03:00
if ( ! serverid_exists ( & state - > pid ) ) {
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
2024-04-25 16:24:57 +03:00
next = tevent_timeval_add ( & state - > start , 0 , 500000 ) ;
subreq = tevent_wakeup_send ( state , ev , next ) ;
2016-07-12 16:33:59 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , server_id_watch_waited , req ) ;
return req ;
}
static void server_id_watch_waited ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct server_id_watch_state * state = tevent_req_data (
req , struct server_id_watch_state ) ;
2024-04-25 16:24:57 +03:00
struct timeval now ;
struct timeval next ;
2016-07-12 16:33:59 +03:00
bool ok ;
ok = tevent_wakeup_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! ok ) {
tevent_req_oom ( req ) ;
return ;
}
if ( ! serverid_exists ( & state - > pid ) ) {
tevent_req_done ( req ) ;
return ;
}
2024-04-25 16:24:57 +03:00
now = tevent_timeval_current ( ) ;
2024-04-04 13:31:05 +03:00
if ( ! state - > debug ) {
goto next ;
}
if ( timeval_compare ( & state - > warn , & now ) = = - 1 ) {
double duration = timeval_elapsed2 ( & state - > start , & now ) ;
2024-04-25 16:17:08 +03:00
const char * cmd = NULL ;
2024-04-04 13:31:05 +03:00
char proc_path [ 64 ] = { 0 , } ;
char * kstack = NULL ;
struct server_id_buf buf ;
const char * pid = server_id_str_buf ( state - > pid , & buf ) ;
int ret ;
state - > warn = tevent_timeval_add ( & now , 10 , 0 ) ;
2024-04-25 16:17:08 +03:00
cmd = lp_parm_const_string ( GLOBAL_SECTION_SNUM ,
" serverid watch " ,
" debug script " ,
NULL ) ;
if ( cmd ! = NULL ) {
char * cmdstr = NULL ;
char * output = NULL ;
int fd ;
/*
* Note in a cluster setup pid will be
* a NOTE : PID like ' 1 : 3978365 '
*
* Without clustering it is just ' 3978365 '
*/
cmdstr = talloc_asprintf ( state , " %s %s " , cmd , pid ) ;
if ( cmdstr = = NULL ) {
DBG_ERR ( " Process %s hanging for %f seconds? \n "
" talloc_asprintf failed \n " ,
pid , duration ) ;
goto next ;
}
become_root ( ) ;
ret = smbrun ( cmdstr , & fd , NULL ) ;
unbecome_root ( ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Process %s hanging for %f seconds? \n "
" smbrun('%s') failed \n " ,
pid , duration , cmdstr ) ;
TALLOC_FREE ( cmdstr ) ;
goto next ;
}
output = fd_load ( fd , NULL , 0 , state ) ;
close ( fd ) ;
if ( output = = NULL ) {
DBG_ERR ( " Process %s hanging for %f seconds? \n "
" fd_load() of smbrun('%s') failed \n " ,
pid , duration , cmdstr ) ;
TALLOC_FREE ( cmdstr ) ;
goto next ;
}
DBG_ERR ( " Process %s hanging for %f seconds? \n "
" %s returned: \n %s " ,
pid , duration , cmdstr , output ) ;
TALLOC_FREE ( cmdstr ) ;
TALLOC_FREE ( output ) ;
goto next ;
}
2024-04-04 13:31:05 +03:00
if ( ! procid_is_local ( & state - > pid ) | | ! sys_have_proc_fds ( ) ) {
DBG_ERR ( " Process %s hanging for %f seconds? \n " ,
pid , duration ) ;
goto next ;
}
ret = snprintf ( proc_path ,
ARRAY_SIZE ( proc_path ) ,
" /proc/% " PRIu64 " /stack " ,
state - > pid . pid ) ;
if ( ret < 0 ) {
DBG_ERR ( " Process %s hanging for %f seconds? \n "
" snprintf failed \n " ,
pid , duration ) ;
goto next ;
}
become_root ( ) ;
kstack = file_load ( proc_path , NULL , 0 , state ) ;
unbecome_root ( ) ;
if ( kstack = = NULL ) {
DBG_ERR ( " Process %s hanging for %f seconds? \n "
" file_load [%s] failed \n " ,
pid , duration , proc_path ) ;
goto next ;
}
DBG_ERR ( " Process %s hanging for %f seconds? \n "
" %s: \n %s " ,
pid , duration , proc_path , kstack ) ;
TALLOC_FREE ( kstack ) ;
}
next :
2024-04-25 16:24:57 +03:00
next = tevent_timeval_add ( & now , 0 , 500000 ) ;
subreq = tevent_wakeup_send ( state , state - > ev , next ) ;
2016-07-12 16:33:59 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , server_id_watch_waited , req ) ;
}
int server_id_watch_recv ( struct tevent_req * req , struct server_id * pid )
{
struct server_id_watch_state * state = tevent_req_data (
req , struct server_id_watch_state ) ;
int err ;
if ( tevent_req_is_unix_error ( req , & err ) ) {
return err ;
}
if ( pid ) {
* pid = state - > pid ;
}
return 0 ;
}