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  namespace Utils {
26  class SignalMonitorSvc: public extends<Service,
27  Gaudi::ISignalMonitor> {
28  public:
29 #ifdef _WIN32
30  typedef void (__cdecl *handler_t)(int);
31 #else
32  typedef struct sigaction handler_t;
33 #endif
34 
37  void monitorSignal(int signum, bool propagate) override {
38  if (!m_monitored[signum]) {
39  handler_t sa;
40  handler_t oldact;
41 #ifdef _WIN32
43  oldact = signal(signum, sa);
44 #else
45  sa.sa_handler = SignalMonitorSvc::dispatcher;
46  sigemptyset(&sa.sa_mask);
47  sa.sa_flags = 0;
48  sigaction(signum, &sa, &oldact);
49 #endif
50  m_oldActions[signum] = oldact;
53  }
54  }
55 
58  void ignoreSignal(int signum) override {
59  if (m_monitored[signum]) {
60 #ifdef _WIN32
61  (void) signal(signum, m_oldActions[signum]);
62 #else
63  sigaction(signum, &m_oldActions[signum], nullptr);
64 #endif
65  m_oldActions[signum] = m_defaultAction;
66  m_monitored[signum] = ignored;
67  }
68  }
69 
71  bool gotSignal(int signum) const override {
72  return m_caught[signum] != 0;
73  }
74 
76  void setSignal(int signum) override {
77  m_caught[signum] = 1;
78  }
79 
81  void clearSignal(int signum) override {
82  m_caught[signum] = 0;
83  }
84 
86  SignalMonitorSvc(const std::string& name, ISvcLocator* svcLoc): base_class(name, svcLoc) {
87 #ifdef _WIN32
88  m_defaultAction = SIG_DFL;
89 #else
90  m_defaultAction.sa_handler = SIG_DFL;
91  sigemptyset(&m_defaultAction.sa_mask);
92  m_defaultAction.sa_flags = 0;
93 #endif
94  for(int i = 0; i < NSIG; ++i){
95  m_caught[i] = 0;
98  }
99 
100  setInstance(this);
101  }
102 
104  ~SignalMonitorSvc() override {
105  for (int i = 0; i < NSIG; ++i) ignoreSignal(i);
106  setInstance(nullptr);
107  }
108 
109  private:
112  ignored, //< the signal is not monitored
113  trap, //< the signal is monitored and not propagated to previously registered handlers
114  propagate //< the signal is monitored and propagated to previously registered handlers
115  };
119  sig_atomic_t m_caught[NSIG];
121  handler_t m_defaultAction;
123  handler_t m_oldActions[NSIG];
124 
125  void i_handle(int signum) {
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) {
146  s_instance = i;
147  }
148 
151  static inline SignalMonitorSvc *instance() {
152  return s_instance;
153  }
154 
156  static void dispatcher(int signum);
157  };
158 
159  // Implementation of the signal handler function.
161  if (instance()) instance()->i_handle(signum);
162  }
163 
164  } // namespace Utils
165 } // namespace Gaudi
166 
167 #include <cctype>
168 #include <sstream>
169 
170 #include <map>
171 
172 #include "GaudiKernel/HashMap.h"
173 
178 
179 namespace {
180  // hack because windows doesn't provide sys_siglist
181  const char *sig_desc(int signum) {
182  if (signum >= NSIG || signum < 0)
183  return nullptr;
184 #ifdef _WIN32
185  switch (signum) {
186  case SIGINT: return "Interrupt";
187  case SIGILL: return "Illegal instruction";
188  case SIGFPE: return "Floating point exception";
189  case SIGSEGV: return "Segmentation fault";
190  case SIGTERM: return "Terminated";
191  case SIGBREAK: return "Trace/breakpoint trap";
192  case SIGABRT: return "Aborted";
193  default: return 0;
194  }
195 #else
196  return sys_siglist[signum];
197 #endif
198  }
199 
201  class SigMap {
202  public:
204  static const SigMap& instance() {
205  static SigMap _instance;
206  return _instance;
207  }
209  inline const std::string &name(int signum) const {
210  return m_num2id[signum];
211  }
213  inline const std::string &desc(int signum) const {
214  return m_num2desc[signum];
215  }
217  inline int signum(const std::string &str) const {
218  auto it = m_name2num.find(str);
219  return it != m_name2num.end() ? it->second : -1;
220  }
221  private:
224  SigMap(){
225 #define addSignal(id) i_addSignal(id, #id);
226  // List of signals from http://en.wikipedia.org/wiki/POSIX_signal
227 #ifdef SIGABRT
228  addSignal(SIGABRT); // Process aborted
229 #endif
230 #ifdef SIGALRM
231  addSignal(SIGALRM); // Signal raised by alarm
232 #endif
233 #ifdef SIGBUS
234  addSignal(SIGBUS); // Bus error: "access to undefined portion of memory object"
235 #endif
236 #ifdef SIGCHLD
237  addSignal(SIGCHLD); // Child process terminated, stopped (or continued*)
238 #endif
239 #ifdef SIGCONT
240  addSignal(SIGCONT); // Continue if stopped
241 #endif
242 #ifdef SIGFPE
243  addSignal(SIGFPE); // Floating point exception: "erroneous arithmetic operation"
244 #endif
245 #ifdef SIGHUP
246  addSignal(SIGHUP); // Hangup
247 #endif
248 #ifdef SIGILL
249  addSignal(SIGILL); // Illegal instruction
250 #endif
251 #ifdef SIGINT
252  addSignal(SIGINT); // Interrupt
253 #endif
254 #ifdef SIGKILL
255  addSignal(SIGKILL); // Kill (terminate immediately)
256 #endif
257 #ifdef SIGPIPE
258  addSignal(SIGPIPE); // Write to pipe with no one reading
259 #endif
260 #ifdef SIGQUIT
261  addSignal(SIGQUIT); // Quit and dump core
262 #endif
263 #ifdef SIGSEGV
264  addSignal(SIGSEGV); // Segmentation violation
265 #endif
266 #ifdef SIGSTOP
267  addSignal(SIGSTOP); // Stop executing temporarily
268 #endif
269 #ifdef SIGTERM
270  addSignal(SIGTERM); // Termination (request to terminate)
271 #endif
272 #ifdef SIGTSTP
273  addSignal(SIGTSTP); // Terminal stop signal
274 #endif
275 #ifdef SIGTTIN
276  addSignal(SIGTTIN); // Background process attempting to read from tty ("in")
277 #endif
278 #ifdef SIGTTOU
279  addSignal(SIGTTOU); // Background process attempting to write to tty ("out")
280 #endif
281 #ifdef SIGUSR1
282  addSignal(SIGUSR1); // User-defined 1
283 #endif
284 #ifdef SIGUSR2
285  addSignal(SIGUSR2); // User-defined 2
286 #endif
287 #ifdef SIGPOLL
288  addSignal(SIGPOLL); // Pollable event
289 #endif
290 #ifdef SIGPROF
291  addSignal(SIGPROF); // Profiling timer expired
292 #endif
293 #ifdef SIGSYS
294  addSignal(SIGSYS); // Bad syscall
295 #endif
296 #ifdef SIGTRAP
297  addSignal(SIGTRAP); // Trace/breakpoint trap
298 #endif
299 #ifdef SIGURG
300  addSignal(SIGURG); // Urgent data available on socket
301 #endif
302 #ifdef SIGVTALRM
303  addSignal(SIGVTALRM); // Signal raised by timer counting virtual time: "virtual timer expired"
304 #endif
305 #ifdef SIGXCPU
306  addSignal(SIGXCPU); // CPU time limit exceeded
307 #endif
308 #ifdef SIGXFSZ
309  addSignal(SIGXFSZ); // File size limit exceeded
310 #endif
311 #undef addSignal
312  }
314  inline void i_addSignal(int signum, const char *signame) {
315  m_num2id[signum] = signame;
316  m_name2num[signame] = signum;
317  const char* desc = sig_desc(signum);
318  if (desc) {
319  m_num2desc[signum] = desc;
320  m_name2num[desc] = signum;
321  }
322  }
323  GaudiUtils::HashMap<std::string, int> m_name2num; //< Map signal string id or description to number
324  GaudiUtils::HashMap<int, std::string> m_num2id; //< Map signal number to string id
325  GaudiUtils::HashMap<int, std::string> m_num2desc; //< Map signal number to description
326  };
327 }
328 
329 namespace Gaudi {
330  namespace Utils {
340  class StopSignalHandler: public extends<Service,
341  IIncidentListener> {
342  public:
343  StopSignalHandler(const std::string& name, ISvcLocator* svcLoc): base_class(name, svcLoc) {
345  m_usedSignals.push_back("SIGINT");
346  m_usedSignals.push_back("SIGXCPU");
347  m_stopRequested = false;
348  declareProperty("Signals", m_usedSignals,
349  "List of signal names or numbers to use to schedule a stop. "
350  "If the signal is followed by a '+' the signal is propagated the previously "
351  "registered handler (if any).");
352  }
353  StatusCode initialize() override {
355  if (sc.isFailure()) {
356  return sc;
357  }
358  std::string serviceName("Gaudi::Utils::SignalMonitorSvc");
359  m_signalMonitor = serviceLocator()->service(serviceName);
360  if ( ! m_signalMonitor ) {
361  error() << "Cannot retrieve " << serviceName << endmsg;
362  return StatusCode::FAILURE;
363  }
364  serviceName = "IncidentSvc";
365  m_incidentSvc = serviceLocator()->service(serviceName);
366  if ( ! m_incidentSvc ) {
367  error() << "Cannot retrieve " << serviceName << endmsg;
368  return StatusCode::FAILURE;
369  }
370  // Get the IMainAppStatus interface of the ApplicationMgr
372  if ( ! m_appProperty ) {
373  warning() << "Cannot retrieve IProperty interface of ApplicationMgr, "
374  "the return code will not be changed" << endmsg;
375  }
376  // Decode the signal names
377  for (const auto& signame : m_usedSignals ) {
378  auto sigid = i_decodeSignal(signame);
379  if (sigid.first >= 0) {
380  m_signals[sigid.first] = sigid.second;
381  }
382  }
383  debug() << "Stopping on the signals:" << endmsg;
384  const SigMap& sigmap(SigMap::instance());
385  for (const auto& s : m_signals ) {
386  debug() << "\t" << sigmap.name(s.first) << ": "
387  << sigmap.desc(s.first) << " (" << s.first << ")";
388  if (s.second) debug() << " propagated";
389  debug() << endmsg;
390  // tell the signal monitor that we are interested in these signals
391  m_signalMonitor->monitorSignal(s.first, s.second);
392  }
393  m_stopRequested = false;
394  debug() << "Register to the IncidentSvc" << endmsg;
395  m_incidentSvc->addListener(this, IncidentType::BeginEvent);
396  return StatusCode::SUCCESS;
397  }
398  StatusCode finalize() override {
399  m_incidentSvc->removeListener(this, IncidentType::BeginEvent);
401  // disable the monitoring of the signals
403  [&](const std::pair<int,bool>& s) {
404  // tell the signal monitor that we are interested in these signals
405  m_signalMonitor->ignoreSignal(s.first);
406  } );
407  m_signalMonitor.reset();
408  return Service::finalize();
409  }
410 
411  void handle(const Incident&) override {
412  if (!m_stopRequested) {
413  const SigMap& sigmap(SigMap::instance());
414  for (const auto& s : m_signals ) {
415  if (!m_signalMonitor->gotSignal(s.first)) continue;
416  warning() << "Received signal '" << sigmap.name(s.first)
417  << "' (" << s.first;
418  const std::string &desc = sigmap.desc(s.first);
419  if ( ! desc.empty() ) warning() << ", " << desc;
420  warning() << ")" << endmsg;
421  m_stopRequested = true;
422  // Report the termination by signal at the end of the application
425  error() << "Could not set return code of the application ("
426  << SignalOffset + s.first << ")"
427  << endmsg;
428  }
429 
430  }
431  if (m_stopRequested) {
432  auto ep = serviceLocator()->as<IEventProcessor>();
433  if (ep) {
434  warning() << "Scheduling a stop" << endmsg;
435  ep->stopRun().ignore();
436  }
437  else {
438  warning() << "Cannot stop the processing because the IEventProcessor interface cannot be retrieved." << endmsg;
439  }
440  }
441  }
442  }
443  private:
458  debug() << "Decoding signal declaration '" << sig << "'" << endmsg;
459  if ( sig.empty() || sig == "+" ) {
460  debug() << "Empty signal, ignored" << endmsg;
461  return {-1, false}; // silently ignore empty strings
462  }
463  const SigMap& sigmap(SigMap::instance());
464  std::string signal = sig;
465  bool propagate = false;
466  // Check if the signal must be propagated
467  if (signal[signal.size() - 1] == '+') {
468  debug() << "Must be propagated to previously registered signal handlers" << endmsg;
469  propagate = true;
470  signal.erase(signal.size() - 1, 1); // remove the '+' at the end of the string
471  }
472  int signum = -1;
473  // check if the signal is a number
474  if (std::isdigit(signal[0])){
475  std::istringstream ss(signal);
476  ss >> signum;
477  } else {
478  // try to find the signal name in the list of known signals
479  signum = sigmap.signum(signal);
480  }
481  if (signum < 0) {
482  warning() << "Cannot understand signal identifier '" << sig << "', ignored" << endmsg;
483  } else {
484  verbose() << "Matched signal '" << sigmap.name(signum)
485  << "' (" << signum;
486  const std::string &desc = sigmap.desc(signum);
487  if ( ! desc.empty() ) {
488  verbose() << ", " << desc;
489  }
490  verbose() << ")" << endmsg;
491  }
492  return {signum, propagate};
493  }
494  };
495 
496  } // namespace Utils
497 } // namespace Gaudi
498 
499 // Initialization of static data member
501 
502 // ========================================================================
503 // Instantiation of a static factory class used by clients to create instances of this service
505 DECLARE_COMPONENT(g_u_sms)
506 
507 // Instantiation of a static factory class used by clients to create instances of this service
508 typedef Gaudi::Utils::StopSignalHandler g_u_ssh;
509 DECLARE_COMPONENT(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:68
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:14
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator.
Definition: Service.cpp:324
T empty(T...args)
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition: ISvcLocator.h:25
bool m_stopRequested
Flag to remember if the stop has been requested because of a signal.
StatusCode finalize() override
Definition: Service.cpp:193
SmartIF< IIncidentSvc > m_incidentSvc
Pointer to the incident service.
int maxevt auto ep
Definition: Bootstrap.cpp:285
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...
MsgStream & verbose() const
shortcut for the method msgStream(MSG::VERBOSE)
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:106
SmartIF< Gaudi::ISignalMonitor > m_signalMonitor
Pointer to the signal monitor service.
std::vector< std::string > m_usedSignals
List of signal names or numbers (encoded as strings) to use to schedule a stop.
bool isFailure() const
Test for a status code of FAILURE.
Definition: StatusCode.h:86
#define DECLARE_COMPONENT(type)
Definition: PluginService.h:36
STL class.
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:78
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:319
static void setInstance(SignalMonitorSvc *i)
T push_back(T...args)
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:26
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:50
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)
bool gotSignal(int signum) const override
Check if the given signal has been received.
Base class for all Incidents (computing events).
Definition: Incident.h:17
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:245
The IEventProcessor is the interface to process events.
#define addSignal(id)
StopSignalHandler(const std::string &name, ISvcLocator *svcLoc)
MonitoringMode m_monitored[NSIG]
Array of flags to keep track of monitored signals.
constexpr int SignalOffset
Definition: AppReturnCode.h:34
void reset(TYPE *ptr=nullptr)
Set the internal pointer to the passed one disposing of the old one.
Definition: SmartIF.h:88
handler_t m_oldActions[NSIG]
List of replaced signal actions (for the recovery when disable the monitoring).
#define __cdecl
Definition: xtoa.h:2
virtual void removeListener(IIncidentListener *lis, const std::string &type="")=0
Remove listener.
handler_t m_defaultAction
Helper variable for default signal action.
Property * declareProperty(const std::string &name, T &property, const std::string &doc="none") const
Declare the named property.
Definition: Service.h:215
T for_each(T...args)
list i
Definition: ana.py:128
Gaudi::Utils::StopSignalHandler g_u_ssh
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:244
T reserve(T...args)