The Gaudi Framework  v33r1 (b1225454)
SignalMonitorSvc.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2019 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
11 /*
12  * SignalMonitorSvc.cpp
13  *
14  * Created on: Apr 14, 2010
15  * Author: Marco Clemencic
16  */
17 #include "GaudiKernel/Service.h"
19 
20 #include <csignal>
21 
22 #include <iostream>
23 
24 namespace Gaudi {
25  namespace Utils {
36  class SignalMonitorSvc : public extends<Service, Gaudi::ISignalMonitor> {
37  public:
38 #ifdef _WIN32
39  typedef void( __cdecl* handler_t )( int );
40 #else
41  typedef struct sigaction handler_t;
42 #endif
43 
46  void monitorSignal( int signum, bool propagate ) override {
47  if ( !m_monitored[signum] ) {
48  handler_t sa;
49  handler_t oldact;
50 #ifdef _WIN32
52  oldact = signal( signum, sa );
53 #else
54  sa.sa_handler = SignalMonitorSvc::dispatcher;
55  sigemptyset( &sa.sa_mask );
56  sa.sa_flags = 0;
57  sigaction( signum, &sa, &oldact );
58 #endif
59  m_oldActions[signum] = oldact;
61  }
62  }
63 
66  void ignoreSignal( int signum ) override {
67  if ( m_monitored[signum] ) {
68 #ifdef _WIN32
69  (void)signal( signum, m_oldActions[signum] );
70 #else
71  sigaction( signum, &m_oldActions[signum], nullptr );
72 #endif
73  m_oldActions[signum] = m_defaultAction;
74  m_monitored[signum] = ignored;
75  }
76  }
77 
79  bool gotSignal( int signum ) const override { return m_caught[signum] != 0; }
80 
82  void setSignal( int signum ) override { m_caught[signum] = 1; }
83 
85  void clearSignal( int signum ) override { m_caught[signum] = 0; }
86 
88  SignalMonitorSvc( const std::string& name, ISvcLocator* svcLoc ) : base_class( name, svcLoc ) {
89 #ifdef _WIN32
90  m_defaultAction = SIG_DFL;
91 #else
92  m_defaultAction.sa_handler = SIG_DFL;
93  sigemptyset( &m_defaultAction.sa_mask );
94  m_defaultAction.sa_flags = 0;
95 #endif
96  for ( int i = 0; i < NSIG; ++i ) {
97  m_caught[i] = 0;
98  m_monitored[i] = ignored;
100  }
101 
102  setInstance( this );
103  }
104 
106  ~SignalMonitorSvc() override {
107  for ( int i = 0; i < NSIG; ++i ) ignoreSignal( i );
108  setInstance( nullptr );
109  }
110 
111  private:
114  ignored, //< the signal is not monitored
115  trap, //< the signal is monitored and not propagated to previously registered handlers
116  propagate //< the signal is monitored and propagated to previously registered handlers
117  };
121  sig_atomic_t m_caught[NSIG];
123 #ifdef _WIN32
124  handler_t m_defaultAction{nullptr};
125 #else
127 #endif
128  handler_t m_oldActions[NSIG];
130 
131  void i_handle( int signum ) {
132  m_caught[signum] = 1;
133  if ( m_monitored[signum] == propagate &&
134 #ifdef _WIN32
135  m_oldActions[signum] != SIG_DFL
136 #else
137  m_oldActions[signum].sa_handler != SIG_DFL
138 #endif
139  ) {
140 #ifdef _WIN32
141  m_oldActions[signum]( signum );
142 #else
143  m_oldActions[signum].sa_handler( signum );
144 #endif
145  }
146  }
147 
150 
151  static inline void setInstance( SignalMonitorSvc* i ) { s_instance = i; }
152 
155  static inline SignalMonitorSvc* instance() { return s_instance; }
156 
158  static void dispatcher( int signum );
159  };
160 
161  // Implementation of the signal handler function.
162  void SignalMonitorSvc::dispatcher( int signum ) {
163  if ( instance() ) instance()->i_handle( signum );
164  }
165 
166  } // namespace Utils
167 } // namespace Gaudi
168 
169 #include <cctype>
170 #include <sstream>
171 
172 #include <map>
173 
174 #include "GaudiKernel/HashMap.h"
175 
180 
181 namespace {
182  // hack because windows doesn't provide sys_siglist
183  const char* sig_desc( int signum ) {
184  if ( signum >= NSIG || signum < 0 ) return nullptr;
185 #ifdef _WIN32
186  switch ( signum ) {
187  case SIGINT:
188  return "Interrupt";
189  case SIGILL:
190  return "Illegal instruction";
191  case SIGFPE:
192  return "Floating point exception";
193  case SIGSEGV:
194  return "Segmentation fault";
195  case SIGTERM:
196  return "Terminated";
197  case SIGBREAK:
198  return "Trace/breakpoint trap";
199  case SIGABRT:
200  return "Aborted";
201  default:
202  return 0;
203  }
204 #else
205  return sys_siglist[signum];
206 #endif
207  }
208 
210  class SigMap {
211  public:
213  static const SigMap& instance() {
214  static SigMap _instance;
215  return _instance;
216  }
218  inline const std::string& name( int signum ) const { return m_num2id[signum]; }
220  inline const std::string& desc( int signum ) const { return m_num2desc[signum]; }
222  inline int signum( const std::string& str ) const {
223  auto it = m_name2num.find( str );
224  return it != m_name2num.end() ? it->second : -1;
225  }
226 
227  private:
230  SigMap() {
231 #define addSignal( id ) i_addSignal( id, #id );
232 // List of signals from http://en.wikipedia.org/wiki/POSIX_signal
233 #ifdef SIGABRT
234  addSignal( SIGABRT ); // Process aborted
235 #endif
236 #ifdef SIGALRM
237  addSignal( SIGALRM ); // Signal raised by alarm
238 #endif
239 #ifdef SIGBUS
240  addSignal( SIGBUS ); // Bus error: "access to undefined portion of memory object"
241 #endif
242 #ifdef SIGCHLD
243  addSignal( SIGCHLD ); // Child process terminated, stopped (or continued*)
244 #endif
245 #ifdef SIGCONT
246  addSignal( SIGCONT ); // Continue if stopped
247 #endif
248 #ifdef SIGFPE
249  addSignal( SIGFPE ); // Floating point exception: "erroneous arithmetic operation"
250 #endif
251 #ifdef SIGHUP
252  addSignal( SIGHUP ); // Hangup
253 #endif
254 #ifdef SIGILL
255  addSignal( SIGILL ); // Illegal instruction
256 #endif
257 #ifdef SIGINT
258  addSignal( SIGINT ); // Interrupt
259 #endif
260 #ifdef SIGKILL
261  addSignal( SIGKILL ); // Kill (terminate immediately)
262 #endif
263 #ifdef SIGPIPE
264  addSignal( SIGPIPE ); // Write to pipe with no one reading
265 #endif
266 #ifdef SIGQUIT
267  addSignal( SIGQUIT ); // Quit and dump core
268 #endif
269 #ifdef SIGSEGV
270  addSignal( SIGSEGV ); // Segmentation violation
271 #endif
272 #ifdef SIGSTOP
273  addSignal( SIGSTOP ); // Stop executing temporarily
274 #endif
275 #ifdef SIGTERM
276  addSignal( SIGTERM ); // Termination (request to terminate)
277 #endif
278 #ifdef SIGTSTP
279  addSignal( SIGTSTP ); // Terminal stop signal
280 #endif
281 #ifdef SIGTTIN
282  addSignal( SIGTTIN ); // Background process attempting to read from tty ("in")
283 #endif
284 #ifdef SIGTTOU
285  addSignal( SIGTTOU ); // Background process attempting to write to tty ("out")
286 #endif
287 #ifdef SIGUSR1
288  addSignal( SIGUSR1 ); // User-defined 1
289 #endif
290 #ifdef SIGUSR2
291  addSignal( SIGUSR2 ); // User-defined 2
292 #endif
293 #ifdef SIGPOLL
294  addSignal( SIGPOLL ); // Pollable event
295 #endif
296 #ifdef SIGPROF
297  addSignal( SIGPROF ); // Profiling timer expired
298 #endif
299 #ifdef SIGSYS
300  addSignal( SIGSYS ); // Bad syscall
301 #endif
302 #ifdef SIGTRAP
303  addSignal( SIGTRAP ); // Trace/breakpoint trap
304 #endif
305 #ifdef SIGURG
306  addSignal( SIGURG ); // Urgent data available on socket
307 #endif
308 #ifdef SIGVTALRM
309  addSignal( SIGVTALRM ); // Signal raised by timer counting virtual time: "virtual timer expired"
310 #endif
311 #ifdef SIGXCPU
312  addSignal( SIGXCPU ); // CPU time limit exceeded
313 #endif
314 #ifdef SIGXFSZ
315  addSignal( SIGXFSZ ); // File size limit exceeded
316 #endif
317 #undef addSignal
318  }
320  inline void i_addSignal( int signum, const char* signame ) {
321  m_num2id[signum] = signame;
322  m_name2num[signame] = signum;
323  const char* desc = sig_desc( signum );
324  if ( desc ) {
325  m_num2desc[signum] = desc;
326  m_name2num[desc] = signum;
327  }
328  }
329  GaudiUtils::HashMap<std::string, int> m_name2num; //< Map signal string id or description to number
330  GaudiUtils::HashMap<int, std::string> m_num2id; //< Map signal number to string id
331  GaudiUtils::HashMap<int, std::string> m_num2desc; //< Map signal number to description
332  };
333 } // namespace
334 
335 namespace Gaudi {
336  namespace Utils {
346  class StopSignalHandler : public extends<Service, IIncidentListener> {
347  public:
348  using extends::extends;
349  StatusCode initialize() override {
351  if ( sc.isFailure() ) { return sc; }
352  std::string serviceName( "Gaudi::Utils::SignalMonitorSvc" );
353  m_signalMonitor = serviceLocator()->service( serviceName );
354  if ( !m_signalMonitor ) {
355  error() << "Cannot retrieve " << serviceName << endmsg;
356  return StatusCode::FAILURE;
357  }
358  serviceName = "IncidentSvc";
359  m_incidentSvc = serviceLocator()->service( serviceName );
360  if ( !m_incidentSvc ) {
361  error() << "Cannot retrieve " << serviceName << endmsg;
362  return StatusCode::FAILURE;
363  }
364  // Get the IMainAppStatus interface of the ApplicationMgr
366  if ( !m_appProperty ) {
367  warning() << "Cannot retrieve IProperty interface of ApplicationMgr, "
368  "the return code will not be changed"
369  << endmsg;
370  }
371  // Decode the signal names
372  for ( const auto& signame : m_usedSignals ) {
373  auto sigid = i_decodeSignal( signame );
374  if ( sigid.first >= 0 ) { m_signals[sigid.first] = sigid.second; }
375  }
376  debug() << "Stopping on the signals:" << endmsg;
377  const SigMap& sigmap( SigMap::instance() );
378  for ( const auto& s : m_signals ) {
379  debug() << "\t" << sigmap.name( s.first ) << ": " << sigmap.desc( s.first ) << " (" << s.first << ")";
380  if ( s.second ) debug() << " propagated";
381  debug() << endmsg;
382  // tell the signal monitor that we are interested in these signals
383  m_signalMonitor->monitorSignal( s.first, s.second );
384  }
385  m_stopRequested = false;
386  debug() << "Register to the IncidentSvc" << endmsg;
387  m_incidentSvc->addListener( this, IncidentType::BeginEvent );
388  return StatusCode::SUCCESS;
389  }
390  StatusCode finalize() override {
391  m_incidentSvc->removeListener( this, IncidentType::BeginEvent );
393  // disable the monitoring of the signals
395  // tell the signal monitor that we are interested in these signals
396  m_signalMonitor->ignoreSignal( s.first );
397  } );
399  return Service::finalize();
400  }
401 
402  void handle( const Incident& ) override {
403  if ( !m_stopRequested ) {
404  const SigMap& sigmap( SigMap::instance() );
405  for ( const auto& s : m_signals ) {
406  if ( !m_signalMonitor->gotSignal( s.first ) ) continue;
407  warning() << "Received signal '" << sigmap.name( s.first ) << "' (" << s.first;
408  const std::string& desc = sigmap.desc( s.first );
409  if ( !desc.empty() ) warning() << ", " << desc;
410  warning() << ")" << endmsg;
411  m_stopRequested = true;
412  // Report the termination by signal at the end of the application
415  error() << "Could not set return code of the application (" << SignalOffset + s.first << ")" << endmsg;
416  }
417  }
418  if ( m_stopRequested ) {
419  auto ep = serviceLocator()->as<IEventProcessor>();
420  if ( ep ) {
421  warning() << "Scheduling a stop" << endmsg;
422  ep->stopRun().ignore();
423  } else {
424  warning() << "Cannot stop the processing because the IEventProcessor interface cannot be retrieved."
425  << endmsg;
426  }
427  }
428  }
429  }
430 
431  private:
434  this,
435  "Signals",
436  {"SIGINT", "SIGXCPU"},
437  "List of signal names or numbers to use to schedule a stop. "
438  "If the signal is followed by a '+' the signal is propagated the previously "
439  "registered handler (if any)."};
443  bool m_stopRequested = false;
452  debug() << "Decoding signal declaration '" << sig << "'" << endmsg;
453  if ( sig.empty() || sig == "+" ) {
454  debug() << "Empty signal, ignored" << endmsg;
455  return {-1, false}; // silently ignore empty strings
456  }
457  const SigMap& sigmap( SigMap::instance() );
458  std::string signal = sig;
459  bool propagate = false;
460  // Check if the signal must be propagated
461  if ( signal[signal.size() - 1] == '+' ) {
462  debug() << "Must be propagated to previously registered signal handlers" << endmsg;
463  propagate = true;
464  signal.erase( signal.size() - 1, 1 ); // remove the '+' at the end of the string
465  }
466  int signum = -1;
467  // check if the signal is a number
468  if ( std::isdigit( signal[0] ) ) {
469  std::istringstream ss( signal );
470  ss >> signum;
471  } else {
472  // try to find the signal name in the list of known signals
473  signum = sigmap.signum( signal );
474  }
475  if ( signum < 0 ) {
476  warning() << "Cannot understand signal identifier '" << sig << "', ignored" << endmsg;
477  } else {
478  verbose() << "Matched signal '" << sigmap.name( signum ) << "' (" << signum;
479  const std::string& desc = sigmap.desc( signum );
480  if ( !desc.empty() ) { verbose() << ", " << desc; }
481  verbose() << ")" << endmsg;
482  }
483  return {signum, propagate};
484  }
485  };
486 
487  } // namespace Utils
488 } // namespace Gaudi
489 
490 // Initialization of static data member
492 
493 // ========================================================================
494 // Instantiation of a static factory class used by clients to create instances of this service
497 
498 // Instantiation of a static factory class used by clients to create instances of this service
499 typedef Gaudi::Utils::StopSignalHandler g_u_ssh;
void setSignal(int signum) override
Set the flag for the given signal, as if the signal was received.
StatusCode initialize() override
Definition: Service.cpp:70
sig_atomic_t m_caught[NSIG]
Array of flags for received signals.
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator.
Definition: Service.cpp:287
T empty(T... args)
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition: ISvcLocator.h:35
bool m_stopRequested
Flag to remember if the stop has been requested because of a signal.
StatusCode finalize() override
Definition: Service.cpp:174
SmartIF< IIncidentSvc > m_incidentSvc
Pointer to the incident service.
Implementation of property with value of concrete type.
Definition: Property.h:370
MsgStream & warning() const
shortcut for the method msgStream(MSG::WARNING)
Gaudi::Utils::SignalMonitorSvc g_u_sms
~SignalMonitorSvc() override
Stop monitoring signals and clear the instance pointer.
void ignoreSignal(int signum) override
Remove the specific signal handler for the requested signal, restoring the previous signal handler.
constexpr static const auto SUCCESS
Definition: StatusCode.h:100
static void dispatcher(int signum)
Signal handler function.
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:113
SmartIF< Gaudi::ISignalMonitor > m_signalMonitor
Pointer to the signal monitor service.
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:86
std::map< int, bool > m_signals
Map of monitored signal numbers to the flag telling if they have to be propagated or not.
const std::string & name() const override
Retrieve name of the service.
Definition: Service.cpp:284
static void setInstance(SignalMonitorSvc *i)
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
virtual bool gotSignal(int signum) const =0
Tell if the given signal has been received or not.
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:61
SignalMonitorSvc(const std::string &name, ISvcLocator *svcLoc)
Initialize internal variables of the service and set the instance pointer.
virtual void ignoreSignal(int signum)=0
Ignore future occurrences of the given signal number.
void handle(const Incident &) override
Gaudi::Property< std::vector< std::string > > m_usedSignals
List of signal names or numbers (encoded as strings) to use to schedule a stop.
MsgStream & verbose() const
shortcut for the method msgStream(MSG::VERBOSE)
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.
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
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:59
static SignalMonitorSvc * s_instance
Pointer to the current instance.
T find(T... args)
void monitorSignal(int signum, bool propagate) override
Declare a signal to be monitored.
Base class used to extend a class implementing other interfaces.
Definition: extends.h:20
T begin(T... args)
bool gotSignal(int signum) const override
Check if the given signal has been received.
Base class for all Incidents (computing events).
Definition: Incident.h:27
virtual void addListener(IIncidentListener *lis, const std::string &type="", long priority=0, bool rethrow=false, bool singleShot=false)=0
Add listener.
string s
Definition: gaudirun.py:328
constexpr static const auto FAILURE
Definition: StatusCode.h:101
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:44
void reset(TYPE *ptr=nullptr)
Set the internal pointer to the passed one disposing of the old one.
Definition: SmartIF.h:96
handler_t m_oldActions[NSIG]
List of replaced signal actions (for the recovery when disable the monitoring).
virtual void removeListener(IIncidentListener *lis, const std::string &type="")=0
Remove listener.
bool isFailure() const
Definition: StatusCode.h:145
handler_t m_defaultAction
Helper variable for default signal action.
T for_each(T... args)
Gaudi::Utils::StopSignalHandler g_u_ssh
virtual void monitorSignal(int signum, bool propagate=true)=0
Add a signal (number) to the list of signals to be monitored.
Header file for std:chrono::duration-based Counters.
Definition: __init__.py:1
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:202