2000-10-08 09:25:20 +02:00
/*
* thread . c : A simple thread base class
*
* See the main source file ' vdr . c ' for copyright information and
* how to reach the author .
*
2020-09-16 13:48:33 +02:00
* $ Id : thread . c 4.15 2020 / 09 / 16 13 : 48 : 33 kls Exp $
2000-10-08 09:25:20 +02:00
*/
# include "thread.h"
2017-06-03 12:43:22 +02:00
# include <cxxabi.h>
# include <dlfcn.h>
2000-12-08 16:23:32 +01:00
# include <errno.h>
2017-06-03 12:43:22 +02:00
# include <execinfo.h>
2006-01-08 16:05:23 +01:00
# include <linux/unistd.h>
2003-10-18 12:29:08 +02:00
# include <malloc.h>
# include <stdarg.h>
2007-06-17 12:45:57 +02:00
# include <stdlib.h>
2017-06-03 12:43:22 +02:00
# include <sys/prctl.h>
2001-09-15 13:00:58 +02:00
# include <sys/resource.h>
2005-12-11 12:10:28 +01:00
# include <sys/syscall.h>
2002-08-15 11:46:22 +02:00
# include <sys/time.h>
2000-12-08 16:23:32 +01:00
# include <sys/wait.h>
2000-10-08 09:25:20 +02:00
# include <unistd.h>
2000-12-08 16:23:32 +01:00
# include "tools.h"
2000-10-08 09:25:20 +02:00
2017-06-03 12:43:22 +02:00
# define ABORT { dsyslog("ABORT!"); cBackTrace::BackTrace(); abort(); }
2015-09-01 11:14:27 +02:00
2017-06-03 12:43:22 +02:00
//#define DEBUG_LOCKING // uncomment this line to activate debug output for locking
# define DEBUG_LOCKSEQ // uncomment this line to activate debug output for invalid locking sequence
//#define DEBUG_LOCKCALL // uncomment this line to activate caller information with DEBUG_LOCKSEQ (WARNING: expensive operation, use only when actually debugging the locking sequence!)
2015-09-01 11:14:27 +02:00
# ifdef DEBUG_LOCKING
# define dbglocking(a...) fprintf(stderr, a)
# else
# define dbglocking(a...)
# endif
2005-05-06 14:43:17 +02:00
static bool GetAbsTime ( struct timespec * Abstime , int MillisecondsFromNow )
{
struct timeval now ;
if ( gettimeofday ( & now , NULL ) = = 0 ) { // get current time
2008-09-06 09:41:15 +02:00
now . tv_sec + = MillisecondsFromNow / 1000 ; // add full seconds
now . tv_usec + = ( MillisecondsFromNow % 1000 ) * 1000 ; // add microseconds
if ( now . tv_usec > = 1000000 ) { // take care of an overflow
now . tv_sec + + ;
now . tv_usec - = 1000000 ;
}
2005-05-06 14:43:17 +02:00
Abstime - > tv_sec = now . tv_sec ; // seconds
Abstime - > tv_nsec = now . tv_usec * 1000 ; // nano seconds
return true ;
}
return false ;
}
2004-10-16 09:36:28 +02:00
// --- cCondWait -------------------------------------------------------------
cCondWait : : cCondWait ( void )
{
signaled = false ;
pthread_mutex_init ( & mutex , NULL ) ;
pthread_cond_init ( & cond , NULL ) ;
}
cCondWait : : ~ cCondWait ( )
{
2004-10-31 09:54:50 +01:00
pthread_cond_broadcast ( & cond ) ; // wake up any sleepers
2004-10-16 09:36:28 +02:00
pthread_cond_destroy ( & cond ) ;
pthread_mutex_destroy ( & mutex ) ;
}
2004-10-24 11:12:05 +02:00
void cCondWait : : SleepMs ( int TimeoutMs )
{
cCondWait w ;
2005-01-14 14:08:47 +01:00
w . Wait ( max ( TimeoutMs , 3 ) ) ; // making sure the time is >2ms to avoid a possible busy wait
2004-10-24 11:12:05 +02:00
}
2004-10-16 09:36:28 +02:00
bool cCondWait : : Wait ( int TimeoutMs )
{
pthread_mutex_lock ( & mutex ) ;
if ( ! signaled ) {
if ( TimeoutMs ) {
2005-05-06 14:43:17 +02:00
struct timespec abstime ;
if ( GetAbsTime ( & abstime , TimeoutMs ) ) {
2004-10-16 09:36:28 +02:00
while ( ! signaled ) {
if ( pthread_cond_timedwait ( & cond , & mutex , & abstime ) = = ETIMEDOUT )
break ;
}
}
}
else
pthread_cond_wait ( & cond , & mutex ) ;
}
bool r = signaled ;
signaled = false ;
pthread_mutex_unlock ( & mutex ) ;
return r ;
}
void cCondWait : : Signal ( void )
{
pthread_mutex_lock ( & mutex ) ;
signaled = true ;
2004-10-31 09:54:50 +01:00
pthread_cond_broadcast ( & cond ) ;
2004-10-16 09:36:28 +02:00
pthread_mutex_unlock ( & mutex ) ;
}
2001-08-03 14:18:08 +02:00
// --- cCondVar --------------------------------------------------------------
cCondVar : : cCondVar ( void )
{
pthread_cond_init ( & cond , 0 ) ;
}
cCondVar : : ~ cCondVar ( )
{
2004-10-31 09:54:50 +01:00
pthread_cond_broadcast ( & cond ) ; // wake up any sleepers
2001-08-03 14:18:08 +02:00
pthread_cond_destroy ( & cond ) ;
}
2002-08-15 11:46:22 +02:00
void cCondVar : : Wait ( cMutex & Mutex )
2001-08-03 14:18:08 +02:00
{
2003-10-18 13:20:01 +02:00
if ( Mutex . locked ) {
2002-08-15 11:46:22 +02:00
int locked = Mutex . locked ;
Mutex . locked = 0 ; // have to clear the locked count here, as pthread_cond_wait
2006-01-28 11:34:35 +01:00
// does an implicit unlock of the mutex
2002-08-15 11:46:22 +02:00
pthread_cond_wait ( & cond , & Mutex . mutex ) ;
Mutex . locked = locked ;
}
2001-08-03 14:18:08 +02:00
}
2002-08-15 11:46:22 +02:00
bool cCondVar : : TimedWait ( cMutex & Mutex , int TimeoutMs )
2001-08-03 14:18:08 +02:00
{
2006-01-28 11:34:35 +01:00
bool r = true ; // true = condition signaled, false = timeout
2002-08-15 11:46:22 +02:00
2003-10-18 13:20:01 +02:00
if ( Mutex . locked ) {
2005-05-06 14:43:17 +02:00
struct timespec abstime ;
if ( GetAbsTime ( & abstime , TimeoutMs ) ) {
2002-08-15 11:46:22 +02:00
int locked = Mutex . locked ;
Mutex . locked = 0 ; // have to clear the locked count here, as pthread_cond_timedwait
2006-01-28 11:34:35 +01:00
// does an implicit unlock of the mutex.
2002-08-15 11:46:22 +02:00
if ( pthread_cond_timedwait ( & cond , & Mutex . mutex , & abstime ) = = ETIMEDOUT )
r = false ;
Mutex . locked = locked ;
}
}
return r ;
2001-08-03 14:18:08 +02:00
}
void cCondVar : : Broadcast ( void )
{
pthread_cond_broadcast ( & cond ) ;
}
2004-01-04 12:30:00 +01:00
// --- cRwLock ---------------------------------------------------------------
2003-12-22 13:29:24 +01:00
2004-01-04 12:30:00 +01:00
cRwLock : : cRwLock ( bool PreferWriter )
2003-12-22 13:29:24 +01:00
{
2016-12-08 10:18:32 +01:00
locked = 0 ;
writeLockThreadId = 0 ;
2006-01-01 14:53:03 +01:00
pthread_rwlockattr_t attr ;
pthread_rwlockattr_init ( & attr ) ;
pthread_rwlockattr_setkind_np ( & attr , PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP ) ;
2003-12-22 13:29:24 +01:00
pthread_rwlock_init ( & rwlock , & attr ) ;
}
2004-01-04 12:30:00 +01:00
cRwLock : : ~ cRwLock ( )
2003-12-22 13:29:24 +01:00
{
pthread_rwlock_destroy ( & rwlock ) ;
}
2004-01-04 12:30:00 +01:00
bool cRwLock : : Lock ( bool Write , int TimeoutMs )
2003-12-22 13:29:24 +01:00
{
int Result = 0 ;
struct timespec abstime ;
if ( TimeoutMs ) {
2005-05-06 14:43:17 +02:00
if ( ! GetAbsTime ( & abstime , TimeoutMs ) )
TimeoutMs = 0 ;
2003-12-22 13:29:24 +01:00
}
2016-12-08 10:18:32 +01:00
if ( Write ) {
2003-12-22 13:29:24 +01:00
Result = TimeoutMs ? pthread_rwlock_timedwrlock ( & rwlock , & abstime ) : pthread_rwlock_wrlock ( & rwlock ) ;
2016-12-08 10:18:32 +01:00
if ( Result = = 0 )
writeLockThreadId = cThread : : ThreadId ( ) ;
}
else if ( writeLockThreadId = = cThread : : ThreadId ( ) ) {
locked + + ; // there can be any number of stacked read locks, so we keep track here
2020-09-16 13:48:33 +02:00
Result = 0 ; // acquiring a read lock while holding a write lock within the same thread is OK
2016-12-08 10:18:32 +01:00
}
2003-12-22 13:29:24 +01:00
else
Result = TimeoutMs ? pthread_rwlock_timedrdlock ( & rwlock , & abstime ) : pthread_rwlock_rdlock ( & rwlock ) ;
return Result = = 0 ;
}
2004-01-04 12:30:00 +01:00
void cRwLock : : Unlock ( void )
2003-12-22 13:29:24 +01:00
{
2016-12-08 10:18:32 +01:00
if ( writeLockThreadId = = cThread : : ThreadId ( ) ) { // this is the thread that obtained the initial write lock
if ( locked ) { // this is the unlock of a read lock within the write lock
locked - - ;
return ;
}
}
writeLockThreadId = 0 ;
2003-12-22 13:29:24 +01:00
pthread_rwlock_unlock ( & rwlock ) ;
}
2001-06-02 10:47:40 +02:00
// --- cMutex ----------------------------------------------------------------
cMutex : : cMutex ( void )
{
locked = 0 ;
2006-01-01 14:53:03 +01:00
pthread_mutexattr_t attr ;
pthread_mutexattr_init ( & attr ) ;
pthread_mutexattr_settype ( & attr , PTHREAD_MUTEX_ERRORCHECK_NP ) ;
2003-10-18 13:20:01 +02:00
pthread_mutex_init ( & mutex , & attr ) ;
2001-06-02 10:47:40 +02:00
}
cMutex : : ~ cMutex ( )
{
pthread_mutex_destroy ( & mutex ) ;
}
void cMutex : : Lock ( void )
{
2003-10-18 13:20:01 +02:00
pthread_mutex_lock ( & mutex ) ;
2001-06-02 10:47:40 +02:00
locked + + ;
}
void cMutex : : Unlock ( void )
{
2016-12-08 10:18:32 +01:00
if ( ! - - locked )
pthread_mutex_unlock ( & mutex ) ;
2001-06-02 10:47:40 +02:00
}
2000-10-08 09:25:20 +02:00
// --- cThread ---------------------------------------------------------------
2006-01-03 10:20:41 +01:00
tThreadId cThread : : mainThreadId = 0 ;
2000-10-08 09:25:20 +02:00
2012-10-04 12:32:31 +02:00
cThread : : cThread ( const char * Description , bool LowPriority )
2000-10-08 09:25:20 +02:00
{
2005-08-14 11:24:57 +02:00
active = running = false ;
2004-12-19 10:58:20 +01:00
childTid = 0 ;
2006-01-04 15:01:22 +01:00
childThreadId = 0 ;
2003-10-18 12:29:08 +02:00
description = NULL ;
2006-03-26 09:27:30 +02:00
if ( Description )
SetDescription ( " %s " , Description ) ;
2012-10-04 12:32:31 +02:00
lowPriority = LowPriority ;
2000-10-08 09:25:20 +02:00
}
cThread : : ~ cThread ( )
{
2005-08-13 13:17:24 +02:00
Cancel ( ) ; // just in case the derived class didn't call it
2003-10-18 12:29:08 +02:00
free ( description ) ;
}
2005-05-29 11:44:52 +02:00
void cThread : : SetPriority ( int Priority )
{
if ( setpriority ( PRIO_PROCESS , 0 , Priority ) < 0 )
LOG_ERROR ;
}
2009-04-13 13:55:23 +02:00
void cThread : : SetIOPriority ( int Priority )
{
2013-04-10 15:38:29 +02:00
if ( syscall ( SYS_ioprio_set , 1 , 0 , ( Priority & 0xff ) | ( 3 < < 13 ) ) < 0 ) // idle class
2009-04-13 13:55:23 +02:00
LOG_ERROR ;
}
2003-10-18 12:29:08 +02:00
void cThread : : SetDescription ( const char * Description , . . . )
{
free ( description ) ;
description = NULL ;
if ( Description ) {
va_list ap ;
va_start ( ap , Description ) ;
2012-05-08 11:23:56 +02:00
description = strdup ( cString : : vsprintf ( Description , ap ) ) ;
2003-10-18 12:29:08 +02:00
va_end ( ap ) ;
}
2000-10-08 09:25:20 +02:00
}
void * cThread : : StartThread ( cThread * Thread )
{
2006-01-04 15:01:22 +01:00
Thread - > childThreadId = ThreadId ( ) ;
2008-04-13 12:14:58 +02:00
if ( Thread - > description ) {
2012-10-04 12:32:31 +02:00
dsyslog ( " %s thread started (pid=%d, tid=%d, prio=%s) " , Thread - > description , getpid ( ) , Thread - > childThreadId , Thread - > lowPriority ? " low " : " high " ) ;
2008-04-13 12:14:58 +02:00
# ifdef PR_SET_NAME
if ( prctl ( PR_SET_NAME , Thread - > description , 0 , 0 , 0 ) < 0 )
esyslog ( " %s thread naming failed (pid=%d, tid=%d) " , Thread - > description , getpid ( ) , Thread - > childThreadId ) ;
# endif
}
2012-10-04 12:32:31 +02:00
if ( Thread - > lowPriority ) {
Thread - > SetPriority ( 19 ) ;
Thread - > SetIOPriority ( 7 ) ;
}
2000-10-08 09:25:20 +02:00
Thread - > Action ( ) ;
2003-10-18 12:29:08 +02:00
if ( Thread - > description )
2006-01-04 15:01:22 +01:00
dsyslog ( " %s thread ended (pid=%d, tid=%d) " , Thread - > description , getpid ( ) , Thread - > childThreadId ) ;
2004-12-19 10:58:20 +01:00
Thread - > running = false ;
2005-08-14 11:24:57 +02:00
Thread - > active = false ;
2000-10-08 09:25:20 +02:00
return NULL ;
}
2007-01-07 14:46:14 +01:00
# define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
# define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
2000-10-08 09:25:20 +02:00
bool cThread : : Start ( void )
{
2007-01-07 14:46:14 +01:00
if ( ! running ) {
if ( active ) {
// Wait until the previous incarnation of this thread has completely ended
// before starting it newly:
cTimeMs RestartTimeout ;
while ( ! running & & active & & RestartTimeout . Elapsed ( ) < THREAD_STOP_TIMEOUT )
cCondWait : : SleepMs ( THREAD_STOP_SLEEP ) ;
2004-12-19 10:58:20 +01:00
}
2007-01-07 14:46:14 +01:00
if ( ! active ) {
active = running = true ;
if ( pthread_create ( & childTid , NULL , ( void * ( * ) ( void * ) ) & StartThread , ( void * ) this ) = = 0 ) {
pthread_detach ( childTid ) ; // auto-reap
}
else {
LOG_ERROR ;
active = running = false ;
return false ;
}
2004-12-19 10:58:20 +01:00
}
2000-10-08 09:25:20 +02:00
}
2004-12-19 10:58:20 +01:00
return true ;
2000-10-08 09:25:20 +02:00
}
2005-08-14 11:24:57 +02:00
bool cThread : : Active ( void )
2000-10-08 09:25:20 +02:00
{
2005-08-14 11:24:57 +02:00
if ( active ) {
2003-10-18 11:14:33 +02:00
//
// Single UNIX Spec v2 says:
//
// The pthread_kill() function is used to request
// that a signal be delivered to the specified thread.
//
// As in kill(), if sig is zero, error checking is
// performed but no signal is actually sent.
//
int err ;
if ( ( err = pthread_kill ( childTid , 0 ) ) ! = 0 ) {
if ( err ! = ESRCH )
2000-12-08 16:23:32 +01:00
LOG_ERROR ;
2003-10-18 11:14:33 +02:00
childTid = 0 ;
2005-08-14 11:24:57 +02:00
active = running = false ;
2000-12-08 16:23:32 +01:00
}
else
return true ;
}
return false ;
}
void cThread : : Cancel ( int WaitSeconds )
{
2005-08-14 11:24:57 +02:00
running = false ;
2006-09-24 12:54:47 +02:00
if ( active & & WaitSeconds > - 1 ) {
2004-10-24 10:34:20 +02:00
if ( WaitSeconds > 0 ) {
for ( time_t t0 = time ( NULL ) + WaitSeconds ; time ( NULL ) < t0 ; ) {
2005-08-14 11:24:57 +02:00
if ( ! Active ( ) )
2004-10-24 10:34:20 +02:00
return ;
2004-10-24 11:12:05 +02:00
cCondWait : : SleepMs ( 10 ) ;
2004-10-24 10:34:20 +02:00
}
2006-02-12 12:26:06 +01:00
esyslog ( " ERROR: %s thread %d won't end (waited %d seconds) - canceling it... " , description ? description : " " , childThreadId , WaitSeconds ) ;
2004-10-24 10:34:20 +02:00
}
2004-12-19 10:58:20 +01:00
pthread_cancel ( childTid ) ;
childTid = 0 ;
2005-08-14 11:24:57 +02:00
active = false ;
2000-12-08 16:23:32 +01:00
}
2000-10-08 09:25:20 +02:00
}
2005-12-11 12:10:28 +01:00
tThreadId cThread : : ThreadId ( void )
{
2006-08-20 10:28:35 +02:00
return syscall ( __NR_gettid ) ;
2005-12-11 12:10:28 +01:00
}
2006-01-03 10:20:41 +01:00
void cThread : : SetMainThreadId ( void )
{
if ( mainThreadId = = 0 )
mainThreadId = ThreadId ( ) ;
else
esyslog ( " ERROR: attempt to set main thread id to %d while it already is %d " , ThreadId ( ) , mainThreadId ) ;
}
2002-02-23 13:55:57 +01:00
// --- cMutexLock ------------------------------------------------------------
cMutexLock : : cMutexLock ( cMutex * Mutex )
{
mutex = NULL ;
locked = false ;
Lock ( Mutex ) ;
}
cMutexLock : : ~ cMutexLock ( )
{
if ( mutex & & locked )
mutex - > Unlock ( ) ;
}
bool cMutexLock : : Lock ( cMutex * Mutex )
{
if ( Mutex & & ! mutex ) {
mutex = Mutex ;
Mutex - > Lock ( ) ;
locked = true ;
return true ;
}
return false ;
}
2000-10-08 09:25:20 +02:00
// --- cThreadLock -----------------------------------------------------------
cThreadLock : : cThreadLock ( cThread * Thread )
{
2000-10-29 13:17:22 +01:00
thread = NULL ;
locked = false ;
Lock ( Thread ) ;
2000-10-08 09:25:20 +02:00
}
cThreadLock : : ~ cThreadLock ( )
{
2000-10-29 13:17:22 +01:00
if ( thread & & locked )
2000-10-08 09:25:20 +02:00
thread - > Unlock ( ) ;
}
2000-10-29 13:17:22 +01:00
bool cThreadLock : : Lock ( cThread * Thread )
{
if ( Thread & & ! thread ) {
thread = Thread ;
2001-10-27 13:23:06 +02:00
Thread - > Lock ( ) ;
locked = true ;
return true ;
2000-10-29 13:17:22 +01:00
}
return false ;
}
2017-06-03 12:43:22 +02:00
// --- cBackTrace ------------------------------------------------------------
2015-09-01 11:14:27 +02:00
2017-05-28 12:47:54 +02:00
# define BT_BUF_SIZE 100
2017-06-03 12:43:22 +02:00
cString cBackTrace : : Demangle ( char * s )
2017-05-28 12:47:54 +02:00
{
char * Module = s ;
char * Function = NULL ;
2017-06-03 12:43:22 +02:00
char * Offset = NULL ;
2017-05-28 12:47:54 +02:00
char * Address = NULL ;
// separate the string:
for ( char * q = Module ; * q ; q + + ) {
if ( * q = = ' ( ' ) {
* q = 0 ;
Function = q + 1 ;
}
else if ( * q = = ' + ' ) {
* q = 0 ;
2017-06-03 12:43:22 +02:00
Offset = q + 1 ;
2017-05-28 12:47:54 +02:00
}
else if ( * q = = ' ) ' )
* q = 0 ;
else if ( * q = = ' [ ' )
Address = q + 1 ;
else if ( * q = = ' ] ' ) {
* q = 0 ;
break ;
}
}
// demangle the function name:
2017-06-03 12:43:22 +02:00
char * DemangledFunction = NULL ;
if ( Function ) {
int status ;
DemangledFunction = abi : : __cxa_demangle ( Function , NULL , 0 , & status ) ;
if ( DemangledFunction )
Function = DemangledFunction ;
if ( ! * Function )
Function = NULL ;
}
cString d = cString : : sprintf ( " %s%s%s " , Module , Function ? " " : " " , Function ? Function : " " ) ;
// convert string address to numbers:
unsigned long long addr = Address ? strtoull ( Address , NULL , 0 ) : 0 ;
unsigned long long offs = Offset ? strtoull ( Offset , NULL , 0 ) : 0 ;
// for shared libraries we need get the offset inside the library:
if ( Function ) {
// check whether the module name ends with ".so*":
char * e = Module ;
char * p = NULL ;
while ( e = strstr ( e , " .so " ) )
p = e + + ;
if ( p & & ! strchr ( p , ' / ' ) ) {
Dl_info dlinfo ;
if ( dladdr ( reinterpret_cast < void * > ( addr ) , & dlinfo ) ) {
if ( ( strcmp ( Module , dlinfo . dli_fname ) = = 0 ) & & dlinfo . dli_fbase ) {
unsigned long long base = reinterpret_cast < unsigned long long > ( dlinfo . dli_fbase ) ;
addr - = base ;
addr & = 0x0FFFFFFFF ; // to make it work on both 32 and 64 bit systems
}
}
}
}
2017-05-28 12:47:54 +02:00
// determine the file name and line number:
2017-06-03 12:43:22 +02:00
cString cmd = cString : : sprintf ( " addr2line --functions --demangle --inlines --basename --exe=%s 0x%llx " , Module , Function ? addr : offs ) ;
2017-05-28 12:47:54 +02:00
cPipe p ;
if ( p . Open ( cmd , " r " ) ) {
int n = 0 ;
cReadLine rl ;
while ( char * l = rl . Read ( p ) ) {
if ( n = = 0 ) {
2017-06-03 12:43:22 +02:00
if ( Function & & strcmp ( l , Function ) )
2017-05-28 12:47:54 +02:00
d = cString : : sprintf ( " %s calling %s " , * d , l ) ;
}
else
d = cString : : sprintf ( " %s at %s " , * d , l ) ;
n + + ;
}
p . Close ( ) ;
}
2017-06-03 12:43:22 +02:00
free ( DemangledFunction ) ;
2017-05-28 12:47:54 +02:00
return d ;
}
2017-06-03 12:43:22 +02:00
void cBackTrace : : BackTrace ( cStringList & StringList , int Level , bool Mangled )
2017-05-28 12:47:54 +02:00
{
void * b [ BT_BUF_SIZE ] ;
int n = backtrace ( b , BT_BUF_SIZE ) ;
if ( char * * s = backtrace_symbols ( b , n ) ) {
2017-06-03 12:43:22 +02:00
for ( int i = max ( Level , 0 ) + 1 ; i < n ; i + + ) // 1 is the call to this function itself
StringList . Append ( strdup ( Mangled ? s [ i ] : * Demangle ( s [ i ] ) ) ) ;
2017-05-28 12:47:54 +02:00
free ( s ) ;
}
}
2017-06-03 12:43:22 +02:00
void cBackTrace : : BackTrace ( FILE * f , int Level , bool Mangled )
2017-05-28 12:47:54 +02:00
{
2017-06-03 12:43:22 +02:00
cStringList sl ;
BackTrace ( sl , Level + 1 , Mangled ) ; // 1 is the call to this function itself
for ( int i = 0 ; i < sl . Size ( ) ; i + + ) {
if ( f )
fprintf ( f , " %s \n " , sl [ i ] ) ;
else
dsyslog ( " %s " , sl [ i ] ) ;
}
}
cString cBackTrace : : GetCaller ( int Level , bool Mangled )
{
cString Caller ;
Level = max ( Level , 0 ) + 1 ; // 1 is the call to this function itself
2017-05-28 12:47:54 +02:00
void * b [ BT_BUF_SIZE ] ;
int n = backtrace ( b , BT_BUF_SIZE ) ;
if ( char * * s = backtrace_symbols ( b , n ) ) {
2017-06-03 12:43:22 +02:00
if ( Level < n )
Caller = Mangled ? s [ Level ] : * Demangle ( s [ Level ] ) ;
2017-05-28 12:47:54 +02:00
free ( s ) ;
}
2017-06-03 12:43:22 +02:00
return Caller ;
2017-05-28 12:47:54 +02:00
}
2017-06-03 12:43:22 +02:00
// --- cStateLockLog ---------------------------------------------------------
# ifdef DEBUG_LOCKSEQ
# define SLL_SIZE 20 // the number of log entries
2017-06-08 08:29:45 +02:00
# define SLL_LENGTH 512 // the maximum length of log entries
2017-06-23 09:03:19 +02:00
# define SLL_THREADS 20 // the maximum number of threads holding locks at the same time (typically well below 10)
2017-06-03 12:43:22 +02:00
# define SLL_MAX_LIST 9 // max. number of lists to log
# define SLL_WRITE_FLAG 0x80000000
2017-06-08 08:24:30 +02:00
# define SLL_LOCK_FLAG 0x40000000
2017-06-03 12:43:22 +02:00
class cStateLockLog {
private :
cMutex mutex ;
cVector < tThreadId > threadIds ;
cVector < int > flags ;
tThreadId logThreadIds [ SLL_SIZE ] ;
int logFlags [ SLL_SIZE ] ;
2017-06-23 09:03:19 +02:00
uint8_t logCounter [ SLL_THREADS ] [ SLL_MAX_LIST ] ;
2017-06-08 08:29:45 +02:00
# ifdef DEBUG_LOCKCALL
2017-06-03 12:43:22 +02:00
char logCaller [ SLL_SIZE ] [ SLL_LENGTH ] ;
2017-06-08 08:29:45 +02:00
# endif
2017-06-03 12:43:22 +02:00
int logIndex ;
bool dumped ;
void Dump ( const char * Name , tThreadId ThreadId ) ;
public :
cStateLockLog ( void ) ;
void Check ( const char * Name , bool Lock , bool Write = false ) ;
} ;
cStateLockLog : : cStateLockLog ( void )
{
memset ( logThreadIds , 0 , sizeof ( logThreadIds ) ) ;
memset ( logFlags , 0 , sizeof ( logFlags ) ) ;
2017-06-06 09:25:48 +02:00
memset ( logCounter , 0 , sizeof ( logCounter ) ) ;
2017-06-08 08:29:45 +02:00
# ifdef DEBUG_LOCKCALL
2017-06-03 12:43:22 +02:00
memset ( logCaller , 0 , sizeof ( logCaller ) ) ;
2017-06-08 08:29:45 +02:00
# endif
2017-06-03 12:43:22 +02:00
logIndex = 0 ;
dumped = false ;
}
void cStateLockLog : : Dump ( const char * Name , tThreadId ThreadId )
{
dsyslog ( " --- begin invalid lock sequence report " ) ;
int LastFlags = 0 ;
2017-05-28 12:47:54 +02:00
for ( int i = 0 ; i < SLL_SIZE ; i + + ) {
2017-06-03 12:43:22 +02:00
if ( tThreadId tid = logThreadIds [ logIndex ] ) {
char msg [ SLL_LENGTH ] ;
char * q = msg ;
q + = sprintf ( q , " %5d " , tid ) ;
int Flags = logFlags [ logIndex ] ;
bool Write = Flags & SLL_WRITE_FLAG ;
2017-06-08 08:24:30 +02:00
bool Lock = Flags & SLL_LOCK_FLAG ;
Flags & = ~ ( SLL_WRITE_FLAG | SLL_LOCK_FLAG ) ;
2017-06-03 12:43:22 +02:00
int Changed = LastFlags ^ Flags ;
LastFlags = Flags ;
for ( int i = 0 ; i < = SLL_MAX_LIST ; i + + ) {
char c = ' - ' ;
int b = 1 < < i ;
if ( ( Flags & b ) ! = 0 )
c = ' * ' ;
if ( ( Changed & b ) ! = 0 )
2017-06-09 08:27:22 +02:00
c = Lock ? Write ? ' W ' : ' R ' : ' U ' ;
2017-06-03 12:43:22 +02:00
q + = sprintf ( q , " %c " , c ) ;
}
q + = sprintf ( q , " %c " , Lock ? ' L ' : ' U ' ) ;
2017-06-08 08:29:45 +02:00
# ifdef DEBUG_LOCKCALL
2017-06-03 12:43:22 +02:00
if ( * logCaller [ logIndex ] ) {
* q + + = ' ' ;
strn0cpy ( q , * cBackTrace : : Demangle ( logCaller [ logIndex ] ) , sizeof ( msg ) - ( q - msg ) ) ;
2017-05-28 12:47:54 +02:00
}
2017-06-08 08:29:45 +02:00
# endif
2017-06-03 12:43:22 +02:00
dsyslog ( " %s " , msg ) ;
2017-05-28 12:47:54 +02:00
}
2017-06-03 12:43:22 +02:00
if ( + + logIndex > = SLL_SIZE )
logIndex = 0 ;
2017-05-28 12:47:54 +02:00
}
2017-06-03 12:43:22 +02:00
dsyslog ( " %5d invalid lock sequence: %s " , ThreadId , Name ) ;
dsyslog ( " full backtrace: " ) ;
cBackTrace : : BackTrace ( NULL , 2 ) ;
dsyslog ( " --- end invalid lock sequence report " ) ;
2017-06-25 12:10:19 +02:00
dsyslog ( " --- THERE WILL BE NO FURTHER REPORTS UNTIL VDR IS RESTARTED! " ) ;
2017-06-03 12:43:22 +02:00
fprintf ( stderr , " invalid lock sequence at %s \n " , * DayDateTime ( time ( NULL ) ) ) ;
2017-05-28 12:47:54 +02:00
}
2017-06-03 12:43:22 +02:00
void cStateLockLog : : Check ( const char * Name , bool Lock , bool Write )
2017-05-28 12:47:54 +02:00
{
2017-06-03 12:43:22 +02:00
if ( ! dumped & & Name ) {
2017-06-06 09:25:48 +02:00
int n = * Name - ' 0 ' - 1 ;
2017-06-07 12:31:31 +02:00
if ( 0 < = n & & n < SLL_MAX_LIST ) {
2017-06-06 09:25:48 +02:00
int b = 1 < < n ;
2017-06-03 12:43:22 +02:00
cMutexLock MutexLock ( & mutex ) ;
2017-05-28 12:47:54 +02:00
tThreadId ThreadId = cThread : : ThreadId ( ) ;
2017-06-23 09:03:19 +02:00
int Index = - 1 ;
int AvailableIndex = - 1 ;
for ( int i = 0 ; i < threadIds . Size ( ) ; i + + ) {
if ( ThreadId = = threadIds [ i ] ) {
Index = i ;
break ;
}
if ( threadIds [ i ] = = 0 )
AvailableIndex = i ;
}
2017-05-28 12:47:54 +02:00
if ( Index < 0 ) {
2017-06-23 09:03:19 +02:00
if ( AvailableIndex < 0 ) {
2017-06-03 12:43:22 +02:00
Index = threadIds . Size ( ) ;
threadIds . Append ( ThreadId ) ;
flags . Append ( 0 ) ;
2017-05-28 12:47:54 +02:00
}
2017-06-23 09:03:19 +02:00
else {
Index = AvailableIndex ;
threadIds [ Index ] = ThreadId ;
}
}
if ( Index > = SLL_THREADS ) {
// should never happen!
esyslog ( " ERROR: too many threads holding list locks at the same time - stopped logging locks! " ) ;
dumped = true ;
return ;
2017-05-28 12:47:54 +02:00
}
2017-06-03 12:43:22 +02:00
bool DoDump = false ;
2017-05-28 12:47:54 +02:00
if ( Lock ) {
2017-06-03 12:43:22 +02:00
if ( ( flags [ Index ] & ~ b ) < b ) // thread holds only "smaller" locks -> OK
;
else if ( ( flags [ Index ] & b ) = = 0 ) // thread already holds "bigger" locks, so it may only re-lock one that it already has!
DoDump = true ;
2017-06-06 09:25:48 +02:00
logCounter [ Index ] [ n ] + + ;
2017-06-03 12:43:22 +02:00
flags [ Index ] | = b ;
2017-05-28 12:47:54 +02:00
}
2017-06-06 09:25:48 +02:00
else if ( - - logCounter [ Index ] [ n ] = = 0 )
2017-06-03 12:43:22 +02:00
flags [ Index ] & = ~ b ;
logThreadIds [ logIndex ] = ThreadId ;
2017-06-08 08:24:30 +02:00
logFlags [ logIndex ] = flags [ Index ] | ( Write ? SLL_WRITE_FLAG : 0 ) | ( Lock ? SLL_LOCK_FLAG : 0 ) ;
2017-06-23 09:03:19 +02:00
if ( flags [ Index ] = = 0 )
threadIds [ Index ] = 0 ;
2017-06-03 12:43:22 +02:00
# ifdef DEBUG_LOCKCALL
2017-06-07 12:31:31 +02:00
strn0cpy ( logCaller [ logIndex ] , cBackTrace : : GetCaller ( Lock ? 3 : 5 , true ) , SLL_LENGTH ) ;
2017-06-03 12:43:22 +02:00
# endif
if ( + + logIndex > = SLL_SIZE )
logIndex = 0 ;
if ( DoDump ) {
Dump ( Name , ThreadId ) ;
dumped = true ;
}
2017-05-28 12:47:54 +02:00
}
}
}
2017-06-03 12:43:22 +02:00
static cStateLockLog StateLockLog ;
# define dbglockseq(n, l, w) StateLockLog.Check(n, l, w)
2017-05-28 12:47:54 +02:00
# else
# define dbglockseq(n, l, w)
# endif // DEBUG_LOCKSEQ
2017-06-03 12:43:22 +02:00
// --- cStateLock ------------------------------------------------------------
2015-09-01 11:14:27 +02:00
cStateLock : : cStateLock ( const char * Name )
: rwLock ( true )
{
name = Name ;
threadId = 0 ;
state = 0 ;
2018-03-04 11:31:21 +01:00
explicitModify = emDisabled ;
syncStateKey = NULL ;
2015-09-01 11:14:27 +02:00
}
bool cStateLock : : Lock ( cStateKey & StateKey , bool Write , int TimeoutMs )
{
2017-05-28 12:47:54 +02:00
dbglocking ( " %5d %-12s %10p lock state = %d/%d write = %d timeout = %d \n " , cThread : : ThreadId ( ) , name , & StateKey , state , StateKey . state , Write , TimeoutMs ) ;
2015-09-01 11:14:27 +02:00
StateKey . timedOut = false ;
if ( StateKey . stateLock ) {
esyslog ( " ERROR: StateKey already in use in call to cStateLock::Lock() (tid=%d, lock=%s) " , StateKey . stateLock - > threadId , name ) ;
ABORT ;
return false ;
}
if ( rwLock . Lock ( Write , TimeoutMs ) ) {
2017-05-28 12:47:54 +02:00
dbglockseq ( name , true , Write ) ;
2015-09-01 11:14:27 +02:00
StateKey . stateLock = this ;
if ( Write ) {
2017-05-28 12:47:54 +02:00
dbglocking ( " %5d %-12s %10p locked write \n " , cThread : : ThreadId ( ) , name , & StateKey ) ;
2015-09-01 11:14:27 +02:00
threadId = cThread : : ThreadId ( ) ;
StateKey . write = true ;
return true ;
}
else if ( state ! = StateKey . state ) {
2017-05-28 12:47:54 +02:00
dbglocking ( " %5d %-12s %10p locked read \n " , cThread : : ThreadId ( ) , name , & StateKey ) ;
2015-09-01 11:14:27 +02:00
return true ;
}
else {
2017-05-28 12:47:54 +02:00
dbglocking ( " %5d %-12s %10p state unchanged \n " , cThread : : ThreadId ( ) , name , & StateKey ) ;
2015-09-01 11:14:27 +02:00
StateKey . stateLock = NULL ;
2017-05-28 12:47:54 +02:00
dbglockseq ( name , false , false ) ;
2015-09-01 11:14:27 +02:00
rwLock . Unlock ( ) ;
}
}
else if ( TimeoutMs ) {
2017-05-28 12:47:54 +02:00
dbglocking ( " %5d %-12s %10p timeout \n " , cThread : : ThreadId ( ) , name , & StateKey ) ;
2015-09-01 11:14:27 +02:00
StateKey . timedOut = true ;
}
return false ;
}
void cStateLock : : Unlock ( cStateKey & StateKey , bool IncState )
{
2017-05-28 12:47:54 +02:00
dbglocking ( " %5d %-12s %10p unlock state = %d/%d inc = %d \n " , cThread : : ThreadId ( ) , name , & StateKey , state , StateKey . state , IncState ) ;
2015-09-01 11:14:27 +02:00
if ( StateKey . stateLock ! = this ) {
esyslog ( " ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s) " , threadId , name ) ;
ABORT ;
return ;
}
if ( StateKey . write & & threadId ! = cThread : : ThreadId ( ) ) {
2018-03-04 11:31:21 +01:00
esyslog ( " ERROR: cStateLock::Unlock() called without holding a write lock (tid=%d, lock=%s) " , threadId , name ) ;
2015-09-01 11:14:27 +02:00
ABORT ;
return ;
}
2018-03-04 11:31:21 +01:00
if ( StateKey . write & & ( IncState & & explicitModify ! = emArmed | | explicitModify = = emEnabled ) ) {
if ( syncStateKey & & syncStateKey - > state = = state )
syncStateKey - > state + + ;
2015-09-01 11:14:27 +02:00
state + + ;
2018-03-04 11:31:21 +01:00
}
2015-09-01 11:14:27 +02:00
StateKey . state = state ;
2018-03-05 22:40:12 +01:00
StateKey . stateLock = NULL ;
2016-12-08 10:18:32 +01:00
if ( StateKey . write ) {
StateKey . write = false ;
threadId = 0 ;
2018-03-04 11:31:21 +01:00
explicitModify = emDisabled ;
syncStateKey = NULL ;
2016-12-08 10:18:32 +01:00
}
2017-05-28 12:47:54 +02:00
dbglockseq ( name , false , false ) ;
2015-09-01 11:14:27 +02:00
rwLock . Unlock ( ) ;
}
2018-03-04 11:31:21 +01:00
void cStateLock : : SetSyncStateKey ( cStateKey & StateKey )
2015-09-01 11:14:27 +02:00
{
2018-03-04 11:31:21 +01:00
dbglocking ( " %5d %-12s %10p SetSyncStateKey \n " , cThread : : ThreadId ( ) , name , & StateKey ) ;
2015-09-01 11:14:27 +02:00
if ( threadId ! = cThread : : ThreadId ( ) ) {
2018-03-04 11:31:21 +01:00
esyslog ( " ERROR: cStateLock::SetSyncStateKey() called without holding a write lock (tid=%d, lock=%s) " , threadId , name ) ;
2015-09-01 11:14:27 +02:00
ABORT ;
2018-03-04 11:31:21 +01:00
return ;
2015-09-01 11:14:27 +02:00
}
2018-03-04 11:31:21 +01:00
if ( StateKey . stateLock = = this ) {
esyslog ( " ERROR: cStateLock::SetSyncStateKey() called with locked key (tid=%d, lock=%s) " , threadId , name ) ;
ABORT ;
return ;
}
if ( syncStateKey ) {
esyslog ( " ERROR: cStateLock::SetSyncStateKey() called twice (tid=%d, lock=%s) " , threadId , name ) ;
ABORT ;
return ;
}
syncStateKey = & StateKey ;
}
void cStateLock : : SetExplicitModify ( void )
{
if ( threadId ! = cThread : : ThreadId ( ) ) {
esyslog ( " ERROR: cStateLock::SetExplicitModify() called without holding a write lock (tid=%d, lock=%s) " , threadId , name ) ;
ABORT ;
return ;
}
if ( explicitModify ! = emDisabled ) {
esyslog ( " ERROR: cStateLock::SetExplicitModify() called twice (tid=%d, lock=%s) " , threadId , name ) ;
ABORT ;
return ;
}
explicitModify = emArmed ;
}
void cStateLock : : SetModified ( void )
{
if ( threadId ! = cThread : : ThreadId ( ) ) {
esyslog ( " ERROR: cStateLock::SetModified() called without holding a write lock (tid=%d, lock=%s) " , threadId , name ) ;
ABORT ;
return ;
}
explicitModify = emEnabled ;
2015-09-01 11:14:27 +02:00
}
// --- cStateKey -------------------------------------------------------------
cStateKey : : cStateKey ( bool IgnoreFirst )
{
stateLock = NULL ;
write = false ;
state = 0 ;
if ( ! IgnoreFirst )
Reset ( ) ;
}
cStateKey : : ~ cStateKey ( )
{
if ( stateLock ) {
esyslog ( " ERROR: cStateKey::~cStateKey() called without releasing the lock first (tid=%d, lock=%s, key=%p) " , stateLock - > threadId , stateLock - > name , this ) ;
ABORT ;
}
}
void cStateKey : : Reset ( void )
{
state = - 1 ; // lock and key are initialized differently, to make the first check return true
}
void cStateKey : : Remove ( bool IncState )
{
2018-03-05 22:40:12 +01:00
if ( stateLock )
2015-09-01 11:14:27 +02:00
stateLock - > Unlock ( * this , IncState ) ;
else {
esyslog ( " ERROR: cStateKey::Remove() called without holding a lock (key=%p) " , this ) ;
ABORT ;
}
}
bool cStateKey : : StateChanged ( void )
{
if ( ! stateLock ) {
esyslog ( " ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p) " , cThread : : ThreadId ( ) , this ) ;
ABORT ;
}
else if ( write )
return state ! = stateLock - > state ;
else
return true ;
}
2012-09-22 11:52:33 +02:00
// --- cIoThrottle -----------------------------------------------------------
cMutex cIoThrottle : : mutex ;
int cIoThrottle : : count = 0 ;
cIoThrottle : : cIoThrottle ( void )
{
active = false ;
}
cIoThrottle : : ~ cIoThrottle ( )
{
Release ( ) ;
}
void cIoThrottle : : Activate ( void )
{
if ( ! active ) {
mutex . Lock ( ) ;
count + + ;
active = true ;
dsyslog ( " i/o throttle activated, count = %d (tid=%d) " , count , cThread : : ThreadId ( ) ) ;
mutex . Unlock ( ) ;
}
}
void cIoThrottle : : Release ( void )
{
if ( active ) {
mutex . Lock ( ) ;
count - - ;
active = false ;
dsyslog ( " i/o throttle released, count = %d (tid=%d) " , count , cThread : : ThreadId ( ) ) ;
mutex . Unlock ( ) ;
}
}
bool cIoThrottle : : Engaged ( void )
{
return count > 0 ;
}
2001-09-15 13:00:58 +02:00
// --- cPipe -----------------------------------------------------------------
// cPipe::Open() and cPipe::Close() are based on code originally received from
// Andreas Vitting <Andreas@huji.de>
cPipe : : cPipe ( void )
{
pid = - 1 ;
f = NULL ;
}
cPipe : : ~ cPipe ( )
{
Close ( ) ;
}
bool cPipe : : Open ( const char * Command , const char * Mode )
{
int fd [ 2 ] ;
if ( pipe ( fd ) < 0 ) {
LOG_ERROR ;
return false ;
}
if ( ( pid = fork ( ) ) < 0 ) { // fork failed
LOG_ERROR ;
close ( fd [ 0 ] ) ;
close ( fd [ 1 ] ) ;
return false ;
}
2007-08-24 13:18:21 +02:00
const char * mode = " w " ;
2001-09-15 13:00:58 +02:00
int iopipe = 0 ;
if ( pid > 0 ) { // parent process
if ( strcmp ( Mode , " r " ) = = 0 ) {
mode = " r " ;
iopipe = 1 ;
}
close ( fd [ iopipe ] ) ;
if ( ( f = fdopen ( fd [ 1 - iopipe ] , mode ) ) = = NULL ) {
LOG_ERROR ;
close ( fd [ 1 - iopipe ] ) ;
}
return f ! = NULL ;
}
else { // child process
int iofd = STDOUT_FILENO ;
if ( strcmp ( Mode , " w " ) = = 0 ) {
iopipe = 1 ;
iofd = STDIN_FILENO ;
}
close ( fd [ iopipe ] ) ;
if ( dup2 ( fd [ 1 - iopipe ] , iofd ) = = - 1 ) { // now redirect
LOG_ERROR ;
close ( fd [ 1 - iopipe ] ) ;
_exit ( - 1 ) ;
}
else {
2002-11-02 14:00:25 +01:00
int MaxPossibleFileDescriptors = getdtablesize ( ) ;
for ( int i = STDERR_FILENO + 1 ; i < MaxPossibleFileDescriptors ; i + + )
close ( i ) ; //close all dup'ed filedescriptors
2001-09-15 13:00:58 +02:00
if ( execl ( " /bin/sh " , " sh " , " -c " , Command , NULL ) = = - 1 ) {
LOG_ERROR_STR ( Command ) ;
close ( fd [ 1 - iopipe ] ) ;
_exit ( - 1 ) ;
}
}
_exit ( 0 ) ;
}
}
int cPipe : : Close ( void )
{
int ret = - 1 ;
if ( f ) {
fclose ( f ) ;
f = NULL ;
}
2002-03-09 11:51:56 +01:00
if ( pid > 0 ) {
2001-09-15 13:00:58 +02:00
int status = 0 ;
int i = 5 ;
2002-03-09 11:51:56 +01:00
while ( i > 0 ) {
ret = waitpid ( pid , & status , WNOHANG ) ;
if ( ret < 0 ) {
if ( errno ! = EINTR & & errno ! = ECHILD ) {
LOG_ERROR ;
break ;
}
}
else if ( ret = = pid )
break ;
2001-09-15 13:00:58 +02:00
i - - ;
2004-10-24 11:12:05 +02:00
cCondWait : : SleepMs ( 100 ) ;
2001-09-15 13:00:58 +02:00
}
if ( ! i ) {
kill ( pid , SIGKILL ) ;
ret = - 1 ;
}
else if ( ret = = - 1 | | ! WIFEXITED ( status ) )
ret = - 1 ;
pid = - 1 ;
}
return ret ;
}
2001-10-20 10:39:27 +02:00
// --- SystemExec ------------------------------------------------------------
2007-02-25 10:56:29 +01:00
int SystemExec ( const char * Command , bool Detached )
2001-10-20 10:39:27 +02:00
{
pid_t pid ;
if ( ( pid = fork ( ) ) < 0 ) { // fork failed
LOG_ERROR ;
return - 1 ;
}
if ( pid > 0 ) { // parent process
2007-02-25 10:56:29 +01:00
int status = 0 ;
2007-06-17 12:45:57 +02:00
if ( waitpid ( pid , & status , 0 ) < 0 ) {
2001-10-20 10:39:27 +02:00
LOG_ERROR ;
return - 1 ;
}
return status ;
}
else { // child process
2007-02-25 10:56:29 +01:00
if ( Detached ) {
2007-06-17 12:45:57 +02:00
// Fork again and let first child die - grandchild stays alive without parent
if ( fork ( ) > 0 )
2007-10-19 14:37:03 +02:00
_exit ( 0 ) ;
2007-02-25 10:56:29 +01:00
// Start a new session
pid_t sid = setsid ( ) ;
if ( sid < 0 )
LOG_ERROR ;
// close STDIN and re-open as /dev/null
int devnull = open ( " /dev/null " , O_RDONLY ) ;
if ( devnull < 0 | | dup2 ( devnull , 0 ) < 0 )
LOG_ERROR ;
}
2001-10-20 10:39:27 +02:00
int MaxPossibleFileDescriptors = getdtablesize ( ) ;
for ( int i = STDERR_FILENO + 1 ; i < MaxPossibleFileDescriptors ; i + + )
close ( i ) ; //close all dup'ed filedescriptors
if ( execl ( " /bin/sh " , " sh " , " -c " , Command , NULL ) = = - 1 ) {
LOG_ERROR_STR ( Command ) ;
_exit ( - 1 ) ;
}
_exit ( 0 ) ;
}
}