The Gaudi Framework  v30r3 (a5ef0a68)
SignalMonitorSvc.cpp
Go to the documentation of this file.
1 /*
2  * SignalMonitorSvc.cpp
3  *
4  * Created on: Apr 14, 2010
5  * Author: Marco Clemencic
6  */
7 #include "GaudiKernel/Service.h"
9 
10 #include <csignal>
11 
12 #include <iostream>
13 
14 namespace Gaudi
15 {
16  namespace Utils
17  {
28  class SignalMonitorSvc : public extends<Service, Gaudi::ISignalMonitor>
29  {
30  public:
31 #ifdef _WIN32
32  typedef void( __cdecl* handler_t )( int );
33 #else
34  typedef struct sigaction handler_t;
35 #endif
36 
39  void monitorSignal( int signum, bool propagate ) override
40  {
41  if ( !m_monitored[signum] ) {
42  handler_t sa;
43  handler_t oldact;
44 #ifdef _WIN32
46  oldact = signal( signum, sa );
47 #else
48  sa.sa_handler = SignalMonitorSvc::dispatcher;
49  sigemptyset( &sa.sa_mask );
50  sa.sa_flags = 0;
51  sigaction( signum, &sa, &oldact );
52 #endif
53  m_oldActions[signum] = oldact;
55  }
56  }
57 
60  void ignoreSignal( int signum ) override
61  {
62  if ( m_monitored[signum] ) {
63 #ifdef _WIN32
64  (void)signal( signum, m_oldActions[signum] );
65 #else
66  sigaction( signum, &m_oldActions[signum], nullptr );
67 #endif
68  m_oldActions[signum] = m_defaultAction;
69  m_monitored[signum] = ignored;
70  }
71  }
72 
74  bool gotSignal( int signum ) const override { return m_caught[signum] != 0; }
75 
77  void setSignal( int signum ) override { m_caught[signum] = 1; }
78 
80  void clearSignal( int signum ) override { m_caught[signum] = 0; }
81 
83  SignalMonitorSvc( const std::string& name, ISvcLocator* svcLoc ) : base_class( name, svcLoc )
84  {
85 #ifdef _WIN32
86  m_defaultAction = SIG_DFL;
87 #else
88  m_defaultAction.sa_handler = SIG_DFL;
89  sigemptyset( &m_defaultAction.sa_mask );
90  m_defaultAction.sa_flags = 0;
91 #endif
92  for ( int i = 0; i < NSIG; ++i ) {
93  m_caught[i] = 0;
94  m_monitored[i] = ignored;
96  }
97 
98  setInstance( this );
99  }
100 
102  ~SignalMonitorSvc() override
103  {
104  for ( int i = 0; i < NSIG; ++i ) ignoreSignal( i );
105  setInstance( nullptr );
106  }
107 
108  private:
111  ignored, //< the signal is not monitored
112  trap, //< the signal is monitored and not propagated to previously registered handlers
113  propagate //< the signal is monitored and propagated to previously registered handlers
114  };
118  sig_atomic_t m_caught[NSIG];
120  handler_t m_defaultAction;
122  handler_t m_oldActions[NSIG];
123 
124  void i_handle( int signum )
125  {
126  m_caught[signum] = 1;
127  if ( m_monitored[signum] == propagate &&
128 #ifdef _WIN32
129  m_oldActions[signum] != SIG_DFL
130 #else
131  m_oldActions[signum].sa_handler != SIG_DFL
132 #endif
133  ) {
134 #ifdef _WIN32
135  m_oldActions[signum]( signum );
136 #else
137  m_oldActions[signum].sa_handler( signum );
138 #endif
139  }
140  }
141 
144 
145  static inline void setInstance( SignalMonitorSvc* i ) { s_instance = i; }
146 
149  static inline SignalMonitorSvc* instance() { return s_instance; }
150 
152  static void dispatcher( int signum );
153  };
154 
155  // Implementation of the signal handler function.
156  void SignalMonitorSvc::dispatcher( int signum )
157  {
158  if ( instance() ) instance()->i_handle( signum );
159  }
160 
161  } // namespace Utils
162 } // namespace Gaudi
163 
164 #include <cctype>
165 #include <sstream>
166 
167 #include <map>
168 
169 #include "GaudiKernel/HashMap.h"
170 
175 
176 namespace
177 {
178  // hack because windows doesn't provide sys_siglist
179  const char* sig_desc( int signum )
180  {
181  if ( signum >= NSIG || signum < 0 ) return nullptr;
182 #ifdef _WIN32
183  switch ( signum ) {
184  case SIGINT:
185  return "Interrupt";
186  case SIGILL:
187  return "Illegal instruction";
188  case SIGFPE:
189  return "Floating point exception";
190  case SIGSEGV:
191  return "Segmentation fault";
192  case SIGTERM:
193  return "Terminated";
194  case SIGBREAK:
195  return "Trace/breakpoint trap";
196  case SIGABRT:
197  return "Aborted";
198  default:
199  return 0;
200  }
201 #else
202  return sys_siglist[signum];
203 #endif
204  }
205 
207  class SigMap
208  {
209  public:
211  static const SigMap& instance()
212  {
213  static SigMap _instance;
214  return _instance;
215  }
217  inline const std::string& name( int signum ) const { return m_num2id[signum]; }
219  inline const std::string& desc( int signum ) const { return m_num2desc[signum]; }
221  inline int signum( const std::string& str ) const
222  {
223  auto it = m_name2num.find( str );
224  return it != m_name2num.end() ? it->second : -1;
225  }
226 
227  private:
230  SigMap()
231  {
232 #define addSignal( id ) i_addSignal( id, #id );
233 // List of signals from http://en.wikipedia.org/wiki/POSIX_signal
234 #ifdef SIGABRT
235  addSignal( SIGABRT ); // Process aborted
236 #endif
237 #ifdef SIGALRM
238  addSignal( SIGALRM ); // Signal raised by alarm
239 #endif
240 #ifdef SIGBUS
241  addSignal( SIGBUS ); // Bus error: "access to undefined portion of memory object"
242 #endif
243 #ifdef SIGCHLD
244  addSignal( SIGCHLD ); // Child process terminated, stopped (or continued*)
245 #endif
246 #ifdef SIGCONT
247  addSignal( SIGCONT ); // Continue if stopped
248 #endif
249 #ifdef SIGFPE
250  addSignal( SIGFPE ); // Floating point exception: "erroneous arithmetic operation"
251 #endif
252 #ifdef SIGHUP
253  addSignal( SIGHUP ); // Hangup
254 #endif
255 #ifdef SIGILL
256  addSignal( SIGILL ); // Illegal instruction
257 #endif
258 #ifdef SIGINT
259  addSignal( SIGINT ); // Interrupt
260 #endif
261 #ifdef SIGKILL
262  addSignal( SIGKILL ); // Kill (terminate immediately)
263 #endif
264 #ifdef SIGPIPE
265  addSignal( SIGPIPE ); // Write to pipe with no one reading
266 #endif
267 #ifdef SIGQUIT
268  addSignal( SIGQUIT ); // Quit and dump core
269 #endif
270 #ifdef SIGSEGV
271  addSignal( SIGSEGV ); // Segmentation violation
272 #endif
273 #ifdef SIGSTOP
274  addSignal( SIGSTOP ); // Stop executing temporarily
275 #endif
276 #ifdef SIGTERM
277  addSignal( SIGTERM ); // Termination (request to terminate)
278 #endif
279 #ifdef SIGTSTP
280  addSignal( SIGTSTP ); // Terminal stop signal
281 #endif
282 #ifdef SIGTTIN
283  addSignal( SIGTTIN ); // Background process attempting to read from tty ("in")
284 #endif
285 #ifdef SIGTTOU
286  addSignal( SIGTTOU ); // Background process attempting to write to tty ("out")
287 #endif
288 #ifdef SIGUSR1
289  addSignal( SIGUSR1 ); // User-defined 1
290 #endif
291 #ifdef SIGUSR2
292  addSignal( SIGUSR2 ); // User-defined 2
293 #endif
294 #ifdef SIGPOLL
295  addSignal( SIGPOLL ); // Pollable event
296 #endif
297 #ifdef SIGPROF
298  addSignal( SIGPROF ); // Profiling timer expired
299 #endif
300 #ifdef SIGSYS
301  addSignal( SIGSYS ); // Bad syscall
302 #endif
303 #ifdef SIGTRAP
304  addSignal( SIGTRAP ); // Trace/breakpoint trap
305 #endif
306 #ifdef SIGURG
307  addSignal( SIGURG ); // Urgent data available on socket
308 #endif
309 #ifdef SIGVTALRM
310  addSignal( SIGVTALRM ); // Signal raised by timer counting virtual time: "virtual timer expired"
311 #endif
312 #ifdef SIGXCPU
313  addSignal( SIGXCPU ); // CPU time limit exceeded
314 #endif
315 #ifdef SIGXFSZ
316  addSignal( SIGXFSZ ); // File size limit exceeded
317 #endif
318 #undef addSignal
319  }
321  inline void i_addSignal( int signum, const char* signame )
322  {
323  m_num2id[signum] = signame;
324  m_name2num[signame] = signum;
325  const char* desc = sig_desc( signum );
326  if ( desc ) {
327  m_num2desc[signum] = desc;
328  m_name2num[desc] = signum;
329  }
330  }
331  GaudiUtils::HashMap<std::string, int> m_name2num; //< Map signal string id or description to number
332  GaudiUtils::HashMap<int, std::string> m_num2id; //< Map signal number to string id
333  GaudiUtils::HashMap<int, std::string> m_num2desc; //< Map signal number to description
334  };
335 }
336 
337 namespace Gaudi
338 {
339  namespace Utils
340  {
350  class StopSignalHandler : public extends<Service, IIncidentListener>
351  {
352  public:
353  using extends::extends;
355  {
357  if ( sc.isFailure() ) {
358  return sc;
359  }
360  std::string serviceName( "Gaudi::Utils::SignalMonitorSvc" );
361  m_signalMonitor = serviceLocator()->service( serviceName );
362  if ( !m_signalMonitor ) {
363  error() << "Cannot retrieve " << serviceName << endmsg;
364  return StatusCode::FAILURE;
365  }
366  serviceName = "IncidentSvc";
367  m_incidentSvc = serviceLocator()->service( serviceName );
368  if ( !m_incidentSvc ) {
369  error() << "Cannot retrieve " << serviceName << endmsg;
370  return StatusCode::FAILURE;
371  }
372  // Get the IMainAppStatus interface of the ApplicationMgr
373  m_appProperty = serviceLocator();
374  if ( !m_appProperty ) {
375  warning() << "Cannot retrieve IProperty interface of ApplicationMgr, "
376  "the return code will not be changed"
377  << endmsg;
378  }
379  // Decode the signal names
380  for ( const auto& signame : m_usedSignals ) {
381  auto sigid = i_decodeSignal( signame );
382  if ( sigid.first >= 0 ) {
383  m_signals[sigid.first] = sigid.second;
384  }
385  }
386  debug() << "Stopping on the signals:" << endmsg;
387  const SigMap& sigmap( SigMap::instance() );
388  for ( const auto& s : m_signals ) {
389  debug() << "\t" << sigmap.name( s.first ) << ": " << sigmap.desc( s.first ) << " (" << s.first << ")";
390  if ( s.second ) debug() << " propagated";
391  debug() << endmsg;
392  // tell the signal monitor that we are interested in these signals
393  m_signalMonitor->monitorSignal( s.first, s.second );
394  }
395  m_stopRequested = false;
396  debug() << "Register to the IncidentSvc" << endmsg;
397  m_incidentSvc->addListener( this, IncidentType::BeginEvent );
398  return StatusCode::SUCCESS;
399  }
400  StatusCode finalize() override
401  {
402  m_incidentSvc->removeListener( this, IncidentType::BeginEvent );
403  m_incidentSvc.reset();
404  // disable the monitoring of the signals
405  std::for_each( std::begin( m_signals ), std::end( m_signals ), [&]( const std::pair<int, bool>& s ) {
406  // tell the signal monitor that we are interested in these signals
407  m_signalMonitor->ignoreSignal( s.first );
408  } );
409  m_signalMonitor.reset();
410  return Service::finalize();
411  }
412 
413  void handle( const Incident& ) override
414  {
415  if ( !m_stopRequested ) {
416  const SigMap& sigmap( SigMap::instance() );
417  for ( const auto& s : m_signals ) {
418  if ( !m_signalMonitor->gotSignal( s.first ) ) continue;
419  warning() << "Received signal '" << sigmap.name( s.first ) << "' (" << s.first;
420  const std::string& desc = sigmap.desc( s.first );
421  if ( !desc.empty() ) warning() << ", " << desc;
422  warning() << ")" << endmsg;
423  m_stopRequested = true;
424  // Report the termination by signal at the end of the application
426  if ( Gaudi::setAppReturnCode( m_appProperty, SignalOffset + s.first ).isFailure() ) {
427  error() << "Could not set return code of the application (" << SignalOffset + s.first << ")" << endmsg;
428  }
429  }
430  if ( m_stopRequested ) {
431  auto ep = serviceLocator()->as<IEventProcessor>();
432  if ( ep ) {
433  warning() << "Scheduling a stop" << endmsg;
434  ep->stopRun().ignore();
435  } else {
436  warning() << "Cannot stop the processing because the IEventProcessor interface cannot be retrieved."
437  << endmsg;
438  }
439  }
440  }
441  }
442 
443  private:
446  this,
447  "Signals",
448  {"SIGINT", "SIGXCPU"},
449  "List of signal names or numbers to use to schedule a stop. "
450  "If the signal is followed by a '+' the signal is propagated the previously "
451  "registered handler (if any)."};
455  bool m_stopRequested = false;
464  {
465  debug() << "Decoding signal declaration '" << sig << "'" << endmsg;
466  if ( sig.empty() || sig == "+" ) {
467  debug() << "Empty signal, ignored" << endmsg;
468  return {-1, false}; // silently ignore empty strings
469  }
470  const SigMap& sigmap( SigMap::instance() );
471  std::string signal = sig;
472  bool propagate = false;
473  // Check if the signal must be propagated
474  if ( signal[signal.size() - 1] == '+' ) {
475  debug() << "Must be propagated to previously registered signal handlers" << endmsg;
476  propagate = true;
477  signal.erase( signal.size() - 1, 1 ); // remove the '+' at the end of the string
478  }
479  int signum = -1;
480  // check if the signal is a number
481  if ( std::isdigit( signal[0] ) ) {
482  std::istringstream ss( signal );
483  ss >> signum;
484  } else {
485  // try to find the signal name in the list of known signals
486  signum = sigmap.signum( signal );
487  }
488  if ( signum < 0 ) {
489  warning() << "Cannot understand signal identifier '" << sig << "', ignored" << endmsg;
490  } else {
491  verbose() << "Matched signal '" << sigmap.name( signum ) << "' (" << signum;
492  const std::string& desc = sigmap.desc( signum );
493  if ( !desc.empty() ) {
494  verbose() << ", " << desc;
495  }
496  verbose() << ")" << endmsg;
497  }
498  return {signum, propagate};
499  }
500  };
501 
502  } // namespace Utils
503 } // namespace Gaudi
504 
505 // Initialization of static data member
507 
508 // ========================================================================
509 // Instantiation of a static factory class used by clients to create instances of this service
512 
513 // Instantiation of a static factory class used by clients to create instances of this service
514 typedef Gaudi::Utils::StopSignalHandler g_u_ssh;
515 DECLARE_COMPONENT( g_u_ssh )
void setSignal(int signum) override
Set the flag for the given signal, as if the signal was received.
constexpr static const auto FAILURE
Definition: StatusCode.h:88
StatusCode initialize() override
Definition: Service.cpp:63
sig_atomic_t m_caught[NSIG]
Array of flags for received signals.
Small smart pointer class with automatic reference counting for IInterface.
Definition: IConverter.h:15
T empty(T...args)
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition: ISvcLocator.h:25
const std::string & name() const override
Retrieve name of the service.
Definition: Service.cpp:288
StatusCode finalize() override
Definition: Service.cpp:173
SmartIF< IIncidentSvc > m_incidentSvc
Pointer to the incident service.
Implementation of property with value of concrete type.
Definition: Property.h:381
Gaudi::Utils::SignalMonitorSvc g_u_sms
~SignalMonitorSvc() override
Stop monitoring signals and clear the instance pointer.
bool gotSignal(int signum) const override
Check if the given signal has been received.
void ignoreSignal(int signum) override
Remove the specific signal handler for the requested signal, restoring the previous signal handler...
MsgStream & verbose() const
shortcut for the method msgStream(MSG::VERBOSE)
static void dispatcher(int signum)
Signal handler function.
class MergingTransformer< Out(const vector_of_const_< In > void
void clearSignal(int signum) override
Clear the flag for the given signal, so that a new occurrence can be identified.
T end(T...args)
SmartIF< IFace > as()
Definition: ISvcLocator.h:109
SmartIF< Gaudi::ISignalMonitor > m_signalMonitor
Pointer to the signal monitor service.
bool isFailure() const
Definition: StatusCode.h:139
STL class.
#define DECLARE_COMPONENT(type)
MonitoringMode
Possible monitoring modes.
StatusCode service(const Gaudi::Utils::TypeNameString &name, T *&svc, bool createIf=true)
Templated method to access a service by name.
Definition: ISvcLocator.h:79
std::map< int, bool > m_signals
Map of monitored signal numbers to the flag telling if they have to be propagated or not...
static void setInstance(SignalMonitorSvc *i)
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
MsgStream & warning() const
shortcut for the method msgStream(MSG::WARNING)
static SignalMonitorSvc * instance()
Method to get the singleton instance.
Implementation of Gaudi::ISignalMonitor.
T isdigit(T...args)
This class is used for returning status codes from appropriate routines.
Definition: StatusCode.h:51
SignalMonitorSvc(const std::string &name, ISvcLocator *svcLoc)
Initialize internal variables of the service and set the instance pointer.
void handle(const Incident &) override
T erase(T...args)
SmartIF< IProperty > m_appProperty
Pointer to the interface to set the return code of the application.
std::pair< int, bool > i_decodeSignal(const std::string &sig)
Function to translate the signal name to the signal number.
Service that stop the processing if a signal is received.
StatusCode setAppReturnCode(SmartIF< IProperty > &appmgr, int value, bool force=false)
Set the application return code.
Definition: AppReturnCode.h:51
constexpr static const auto SUCCESS
Definition: StatusCode.h:87
static SignalMonitorSvc * s_instance
Pointer to the current instance.
T find(T...args)
T size(T...args)
void monitorSignal(int signum, bool propagate) override
Declare a signal to be monitored.
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
Base class used to extend a class implementing other interfaces.
Definition: extends.h:10
T begin(T...args)
Base class for all Incidents (computing events).
Definition: Incident.h:17
string s
Definition: gaudirun.py:253
The IEventProcessor is the interface to process events.
#define addSignal(id)
MonitoringMode m_monitored[NSIG]
Array of flags to keep track of monitored signals.
constexpr int SignalOffset
Definition: AppReturnCode.h:36
handler_t m_oldActions[NSIG]
List of replaced signal actions (for the recovery when disable the monitoring).
#define __cdecl
Definition: xtoa.h:2
handler_t m_defaultAction
Helper variable for default signal action.
T for_each(T...args)
Gaudi::Utils::StopSignalHandler g_u_ssh
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator.
Definition: Service.cpp:291
Helper functions to set/get the application return code.
Definition: __init__.py:1
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:209