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"
8 #include "GaudiUtils/ISignalMonitor.h"
9 
10 #include <csignal>
11 
12 #include <iostream>
13 
14 namespace Gaudi {
15  namespace Utils {
26  class SignalMonitorSvc: public extends1<Service, Gaudi::ISignalMonitor> {
27  public:
28 #ifdef _WIN32
29  typedef void (__cdecl *handler_t)(int);
30 #else
31  typedef struct sigaction handler_t;
32 #endif
33 
36  void monitorSignal(int signum, bool propagate) {
37  if (!m_monitored[signum]) {
38  handler_t sa;
39  handler_t oldact;
40 #ifdef _WIN32
42  oldact = signal(signum, sa);
43 #else
44  sa.sa_handler = SignalMonitorSvc::dispatcher;
45  sigemptyset(&sa.sa_mask);
46  sa.sa_flags = 0;
47  sigaction(signum, &sa, &oldact);
48 #endif
49  m_oldActions[signum] = oldact;
52  }
53  }
54 
57  void ignoreSignal(int signum) {
58  if (m_monitored[signum]) {
59 #ifdef _WIN32
60  (void) signal(signum, m_oldActions[signum]);
61 #else
62  sigaction(signum, &m_oldActions[signum], 0);
63 #endif
64  m_oldActions[signum] = m_defaultAction;
65  m_monitored[signum] = ignored;
66  }
67  }
68 
70  bool gotSignal(int signum) const {
71  return m_caught[signum] != 0;
72  }
73 
75  void setSignal(int signum) {
76  m_caught[signum] = 1;
77  }
78 
80  void clearSignal(int signum) {
81  m_caught[signum] = 0;
82  }
83 
85  SignalMonitorSvc(const std::string& name, ISvcLocator* svcLoc): base_class(name, svcLoc) {
86 #ifdef _WIN32
87  m_defaultAction = SIG_DFL;
88 #else
89  m_defaultAction.sa_handler = SIG_DFL;
90  sigemptyset(&m_defaultAction.sa_mask);
91  m_defaultAction.sa_flags = 0;
92 #endif
93  for(int i = 0; i < NSIG; ++i){
94  m_caught[i] = 0;
97  }
98 
99  setInstance(this);
100  }
101 
103  virtual ~SignalMonitorSvc() {
104  for (int i = 0; i < NSIG; ++i) {
105  ignoreSignal(i);
106  }
107  setInstance(0);
108  }
109 
110  private:
113  ignored, //< the signal is not monitored
114  trap, //< the signal is monitored and not propagated to previously registered handlers
115  propagate //< the signal is monitored and propagated to previously registered handlers
116  };
120  sig_atomic_t m_caught[NSIG];
122  handler_t m_defaultAction;
124  handler_t m_oldActions[NSIG];
125 
126  void i_handle(int signum) {
127  m_caught[signum] = 1;
128  if ( m_monitored[signum] == propagate &&
129 #ifdef _WIN32
130  m_oldActions[signum] != SIG_DFL
131 #else
132  m_oldActions[signum].sa_handler != SIG_DFL
133 #endif
134  ) {
135 #ifdef _WIN32
136  m_oldActions[signum](signum);
137 #else
138  m_oldActions[signum].sa_handler(signum);
139 #endif
140  }
141  }
142 
145 
146  static inline void setInstance(SignalMonitorSvc *i) {
147  s_instance = i;
148  }
149 
152  static inline SignalMonitorSvc *instance() {
153  return s_instance;
154  }
155 
157  static void dispatcher(int signum);
158  };
159 
160  // Implementation of the signal handler function.
162  if (instance()) instance()->i_handle(signum);
163  }
164 
165  } // namespace Utils
166 } // namespace Gaudi
167 
168 #include <cctype>
169 #include <sstream>
170 
171 #include <map>
172 
173 #include "GaudiKernel/HashMap.h"
174 
175 #include "GaudiKernel/IIncidentListener.h"
176 #include "GaudiKernel/IIncidentSvc.h"
177 #include "GaudiKernel/IEventProcessor.h"
178 #include "GaudiKernel/AppReturnCode.h"
179 
180 namespace {
181  // hack because windows doesn't provide sys_siglist
182  const char *sig_desc(int signum) {
183  if (signum >= NSIG || signum < 0)
184  return 0;
185 #ifdef _WIN32
186  switch (signum) {
187  case SIGINT: return "Interrupt";
188  case SIGILL: return "Illegal instruction";
189  case SIGFPE: return "Floating point exception";
190  case SIGSEGV: return "Segmentation fault";
191  case SIGTERM: return "Terminated";
192  case SIGBREAK: return "Trace/breakpoint trap";
193  case SIGABRT: return "Aborted";
194  default: return 0;
195  }
196 #else
197  return sys_siglist[signum];
198 #endif
199  }
200 
202  class SigMap {
203  public:
205  static const SigMap& instance() {
206  static SigMap _instance;
207  return _instance;
208  }
210  inline const std::string &name(int signum) const {
211  return m_num2id[signum];
212  }
214  inline const std::string &desc(int signum) const {
215  return m_num2desc[signum];
216  }
218  inline int signum(const std::string &str) const {
219  auto it = m_name2num.find(str);
220  return it != m_name2num.end() ? it->second : -1;
221  }
222  private:
225  SigMap(){
226 #define addSignal(id) i_addSignal(id, #id);
227  // List of signals from http://en.wikipedia.org/wiki/POSIX_signal
228 #ifdef SIGABRT
229  addSignal(SIGABRT); // Process aborted
230 #endif
231 #ifdef SIGALRM
232  addSignal(SIGALRM); // Signal raised by alarm
233 #endif
234 #ifdef SIGBUS
235  addSignal(SIGBUS); // Bus error: "access to undefined portion of memory object"
236 #endif
237 #ifdef SIGCHLD
238  addSignal(SIGCHLD); // Child process terminated, stopped (or continued*)
239 #endif
240 #ifdef SIGCONT
241  addSignal(SIGCONT); // Continue if stopped
242 #endif
243 #ifdef SIGFPE
244  addSignal(SIGFPE); // Floating point exception: "erroneous arithmetic operation"
245 #endif
246 #ifdef SIGHUP
247  addSignal(SIGHUP); // Hangup
248 #endif
249 #ifdef SIGILL
250  addSignal(SIGILL); // Illegal instruction
251 #endif
252 #ifdef SIGINT
253  addSignal(SIGINT); // Interrupt
254 #endif
255 #ifdef SIGKILL
256  addSignal(SIGKILL); // Kill (terminate immediately)
257 #endif
258 #ifdef SIGPIPE
259  addSignal(SIGPIPE); // Write to pipe with no one reading
260 #endif
261 #ifdef SIGQUIT
262  addSignal(SIGQUIT); // Quit and dump core
263 #endif
264 #ifdef SIGSEGV
265  addSignal(SIGSEGV); // Segmentation violation
266 #endif
267 #ifdef SIGSTOP
268  addSignal(SIGSTOP); // Stop executing temporarily
269 #endif
270 #ifdef SIGTERM
271  addSignal(SIGTERM); // Termination (request to terminate)
272 #endif
273 #ifdef SIGTSTP
274  addSignal(SIGTSTP); // Terminal stop signal
275 #endif
276 #ifdef SIGTTIN
277  addSignal(SIGTTIN); // Background process attempting to read from tty ("in")
278 #endif
279 #ifdef SIGTTOU
280  addSignal(SIGTTOU); // Background process attempting to write to tty ("out")
281 #endif
282 #ifdef SIGUSR1
283  addSignal(SIGUSR1); // User-defined 1
284 #endif
285 #ifdef SIGUSR2
286  addSignal(SIGUSR2); // User-defined 2
287 #endif
288 #ifdef SIGPOLL
289  addSignal(SIGPOLL); // Pollable event
290 #endif
291 #ifdef SIGPROF
292  addSignal(SIGPROF); // Profiling timer expired
293 #endif
294 #ifdef SIGSYS
295  addSignal(SIGSYS); // Bad syscall
296 #endif
297 #ifdef SIGTRAP
298  addSignal(SIGTRAP); // Trace/breakpoint trap
299 #endif
300 #ifdef SIGURG
301  addSignal(SIGURG); // Urgent data available on socket
302 #endif
303 #ifdef SIGVTALRM
304  addSignal(SIGVTALRM); // Signal raised by timer counting virtual time: "virtual timer expired"
305 #endif
306 #ifdef SIGXCPU
307  addSignal(SIGXCPU); // CPU time limit exceeded
308 #endif
309 #ifdef SIGXFSZ
310  addSignal(SIGXFSZ); // File size limit exceeded
311 #endif
312 #undef addSignal
313  }
315  inline void i_addSignal(int signum, const char *signame) {
316  m_num2id[signum] = signame;
317  m_name2num[signame] = signum;
318  const char* desc = sig_desc(signum);
319  if (desc) {
320  m_num2desc[signum] = desc;
321  m_name2num[desc] = signum;
322  }
323  }
324  GaudiUtils::HashMap<std::string, int> m_name2num; //< Map signal string id or description to number
325  GaudiUtils::HashMap<int, std::string> m_num2id; //< Map signal number to string id
326  GaudiUtils::HashMap<int, std::string> m_num2desc; //< Map signal number to description
327  };
328 }
329 
330 namespace Gaudi {
331  namespace Utils {
341  class StopSignalHandler: public extends1<Service, IIncidentListener> {
342  public:
343  StopSignalHandler(const std::string& name, ISvcLocator* svcLoc): base_class(name, svcLoc) {
344  m_usedSignals.reserve(2);
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  }
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
371  m_appProperty = serviceLocator();
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 (std::vector<std::string>::const_iterator signame = m_usedSignals.begin();
378  signame != m_usedSignals.end(); ++signame) {
379  auto sigid = i_decodeSignal(*signame);
380  if (sigid.first >= 0) {
381  m_signals[sigid.first] = sigid.second;
382  }
383  }
384  debug() << "Stopping on the signals:" << endmsg;
385  const SigMap& sigmap(SigMap::instance());
386  for (const auto& s : m_signals ) {
387  debug() << "\t" << sigmap.name(s.first) << ": "
388  << sigmap.desc(s.first) << " (" << s.first << ")";
389  if (s.second) debug() << " propagated";
390  debug() << endmsg;
391  // tell the signal monitor that we are interested in these signals
392  m_signalMonitor->monitorSignal(s.first, s.second);
393  }
394  m_stopRequested = false;
395  debug() << "Register to the IncidentSvc" << endmsg;
397  return StatusCode::SUCCESS;
398  }
402  // disable the monitoring of the signals
403  std::for_each( std::begin(m_signals), std::end(m_signals),
404  [&](const std::pair<int,bool>& s) {
405  // tell the signal monitor that we are interested in these signals
406  m_signalMonitor->ignoreSignal(s.first);
407  } );
408  m_signalMonitor.reset();
409  return Service::finalize();
410  }
411 
412  virtual void handle(const Incident&) {
413  if (!m_stopRequested) {
414  const SigMap& sigmap(SigMap::instance());
415  for (const auto& s : m_signals ) {
416  if (!m_signalMonitor->gotSignal(s.first)) continue;
417  warning() << "Received signal '" << sigmap.name(s.first)
418  << "' (" << s.first;
419  const std::string &desc = sigmap.desc(s.first);
420  if ( ! desc.empty() ) warning() << ", " << desc;
421  warning() << ")" << endmsg;
422  m_stopRequested = true;
423  // Report the termination by signal at the end of the application
426  error() << "Could not set return code of the application ("
427  << SignalOffset + s.first << ")"
428  << endmsg;
429  }
430 
431  }
432  if (m_stopRequested) {
433  auto ep = serviceLocator()->as<IEventProcessor>();
434  if (ep) {
435  warning() << "Scheduling a stop" << endmsg;
436  ep->stopRun().ignore();
437  }
438  else {
439  warning() << "Cannot stop the processing because the IEventProcessor interface cannot be retrieved." << endmsg;
440  }
441  }
442  }
443  }
444  private:
446  std::vector<std::string> m_usedSignals;
448  std::map<int, bool> m_signals;
458  std::pair<int, bool> i_decodeSignal(const std::string &sig) {
459  debug() << "Decoding signal declaration '" << sig << "'" << endmsg;
460  if ( sig.empty() || sig == "+" ) {
461  debug() << "Empty signal, ignored" << endmsg;
462  return std::make_pair<int, bool>(-1, false); // silently ignore empty strings
463  }
464  const SigMap& sigmap(SigMap::instance());
465  std::string signal = sig;
466  bool propagate = false;
467  // Check if the signal must be propagated
468  if (signal[signal.size() - 1] == '+') {
469  debug() << "Must be propagated to previously registered signal handlers" << endmsg;
470  propagate = true;
471  signal.erase(signal.size() - 1, 1); // remove the '+' at the end of the string
472  }
473  int signum = -1;
474  // check if the signal is a number
475  if (std::isdigit(signal[0])){
476  std::istringstream ss(signal);
477  ss >> signum;
478  } else {
479  // try to find the signal name in the list of known signals
480  signum = sigmap.signum(signal);
481  }
482  if (signum < 0) {
483  warning() << "Cannot understand signal identifier '" << sig << "', ignored" << endmsg;
484  } else {
485  verbose() << "Matched signal '" << sigmap.name(signum)
486  << "' (" << signum;
487  const std::string &desc = sigmap.desc(signum);
488  if ( ! desc.empty() ) {
489  verbose() << ", " << desc;
490  }
491  verbose() << ")" << endmsg;
492  }
493  return std::make_pair(signum, propagate);
494  }
495  };
496 
497  } // namespace Utils
498 } // namespace Gaudi
499 
500 // Initialization of static data member
502 
503 // ========================================================================
504 // Instantiation of a static factory class used by clients to create instances of this service
506 DECLARE_COMPONENT(g_u_sms)
507 
508 // Instantiation of a static factory class used by clients to create instances of this service
509 typedef Gaudi::Utils::StopSignalHandler g_u_ssh;
510 DECLARE_COMPONENT(g_u_ssh)
const std::string BeginEvent
Processing of a new event has started.
Definition: Incident.h:60
StatusCode initialize() override
Definition: Service.cpp:62
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
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.
bool gotSignal(int signum) const
Check if the given signal has been received.
StatusCode finalize() override
Definition: Service.cpp:187
SmartIF< IIncidentSvc > m_incidentSvc
Pointer to the incident service.
void clearSignal(int signum)
Clear the flag for the given signal, so that a new occurrence can be identified.
int maxevt auto ep
Definition: Bootstrap.cpp:286
Gaudi::Utils::SignalMonitorSvc g_u_sms
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:244
auto begin(reverse_wrapper< T > &w)
Definition: reverse.h:45
void ignoreSignal(int signum)
Remove the specific signal handler for the requested signal, restoring the previous signal handler...
static void dispatcher(int signum)
Signal handler function.
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
MonitoringMode
Possible monitoring modes.
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)
static SignalMonitorSvc * instance()
Method to get the singleton instance.
Implementation of Gaudi::ISignalMonitor.
#define DECLARE_COMPONENT(type)
Definition: PluginService.h:36
auto end(reverse_wrapper< T > &w)
Definition: reverse.h:47
#define __cdecl
Definition: xtoa.h:2
virtual void handle(const Incident &)
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.
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.
void setSignal(int signum)
Set the flag for the given signal, as if the signal was received.
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.
void monitorSignal(int signum, bool propagate)
Declare a signal to be monitored.
Base class used to extend a class implementing other interfaces.
Definition: extends.h:10
Base class for all Incidents (computing events).
Definition: Incident.h:16
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:246
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).
virtual void removeListener(IIncidentListener *lis, const std::string &type="")=0
Remove listener.
handler_t m_defaultAction
Helper variable for default signal action.
virtual ~SignalMonitorSvc()
Stop monitoring signals and clear the instance pointer.
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