All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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) override {
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) override {
58  if (m_monitored[signum]) {
59 #ifdef _WIN32
60  (void) signal(signum, m_oldActions[signum]);
61 #else
62  sigaction(signum, &m_oldActions[signum], nullptr);
63 #endif
64  m_oldActions[signum] = m_defaultAction;
65  m_monitored[signum] = ignored;
66  }
67  }
68 
70  bool gotSignal(int signum) const override {
71  return m_caught[signum] != 0;
72  }
73 
75  void setSignal(int signum) override {
76  m_caught[signum] = 1;
77  }
78 
80  void clearSignal(int signum) override {
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  ~SignalMonitorSvc() override {
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  m_caught[signum] = 1;
126  if ( m_monitored[signum] == propagate &&
127 #ifdef _WIN32
128  m_oldActions[signum] != SIG_DFL
129 #else
130  m_oldActions[signum].sa_handler != SIG_DFL
131 #endif
132  ) {
133 #ifdef _WIN32
134  m_oldActions[signum](signum);
135 #else
136  m_oldActions[signum].sa_handler(signum);
137 #endif
138  }
139  }
140 
143 
144  static inline void setInstance(SignalMonitorSvc *i) {
145  s_instance = i;
146  }
147 
150  static inline SignalMonitorSvc *instance() {
151  return s_instance;
152  }
153 
155  static void dispatcher(int signum);
156  };
157 
158  // Implementation of the signal handler function.
160  if (instance()) instance()->i_handle(signum);
161  }
162 
163  } // namespace Utils
164 } // namespace Gaudi
165 
166 #include <cctype>
167 #include <sstream>
168 
169 #include <map>
170 
171 #include "GaudiKernel/HashMap.h"
172 
173 #include "GaudiKernel/IIncidentListener.h"
174 #include "GaudiKernel/IIncidentSvc.h"
175 #include "GaudiKernel/IEventProcessor.h"
176 #include "GaudiKernel/AppReturnCode.h"
177 
178 namespace {
179  // hack because windows doesn't provide sys_siglist
180  const char *sig_desc(int signum) {
181  if (signum >= NSIG || signum < 0)
182  return nullptr;
183 #ifdef _WIN32
184  switch (signum) {
185  case SIGINT: return "Interrupt";
186  case SIGILL: return "Illegal instruction";
187  case SIGFPE: return "Floating point exception";
188  case SIGSEGV: return "Segmentation fault";
189  case SIGTERM: return "Terminated";
190  case SIGBREAK: return "Trace/breakpoint trap";
191  case SIGABRT: return "Aborted";
192  default: return 0;
193  }
194 #else
195  return sys_siglist[signum];
196 #endif
197  }
198 
200  class SigMap {
201  public:
203  static const SigMap& instance() {
204  static SigMap _instance;
205  return _instance;
206  }
208  inline const std::string &name(int signum) const {
209  return m_num2id[signum];
210  }
212  inline const std::string &desc(int signum) const {
213  return m_num2desc[signum];
214  }
216  inline int signum(const std::string &str) const {
217  auto it = m_name2num.find(str);
218  return it != m_name2num.end() ? it->second : -1;
219  }
220  private:
223  SigMap(){
224 #define addSignal(id) i_addSignal(id, #id);
225  // List of signals from http://en.wikipedia.org/wiki/POSIX_signal
226 #ifdef SIGABRT
227  addSignal(SIGABRT); // Process aborted
228 #endif
229 #ifdef SIGALRM
230  addSignal(SIGALRM); // Signal raised by alarm
231 #endif
232 #ifdef SIGBUS
233  addSignal(SIGBUS); // Bus error: "access to undefined portion of memory object"
234 #endif
235 #ifdef SIGCHLD
236  addSignal(SIGCHLD); // Child process terminated, stopped (or continued*)
237 #endif
238 #ifdef SIGCONT
239  addSignal(SIGCONT); // Continue if stopped
240 #endif
241 #ifdef SIGFPE
242  addSignal(SIGFPE); // Floating point exception: "erroneous arithmetic operation"
243 #endif
244 #ifdef SIGHUP
245  addSignal(SIGHUP); // Hangup
246 #endif
247 #ifdef SIGILL
248  addSignal(SIGILL); // Illegal instruction
249 #endif
250 #ifdef SIGINT
251  addSignal(SIGINT); // Interrupt
252 #endif
253 #ifdef SIGKILL
254  addSignal(SIGKILL); // Kill (terminate immediately)
255 #endif
256 #ifdef SIGPIPE
257  addSignal(SIGPIPE); // Write to pipe with no one reading
258 #endif
259 #ifdef SIGQUIT
260  addSignal(SIGQUIT); // Quit and dump core
261 #endif
262 #ifdef SIGSEGV
263  addSignal(SIGSEGV); // Segmentation violation
264 #endif
265 #ifdef SIGSTOP
266  addSignal(SIGSTOP); // Stop executing temporarily
267 #endif
268 #ifdef SIGTERM
269  addSignal(SIGTERM); // Termination (request to terminate)
270 #endif
271 #ifdef SIGTSTP
272  addSignal(SIGTSTP); // Terminal stop signal
273 #endif
274 #ifdef SIGTTIN
275  addSignal(SIGTTIN); // Background process attempting to read from tty ("in")
276 #endif
277 #ifdef SIGTTOU
278  addSignal(SIGTTOU); // Background process attempting to write to tty ("out")
279 #endif
280 #ifdef SIGUSR1
281  addSignal(SIGUSR1); // User-defined 1
282 #endif
283 #ifdef SIGUSR2
284  addSignal(SIGUSR2); // User-defined 2
285 #endif
286 #ifdef SIGPOLL
287  addSignal(SIGPOLL); // Pollable event
288 #endif
289 #ifdef SIGPROF
290  addSignal(SIGPROF); // Profiling timer expired
291 #endif
292 #ifdef SIGSYS
293  addSignal(SIGSYS); // Bad syscall
294 #endif
295 #ifdef SIGTRAP
296  addSignal(SIGTRAP); // Trace/breakpoint trap
297 #endif
298 #ifdef SIGURG
299  addSignal(SIGURG); // Urgent data available on socket
300 #endif
301 #ifdef SIGVTALRM
302  addSignal(SIGVTALRM); // Signal raised by timer counting virtual time: "virtual timer expired"
303 #endif
304 #ifdef SIGXCPU
305  addSignal(SIGXCPU); // CPU time limit exceeded
306 #endif
307 #ifdef SIGXFSZ
308  addSignal(SIGXFSZ); // File size limit exceeded
309 #endif
310 #undef addSignal
311  }
313  inline void i_addSignal(int signum, const char *signame) {
314  m_num2id[signum] = signame;
315  m_name2num[signame] = signum;
316  const char* desc = sig_desc(signum);
317  if (desc) {
318  m_num2desc[signum] = desc;
319  m_name2num[desc] = signum;
320  }
321  }
322  GaudiUtils::HashMap<std::string, int> m_name2num; //< Map signal string id or description to number
323  GaudiUtils::HashMap<int, std::string> m_num2id; //< Map signal number to string id
324  GaudiUtils::HashMap<int, std::string> m_num2desc; //< Map signal number to description
325  };
326 }
327 
328 namespace Gaudi {
329  namespace Utils {
339  class StopSignalHandler: public extends1<Service, IIncidentListener> {
340  public:
341  StopSignalHandler(const std::string& name, ISvcLocator* svcLoc): base_class(name, svcLoc) {
342  m_usedSignals.reserve(2);
343  m_usedSignals.push_back("SIGINT");
344  m_usedSignals.push_back("SIGXCPU");
345  m_stopRequested = false;
346  declareProperty("Signals", m_usedSignals,
347  "List of signal names or numbers to use to schedule a stop. "
348  "If the signal is followed by a '+' the signal is propagated the previously "
349  "registered handler (if any).");
350  }
351  StatusCode initialize() override {
353  if (sc.isFailure()) {
354  return sc;
355  }
356  std::string serviceName("Gaudi::Utils::SignalMonitorSvc");
357  m_signalMonitor = serviceLocator()->service(serviceName);
358  if ( ! m_signalMonitor ) {
359  error() << "Cannot retrieve " << serviceName << endmsg;
360  return StatusCode::FAILURE;
361  }
362  serviceName = "IncidentSvc";
363  m_incidentSvc = serviceLocator()->service(serviceName);
364  if ( ! m_incidentSvc ) {
365  error() << "Cannot retrieve " << serviceName << endmsg;
366  return StatusCode::FAILURE;
367  }
368  // Get the IMainAppStatus interface of the ApplicationMgr
369  m_appProperty = serviceLocator();
370  if ( ! m_appProperty ) {
371  warning() << "Cannot retrieve IProperty interface of ApplicationMgr, "
372  "the return code will not be changed" << endmsg;
373  }
374  // Decode the signal names
375  for (const auto& signame : m_usedSignals ) {
376  auto sigid = i_decodeSignal(signame);
377  if (sigid.first >= 0) {
378  m_signals[sigid.first] = sigid.second;
379  }
380  }
381  debug() << "Stopping on the signals:" << endmsg;
382  const SigMap& sigmap(SigMap::instance());
383  for (const auto& s : m_signals ) {
384  debug() << "\t" << sigmap.name(s.first) << ": "
385  << sigmap.desc(s.first) << " (" << s.first << ")";
386  if (s.second) debug() << " propagated";
387  debug() << endmsg;
388  // tell the signal monitor that we are interested in these signals
389  m_signalMonitor->monitorSignal(s.first, s.second);
390  }
391  m_stopRequested = false;
392  debug() << "Register to the IncidentSvc" << endmsg;
393  m_incidentSvc->addListener(this, IncidentType::BeginEvent);
394  return StatusCode::SUCCESS;
395  }
396  StatusCode finalize() override {
397  m_incidentSvc->removeListener(this, IncidentType::BeginEvent);
399  // disable the monitoring of the signals
400  std::for_each( std::begin(m_signals), std::end(m_signals),
401  [&](const std::pair<int,bool>& s) {
402  // tell the signal monitor that we are interested in these signals
403  m_signalMonitor->ignoreSignal(s.first);
404  } );
405  m_signalMonitor.reset();
406  return Service::finalize();
407  }
408 
409  void handle(const Incident&) override {
410  if (!m_stopRequested) {
411  const SigMap& sigmap(SigMap::instance());
412  for (const auto& s : m_signals ) {
413  if (!m_signalMonitor->gotSignal(s.first)) continue;
414  warning() << "Received signal '" << sigmap.name(s.first)
415  << "' (" << s.first;
416  const std::string &desc = sigmap.desc(s.first);
417  if ( ! desc.empty() ) warning() << ", " << desc;
418  warning() << ")" << endmsg;
419  m_stopRequested = true;
420  // Report the termination by signal at the end of the application
423  error() << "Could not set return code of the application ("
424  << SignalOffset + s.first << ")"
425  << endmsg;
426  }
427 
428  }
429  if (m_stopRequested) {
430  auto ep = serviceLocator()->as<IEventProcessor>();
431  if (ep) {
432  warning() << "Scheduling a stop" << endmsg;
433  ep->stopRun().ignore();
434  }
435  else {
436  warning() << "Cannot stop the processing because the IEventProcessor interface cannot be retrieved." << endmsg;
437  }
438  }
439  }
440  }
441  private:
443  std::vector<std::string> m_usedSignals;
445  std::map<int, bool> m_signals;
455  std::pair<int, bool> i_decodeSignal(const std::string &sig) {
456  debug() << "Decoding signal declaration '" << sig << "'" << endmsg;
457  if ( sig.empty() || sig == "+" ) {
458  debug() << "Empty signal, ignored" << endmsg;
459  return {-1, false}; // silently ignore empty strings
460  }
461  const SigMap& sigmap(SigMap::instance());
462  std::string signal = sig;
463  bool propagate = false;
464  // Check if the signal must be propagated
465  if (signal[signal.size() - 1] == '+') {
466  debug() << "Must be propagated to previously registered signal handlers" << endmsg;
467  propagate = true;
468  signal.erase(signal.size() - 1, 1); // remove the '+' at the end of the string
469  }
470  int signum = -1;
471  // check if the signal is a number
472  if (std::isdigit(signal[0])){
473  std::istringstream ss(signal);
474  ss >> signum;
475  } else {
476  // try to find the signal name in the list of known signals
477  signum = sigmap.signum(signal);
478  }
479  if (signum < 0) {
480  warning() << "Cannot understand signal identifier '" << sig << "', ignored" << endmsg;
481  } else {
482  verbose() << "Matched signal '" << sigmap.name(signum)
483  << "' (" << signum;
484  const std::string &desc = sigmap.desc(signum);
485  if ( ! desc.empty() ) {
486  verbose() << ", " << desc;
487  }
488  verbose() << ")" << endmsg;
489  }
490  return {signum, propagate};
491  }
492  };
493 
494  } // namespace Utils
495 } // namespace Gaudi
496 
497 // Initialization of static data member
499 
500 // ========================================================================
501 // Instantiation of a static factory class used by clients to create instances of this service
503 DECLARE_COMPONENT(g_u_sms)
504 
505 // Instantiation of a static factory class used by clients to create instances of this service
506 typedef Gaudi::Utils::StopSignalHandler g_u_ssh;
507 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: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:14
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition: ISvcLocator.h:25
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:244
bool m_stopRequested
Flag to remember if the stop has been requested because of a signal.
StatusCode finalize() override
Definition: Service.cpp:188
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.
auto begin(reverse_wrapper< T > &w)
Definition: reverse.h:45
void ignoreSignal(int signum) override
Remove the specific signal handler for the requested signal, restoring the previous signal handler...
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.
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.
auto end(reverse_wrapper< T > &w)
Definition: reverse.h:47
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.
#define DECLARE_COMPONENT(type)
Definition: PluginService.h:36
void handle(const Incident &) override
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.
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:10
bool gotSignal(int signum) const override
Check if the given signal has been received.
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: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).
virtual void removeListener(IIncidentListener *lis, const std::string &type="")=0
Remove listener.
handler_t m_defaultAction
Helper variable for default signal action.
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
#define __cdecl
Definition: xtoa.h:2