Scarab  3.6.0
Project 8 C++ Utility Library
signal_handler.cc
Go to the documentation of this file.
1 /*
2  * signal_handler.cc
3  *
4  * Created on: Dec 3, 2013
5  * Author: N.S. Oblath
6  */
7 
8 #define SCARAB_API_EXPORTS
9 
10 #include "signal_handler.hh"
11 
12 #include "cancelable.hh"
13 #include "error.hh"
14 #include "logger.hh"
15 
16 #include <execinfo.h>
17 #include <signal.h>
18 #include <stdexcept>
19 #include <thread>
20 
21 //#include <signal.h>
22 //#include <string.h>
23 
24 //#include <cstdlib>
25 //#include <mutex>
26 
27 
28 #ifdef _WIN32
29 #include <Windows.h>
30 //#include "processthreadsapi.h"
31 #endif
32 
33 namespace {
34  // function to catch unhandled exceptions
35  // invoke set_terminate as part of global constant initialization
36  static const bool SET_TERMINATE = std::set_terminate( scarab::signal_handler::handle_terminate );
37  static const scarab::signal_handler HANDLER;
38 }
39 
40 
41 namespace scarab
42 {
43  LOGGER( slog, "signal_handler" );
44 
45  bool signal_handler::s_exited = false;
46  int signal_handler::s_return_code = RETURN_SUCCESS;
47 
48  bool signal_handler::s_handling_sig_abrt = false;
49  bool signal_handler::s_handling_sig_term = false;
50  bool signal_handler::s_handling_sig_int = false;
51  bool signal_handler::s_handling_sig_quit = false;
52 
53  std::recursive_mutex signal_handler::s_mutex;
55 
57  {
58  LOGGER( slog_constr, "signal_handler constructor" );
59 
60  // setup to handle SIGABRT
61  if( ! s_handling_sig_abrt && signal( SIGABRT, signal_handler::handle_exit_error ) == SIG_ERR )
62  {
63  LWARN( slog_constr, "Unable to setup handling of SIGABRT: abort() and unhandled exceptions will result in an unclean exit" );
64  }
65  else
66  {
67  LDEBUG( slog_constr, "Handling SIGABRT (abort() and unhandled exceptions)" );
68  s_handling_sig_abrt = true;
69  }
70 
71  // setup to handle SIGTERM
72  if( ! s_handling_sig_term && signal( SIGTERM, signal_handler::handle_exit_error ) == SIG_ERR )
73  {
74  LWARN( slog_constr, "Unable to setup handling of SIGTERM: SIGTERM will result in an unclean exit" );
75  }
76  else
77  {
78  LDEBUG( slog_constr, "Handling SIGTERM" );
79  s_handling_sig_term = true;
80  }
81 
82  // setup to handle SIGINT
83  if( ! s_handling_sig_int && signal( SIGINT, signal_handler::handle_exit_success ) == SIG_ERR )
84  {
85  LWARN( slog_constr, "Unable to setup handling of SIGINT: ctrl-c cancellation will result in an unclean exit" );
86  }
87  else
88  {
89  LDEBUG( slog_constr, "Handling SIGINT (ctrl-c)" );
90  s_handling_sig_int = true;
91  }
92 
93 #ifndef _WIN32
94  // setup to handle SIGQUIT
95  if( ! s_handling_sig_quit && signal( SIGQUIT, signal_handler::handle_exit_success ) == SIG_ERR )
96  {
97  LWARN( slog_constr, "Unable to setup handling of SIGQUIT: ctrl-\\ cancellation will result in an unclean exit" );
98  }
99  else
100  {
101  LDEBUG( slog_constr, "Handling SIGQUIT (ctrl-\\)" );
102  s_handling_sig_quit = true;
103  }
104 
105  if( signal(SIGPIPE, SIG_IGN) == SIG_ERR )
106  {
107  throw error() << "Unable to ignore SIGPIPE\n";
108  }
109 #endif
110  }
111 
113  {}
114 
115  void signal_handler::add_cancelable( std::shared_ptr< scarab::cancelable > a_cancelable )
116  {
117  signal_handler::add_cancelable_s( a_cancelable );
118  return;
119  }
120 
121  void signal_handler::remove_cancelable( std::shared_ptr< scarab::cancelable > a_cancelable )
122  {
123  signal_handler::remove_cancelable_s( a_cancelable );
124  return;
125  }
126 
128  {
129  signal_handler::remove_cancelable_s( a_cancelable );
130  return;
131  }
132 
133  void signal_handler::add_cancelable_s( std::shared_ptr< scarab::cancelable > a_cancelable )
134  {
135  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
136  s_cancelers.insert( std::make_pair(a_cancelable.get(), cancelable_wptr_t(a_cancelable) ) );
137  return;
138  }
139 
140  void signal_handler::remove_cancelable_s( std::shared_ptr< scarab::cancelable > a_cancelable )
141  {
142  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
143  s_cancelers.erase( a_cancelable.get() );
144  return;
145  }
146 
148  {
149  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
150  s_cancelers.erase( a_cancelable );
151  return;
152  }
153 
155  {
156  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
157  LDEBUG( slog, "Resetting signal_handler" );
158  s_exited = false;
159  s_return_code = RETURN_SUCCESS;
160  s_cancelers.clear();
161  if( s_handling_sig_abrt && signal( SIGABRT, SIG_DFL ) == SIG_ERR )
162  {
163  LWARN( slog, "Unable to switch SIGABRT to default handler" );
164  }
165  else
166  {
167  s_handling_sig_abrt = false;
168  }
169 
170  if( s_handling_sig_term && signal( SIGTERM, SIG_DFL ) == SIG_ERR )
171  {
172  LWARN( slog, "Unable to switch SIGTERM to default handler" );
173  }
174  else
175  {
176  s_handling_sig_term = false;
177  }
178 
179  if( s_handling_sig_int && signal( SIGINT, SIG_DFL ) == SIG_ERR )
180  {
181  LWARN( slog, "Unable to switch SIGINT to default handler" );
182  }
183  else
184  {
185  s_handling_sig_int = false;
186  }
187 
188 #ifndef _WIN32
189  if( s_handling_sig_quit && signal( SIGQUIT, SIG_DFL ) == SIG_ERR )
190  {
191  LWARN( slog, "Unable to switch SIGQUIT to default handler" );
192  }
193  else
194  {
195  s_handling_sig_quit = false;
196  }
197 
198 #endif
199  return;
200  }
201 
202  [[noreturn]] void signal_handler::handle_terminate() noexcept
203  {
205  }
206 
208  {
209  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
210  LERROR( slog, "Handling termination due to an error condition; signal <" << a_sig << ">" );
211  exit( RETURN_ERROR );
212  return;
213  }
214 
216  {
217  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
218  LPROG( slog, "Handling termination; signal <" << a_sig << ">" );
219  exit( RETURN_SUCCESS );
220  return;
221  }
222 
223  [[noreturn]] void signal_handler::terminate( int a_code ) noexcept
224  {
225  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
226  print_current_exception( false );
227  if( a_code > 0 )
228  {
229  print_stack_trace( false );
230  }
231  std::cerr << "Exiting abruptly" << std::endl;
232  std::_Exit( a_code );
233  }
234 
235  void signal_handler::exit( int a_code )
236  {
237  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
238  s_exited = true;
239  s_return_code = a_code;
240  print_current_exception( true );
241  if( a_code > 0 )
242  {
243  print_stack_trace( true );
244  }
245  cancel_all( a_code );
246  return;
247  }
248 
249  void signal_handler::print_current_exception( bool a_use_logging )
250  {
251  // no mutex locking needed here
252 
253  // if there's a current exception, rethrow to print out what()
254  if( auto t_exc_ptr = std::current_exception() )
255  {
256  // we have an exception
257  try
258  {
259  if( a_use_logging ) { LDEBUG( slog, "Rethrowing current exception" ); }
260  else { std::cerr << "Rethrowing current exception" << std::endl; }
261  std::rethrow_exception( t_exc_ptr ); // throw to recognize the type
262  }
263  catch( const std::exception& e ) {
264  if( a_use_logging ) { LERROR( slog, "Caught unhandled exception. what(): " << e.what() ); }
265  else { std::cerr << "Caught unhandled exception. what(): " << e.what() << std::endl; }
266  }
267  catch( ... ) {
268  if( a_use_logging ) LERROR( slog, "Caught unknown (non-std::exception) & unhandled exception." )
269  else { std::cerr << "Caught unknown (non-std::exception) & unhandled exception." << std::endl; }
270  }
271  }
272  return;
273  }
274 
275  void signal_handler::print_stack_trace( bool a_use_logging )
276  {
277  // no mutex locking needed here
278 #ifndef _WIN32 // stack trace printing not implemented for windows
279  void* t_bt_array[50];
280  int t_size = backtrace( t_bt_array, 50 );
281 
282  if( a_use_logging ) { LERROR( slog, "Backtrace returned " << t_size << " frames\n" ); }
283  else { std::cerr << "Backtrace returned " << t_size << " frames\n" << std::endl; }
284 
285  char** t_messages = backtrace_symbols( t_bt_array, t_size );
286 
287  std::stringstream t_bt_str;
288  for( int i = 0; i < t_size && t_messages != nullptr; ++i )
289  {
290  t_bt_str << "[bt]: (" << i << ") " << t_messages[i] << '\n';
291  }
292  if( a_use_logging ) { LERROR( slog, "Backtrace:\n" << t_bt_str.str() ); }
293  else { std::cerr << "Backtrace:\n" << t_bt_str.str() << std::endl; }
294 
295  free( t_messages );
296 #endif
297  return;
298  }
299 
300  void signal_handler::cancel_all( int a_code )
301  {
302  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
303  LDEBUG( slog, "Canceling all cancelables" );
304 
305  while( ! s_cancelers.empty() )
306  {
307  auto t_canceler_it = s_cancelers.begin();
308  if( ! t_canceler_it->second.expired() )
309  {
310  t_canceler_it->second.lock()->cancel( a_code );
311  }
312  s_cancelers.erase( t_canceler_it );
313  std::this_thread::sleep_for( std::chrono::seconds(1) );
314  }
315 
316 #ifdef _WIN32
317  ExitProcess( a_code );
318 #endif
319 
320  return;
321  }
322 
323 } /* namespace scarab */
std::weak_ptr< cancelable > cancelable_wptr_t
static void terminate(int a_code) noexcept
Main terminate function – does not cleanup memory or threads.
#define LWARN(...)
Definition: logger.hh:393
void add_cancelable(std::shared_ptr< cancelable > a_cancelable)
Add a cancelable object.
#define RETURN_SUCCESS
Definition: macros.hh:12
static cancelers s_cancelers
std::map< cancelable *, cancelable_wptr_t > cancelers
Deals with cleanly exiting an application, and includes signal and std::terminate handler functions...
LOGGER(mtlog, "authentication")
static std::recursive_mutex s_mutex
#define RETURN_ERROR
Definition: macros.hh:13
#define LPROG(...)
Definition: logger.hh:392
#define LERROR(...)
Definition: logger.hh:394
Contains the logger class and macros, based on Kasper&#39;s KLogger class.
static void print_current_exception(bool a_use_logging)
Prints the current exception, if there is one.
static void cancel_all(int a_code)
Asynchronous call to cancel all cancelables with the given exit code.
static void exit(int a_code)
Main exit function – cleanly exits.
static void handle_exit_error(int a_sig)
Handler for error signals – cleanly exits.
#define LDEBUG(...)
Definition: logger.hh:389
Base class for a cancelable object (i.e. an object that can be canceled by scarab::signal_handler or ...
Definition: cancelable.hh:51
static void add_cancelable_s(std::shared_ptr< cancelable > a_cancelable)
Static version: add a cancelable object.
void remove_cancelable(std::shared_ptr< cancelable > a_cancelable)
Remove a cancelable object.
void reset()
Remove all cancelables and signal handling.
static void remove_cancelable_s(std::shared_ptr< cancelable > a_cancelable)
Static version: remove a cancelable object.
static void handle_exit_success(int a_sig)
Handler for success signals – cleanly exits.
static void handle_terminate() noexcept
Handler for std::terminate – does not cleanup memory or threads.
static void print_stack_trace(bool a_use_logging)
Prints the stack trace.