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