diff --git a/core/base/inc/LinkDef2.h b/core/base/inc/LinkDef2.h index 44883b3c03f0f..07db22e4fb42b 100644 --- a/core/base/inc/LinkDef2.h +++ b/core/base/inc/LinkDef2.h @@ -131,6 +131,7 @@ #pragma link C++ class TStringToken; #pragma link C++ class TSubString; #pragma link C++ class TSysEvtHandler; +#pragma link C++ class TSigHandling+; #pragma link C++ class TSystem+; #pragma link C++ class TSystemFile+; #pragma link C++ class TSystemDirectory+; diff --git a/core/base/inc/TSigHandling.h b/core/base/inc/TSigHandling.h new file mode 100644 index 0000000000000..3a7bcdd5e52b8 --- /dev/null +++ b/core/base/inc/TSigHandling.h @@ -0,0 +1,94 @@ +// @(#)root/base:$Id$ +// Author: Fons Rademakers 15/09/95 + +/************************************************************************* + * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT_TSigHandling +#define ROOT_TSigHandling + + +////////////////////////////////////////////////////////////////////////// +// // +// TSigHandling // +// // +// Abstract base class defining a generic interface to the underlying // +// Operating System. // +// // +////////////////////////////////////////////////////////////////////////// + +#ifndef __CINT__ +#include +#include +#include +#ifndef WIN32 +#include +#endif +#endif + +#ifndef ROOT_TNamed +#include "TNamed.h" +#endif +#ifndef ROOT_TString +#include "TString.h" +#endif +#ifndef ROOT_TInetAddress +#include "TInetAddress.h" +#endif +#ifndef ROOT_TTimer +#include "TTimer.h" +#endif +#ifndef ROOT_ThreadLocalStorage +#include "ThreadLocalStorage.h" +#endif + +class TSeqCollection; +class TFdSet; +class TVirtualMutex; + +#ifdef __CINT__ +typedef void *Func_t; +#else +typedef void ((*Func_t)()); +#endif + +R__EXTERN const char *gRootDir; +R__EXTERN TVirtualMutex *gSystemMutex; + +class TSigHandling : public TNamed { + +protected: + TFdSet *fSignals; //!Signals that were trapped + Int_t fSigcnt; //Number of pending signals + TSeqCollection *fSignalHandler; //List of signal handlers + +public: + TSigHandling(const char *name = "Generic", const char *title = "Generic Signal Handling"); + virtual ~TSigHandling(); + + //---- Misc + virtual void Init(); + virtual void StackTrace(); + + //---- Handling of system signals + virtual Bool_t HaveTrappedSignal(Bool_t pendingOnly); + virtual void AddSignalHandler(TSignalHandler *sh); + virtual TSignalHandler *RemoveSignalHandler(TSignalHandler *sh); + virtual void ResetSignal(ESignals sig, Bool_t reset = kTRUE); + virtual void ResetSignals(); + virtual void IgnoreSignal(ESignals sig, Bool_t ignore = kTRUE); + virtual void IgnoreInterrupt(Bool_t ignore = kTRUE); + virtual TSeqCollection *GetListOfSignalHandlers(); + virtual void SigAlarmInterruptsSyscalls(Bool_t) { } + + ClassDef(TSigHandling,0) +}; + +R__EXTERN TSigHandling *gSigHandling; + +#endif diff --git a/core/base/inc/TSystem.h b/core/base/inc/TSystem.h index 45d407390249c..1bfab3f8993e9 100644 --- a/core/base/inc/TSystem.h +++ b/core/base/inc/TSystem.h @@ -268,11 +268,9 @@ class TSystem : public TNamed { TFdSet *fWritemask; //!Files that should be checked for write events TFdSet *fReadready; //!Files with reads waiting TFdSet *fWriteready; //!Files with writes waiting - TFdSet *fSignals; //!Signals that were trapped Int_t fNfd; //Number of fd's in masks Int_t fMaxrfd; //Largest fd in read mask Int_t fMaxwfd; //Largest fd in write mask - Int_t fSigcnt; //Number of pending signals TString fWdpath; //Working directory TString fHostname; //Hostname Bool_t fInsideNotify; //Used by DispatchTimers() @@ -284,7 +282,6 @@ class TSystem : public TNamed { Int_t fLevel; //Level of nested eventloops TSeqCollection *fTimers; //List of timers - TSeqCollection *fSignalHandler; //List of signal handlers TSeqCollection *fFileHandler; //List of file handlers TSeqCollection *fStdExceptionHandler; //List of std::exception handlers TSeqCollection *fOnExitList; //List of items to be cleaned-up on exit @@ -366,7 +363,7 @@ class TSystem : public TNamed { virtual void ResetSignals(); virtual void IgnoreSignal(ESignals sig, Bool_t ignore = kTRUE); virtual void IgnoreInterrupt(Bool_t ignore = kTRUE); - virtual TSeqCollection *GetListOfSignalHandlers() const { return fSignalHandler; } + virtual TSeqCollection *GetListOfSignalHandlers(); virtual void AddFileHandler(TFileHandler *fh); virtual TFileHandler *RemoveFileHandler(TFileHandler *fh); virtual TSeqCollection *GetListOfFileHandlers() const { return fFileHandler; } diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 8ceb73e38c02d..181e78f77b665 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -143,6 +143,7 @@ namespace std {} using namespace std; #include "TUrl.h" #else #include "TUnixSystem.h" +#include "TUnixSigHandling.h" #endif #elif defined(R__WIN32) #include "TWinNTSystem.h" @@ -1686,6 +1687,7 @@ void TROOT::InitSystem() gSystem = new TMacOSXSystem; #else gSystem = new TUnixSystem; + gSigHandling = new TUnixSigHandling; #endif #elif defined(R__WIN32) gSystem = new TWinNTSystem; diff --git a/core/base/src/TSigHandling.cxx b/core/base/src/TSigHandling.cxx new file mode 100644 index 0000000000000..e0ea1cafd09be --- /dev/null +++ b/core/base/src/TSigHandling.cxx @@ -0,0 +1,162 @@ +// @(#)root/base:$Id: 8944840ba34631ec28efc779647618db43c0eee5 $ +// Author: Fons Rademakers 15/09/95 + +/************************************************************************* + * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +/** \class TSystem + +Abstract base class defining a generic interface to the underlying +Operating System. +This is not an ABC in the strict sense of the (C++) word. For +every member function there is an implementation (often not more +than a call to AbstractMethod() which prints a warning saying +that the method should be overridden in a derived class), which +allows a simple partial implementation for new OS'es. +*/ + +#ifdef WIN32 +#include +#endif +#include +#include +#include +#include + +#include "Riostream.h" +#include "TSystem.h" +#include "TSigHandling.h" +#include "TApplication.h" +#include "TException.h" +#include "TROOT.h" +#include "TClass.h" +#include "TClassTable.h" +#include "TEnv.h" +#include "TBrowser.h" +#include "TString.h" +#include "TOrdCollection.h" +#include "TInterpreter.h" +#include "TRegexp.h" +#include "TTimer.h" +#include "TObjString.h" +#include "TError.h" +#include "TPluginManager.h" +#include "TUrl.h" +#include "TVirtualMutex.h" +#include "compiledata.h" +#include "RConfigure.h" + +TSigHandling *gSigHandling = 0; + +ClassImp(TSigHandling) + +//////////////////////////////////////////////////////////////////////////////// +/// Create a new OS interface. + +TSigHandling::TSigHandling(const char *name, const char *title) : TNamed(name, title) +{ + fSignals = 0; + fSigcnt = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Delete the OS interface. + +TSigHandling::~TSigHandling() +{ + if (fSignalHandler) { + fSignalHandler->Delete(); + SafeDelete(fSignalHandler); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Init the OS interface. +void TSigHandling::Init() +{ + fSignalHandler = new TOrdCollection; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a signal handler to list of system signal handlers. Only adds +/// the handler if it is not already in the list of signal handlers. + +Bool_t TSigHandling::HaveTrappedSignal(Bool_t) +{ + AbstractMethod("HaveTrappedSignal"); + return kFALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a signal handler to list of system signal handlers. Only adds +/// the handler if it is not already in the list of signal handlers. + +void TSigHandling::AddSignalHandler(TSignalHandler *) +{ + AbstractMethod("AddSignalHandler"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Remove a signal handler from list of signal handlers. Returns +/// the handler or 0 if the handler was not in the list of signal handlers. + +TSignalHandler *TSigHandling::RemoveSignalHandler(TSignalHandler *) +{ + AbstractMethod("RemoveSignalHandler"); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// If reset is true reset the signal handler for the specified signal +/// to the default handler, else restore previous behaviour. + +void TSigHandling::ResetSignal(ESignals /*sig*/, Bool_t /*reset*/) +{ + AbstractMethod("ResetSignal"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Reset signals handlers to previous behaviour. + +void TSigHandling::ResetSignals() +{ + AbstractMethod("ResetSignals"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// If ignore is true ignore the specified signal, else restore previous +/// behaviour. + +void TSigHandling::IgnoreSignal(ESignals /*sig*/, Bool_t /*ignore*/) +{ + AbstractMethod("IgnoreSignal"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// If ignore is true ignore the interrupt signal, else restore previous +/// behaviour. Typically call ignore interrupt before writing to disk. + +void TSigHandling::IgnoreInterrupt(Bool_t ignore) +{ + IgnoreSignal(kSigInterrupt, ignore); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Obtain the current signal handlers +TSeqCollection *TSigHandling::GetListOfSignalHandlers() +{ + return fSignalHandler; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Print a stack trace. + +void TSigHandling::StackTrace() +{ + AbstractMethod("StackTrace"); +} diff --git a/core/base/src/TSystem.cxx b/core/base/src/TSystem.cxx index cd12b72fbb390..7db9d7bbcb32f 100644 --- a/core/base/src/TSystem.cxx +++ b/core/base/src/TSystem.cxx @@ -47,6 +47,7 @@ allows a simple partial implementation for new OS'es. #include "TPluginManager.h" #include "TUrl.h" #include "TVirtualMutex.h" +#include "TSigHandling.h" #include "compiledata.h" #include "RConfigure.h" @@ -107,7 +108,6 @@ TSystem::TSystem(const char *name, const char *title) : TNamed(name, title), fAc Error("TSystem", "only one instance of TSystem allowed"); fOnExitList = 0; - fSignalHandler = 0; fFileHandler = 0; fStdExceptionHandler = 0; fTimers = 0; @@ -120,7 +120,6 @@ TSystem::TSystem(const char *name, const char *title) : TNamed(name, title), fAc fWritemask = 0; fReadready = 0; fWriteready = 0; - fSignals = 0; fDone = kFALSE; fAclicMode = kDefault; fInControl = kFALSE; @@ -128,12 +127,12 @@ TSystem::TSystem(const char *name, const char *title) : TNamed(name, title), fAc fMaxrfd = -1; fMaxwfd = -1; fNfd = 0; - fSigcnt = 0; if (!gLibraryVersion) { gLibraryVersion = new Int_t [gLibraryVersionMax]; memset(gLibraryVersion, 0, gLibraryVersionMax*sizeof(Int_t)); } + } //////////////////////////////////////////////////////////////////////////////// @@ -146,11 +145,6 @@ TSystem::~TSystem() SafeDelete(fOnExitList); } - if (fSignalHandler) { - fSignalHandler->Delete(); - SafeDelete(fSignalHandler); - } - if (fFileHandler) { fFileHandler->Delete(); SafeDelete(fFileHandler); @@ -189,10 +183,8 @@ Bool_t TSystem::Init() fMaxrfd = -1; fMaxwfd = -1; - fSigcnt = 0; fLevel = 0; - fSignalHandler = new TOrdCollection; fFileHandler = new TOrdCollection; fStdExceptionHandler = new TOrdCollection; fTimers = new TOrdCollection; @@ -535,8 +527,7 @@ Long_t TSystem::NextTimeOut(Bool_t mode) void TSystem::AddSignalHandler(TSignalHandler *h) { - if (h && fSignalHandler && (fSignalHandler->FindObject(h) == 0)) - fSignalHandler->Add(h); + gSigHandling->AddSignalHandler(h); } //////////////////////////////////////////////////////////////////////////////// @@ -545,10 +536,7 @@ void TSystem::AddSignalHandler(TSignalHandler *h) TSignalHandler *TSystem::RemoveSignalHandler(TSignalHandler *h) { - if (fSignalHandler) - return (TSignalHandler *)fSignalHandler->Remove(h); - - return 0; + return (TSignalHandler *)gSigHandling->RemoveSignalHandler(h); } //////////////////////////////////////////////////////////////////////////////// @@ -608,6 +596,17 @@ void TSystem::IgnoreInterrupt(Bool_t ignore) IgnoreSignal(kSigInterrupt, ignore); } +//////////////////////////////////////////////////////////////////////////////// +/// Obtain the current signal handlers +TSeqCollection *TSystem::GetListOfSignalHandlers() +{ + AbstractMethod("GetListOfSignalHandlers"); + return 0; +// TSeqCollection *handler; +// handler = (TSeqCollection *)gSigHandling->GetListOfSignalHandlers(); +// return handler; +} + //////////////////////////////////////////////////////////////////////////////// /// Add an exception handler to list of system exception handlers. Only adds /// the handler if it is not already in the list of exception handlers. diff --git a/core/unix/CMakeLists.txt b/core/unix/CMakeLists.txt index dc4768b1b0040..aa0f9bfedca17 100644 --- a/core/unix/CMakeLists.txt +++ b/core/unix/CMakeLists.txt @@ -4,7 +4,7 @@ ROOT_GLOB_SOURCES(sources ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cxx) -set(Unix_dict_headers ${CMAKE_CURRENT_SOURCE_DIR}/inc/TUnixSystem.h PARENT_SCOPE) +set(Unix_dict_headers ${CMAKE_CURRENT_SOURCE_DIR}/inc/T*.h PARENT_SCOPE) include_directories(${CMAKE_SOURCE_DIR}/net/net/inc) ROOT_OBJECT_LIBRARY(Unix ${sources}) diff --git a/core/unix/inc/LinkDef.h b/core/unix/inc/LinkDef.h index b62af47c4a59c..e3747abacc43c 100644 --- a/core/unix/inc/LinkDef.h +++ b/core/unix/inc/LinkDef.h @@ -14,6 +14,7 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class TUnixSigHandling; #pragma link C++ class TUnixSystem; #endif diff --git a/core/unix/inc/TUnixSigHandling.h b/core/unix/inc/TUnixSigHandling.h new file mode 100644 index 0000000000000..8d446bc1faac0 --- /dev/null +++ b/core/unix/inc/TUnixSigHandling.h @@ -0,0 +1,83 @@ +// @(#)root/unix:$Id$ +// Author: Fons Rademakers 15/09/95 + +/************************************************************************* + * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT_TUnixSigHandling +#define ROOT_TUnixSigHandling + + +////////////////////////////////////////////////////////////////////////// +// // +// TUnixSigHandling // +// // +// Class providing an interface to the UNIX Operating System. // +// // +////////////////////////////////////////////////////////////////////////// + +#ifndef ROOT_TSigHandling +#include "TSigHandling.h" +#endif +#ifndef ROOT_TSysEvtHandler +#include "TSysEvtHandler.h" +#endif +#ifndef ROOT_TTimer +#include "TTimer.h" +#endif + +typedef void (*SigHandler_t)(ESignals); + + +class TUnixSigHandling : public TSigHandling { + +protected: + //---- Unix signal interface functions ---------------------- + static void UnixSignal(ESignals sig, SigHandler_t h); + static const char *UnixSigname(ESignals sig); + static void UnixSigAlarmInterruptsSyscalls(Bool_t set); + static void UnixResetSignal(ESignals sig); + static void UnixResetSignals(); + static void UnixIgnoreSignal(ESignals sig, Bool_t ignore); + static void UnixSetDefaultSignals(); + + //---- Unix stack trace helper functions --------------------- + static void StackTraceHelperInit(); + static void StackTraceMonitorThread(); + static void StackTraceTriggerThread(); + static void StackTraceForkThread(); + static int StackTraceExecScript(void *); + +public: + TUnixSigHandling(); + virtual ~TUnixSigHandling(); + + //---- Misc ------------------------------------------------- + static const char *Getenv(const char *name); + static int GetPid(); + static void Exit(int code, Bool_t mode = kTRUE); + void Init(); + + //---- Handling of system events ---------------------------- + Bool_t CheckSignals(Bool_t sync); + Bool_t HaveTrappedSignal(Bool_t pendingOnly); + void DispatchSignals(ESignals sig); + void AddSignalHandler(TSignalHandler *sh); + TSignalHandler *RemoveSignalHandler(TSignalHandler *sh); + void ResetSignal(ESignals sig, Bool_t reset = kTRUE); + void ResetSignals(); + void IgnoreSignal(ESignals sig, Bool_t ignore = kTRUE); + void SigAlarmInterruptsSyscalls(Bool_t set); + + //---- Processes -------------------------------------------- + void StackTrace(); + + ClassDef(TUnixSigHandling,0) //Interface to Unix Signal Handling +}; + +#endif diff --git a/core/unix/inc/TUnixSystem.h b/core/unix/inc/TUnixSystem.h index 42abc6beb88ff..926d5f8b47c25 100644 --- a/core/unix/inc/TUnixSystem.h +++ b/core/unix/inc/TUnixSystem.h @@ -33,6 +33,31 @@ typedef void (*SigHandler_t)(ESignals); +//------------------- Unix TFdSet ---------------------------------------------- +#ifndef HOWMANY +# define HOWMANY(x, y) (((x)+((y)-1))/(y)) +#endif + +const Int_t kNFDBITS = (sizeof(Long_t) * 8); // 8 bits per byte +#ifdef FD_SETSIZE +const Int_t kFDSETSIZE = FD_SETSIZE; // Linux = 1024 file descriptors +#else +const Int_t kFDSETSIZE = 256; // upto 256 file descriptors +#endif + +class TFdSet { +private: + ULong_t fds_bits[HOWMANY(kFDSETSIZE, kNFDBITS)]; +public: + TFdSet(); + TFdSet(const TFdSet &org); + TFdSet &operator=(const TFdSet &rhs) { if (this != &rhs) { memcpy(fds_bits, rhs.fds_bits, sizeof(rhs.fds_bits));} return *this; } + void Zero(); + void Set(Int_t n); + void Clr(Int_t n); + Int_t IsSet(Int_t n); + ULong_t *GetBits(); +}; class TUnixSystem : public TSystem { @@ -50,12 +75,6 @@ class TUnixSystem : public TSystem { static int UnixSetitimer(Long_t ms); static int UnixSelect(Int_t nfds, TFdSet *readready, TFdSet *writeready, Long_t timeout); - static void UnixSignal(ESignals sig, SigHandler_t h); - static const char *UnixSigname(ESignals sig); - static void UnixSigAlarmInterruptsSyscalls(Bool_t set); - static void UnixResetSignal(ESignals sig); - static void UnixResetSignals(); - static void UnixIgnoreSignal(ESignals sig, Bool_t ignore); static int UnixFilestat(const char *path, FileStat_t &buf); static int UnixFSstat(const char *path, Long_t *id, Long_t *bsize, Long_t *blocks, Long_t *bfree); @@ -90,6 +109,7 @@ class TUnixSystem : public TSystem { //---- Handling of system events ---------------------------- void CheckChilds(); Bool_t CheckSignals(Bool_t sync); + Bool_t HaveTrappedSignal(Bool_t pendingOnly); Bool_t CheckDescriptors(); void DispatchSignals(ESignals sig); void AddSignalHandler(TSignalHandler *sh); @@ -97,6 +117,7 @@ class TUnixSystem : public TSystem { void ResetSignal(ESignals sig, Bool_t reset = kTRUE); void ResetSignals(); void IgnoreSignal(ESignals sig, Bool_t ignore = kTRUE); + TSeqCollection *GetListOfSignalHandlers(); void SigAlarmInterruptsSyscalls(Bool_t set); void AddFileHandler(TFileHandler *fh); TFileHandler *RemoveFileHandler(TFileHandler *fh); diff --git a/core/unix/src/TUnixSigHandling.cxx b/core/unix/src/TUnixSigHandling.cxx new file mode 100644 index 0000000000000..c4e00421733d8 --- /dev/null +++ b/core/unix/src/TUnixSigHandling.cxx @@ -0,0 +1,903 @@ +// @(#)root/unix:$Id: 887c618d89c4ed436e4034fc133f468fecad651b $ +// Author: Fons Rademakers 15/09/95 + +/************************************************************************* + * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +////////////////////////////////////////////////////////////////////////// +// // +// TUnixSigHandling // +// // +// Class providing an interface to the UNIX Operating System. // +// // +////////////////////////////////////////////////////////////////////////// + +#include "RConfigure.h" +#include "RConfig.h" +#include "TUnixSigHandling.h" +#include "TUnixSystem.h" +#include "TROOT.h" +#include "TError.h" +#include "TEnv.h" +#include "Getline.h" +#include "TOrdCollection.h" +#include "TApplication.h" +#include "TObjString.h" +#include "Riostream.h" +#include "TVirtualMutex.h" +#include "TObjArray.h" +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#endif + +//#define G__OLDEXPAND + +#include +#include + +#ifndef R__WIN32 +#include +#endif + +#include +#if defined(R__AIX) || defined(R__SOLARIS) +# include +#endif +#if defined(R__LINUX) || defined(R__HURD) +# ifndef SIGSYS +# define SIGSYS SIGUNUSED // SIGSYS does not exist in linux ?? +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include + +// stack trace code +#if (defined(R__LINUX) || defined(R__HURD)) && !defined(R__WINGCC) +# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1 +# define HAVE_BACKTRACE_SYMBOLS_FD +# endif +# define HAVE_DLADDR +#endif +#if defined(R__MACOSX) +# if defined(MAC_OS_X_VERSION_10_5) +# define HAVE_BACKTRACE_SYMBOLS_FD +# define HAVE_DLADDR +# else +# define USE_GDB_STACK_TRACE +# endif +#endif + +#ifdef HAVE_BACKTRACE_SYMBOLS_FD +# include +#endif +#ifdef HAVE_DLADDR +# ifndef __USE_GNU +# define __USE_GNU +# endif +# include +#endif + +#ifdef HAVE_BACKTRACE_SYMBOLS_FD + // The maximum stack trace depth for systems where we request the + // stack depth separately (currently glibc-based systems). + static const int kMAX_BACKTRACE_DEPTH = 128; +#endif + +class TFdSet; + +#if defined(HAVE_DLADDR) && !defined(R__MACOSX) +//////////////////////////////////////////////////////////////////////////////// + +static void SetRootSys() +{ +#ifndef ROOTPREFIX + void *addr = (void *)SetRootSys; + Dl_info info; + if (dladdr(addr, &info) && info.dli_fname && info.dli_fname[0]) { + char respath[kMAXPATHLEN]; + if (!realpath(info.dli_fname, respath)) { + if (!gSystem->Getenv("ROOTSYS")) + ::SysError("TUnixSigHandling::SetRootSys", "error getting realpath of libCore, please set ROOTSYS in the shell"); + } else { + TString rs = gSystem->DirName(respath); + gSystem->Setenv("ROOTSYS", gSystem->DirName(rs)); + } + } +#else + return; +#endif +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +/// Unix signal handler. + +static void SigHandler(ESignals sig) +{ + if (gSigHandling) + ((TUnixSigHandling*)gSigHandling)->DispatchSignals(sig); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Async-signal-safe Write functions. + +static int SignalSafeWrite(int fd, const char *text) { + const char *buffer = text; + size_t count = strlen(text); + ssize_t written = 0; + while (count) { + written = write(fd, buffer, count); + if (written == -1) { + if (errno == EINTR) { continue; } + else { return -errno; } + } + count -= written; + buffer += written; + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Async-signal-safe Read functions. + +static int SignalSafeRead(int fd, char *inbuf, size_t len, int timeout=-1) { + char *buf = inbuf; + size_t count = len; + ssize_t complete = 0; + std::chrono::time_point endTime = std::chrono::steady_clock::now() + std::chrono::seconds(timeout); + int flags; + if (timeout < 0) { + flags = O_NONBLOCK; // Prevents us from trying to set / restore flags later. + } else if ((-1 == (flags = fcntl(fd, F_GETFL)))) { + return -errno; + } else { } + if ((flags & O_NONBLOCK) != O_NONBLOCK) { + if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { + return -errno; + } + } + while (count) { + if (timeout >= 0) { + struct pollfd pollInfo{fd, POLLIN, 0}; + int msRemaining = std::chrono::duration_cast(endTime-std::chrono::steady_clock::now()).count(); + if (msRemaining > 0) { + if (poll(&pollInfo, 1, msRemaining) == 0) { + if ((flags & O_NONBLOCK) != O_NONBLOCK) { + fcntl(fd, F_SETFL, flags); + } + return -ETIMEDOUT; + } + } else if (msRemaining < 0) { + if ((flags & O_NONBLOCK) != O_NONBLOCK) { + fcntl(fd, F_SETFL, flags); + } + return -ETIMEDOUT; + } else { } + } + complete = read(fd, buf, count); + if (complete == -1) { + if (errno == EINTR) { continue; } + else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { continue; } + else { + int origErrno = errno; + if ((flags & O_NONBLOCK) != O_NONBLOCK) { + fcntl(fd, F_SETFL, flags); + } + return -origErrno; + } + } + count -= complete; + buf += complete; + } + if ((flags & O_NONBLOCK) != O_NONBLOCK) { + fcntl(fd, F_SETFL, flags); + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Async-signal-safe Write Error functions. + +static int SignalSafeErrWrite(const char *text) { + return SignalSafeWrite(2, text); +} + +////////////////////////////////////////////////////////////////////////// +// // +// Static Protected Unix StackTrace functions. // +// // +////////////////////////////////////////////////////////////////////////// + +//---- helper ----------------------------------------------------------------- + +static const int kStringLength = 255; + +static struct StackTraceHelper_t { + char fShellExec[kStringLength]; + char fPidString[kStringLength]; + char fPidNum[kStringLength]; + int fParentToChild[2]; + int fChildToParent[2]; + std::unique_ptr fHelperThread; +} gStackTraceHelper = { // the order of the signals should be identical + { }, + { }, + { }, + {-1,-1}, + {-1,-1}, + nullptr +}; + +static char * const kStackArgv[] = {gStackTraceHelper.fShellExec, gStackTraceHelper.fPidString, gStackTraceHelper.fPidNum, nullptr}; + +////////////////////////////////////////////////////////////////////////////// +static char * const *GetStackArgv() { + return kStackArgv; +} + +ClassImp(TUnixSigHandling) + +//////////////////////////////////////////////////////////////////////////////// + +TUnixSigHandling::TUnixSigHandling() : TSigHandling("Unix", "Unix Signal Handling") +{ } + +//////////////////////////////////////////////////////////////////////////////// +/// Reset to original state. + +TUnixSigHandling::~TUnixSigHandling() +{ + UnixResetSignals(); + delete fSignals; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Initialize Unix system interface. + +void TUnixSigHandling::Init() +{ + TSigHandling::Init(); + + fSignals = new TFdSet; + + //--- install default handlers + UnixSignal(kSigChild, SigHandler); + UnixSignal(kSigBus, SigHandler); + UnixSignal(kSigSegmentationViolation, SigHandler); + UnixSignal(kSigIllegalInstruction, SigHandler); + UnixSignal(kSigSystem, SigHandler); + UnixSignal(kSigPipe, SigHandler); + UnixSignal(kSigAlarm, SigHandler); + UnixSignal(kSigUrgent, SigHandler); + UnixSignal(kSigFloatingException, SigHandler); + UnixSignal(kSigWindowChanged, SigHandler); + +#if defined(R__MACOSX) + // trap loading of all dylibs to register dylib name, + // sets also ROOTSYS if built without ROOTPREFIX + _dyld_register_func_for_add_image(DylibAdded); +#elif defined(HAVE_DLADDR) + SetRootSys(); +#endif + +#ifndef ROOTPREFIX + gRootDir = Getenv("ROOTSYS"); + if (gRootDir == 0) + gRootDir= "/usr/local/root"; +#else + gRootDir = ROOTPREFIX; +#endif + + if(snprintf(gStackTraceHelper.fShellExec, kStringLength-1, "/bin/sh") >= kStringLength) { + SignalSafeErrWrite("Unable to pre-allocate shell command path"); + return; + } + +#ifdef ROOTETCDIR + if(snprintf(gStackTraceHelper.fPidString, kStringLength-1, "%s/gdb-backtrace.sh", ROOTETCDIR) >= kStringLength) { + SignalSafeErrWrite("Unable to pre-allocate executable information"); + return; + } +#else + if(snprintf(gStackTraceHelper.fPidString, kStringLength-1, "%s/etc/gdb-backtrace.sh", gSystem->Getenv("ROOTSYS")) >= kStringLength) { + SignalSafeErrWrite("Unable to pre-allocate executable information"); + return; + } +#endif + + gStackTraceHelper.fParentToChild[0] = -1; + gStackTraceHelper.fParentToChild[1] = -1; + gStackTraceHelper.fChildToParent[0] = -1; + gStackTraceHelper.fChildToParent[1] = -1; + + StackTraceHelperInit(); +} + +//---- Misc -------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// Get environment variable. + +const char *TUnixSigHandling::Getenv(const char *name) +{ + return ::getenv(name); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get process id. + +int TUnixSigHandling::GetPid() +{ + return ::getpid(); +} + +//////////////////////////////////////////////////////////////////////////////// +///// Exit the application. + +void TUnixSigHandling::Exit(int code, Bool_t mode) +{ + // Insures that the files and sockets are closed before any library is unloaded + // and before emptying CINT. + if (gROOT) { + gROOT->EndOfProcessCleanups(); + } else if (gInterpreter) { + gInterpreter->ResetGlobals(); + } + + if (mode) + ::exit(code); + else + ::_exit(code); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a signal handler to list of system signal handlers. Only adds +/// the handler if it is not already in the list of signal handlers. + +void TUnixSigHandling::AddSignalHandler(TSignalHandler *h) +{ + R__LOCKGUARD2(gSystemMutex); + + if (h && fSignalHandler && (fSignalHandler->FindObject(h) == 0)) + fSignalHandler->Add(h); + + UnixSignal(h->GetSignal(), SigHandler); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Remove a signal handler from list of signal handlers. Returns +/// the handler or 0 if the handler was not in the list of signal handlers. + +TSignalHandler *TUnixSigHandling::RemoveSignalHandler(TSignalHandler *h) +{ + if (!h) return 0; + + R__LOCKGUARD2(gSystemMutex); + + TSignalHandler *oh; + + if (fSignalHandler) + oh = (TSignalHandler *)fSignalHandler->Remove(h); + else + oh = 0; + + Bool_t last = kTRUE; + TSignalHandler *hs; + TIter next(fSignalHandler); + + while ((hs = (TSignalHandler*) next())) { + if (hs->GetSignal() == h->GetSignal()) + last = kFALSE; + } + if (last) + ResetSignal(h->GetSignal(), kTRUE); + + return oh; +} + +//////////////////////////////////////////////////////////////////////////////// +/// If reset is true reset the signal handler for the specified signal +/// to the default handler, else restore previous behaviour. + +void TUnixSigHandling::ResetSignal(ESignals sig, Bool_t reset) +{ + if (reset) + UnixResetSignal(sig); + else + UnixSignal(sig, SigHandler); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Reset signals handlers to previous behaviour. + +void TUnixSigHandling::ResetSignals() +{ + UnixResetSignals(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// If ignore is true ignore the specified signal, else restore previous +/// behaviour. + +void TUnixSigHandling::IgnoreSignal(ESignals sig, Bool_t ignore) +{ + UnixIgnoreSignal(sig, ignore); +} + +//////////////////////////////////////////////////////////////////////////////// +/// When the argument is true the SIGALRM signal handler is set so that +/// interrupted syscalls will not be restarted by the kernel. This is +/// typically used in case one wants to put a timeout on an I/O operation. +/// By default interrupted syscalls will always be restarted (for all +/// signals). This can be controlled for each a-synchronous TTimer via +/// the method TTimer::SetInterruptSyscalls(). + +void TUnixSigHandling::SigAlarmInterruptsSyscalls(Bool_t set) +{ + UnixSigAlarmInterruptsSyscalls(set); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check if there is any signal trapping. +Bool_t TUnixSigHandling::HaveTrappedSignal(Bool_t pendingOnly) +{ + if (fSigcnt > 0 && fSignalHandler->GetSize() > 0) + if (CheckSignals(kTRUE)) + if (!pendingOnly) return 1; + fSigcnt = 0; + fSignals->Zero(); + return 0; +} + +//---- handling of system events ----------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// Check if some signals were raised and call their Notify() member. + +Bool_t TUnixSigHandling::CheckSignals(Bool_t sync) +{ + TSignalHandler *sh; + Int_t sigdone = -1; + { + TOrdCollectionIter it((TOrdCollection*)fSignalHandler); + + while ((sh = (TSignalHandler*)it.Next())) { + if (sync == sh->IsSync()) { + ESignals sig = sh->GetSignal(); + if ((fSignals->IsSet(sig) && sigdone == -1) || sigdone == sig) { + if (sigdone == -1) { + fSignals->Clr(sig); + sigdone = sig; + fSigcnt--; + } + if (sh->IsActive()) + sh->Notify(); + } + } + } + } + if (sigdone != -1) + return kTRUE; + + return kFALSE; +} + +////////////////////////////////////////////////////////////////////////// +// // +// Static Protected Unix Interface functions. // +// // +////////////////////////////////////////////////////////////////////////// + +//---- signals ----------------------------------------------------------------- + +static struct Signalmap_t { + int fCode; + SigHandler_t fHandler; + struct sigaction *fOldHandler; + const char *fSigName; +} gSignalMap[kMAXSIGNALS] = { // the order of the signals should be identical + { SIGBUS, 0, 0, "bus error" }, // to the one in TSysEvtHandler.h + { SIGSEGV, 0, 0, "segmentation violation" }, + { SIGSYS, 0, 0, "bad argument to system call" }, + { SIGPIPE, 0, 0, "write on a pipe with no one to read it" }, + { SIGILL, 0, 0, "illegal instruction" }, + { SIGQUIT, 0, 0, "quit" }, + { SIGINT, 0, 0, "interrupt" }, + { SIGWINCH, 0, 0, "window size change" }, + { SIGALRM, 0, 0, "alarm clock" }, + { SIGCHLD, 0, 0, "death of a child" }, + { SIGURG, 0, 0, "urgent data arrived on an I/O channel" }, + { SIGFPE, 0, 0, "floating point exception" }, + { SIGTERM, 0, 0, "termination signal" }, + { SIGUSR1, 0, 0, "user-defined signal 1" }, + { SIGUSR2, 0, 0, "user-defined signal 2" } +}; + + +//////////////////////////////////////////////////////////////////////////////// +/// Call the signal handler associated with the signal. + +static void sighandler(int sig) +{ + for (int i= 0; i < kMAXSIGNALS; i++) { + if (gSignalMap[i].fCode == sig) { + (*gSignalMap[i].fHandler)((ESignals)i); + return; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Handle and dispatch signals. + +void TUnixSigHandling::DispatchSignals(ESignals sig) +{ + switch (sig) { + case kSigAlarm: +// DispatchTimers(kFALSE); + break; + case kSigChild: +// CheckChilds(); + break; + case kSigBus: + SignalSafeErrWrite("\n\nA fatal system signal has occurred: bus error"); + break; + case kSigSegmentationViolation: + SignalSafeErrWrite("\n\nA fatal system signal has occurred: segmentation violation error"); + break; + case kSigIllegalInstruction: + SignalSafeErrWrite("\n\nA fatal system signal has occurred: illegal instruction error"); + break; + case kSigFloatingException: + Break("TUnixSigHandling::DispatchSignals", "%s", UnixSigname(sig)); + StackTrace(); + if (gApplication) + //sig is ESignal, should it be mapped to the correct signal number? + gApplication->HandleException(sig); + else + //map to the real signal code + set the + //high order bit to indicate a signal (?) + Exit(gSignalMap[sig].fCode + 0x80); + break; + case kSigSystem: + case kSigPipe: + Break("TUnixSigHandling::DispatchSignals", "%s", UnixSigname(sig)); + break; + case kSigWindowChanged: + Gl_windowchanged(); + break; + default: + fSignals->Set(sig); + fSigcnt++; + break; + } + + if ((sig == kSigIllegalInstruction) || (sig == kSigSegmentationViolation) || (sig == kSigBus)) + { + StackTraceTriggerThread(); + signal(sig, SIG_DFL); + raise(sig); + } + + // check a-synchronous signals + if (fSigcnt > 0 && fSignalHandler->GetSize() > 0) + CheckSignals(kFALSE); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set a signal handler for a signal. + +void TUnixSigHandling::UnixSignal(ESignals sig, SigHandler_t handler) +{ + if (gEnv && !gEnv->GetValue("Root.ErrorHandlers", 1)) + return; + + if (gSignalMap[sig].fHandler != handler) { + struct sigaction sigact; + + gSignalMap[sig].fHandler = handler; + gSignalMap[sig].fOldHandler = new struct sigaction(); + +#if defined(R__SUN) + sigact.sa_handler = (void (*)())sighandler; +#elif defined(R__SOLARIS) + sigact.sa_handler = sighandler; +#elif defined(R__LYNXOS) +# if (__GNUG__>=3) + sigact.sa_handler = sighandler; +# else + sigact.sa_handler = (void (*)(...))sighandler; +# endif +#else + sigact.sa_handler = sighandler; +#endif + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; +#if defined(SA_RESTART) + sigact.sa_flags |= SA_RESTART; +#endif + if (sigaction(gSignalMap[sig].fCode, &sigact, + gSignalMap[sig].fOldHandler) < 0) + ::SysError("TUnixSigHandling::UnixSignal", "sigaction"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// If ignore is true ignore the specified signal, else restore previous +/// behaviour. + +void TUnixSigHandling::UnixIgnoreSignal(ESignals sig, Bool_t ignore) +{ + TTHREAD_TLS(Bool_t) ignoreSig[kMAXSIGNALS] = { kFALSE }; + TTHREAD_TLS_ARRAY(struct sigaction,kMAXSIGNALS,oldsigact); + + if (ignore != ignoreSig[sig]) { + ignoreSig[sig] = ignore; + if (ignore) { + struct sigaction sigact; +#if defined(R__SUN) + sigact.sa_handler = (void (*)())SIG_IGN; +#elif defined(R__SOLARIS) + sigact.sa_handler = (void (*)(int))SIG_IGN; +#else + sigact.sa_handler = SIG_IGN; +#endif + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + if (sigaction(gSignalMap[sig].fCode, &sigact, &oldsigact[sig]) < 0) + ::SysError("TUnixSigHandling::UnixIgnoreSignal", "sigaction"); + } else { + if (sigaction(gSignalMap[sig].fCode, &oldsigact[sig], 0) < 0) + ::SysError("TUnixSigHandling::UnixIgnoreSignal", "sigaction"); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// When the argument is true the SIGALRM signal handler is set so that +/// interrupted syscalls will not be restarted by the kernel. This is +/// typically used in case one wants to put a timeout on an I/O operation. +/// By default interrupted syscalls will always be restarted (for all +/// signals). This can be controlled for each a-synchronous TTimer via +/// the method TTimer::SetInterruptSyscalls(). + +void TUnixSigHandling::UnixSigAlarmInterruptsSyscalls(Bool_t set) +{ + if (gSignalMap[kSigAlarm].fHandler) { + struct sigaction sigact; +#if defined(R__SUN) + sigact.sa_handler = (void (*)())sighandler; +#elif defined(R__SOLARIS) + sigact.sa_handler = sighandler; +#elif defined(R__LYNXOS) +# if (__GNUG__>=3) + sigact.sa_handler = sighandler; +# else + sigact.sa_handler = (void (*)(...))sighandler; +# endif +#else + sigact.sa_handler = sighandler; +#endif + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + if (set) { +#if defined(SA_INTERRUPT) // SunOS + sigact.sa_flags |= SA_INTERRUPT; +#endif + } else { +#if defined(SA_RESTART) + sigact.sa_flags |= SA_RESTART; +#endif + } + if (sigaction(gSignalMap[kSigAlarm].fCode, &sigact, 0) < 0) + ::SysError("TUnixSigHandling::UnixSigAlarmInterruptsSyscalls", "sigaction"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return the signal name associated with a signal. + +const char *TUnixSigHandling::UnixSigname(ESignals sig) +{ + return gSignalMap[sig].fSigName; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Restore old signal handler for specified signal. + +void TUnixSigHandling::UnixResetSignal(ESignals sig) +{ + if (gSignalMap[sig].fOldHandler) { + // restore old signal handler + if (sigaction(gSignalMap[sig].fCode, gSignalMap[sig].fOldHandler, 0) < 0) + ::SysError("TUnixSigHandling::UnixSignal", "sigaction"); + delete gSignalMap[sig].fOldHandler; + gSignalMap[sig].fOldHandler = 0; + gSignalMap[sig].fHandler = 0; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Restore old signal handlers. + +void TUnixSigHandling::UnixResetSignals() +{ + for (int sig = 0; sig < kMAXSIGNALS; sig++) + UnixResetSignal((ESignals)sig); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set signal handlers to default signal handlers + +void TUnixSigHandling::UnixSetDefaultSignals() +{ + signal(SIGILL, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGBUS, SIG_DFL); +} + +/////////////////////////////////////////////////////////////////////////////// +/// Stack Trace + +void TUnixSigHandling::StackTrace() +{ + gSystem->StackTrace(); +} + +////////////////////////////////////////////////////////////////////////////// +/// Initialize StackTrace helper structures + +void TUnixSigHandling::StackTraceHelperInit() +{ + if(snprintf(gStackTraceHelper.fPidNum, kStringLength-1, "%d", GetPid()) >= kStringLength) { + SignalSafeErrWrite("Unable to pre-allocate process id information"); + return; + } + + close(gStackTraceHelper.fChildToParent[0]); + close(gStackTraceHelper.fChildToParent[1]); + gStackTraceHelper.fChildToParent[0] = -1; gStackTraceHelper.fChildToParent[1] = -1; + close(gStackTraceHelper.fParentToChild[0]); + close(gStackTraceHelper.fParentToChild[1]); + gStackTraceHelper.fParentToChild[0] = -1; gStackTraceHelper.fParentToChild[1] = -1; + + if (-1 == pipe2(gStackTraceHelper.fChildToParent, O_CLOEXEC)) { + fprintf(stdout, "pipe gStackTraceHelper.fChildToParent failed\n"); + return; + } + if (-1 == pipe2(gStackTraceHelper.fParentToChild, O_CLOEXEC)){ + close(gStackTraceHelper.fChildToParent[0]); close(gStackTraceHelper.fChildToParent[1]); + gStackTraceHelper.fChildToParent[0] = -1; gStackTraceHelper.fChildToParent[1] = -1; + fprintf(stdout, "pipe parentToChild failed\n"); + return; + } + + gStackTraceHelper.fHelperThread.reset(new std::thread(StackTraceMonitorThread)); + gStackTraceHelper.fHelperThread->detach(); +} + +////////////////////////////////////////////////////////////////////////////// +/// StackTrace helper thread to monitor the signal interrupts + +void TUnixSigHandling::StackTraceMonitorThread() +{ + int toParent = gStackTraceHelper.fChildToParent[1]; + int fromParent = gStackTraceHelper.fParentToChild[0]; + char buf[2]; buf[1] = '\0'; + while(true) { + int result = SignalSafeRead(fromParent, buf, 1, 5*60); + if (result < 0) { + UnixSetDefaultSignals(); + close(toParent); + SignalSafeErrWrite("\n\nTraceback helper thread failed to read from parent: "); + SignalSafeErrWrite(strerror(-result)); + SignalSafeErrWrite("\n"); + Exit(1, kFALSE); + } + if (buf[0] == '1') { + UnixSetDefaultSignals(); + StackTraceForkThread(); + SignalSafeWrite(toParent, buf); + } else if (buf[0] == '2') { + close(toParent); + close(fromParent); + toParent = gStackTraceHelper.fChildToParent[1]; + fromParent = gStackTraceHelper.fParentToChild[0]; + } else if (buf[0] == '3') { + break; + } else { + UnixSetDefaultSignals(); + close(toParent); + SignalSafeErrWrite("\n\nTraceback helper thread got unknown command from parent: "); + SignalSafeErrWrite(buf); + SignalSafeErrWrite("\n"); + Exit(1, kFALSE); + } + } + return; +} + +////////////////////////////////////////////////////////////////////////////// +/// One of StackTrace helper threads. +/// This thread is to trigger the monitor thread by pipe. + +void TUnixSigHandling::StackTraceTriggerThread() +{ + int result = SignalSafeWrite(gStackTraceHelper.fParentToChild[1], "1"); + if (result < 0) { + SignalSafeErrWrite("\n\nAttempt to request stacktrace failed: "); + SignalSafeErrWrite(strerror(-result)); + SignalSafeErrWrite("\n"); + return; + } + char buf[2]; buf[1] = '\0'; + if ((result = SignalSafeRead(gStackTraceHelper.fChildToParent[0], buf, 1)) < 0) { + SignalSafeErrWrite("\n\nWaiting for stacktrace completion failed: "); + SignalSafeErrWrite(strerror(-result)); + SignalSafeErrWrite("\n"); + return; + } +} + +////////////////////////////////////////////////////////////////////////////// +/// One of StackTrace helper threads. +/// This thread is to fork a new thread in order to print out StackTrace info. + +void TUnixSigHandling::StackTraceForkThread() +{ + char childStack[4*1024]; + char *childStackPtr = childStack + 4*1024; + int pid = +#ifdef __linux__ + clone(StackTraceExecScript, childStackPtr, CLONE_VM|CLONE_FS|SIGCHLD, nullptr); +#else + fork(); + if (childStackPtr) {} // Suppress 'unused variable' warning on non-Linux + if (pid == 0) { StackTraceExecScript(nullptr); Exit(0, kFALSE); } +#endif + if (pid == -1) { + SignalSafeErrWrite("(Attempt to perform stack dump failed.)\n"); + } else { + int status; + if (waitpid(pid, &status, 0) == -1) { + SignalSafeErrWrite("(Failed to wait on stack dump output.)\n"); + Exit(1, kFALSE); + } else { + Exit(0, kFALSE); + } + } +} + +////////////////////////////////////////////////////////////////////////////// +/// The new thread in StackTraceForkThread() is forked to run this function +/// to print out stack trace. + +int TUnixSigHandling::StackTraceExecScript(void * /*arg*/) +{ + char *const *argv = GetStackArgv(); +#ifdef __linux__ + syscall(SYS_execve, "/bin/sh", argv, __environ); +#else + execv("/bin/sh", argv); +#endif + Exit(0, kFALSE); + return 0; +} diff --git a/core/unix/src/TUnixSystem.cxx b/core/unix/src/TUnixSystem.cxx index 518db78850ce7..523d768138b9a 100644 --- a/core/unix/src/TUnixSystem.cxx +++ b/core/unix/src/TUnixSystem.cxx @@ -20,6 +20,7 @@ #include "RConfigure.h" #include "RConfig.h" #include "TUnixSystem.h" +#include "TUnixSigHandling.h" #include "TROOT.h" #include "TError.h" #include "TOrdCollection.h" @@ -355,63 +356,43 @@ const char *kServerPath = "/tmp"; const char *kProtocolName = "tcp"; //------------------- Unix TFdSet ---------------------------------------------- -#ifndef HOWMANY -# define HOWMANY(x, y) (((x)+((y)-1))/(y)) -#endif -const Int_t kNFDBITS = (sizeof(Long_t) * 8); // 8 bits per byte -#ifdef FD_SETSIZE -const Int_t kFDSETSIZE = FD_SETSIZE; // Linux = 1024 file descriptors -#else -const Int_t kFDSETSIZE = 256; // upto 256 file descriptors -#endif +TFdSet::TFdSet() { memset(fds_bits, 0, sizeof(fds_bits)); } +TFdSet::TFdSet(const TFdSet &org) { memcpy(fds_bits, org.fds_bits, sizeof(org.fds_bits)); } -class TFdSet { -private: - ULong_t fds_bits[HOWMANY(kFDSETSIZE, kNFDBITS)]; -public: - TFdSet() { memset(fds_bits, 0, sizeof(fds_bits)); } - TFdSet(const TFdSet &org) { memcpy(fds_bits, org.fds_bits, sizeof(org.fds_bits)); } - TFdSet &operator=(const TFdSet &rhs) { if (this != &rhs) { memcpy(fds_bits, rhs.fds_bits, sizeof(rhs.fds_bits));} return *this; } - void Zero() { memset(fds_bits, 0, sizeof(fds_bits)); } - void Set(Int_t n) - { - if (n >= 0 && n < kFDSETSIZE) { - fds_bits[n/kNFDBITS] |= (1UL << (n % kNFDBITS)); - } else { - ::Fatal("TFdSet::Set","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1); - } - } - void Clr(Int_t n) - { - if (n >= 0 && n < kFDSETSIZE) { - fds_bits[n/kNFDBITS] &= ~(1UL << (n % kNFDBITS)); - } else { - ::Fatal("TFdSet::Clr","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1); - } - } - Int_t IsSet(Int_t n) - { - if (n >= 0 && n < kFDSETSIZE) { - return (fds_bits[n/kNFDBITS] & (1UL << (n % kNFDBITS))) != 0; - } else { - ::Fatal("TFdSet::IsSet","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1); - return 0; - } +void TFdSet::Zero() { memset(fds_bits, 0, sizeof(fds_bits)); } + +void TFdSet::Set(Int_t n) +{ + if (n >= 0 && n < kFDSETSIZE) { + fds_bits[n/kNFDBITS] |= (1UL << (n % kNFDBITS)); + } else { + ::Fatal("TFdSet::Set","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1); } - ULong_t *GetBits() { return (ULong_t *)fds_bits; } -}; +} -//////////////////////////////////////////////////////////////////////////////// -/// Unix signal handler. +void TFdSet::Clr(Int_t n) +{ + if (n >= 0 && n < kFDSETSIZE) { + fds_bits[n/kNFDBITS] &= ~(1UL << (n % kNFDBITS)); + } else { + ::Fatal("TFdSet::Clr","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1); + } +} -static void SigHandler(ESignals sig) +Int_t TFdSet::IsSet(Int_t n) { - if (gSystem) - ((TUnixSystem*)gSystem)->DispatchSignals(sig); + if (n >= 0 && n < kFDSETSIZE) { + return (fds_bits[n/kNFDBITS] & (1UL << (n % kNFDBITS))) != 0; + } else { + ::Fatal("TFdSet::IsSet","fd (%d) out of range [0..%d]", n, kFDSETSIZE-1); + return 0; + } } +ULong_t* TFdSet::GetBits() { return (ULong_t *)fds_bits; } + //////////////////////////////////////////////////////////////////////////////// static const char *GetExePath() @@ -574,13 +555,10 @@ TUnixSystem::TUnixSystem() : TSystem("Unix", "Unix System") TUnixSystem::~TUnixSystem() { - UnixResetSignals(); - delete fReadmask; delete fWritemask; delete fReadready; delete fWriteready; - delete fSignals; } //////////////////////////////////////////////////////////////////////////////// @@ -595,19 +573,9 @@ Bool_t TUnixSystem::Init() fWritemask = new TFdSet; fReadready = new TFdSet; fWriteready = new TFdSet; - fSignals = new TFdSet; //--- install default handlers - UnixSignal(kSigChild, SigHandler); - UnixSignal(kSigBus, SigHandler); - UnixSignal(kSigSegmentationViolation, SigHandler); - UnixSignal(kSigIllegalInstruction, SigHandler); - UnixSignal(kSigSystem, SigHandler); - UnixSignal(kSigPipe, SigHandler); - UnixSignal(kSigAlarm, SigHandler); - UnixSignal(kSigUrgent, SigHandler); - UnixSignal(kSigFloatingException, SigHandler); - UnixSignal(kSigWindowChanged, SigHandler); + gSigHandling->Init(); #if defined(R__MACOSX) // trap loading of all dylibs to register dylib name, @@ -800,10 +768,7 @@ TFileHandler *TUnixSystem::RemoveFileHandler(TFileHandler *h) void TUnixSystem::AddSignalHandler(TSignalHandler *h) { - R__LOCKGUARD2(gSystemMutex); - - TSystem::AddSignalHandler(h); - UnixSignal(h->GetSignal(), SigHandler); + gSigHandling->AddSignalHandler(h); } //////////////////////////////////////////////////////////////////////////////// @@ -812,23 +777,7 @@ void TUnixSystem::AddSignalHandler(TSignalHandler *h) TSignalHandler *TUnixSystem::RemoveSignalHandler(TSignalHandler *h) { - if (!h) return 0; - - R__LOCKGUARD2(gSystemMutex); - - TSignalHandler *oh = TSystem::RemoveSignalHandler(h); - - Bool_t last = kTRUE; - TSignalHandler *hs; - TIter next(fSignalHandler); - - while ((hs = (TSignalHandler*) next())) { - if (hs->GetSignal() == h->GetSignal()) - last = kFALSE; - } - if (last) - ResetSignal(h->GetSignal(), kTRUE); - + TSignalHandler *oh = gSigHandling->RemoveSignalHandler(h); return oh; } @@ -838,10 +787,7 @@ TSignalHandler *TUnixSystem::RemoveSignalHandler(TSignalHandler *h) void TUnixSystem::ResetSignal(ESignals sig, Bool_t reset) { - if (reset) - UnixResetSignal(sig); - else - UnixSignal(sig, SigHandler); + gSigHandling->ResetSignal(sig, reset); } //////////////////////////////////////////////////////////////////////////////// @@ -849,7 +795,7 @@ void TUnixSystem::ResetSignal(ESignals sig, Bool_t reset) void TUnixSystem::ResetSignals() { - UnixResetSignals(); + gSigHandling->ResetSignals(); } //////////////////////////////////////////////////////////////////////////////// @@ -858,7 +804,16 @@ void TUnixSystem::ResetSignals() void TUnixSystem::IgnoreSignal(ESignals sig, Bool_t ignore) { - UnixIgnoreSignal(sig, ignore); + gSigHandling->IgnoreSignal(sig, ignore); +} + +//////////////////////////////////////////////////////////////////////////////// +/// If ignore is true ignore the specified signal, else restore previous +/// behaviour. + +Bool_t TUnixSystem::HaveTrappedSignal(Bool_t pendingOnly) +{ + return gSigHandling->HaveTrappedSignal(pendingOnly); } //////////////////////////////////////////////////////////////////////////////// @@ -871,7 +826,14 @@ void TUnixSystem::IgnoreSignal(ESignals sig, Bool_t ignore) void TUnixSystem::SigAlarmInterruptsSyscalls(Bool_t set) { - UnixSigAlarmInterruptsSyscalls(set); + gSigHandling->SigAlarmInterruptsSyscalls(set); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Obtain the current signal handlers +TSeqCollection *TUnixSystem::GetListOfSignalHandlers() +{ + return (TSeqCollection *)gSigHandling->GetListOfSignalHandlers(); } //////////////////////////////////////////////////////////////////////////////// @@ -1080,11 +1042,7 @@ void TUnixSystem::DispatchOneEvent(Bool_t pendingOnly) return; // check synchronous signals - if (fSigcnt > 0 && fSignalHandler->GetSize() > 0) - if (CheckSignals(kTRUE)) - if (!pendingOnly) return; - fSigcnt = 0; - fSignals->Zero(); + if (HaveTrappedSignal(pendingOnly)) return; // check synchronous timers Long_t nextto; @@ -1244,37 +1202,6 @@ Int_t TUnixSystem::Select(TFileHandler *h, Long_t to) //---- handling of system events ----------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// Check if some signals were raised and call their Notify() member. - -Bool_t TUnixSystem::CheckSignals(Bool_t sync) -{ - TSignalHandler *sh; - Int_t sigdone = -1; - { - TOrdCollectionIter it((TOrdCollection*)fSignalHandler); - - while ((sh = (TSignalHandler*)it.Next())) { - if (sync == sh->IsSync()) { - ESignals sig = sh->GetSignal(); - if ((fSignals->IsSet(sig) && sigdone == -1) || sigdone == sig) { - if (sigdone == -1) { - fSignals->Clr(sig); - sigdone = sig; - fSigcnt--; - } - if (sh->IsActive()) - sh->Notify(); - } - } - } - } - if (sigdone != -1) - return kTRUE; - - return kFALSE; -} - //////////////////////////////////////////////////////////////////////////////// /// Check if children have finished. @@ -3491,237 +3418,6 @@ int TUnixSystem::GetSockOpt(int sock, int opt, int *val) return 0; } -////////////////////////////////////////////////////////////////////////// -// // -// Static Protected Unix Interface functions. // -// // -////////////////////////////////////////////////////////////////////////// - -//---- signals ----------------------------------------------------------------- - -static struct Signalmap_t { - int fCode; - SigHandler_t fHandler; - struct sigaction *fOldHandler; - const char *fSigName; -} gSignalMap[kMAXSIGNALS] = { // the order of the signals should be identical - { SIGBUS, 0, 0, "bus error" }, // to the one in TSysEvtHandler.h - { SIGSEGV, 0, 0, "segmentation violation" }, - { SIGSYS, 0, 0, "bad argument to system call" }, - { SIGPIPE, 0, 0, "write on a pipe with no one to read it" }, - { SIGILL, 0, 0, "illegal instruction" }, - { SIGQUIT, 0, 0, "quit" }, - { SIGINT, 0, 0, "interrupt" }, - { SIGWINCH, 0, 0, "window size change" }, - { SIGALRM, 0, 0, "alarm clock" }, - { SIGCHLD, 0, 0, "death of a child" }, - { SIGURG, 0, 0, "urgent data arrived on an I/O channel" }, - { SIGFPE, 0, 0, "floating point exception" }, - { SIGTERM, 0, 0, "termination signal" }, - { SIGUSR1, 0, 0, "user-defined signal 1" }, - { SIGUSR2, 0, 0, "user-defined signal 2" } -}; - - -//////////////////////////////////////////////////////////////////////////////// -/// Call the signal handler associated with the signal. - -static void sighandler(int sig) -{ - for (int i= 0; i < kMAXSIGNALS; i++) { - if (gSignalMap[i].fCode == sig) { - (*gSignalMap[i].fHandler)((ESignals)i); - return; - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// Handle and dispatch signals. - -void TUnixSystem::DispatchSignals(ESignals sig) -{ - switch (sig) { - case kSigAlarm: - DispatchTimers(kFALSE); - break; - case kSigChild: - CheckChilds(); - break; - case kSigBus: - case kSigSegmentationViolation: - case kSigIllegalInstruction: - case kSigFloatingException: - Break("TUnixSystem::DispatchSignals", "%s", UnixSigname(sig)); - StackTrace(); - if (gApplication) - //sig is ESignal, should it be mapped to the correct signal number? - gApplication->HandleException(sig); - else - //map to the real signal code + set the - //high order bit to indicate a signal (?) - Exit(gSignalMap[sig].fCode + 0x80); - break; - case kSigSystem: - case kSigPipe: - Break("TUnixSystem::DispatchSignals", "%s", UnixSigname(sig)); - break; - case kSigWindowChanged: - Gl_windowchanged(); - break; - default: - fSignals->Set(sig); - fSigcnt++; - break; - } - - // check a-synchronous signals - if (fSigcnt > 0 && fSignalHandler->GetSize() > 0) - CheckSignals(kFALSE); -} - -//////////////////////////////////////////////////////////////////////////////// -/// Set a signal handler for a signal. - -void TUnixSystem::UnixSignal(ESignals sig, SigHandler_t handler) -{ - if (gEnv && !gEnv->GetValue("Root.ErrorHandlers", 1)) - return; - - if (gSignalMap[sig].fHandler != handler) { - struct sigaction sigact; - - gSignalMap[sig].fHandler = handler; - gSignalMap[sig].fOldHandler = new struct sigaction(); - -#if defined(R__SUN) - sigact.sa_handler = (void (*)())sighandler; -#elif defined(R__SOLARIS) - sigact.sa_handler = sighandler; -#elif defined(R__LYNXOS) -# if (__GNUG__>=3) - sigact.sa_handler = sighandler; -# else - sigact.sa_handler = (void (*)(...))sighandler; -# endif -#else - sigact.sa_handler = sighandler; -#endif - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; -#if defined(SA_RESTART) - sigact.sa_flags |= SA_RESTART; -#endif - if (sigaction(gSignalMap[sig].fCode, &sigact, - gSignalMap[sig].fOldHandler) < 0) - ::SysError("TUnixSystem::UnixSignal", "sigaction"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// If ignore is true ignore the specified signal, else restore previous -/// behaviour. - -void TUnixSystem::UnixIgnoreSignal(ESignals sig, Bool_t ignore) -{ - TTHREAD_TLS(Bool_t) ignoreSig[kMAXSIGNALS] = { kFALSE }; - TTHREAD_TLS_ARRAY(struct sigaction,kMAXSIGNALS,oldsigact); - - if (ignore != ignoreSig[sig]) { - ignoreSig[sig] = ignore; - if (ignore) { - struct sigaction sigact; -#if defined(R__SUN) - sigact.sa_handler = (void (*)())SIG_IGN; -#elif defined(R__SOLARIS) - sigact.sa_handler = (void (*)(int))SIG_IGN; -#else - sigact.sa_handler = SIG_IGN; -#endif - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - if (sigaction(gSignalMap[sig].fCode, &sigact, &oldsigact[sig]) < 0) - ::SysError("TUnixSystem::UnixIgnoreSignal", "sigaction"); - } else { - if (sigaction(gSignalMap[sig].fCode, &oldsigact[sig], 0) < 0) - ::SysError("TUnixSystem::UnixIgnoreSignal", "sigaction"); - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// When the argument is true the SIGALRM signal handler is set so that -/// interrupted syscalls will not be restarted by the kernel. This is -/// typically used in case one wants to put a timeout on an I/O operation. -/// By default interrupted syscalls will always be restarted (for all -/// signals). This can be controlled for each a-synchronous TTimer via -/// the method TTimer::SetInterruptSyscalls(). - -void TUnixSystem::UnixSigAlarmInterruptsSyscalls(Bool_t set) -{ - if (gSignalMap[kSigAlarm].fHandler) { - struct sigaction sigact; -#if defined(R__SUN) - sigact.sa_handler = (void (*)())sighandler; -#elif defined(R__SOLARIS) - sigact.sa_handler = sighandler; -#elif defined(R__LYNXOS) -# if (__GNUG__>=3) - sigact.sa_handler = sighandler; -# else - sigact.sa_handler = (void (*)(...))sighandler; -# endif -#else - sigact.sa_handler = sighandler; -#endif - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = 0; - if (set) { -#if defined(SA_INTERRUPT) // SunOS - sigact.sa_flags |= SA_INTERRUPT; -#endif - } else { -#if defined(SA_RESTART) - sigact.sa_flags |= SA_RESTART; -#endif - } - if (sigaction(gSignalMap[kSigAlarm].fCode, &sigact, 0) < 0) - ::SysError("TUnixSystem::UnixSigAlarmInterruptsSyscalls", "sigaction"); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// Return the signal name associated with a signal. - -const char *TUnixSystem::UnixSigname(ESignals sig) -{ - return gSignalMap[sig].fSigName; -} - -//////////////////////////////////////////////////////////////////////////////// -/// Restore old signal handler for specified signal. - -void TUnixSystem::UnixResetSignal(ESignals sig) -{ - if (gSignalMap[sig].fOldHandler) { - // restore old signal handler - if (sigaction(gSignalMap[sig].fCode, gSignalMap[sig].fOldHandler, 0) < 0) - ::SysError("TUnixSystem::UnixSignal", "sigaction"); - delete gSignalMap[sig].fOldHandler; - gSignalMap[sig].fOldHandler = 0; - gSignalMap[sig].fHandler = 0; - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// Restore old signal handlers. - -void TUnixSystem::UnixResetSignals() -{ - for (int sig = 0; sig < kMAXSIGNALS; sig++) - UnixResetSignal((ESignals)sig); -} - //---- time -------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////////////