Scarab  v3.4.4
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 
116  {
117  signal_handler::add_cancelable_s( a_cancelable );
118  return;
119  }
120 
122  {
123  signal_handler::remove_cancelable_s( a_cancelable );
124  return;
125  }
126 
128  {
129  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
130  s_cancelers.insert( a_cancelable );
131  return;
132  }
133 
135  {
136  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
137  s_cancelers.erase( a_cancelable );
138  return;
139  }
140 
142  {
143  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
144  LDEBUG( slog, "Resetting signal_handler" );
145  s_exited = false;
146  s_return_code = RETURN_SUCCESS;
147  s_cancelers.clear();
148  if( s_handling_sig_abrt && signal( SIGABRT, SIG_DFL ) == SIG_ERR )
149  {
150  LWARN( slog, "Unable to switch SIGABRT to default handler" );
151  }
152  else
153  {
154  s_handling_sig_abrt = false;
155  }
156 
157  if( s_handling_sig_term && signal( SIGTERM, SIG_DFL ) == SIG_ERR )
158  {
159  LWARN( slog, "Unable to switch SIGTERM to default handler" );
160  }
161  else
162  {
163  s_handling_sig_term = false;
164  }
165 
166  if( s_handling_sig_int && signal( SIGINT, SIG_DFL ) == SIG_ERR )
167  {
168  LWARN( slog, "Unable to switch SIGINT to default handler" );
169  }
170  else
171  {
172  s_handling_sig_int = false;
173  }
174 
175 #ifndef _WIN32
176  if( s_handling_sig_quit && signal( SIGQUIT, SIG_DFL ) == SIG_ERR )
177  {
178  LWARN( slog, "Unable to switch SIGQUIT to default handler" );
179  }
180  else
181  {
182  s_handling_sig_quit = false;
183  }
184 
185 #endif
186  return;
187  }
188 
189  [[noreturn]] void signal_handler::handle_terminate() noexcept
190  {
192  }
193 
195  {
196  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
197  LERROR( slog, "Handling termination due to an error condition; signal <" << a_sig << ">" );
198  exit( RETURN_ERROR );
199  return;
200  }
201 
203  {
204  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
205  LPROG( slog, "Handling termination; signal <" << a_sig << ">" );
206  exit( RETURN_SUCCESS );
207  return;
208  }
209 
210  [[noreturn]] void signal_handler::terminate( int a_code ) noexcept
211  {
212  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
213  print_current_exception( false );
214  if( a_code > 0 )
215  {
216  print_stack_trace( false );
217  }
218  std::cerr << "Exiting abruptly" << std::endl;
219  std::_Exit( a_code );
220  }
221 
222  void signal_handler::exit( int a_code )
223  {
224  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
225  s_exited = true;
226  s_return_code = a_code;
227  print_current_exception( true );
228  if( a_code > 0 )
229  {
230  print_stack_trace( true );
231  }
232  cancel_all( a_code );
233  return;
234  }
235 
236  void signal_handler::print_current_exception( bool a_use_logging )
237  {
238  // no mutex locking needed here
239 
240  // if there's a current exception, rethrow to print out what()
241  if( auto t_exc_ptr = std::current_exception() )
242  {
243  // we have an exception
244  try
245  {
246  if( a_use_logging ) { LDEBUG( slog, "Rethrowing current exception" ); }
247  else { std::cerr << "Rethrowing current exception" << std::endl; }
248  std::rethrow_exception( t_exc_ptr ); // throw to recognize the type
249  }
250  catch( const std::exception& e ) {
251  if( a_use_logging ) { LERROR( slog, "Caught unhandled exception. what(): " << e.what() ); }
252  else { std::cerr << "Caught unhandled exception. what(): " << e.what() << std::endl; }
253  }
254  catch( ... ) {
255  if( a_use_logging ) LERROR( slog, "Caught unknown (non-std::exception) & unhandled exception." )
256  else { std::cerr << "Caught unknown (non-std::exception) & unhandled exception." << std::endl; }
257  }
258  }
259  return;
260  }
261 
262  void signal_handler::print_stack_trace( bool a_use_logging )
263  {
264  // no mutex locking needed here
265 #ifndef _WIN32 // stack trace printing not implemented for windows
266  void* t_bt_array[50];
267  int t_size = backtrace( t_bt_array, 50 );
268 
269  if( a_use_logging ) { LERROR( slog, "Backtrace returned " << t_size << " frames\n" ); }
270  else { std::cerr << "Backtrace returned " << t_size << " frames\n" << std::endl; }
271 
272  char** t_messages = backtrace_symbols( t_bt_array, t_size );
273 
274  std::stringstream t_bt_str;
275  for( int i = 0; i < t_size && t_messages != nullptr; ++i )
276  {
277  t_bt_str << "[bt]: (" << i << ") " << t_messages[i] << '\n';
278  }
279  if( a_use_logging ) { LERROR( slog, "Backtrace:\n" << t_bt_str.str() ); }
280  else { std::cerr << "Backtrace:\n" << t_bt_str.str() << std::endl; }
281 
282  free( t_messages );
283 #endif
284  return;
285  }
286 
287  void signal_handler::cancel_all( int a_code )
288  {
289  std::unique_lock< std::recursive_mutex > t_lock( s_mutex );
290  LDEBUG( slog, "Canceling all cancelables" );
291 
292  while( ! s_cancelers.empty() )
293  {
294  (*s_cancelers.begin())->cancel( a_code );
295  s_cancelers.erase( s_cancelers.begin() );
296  std::this_thread::sleep_for( std::chrono::seconds(1) );
297  }
298 
299 #ifdef _WIN32
300  ExitProcess( a_code );
301 #endif
302 
303  return;
304  }
305 
306 } /* namespace scarab */
static void terminate(int a_code) noexcept
Main terminate function – does not cleanup memory or threads.
#define LWARN(...)
Definition: logger.hh:393
#define RETURN_SUCCESS
Definition: macros.hh:12
static cancelers s_cancelers
Deals with cleanly exiting an application, and includes signal and std::terminate handler functions...
LOGGER(mtlog, "authentication")
static std::recursive_mutex s_mutex
static void remove_cancelable_s(cancelable *a_cancelable)
Static version: remove a cancelable object.
#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 add_cancelable_s(cancelable *a_cancelable)
Static version: add a cancelable object.
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
void add_cancelable(cancelable *a_cancelable)
Add a cancelable object.
void remove_cancelable(cancelable *a_cancelable)
Remove a cancelable object.
void reset()
Remove all cancelables and signal handling.
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.
std::set< cancelable *> cancelers