using boost::posix_time::microsec_clock;
using boost::posix_time::ptime;
using boost::posix_time::seconds;
-using boost::thread;
using boost::shared_ptr;
using I2n::Logger::GlobalLogger;
* @param ping_fail_percentage_limit Maximum amount of pings that can fail.
* @param nameserver Server to resolve the addresses.
* @param link_analyzer The object to monitor the link status.
- * @param first_delay Delay in seconds from start_pinging_thread to first ping attempt
+ * @param first_delay Delay in seconds from start_pinging to first ping attempt
*/
PingScheduler::PingScheduler(
- IoServiceItem io_service,
+ const IoServiceItem io_service,
const string &network_interface,
const string &destination_address,
const uint16_t destination_port,
const int first_delay
) :
- IoService( io_service ),
- NextPingTimer( io_service ),
- NextAddressTimer( io_service ),
+ NextPingTimer( *io_service ),
+ NextAddressTimer( *io_service ),
TimeSentLastPing( microsec_clock::universal_time() ),
PingIntervalInSec( ping_interval_in_sec ),
AddressResolveIntervalInSec( ping_interval_in_sec ),
AddressResolutionAttempts( 0 ),
MaxAddressResolutionAttempts( max_address_resolution_attempts ),
EverHadAnyIP( false ),
- Ping(),
- Thread()
+ Ping()
{
BOOST_ASSERT( !network_interface.empty() );
BOOST_ASSERT( !destination_address.empty() );
Ping = PingerFactory::createPinger(
ping_protocol_list,
- IoService,
+ io_service,
network_interface,
destination_address,
destination_port,
{
}
-/**
- * @brief Starts the pinging thread and returns immediately.
- */
-bool PingScheduler::start_pinging_thread()
-{
- Thread = thread( &PingScheduler::start_pinging, this ); //lint !e1793
-
- return true;
-}
-
-/**
- * @brief Waits for the pinging thread to finish.
- */
-void PingScheduler::wait_pinging_thread()
-{
- BOOST_ASSERT( Thread.joinable() );
-
- Thread.join();
-}
-
-/**
- * @brief Stops pinging the host. Request the thread to finish, which makes the
- * any blocking wait (join) on this object to return.
- */
-void PingScheduler::stop_pinging_thread()
-{
- stop_pinging();
-}
-
void PingScheduler::stop_pinging()
{
- // This is thread safe
- IoService.stop();
}
/**
- * @brief Start into infinite loop of calls to resolve_and_ping, using an
- * IoService which is started here and can be stopped through stop_pinging[_thread]
+ * @brief Start into infinite loop of calls to resolve_and_ping
+ * IoService which is started here and can be stopped through stop_pinging
*/
void PingScheduler::start_pinging()
{
}
else
resolve_and_ping();
-
- // event processing loop, it is a blocking call!
- IoService.run(); //lint !e534
- GlobalLogger.info() << "Ping thread was stopped.";
}
#include <boost/shared_ptr.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/numeric/conversion/cast.hpp>
-#include <boost/date_time/posix_time_types.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <daemonfunc.hpp>
#include <logfunc.hpp>
using namespace std;
using boost::shared_ptr;
-using boost:posix::time_duration;
+using boost::posix_time::time_duration;
using I2n::Logger::GlobalLogger;
// a map from interval (in seconds) to delay (in seconds)
typedef std::pair<int, float> IntervalCountPair;
typedef std::map<int, float> DelayMap;
typedef shared_ptr<boost::asio::io_service> IoServiceItem;
+typedef shared_ptr<boost::asio::deadline_timer> TimerItem;
+
+const boost::posix_time::time_duration SIGNAL_CHECK_INTERVAL = boost::posix_time::seconds(1);
//-----------------------------------------------------------------------------
// Declarations
void init_logger();
void set_log_output(const ConfigurationItem &);
DelayMap calc_pinger_delays(const HostList &hosts);
-void init_pingers(const ConfigurationItem&, const LinkStatusItem&, PingSchedulerList*);
+void init_pingers(const IoServiceItem, const ConfigurationItem&,
+ const LinkStatusItem&, PingSchedulerList*);
void start_pingers(const PingSchedulerList&);
void stop_pingers(const PingSchedulerList&);
-void block_all_signals();
-void handle_signals(const int default_log_level);
+void signal_handler(int param);
+void signal_checker( const boost::system::error_code &error );
+void install_signal_handler( const IoServiceItem io_service, const int config_log_level );
+void reset_signal_handlers();
+
+// data required for signal handling (SIGINT, SIGTERM, ... )
+struct signal_data_struct
+{
+ sig_atomic_t signaled_flag;
+ IoServiceItem io_service;
+ void (*old_handler_int )(int);
+ void (*old_handler_term)(int);
+ void (*old_handler_usr1)(int);
+ void (*old_handler_usr2)(int);
+ bool stopped;
+ TimerItem check_timer;
+ int config_log_level;
+
+ signal_data_struct():
+ signaled_flag( false ),
+ io_service(),
+ stopped( false ),
+ check_timer(),
+ config_log_level( I2n::Logger::LogLevel::Notice )
+ { }
+
+};
//-----------------------------------------------------------------------------
// Definitions
//-----------------------------------------------------------------------------
// set default: log at level NOTICE to syslog
const int default_log_level = I2n::Logger::LogLevel::Notice;
- I2n::Logger::enable_syslog( I2n::Logger::Facility::User ); //lint !e1786
- I2n::Logger::set_log_level( default_log_level ); //lint !e534
+ I2n::Logger::enable_syslog( I2n::Logger::Facility::User );
+ I2n::Logger::enable_stderr_log( true );
+ I2n::Logger::set_log_level( default_log_level );
}
void set_log_output(
}
void init_pingers(
- IoServiceItem io_service,
+ const IoServiceItem io_service,
const ConfigurationItem &configuration,
const LinkStatusItem &status_notifier,
PingSchedulerList *scheduler_list
{
// start each ping scheduler
BOOST_FOREACH( const PingSchedulerItem &scheduler, scheduler_list )
- {
- bool started = scheduler->start_pinging_thread();
- if ( !started )
- {
- GlobalLogger.error() << "Could not start pinger." << endl;
- }
- }
+ scheduler->start_pinging();
}
void stop_pingers(
GlobalLogger.info() << "Telling all pingers to stop";
BOOST_FOREACH( const PingSchedulerItem &scheduler, scheduler_list )
{
- scheduler->stop_pinging_thread();
+ scheduler->stop_pinging();
}
-
- GlobalLogger.info() << "Waiting for threads to shut down";
- BOOST_FOREACH( const PingSchedulerItem &scheduler, scheduler_list )
- {
- scheduler->wait_pinging_thread();
- }
- GlobalLogger.info() << "All ping threads are gone";
}
-/**
- * @brief Block all signals so created threads inherit that signal mask.
- *
- * @return void
- **/
-void block_all_signals()
-{
- sigset_t all_signals;
- int result = sigfillset( &all_signals );
- if ( result == -1 ) {
- GlobalLogger.error() << "Sigfillset failed." << endl;
- }
- result = sigprocmask( SIG_SETMASK, &all_signals, NULL );
- if ( result == -1 ) {
- throw runtime_error( "Can't block signals" );
- }
+// the one instance of signal_data_struct
+signal_data_struct signal_data;
+
+void signal_handler(int param)
+{
+ GlobalLogger.info() << "signal handler called with param " << param << endl;
+ signal_data.signaled_flag = param;
}
-/**
- * @brief Wait until a signal receives and act upon it
- *
- * @param default_log_level
- *
- * @return void
- **/
-void handle_signals( const int default_log_level )
+void signal_checker( const boost::system::error_code &error )
{
- // Wait for signals
- sigset_t all_signals;
- int result = sigfillset( &all_signals );
- if ( result == -1 ) {
- GlobalLogger.error() << "Sigfillset failed." << endl;
- }
+ bool want_stop = false;
- // Synchronously process signals
- bool want_quit = false;
- while ( !want_quit )
- {
- int signal_number = 0;
- int err = sigwait( &all_signals, &signal_number );
- if (!err)
+ if ( error )
+ { // there was an error in the timer
+ if ( error == boost::asio::error::operation_aborted )
{
- if ( signal_number == SIGUSR1 )
- {
- int new_log_level = I2n::Logger::get_log_level()+1;
- I2n::Logger::set_log_level( new_log_level ); //lint !e534
- GlobalLogger.info() << "Increased log level to "
- << I2n::Logger::get_log_level_string();
- }
- else if ( signal_number == SIGUSR2 )
- {
- I2n::Logger::set_log_level( default_log_level ); //lint !e534
- GlobalLogger.info() << "Reset log level to normal ("
- << I2n::Logger::get_log_level_string() << ")";
- }
- else if ( ( signal_number == SIGTERM ) || ( signal_number == SIGINT ))
- {
- want_quit = true;
- }
+ GlobalLogger.error() << "Signal check timer was cancelled! Stopping io_service" << endl;
+ want_stop = true;
}
else
{
- want_quit = true;
+ GlobalLogger.error() << "not implemented yet! Stopping io_service" << endl;
+ want_stop = true;
}
}
-}
+ else if ( signal_data.signaled_flag )
+ { // so there has been a signal --> check which one it was
-// data required for signal handling (SIGINT, SIGTERM, ... )
-struct signal_data_struct
-{
- sig_atomic_t signaled_flag;
- void (*old_handler_int )(int);
- void (*old_handler_term)(int);
- void (*old_handler_usr1)(int);
- void (*old_handler_usr2)(int);
- boost::asio::deadline_timer check_timer;
- time_duration check_interval;
- IoServiceItem io_service;
-};
-signal_data_struct signal_data;
+ // quickly copy and reset signal flag
+ sig_atomic_t signaled_flag = signal_data.signaled_flag;
+ signal_data.signaled_flag = 0;
-void signal_handler(int param)
-{
- GlobalLogger.info() << "signal handler called with param " << param << endl;
- signal_data.signaled_flag = param;
-}
+ if ( signaled_flag == SIGUSR1 )
+ {
+ int new_log_level = I2n::Logger::get_log_level()+1;
+ I2n::Logger::set_log_level( new_log_level );
+ GlobalLogger.info() << "Increased log level to "
+ << I2n::Logger::get_log_level_string();
+ }
+ else if ( signaled_flag == SIGUSR2 )
+ {
+ I2n::Logger::set_log_level( signal_data.config_log_level );
+ GlobalLogger.info() << "Reset log level to normal ("
+ << I2n::Logger::get_log_level_string() << ")";
+ }
+ else if ( ( signaled_flag == SIGTERM ) || ( signaled_flag == SIGINT ))
+ {
+ GlobalLogger.info() << "Received signal SIGINT/SIGTERM --> will stop" << endl;
+ want_stop = true;
+ }
+ }
-void signal_checker( const boost::system::error_code &error )
-{
- GlobalLogger.debug() << "signal checker called with arg " << error;
- if ( error )
- if ( !signal_data.signaled_flag)
- { // schedule another timer
- signal_data.check_timer.expires_from_now( signal_data.check_interval );
- signal_data.check_timer.async_wait( signal_checker );
- return;
+ if ( want_stop )
+ { // interrupt infinite loop in main and asio event loop
+ signal_data.stopped = true;
+ signal_data.io_service->stop();
}
else
- {
- // so there has been a signal --> stop
- // actually, see handle_signals for what to do
- GlobalLogger.error() << "not implemented yet, see handle_signals for what to do!"
- << endl;
- signal_data.io_service->stop()
+ { // re-schedule timer
+ GlobalLogger.debug() << "Setting another signal timer" << endl;
+ signal_data.check_timer->expires_from_now( SIGNAL_CHECK_INTERVAL );
+ signal_data.check_timer->async_wait( signal_checker );
}
}
-bool install_signal_handler( IoServiceItem io_service,
- const time_duration &check_interval )
+/// register own signal handlers; see reset_signal_handlers for undo
+void install_signal_handler( const IoServiceItem io_service, const int config_log_level )
{
signal_data.signaled_flag = 0;
+ signal_data.config_log_level = config_log_level;
// install own signal handlers
signal_data.old_handler_int = signal(SIGINT, signal_handler);
signal_data.old_handler_term == SIG_ERR ||
signal_data.old_handler_usr1 == SIG_ERR ||
signal_data.old_handler_usr2 == SIG_ERR )
- throw runtime_error( "Failed to install signal handler: " + strerror(errno) );
+ throw runtime_error( string("Failed to install signal handler: ") + string(strerror(errno)) );
+
+ // create a timer and a shared pointer to it, so it does not get out of scope
+ TimerItem check_timer( new boost::asio::deadline_timer( *io_service ) );
- // add timer to io_service that calls signal_checker
- signal_data.check_interval = check_interval;
+ // remember the io_service and the timer
signal_data.io_service = io_service;
- signal_data.check_timer( io_service );
- signal_data.check_timer.expires_from_now( check_interval );
+ signal_data.check_timer = check_timer;
- // start timer
- signal_data.check_timer.async_wait( signal_checker );
+ // set the timer
+ check_timer->expires_from_now( SIGNAL_CHECK_INTERVAL );
+ check_timer->async_wait( signal_checker );
+ GlobalLogger.debug() << "signal timer set" << endl;
}
-int main( int argc, const char *argv[] )
+/// reset handlers to the ones saved in install_signal_handler
+void reset_signal_handlers()
{
- // Block all signals before creating any threads
- block_all_signals();
+ void (*old_handler_int)(int);
+ void (*old_handler_term)(int);
+ void (*old_handler_usr1)(int);
+ void (*old_handler_usr2)(int);
+ old_handler_int = signal(SIGINT , signal_data.old_handler_int);
+ old_handler_term = signal(SIGTERM, signal_data.old_handler_term);
+ old_handler_usr1 = signal(SIGUSR1, signal_data.old_handler_usr1);
+ old_handler_usr2 = signal(SIGUSR2, signal_data.old_handler_usr2);
+
+ if ( old_handler_int == SIG_ERR ||
+ old_handler_term == SIG_ERR ||
+ old_handler_usr1 == SIG_ERR ||
+ old_handler_usr2 == SIG_ERR )
+ throw runtime_error( string("Failed to reset signal handler: ") + string(strerror(errno)) );
+}
+
+int main( int argc, const char *argv[] )
+{
init_logger();
GlobalLogger.debug() << "logger initiated with default config";
PingSchedulerList scheduler_list;
+ IoServiceItem io_service;
int ret_code = 0;
+ unsigned n_exceptions = 0;
try
{
ConfigurationItem configuration = get_configuration( argc, argv );
}
int log_level = configuration->get_log_level();
- I2n::Logger::set_log_level( log_level ); //lint !e534
+ I2n::Logger::set_log_level( log_level );
GlobalLogger.info() << "Set LogLevel to " << I2n::Logger::get_log_level_string() << endl;
set_log_output( configuration );
LinkStatusItem status_notifier = get_status_notifier( configuration );
- IoServiceItem io_service( new boost::asio::io_service() );
+ IoServiceItem io_service_temp( new boost::asio::io_service() );
+ io_service_temp.swap( io_service );
+ io_service_temp.reset();
init_pingers( io_service, configuration, status_notifier, &scheduler_list );
- //start_pingers( scheduler_list );
-
- install_signal_handler( io_service );
+ install_signal_handler( io_service, log_level );
+ start_pingers( scheduler_list );
}
catch ( const std::exception &ex )
{
GlobalLogger.error() << "Uncaught exception. " << ex.what() << endl;
ret_code = 1;
+ ++n_exceptions;
}
catch (...) {
GlobalLogger.error() << "Caught unknown exception!" << endl;
ret_code = 2;
+ ++n_exceptions;
}
- // call boost::asio main event loop, ignoring exceptions
- while ( true )
+ if ( ret_code == 0 )
{
- try
+ // call boost::asio main event loop, catching exceptions
+ while ( !signal_data.stopped )
{
- io_service->run();
- }
- catch ( const std::exception &ex )
- {
- GlobalLogger.error() << "Caught exception, will continue. " << ex.what() << endl;
- }
- catch (...) {
- GlobalLogger.error() << "Caught unknown exception, will continue!" << endl;
+ try
+ {
+ GlobalLogger.info() << "starting/continuing io_service main loop" << endl;
+ io_service->run();
+ }
+ catch ( const std::exception &ex )
+ {
+ ++n_exceptions;
+ GlobalLogger.error() << "Caught exception, will continue. " << ex.what() << endl;
+ }
+ catch (...) {
+ ++n_exceptions;
+ GlobalLogger.error() << "Caught unknown exception, will continue!" << endl;
+ }
}
}
{
GlobalLogger.info() << "Cleaning up" << endl;
stop_pingers( scheduler_list );
+ reset_signal_handlers();
}
catch ( const std::exception &ex )
{
ret_code += 8;
}
- GlobalLogger.notice() << "Pingcheck done" << endl;
+ GlobalLogger.notice() << "Pingcheck done " << endl;
+ GlobalLogger.debug() << "In main loop, had " << n_exceptions << " exception[s]" << endl;
return ret_code;
}