2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
26 #include <boost/asio.hpp>
27 #include <boost/foreach.hpp>
28 #include <boost/shared_ptr.hpp>
29 #include <boost/math/special_functions/round.hpp>
30 #include <boost/numeric/conversion/cast.hpp>
31 #include <boost/date_time/posix_time/posix_time_types.hpp>
33 #include <daemonfunc.hpp>
34 #include <logfunc.hpp>
36 #include "boost_assert_handler.h"
37 #include "config/configurationreader.h"
38 #include "config/host.h"
39 #include "link/linkstatus.h"
40 #include "host/loglevel.h"
41 #include "host/pingprotocol.h"
42 #include "host/pingscheduler.h"
43 #include "icmp/icmppinger.h" // contains IcmpPacketDistributor
44 #include "dns/dnsmaster.h"
48 using boost::shared_ptr;
49 using boost::posix_time::time_duration;
50 using I2n::Logger::GlobalLogger;
52 // a map from interval (in seconds) to delay (in seconds)
53 typedef std::pair<int, float> IntervalCountPair;
54 typedef std::map<int, float> DelayMap;
55 typedef shared_ptr<boost::asio::deadline_timer> TimerItem;
57 const boost::posix_time::time_duration SIGNAL_CHECK_INTERVAL = boost::posix_time::seconds(1);
59 //-----------------------------------------------------------------------------
61 //-----------------------------------------------------------------------------
63 typedef std::pair<bool, ConfigurationItem> GetConfigReturnType;
64 GetConfigReturnType get_configuration(int, const char**);
65 LinkStatusItem get_status_notifier(const ConfigurationItem&);
67 void set_log_output(const ConfigurationItem &);
68 DelayMap calc_pinger_delays(const HostList &hosts);
69 void init_pingers(const IoServiceItem, const ConfigurationItem&,
70 const LinkStatusItem&, PingSchedulerList*);
71 void start_pingers(const PingSchedulerList&);
72 void stop_pingers(const PingSchedulerList&);
74 void signal_handler_int(int param);
75 void signal_handler_term(int param);
76 void signal_handler_usr1(int param);
77 void signal_handler_usr2(int param);
78 void signal_checker( const boost::system::error_code &error );
79 void install_signal_handlers( const IoServiceItem io_service, const int config_log_level );
80 void reset_signal_handlers();
82 // data required for signal handling (SIGINT, SIGTERM, ... )
83 struct signal_data_struct
85 volatile sig_atomic_t signaled_flag_int;
86 volatile sig_atomic_t signaled_flag_term;
87 volatile sig_atomic_t signaled_flag_usr1;
88 volatile sig_atomic_t signaled_flag_usr2;
89 IoServiceItem io_service;
90 void (*old_handler_int )(int);
91 void (*old_handler_term)(int);
92 void (*old_handler_usr1)(int);
93 void (*old_handler_usr2)(int);
95 TimerItem check_timer;
99 signaled_flag_int( 0 ),
100 signaled_flag_term( 0 ),
101 signaled_flag_usr1( 0 ),
102 signaled_flag_usr2( 0 ),
104 old_handler_int( 0 ),
105 old_handler_term( 0 ),
106 old_handler_usr1( 0 ),
107 old_handler_usr2( 0 ),
110 config_log_level( I2n::Logger::LogLevel::Notice )
114 //-----------------------------------------------------------------------------
116 //-----------------------------------------------------------------------------
118 GetConfigReturnType get_configuration(
123 ConfigurationReader config_reader;
124 bool parsed_success = config_reader.parse( argc, argv );
125 Configuration config_obj = config_reader.get_configuration();
127 ConfigurationItem configuration( new Configuration( config_obj ) );
128 GetConfigReturnType return_val( parsed_success, configuration );
132 LinkStatusItem get_status_notifier(
133 const ConfigurationItem &configuration
136 int hosts_down_limit = configuration->get_hosts_down_limit();
137 int link_up_interval_in_min = configuration->get_link_up_interval_in_min();
138 int link_down_interval_in_min = configuration->get_link_down_interval_in_min();
139 string status_notifier_cmd = configuration->get_status_notifier_cmd();
140 LinkStatusItem link_analyzer(
143 link_up_interval_in_min,
144 link_down_interval_in_min,
149 return link_analyzer;
154 // set default: log at level NOTICE to syslog and stderr
155 // to ensure that in case of faulty config, the error is noticed
156 I2n::Logger::enable_syslog( I2n::Logger::Facility::User );
157 I2n::Logger::enable_stderr_log( true );
158 I2n::Logger::set_log_level( I2n::Logger::LogLevel::Notice );
162 const ConfigurationItem &configuration
165 LogOutput log_output = configuration->get_log_output();
168 case LogOutput_SYSLOG:
169 GlobalLogger.info() << "Setting log output target to syslog" << endl;
170 I2n::Logger::enable_syslog(true);
171 I2n::Logger::enable_stderr_log(false);
172 I2n::Logger::enable_log_file(false);
173 GlobalLogger.info() << "Set log output target to syslog" << endl;
175 case LogOutput_TERMINAL:
176 GlobalLogger.info() << "Setting log output target to terminal" << endl;
177 I2n::Logger::enable_syslog(false);
178 I2n::Logger::enable_stderr_log(true);
179 I2n::Logger::enable_log_file(false);
180 GlobalLogger.info() << "Set log output target to terminal" << endl;
181 GlobalLogger.info() << "(check syslog for earlier messages)" << endl;
184 GlobalLogger.error() << "Unknown log output target!" << endl;
190 * @brief calculate delay between pingers to evenly distribute them in time
192 * If there are many pingers with same interval, will get bursts of pings
193 * and none in-between. This function calculates delays for large numbers
194 * of hosts with same ping intervals, to distribute them as evenly as
195 * possible, right from the start (might diverge over time, anyway).
197 * Will not do much good for pingers with many different intervals, but
198 * then is not required anyway and does no(t much) harm.
200 * Called by init_pingers with
201 * @param hosts list of hosts as obtained from configuration
202 * @returns a map from ping interval to delay between pingers of that interval
204 DelayMap calc_pinger_delays(const HostList &hosts)
206 // first step: count number of hosts with same intervals
207 DelayMap delay_shifts;
209 BOOST_FOREACH( const HostItem &host, hosts )
211 curr_interval = host->get_interval_in_sec();
213 delay_shifts[curr_interval] = 1.0f;
215 delay_shifts[curr_interval] += 1.0f;
218 // second step: divide intervals by counts, round to int
219 // --> for 18 pingers with a 30s interval, get 30s/18 = 1.66667
220 BOOST_FOREACH( IntervalCountPair interval_and_count, delay_shifts )
221 delay_shifts[interval_and_count.first] =
222 boost::numeric_cast<float>(interval_and_count.first) /
223 interval_and_count.second;
229 const IoServiceItem io_service,
230 const ConfigurationItem &configuration,
231 const LinkStatusItem &status_notifier,
232 PingSchedulerList *scheduler_list
235 string default_network_interface = configuration->get_source_network_interface();
236 int ping_fail_limit = configuration->get_ping_fail_limit();
237 int ping_reply_timeout = configuration->get_ping_reply_timeout();
238 int resolved_ip_ttl_threshold = configuration->get_resolved_ip_ttl_threshold();
240 // remove some hosts at random
241 configuration->randomize_hosts();
243 // calculate delays between pingers of same interval
244 DelayMap delay_shifts = calc_pinger_delays(configuration->get_hosts());
246 // setup memory for assigned delays
248 BOOST_FOREACH( IntervalCountPair interval_and_delay, delay_shifts )
249 delays[interval_and_delay.first] = 0.0f;
251 HostList hosts = configuration->get_hosts();
252 BOOST_FOREACH( const HostItem &host, hosts )
254 string destination_address = host->get_address();
255 uint16_t destination_port = host->get_port();
256 string host_network_interface = host->get_source_network_interface();
257 string network_interface = ( host_network_interface == "default" ) ?
258 default_network_interface :
259 host_network_interface;
260 PingProtocolList protocol_list = host->get_ping_protocol_list();
261 int ping_interval_in_sec = host->get_interval_in_sec();
263 // get delay for this scheduler and update assigned delays
264 int current_delay = boost::math::iround(delays[ping_interval_in_sec]);
265 delays[ping_interval_in_sec] += delay_shifts[ping_interval_in_sec];
267 PingSchedulerItem scheduler(
274 ping_interval_in_sec,
277 resolved_ip_ttl_threshold,
282 scheduler_list->push_back( scheduler );
287 const PingSchedulerList &scheduler_list
290 // start each ping scheduler
291 BOOST_FOREACH( const PingSchedulerItem &scheduler, scheduler_list )
292 scheduler->start_pinging();
296 const PingSchedulerList &scheduler_list
299 // Stop each ping scheduler
300 GlobalLogger.info() << "Telling all pingers to stop";
301 BOOST_FOREACH( const PingSchedulerItem &scheduler, scheduler_list )
303 scheduler->stop_pinging();
306 IcmpPacketDistributor::clean_up_all();
310 // the one instance of signal_data_struct
311 signal_data_struct signal_data;
314 /// registered as signal handler; just sets signal_data.signaled_flag
315 void signal_handler_int(int param)
317 signal_data.signaled_flag_int = 1;
319 void signal_handler_term(int param)
321 signal_data.signaled_flag_term = 1;
323 void signal_handler_usr1(int param)
325 signal_data.signaled_flag_usr1 = 1;
327 void signal_handler_usr2(int param)
329 signal_data.signaled_flag_usr2 = 1;
333 /// called regularly from io_service; checks signal_data.signal_flag
334 void signal_checker( const boost::system::error_code &error )
336 bool want_stop = false;
339 { // there was an error in the timer
340 if ( error == boost::asio::error::operation_aborted )
342 GlobalLogger.error() << "Signal check timer was cancelled! Stopping io_service" << endl;
347 GlobalLogger.error() << "Signal check timer handler received error code " << error
348 << "! Stopping io_service" << endl;
353 if ( signal_data.signaled_flag_int )
355 signal_data.signaled_flag_int = 0;
356 GlobalLogger.notice() << "Received signal SIGINT --> will stop" << endl;
359 else if ( signal_data.signaled_flag_term )
361 signal_data.signaled_flag_term = 0;
362 GlobalLogger.notice() << "Received signal SIGTERM --> will stop" << endl;
365 else if ( signal_data.signaled_flag_usr1 )
367 signal_data.signaled_flag_usr1 = 0;
368 int new_log_level = I2n::Logger::get_log_level()+1;
369 I2n::Logger::set_log_level( new_log_level );
370 GlobalLogger.info() << "Received SIGUSR1 -- increased log level to "
371 << I2n::Logger::get_log_level_string();
373 else if ( signal_data.signaled_flag_usr2 )
375 signal_data.signaled_flag_usr2 = 0;
376 I2n::Logger::set_log_level( signal_data.config_log_level );
377 GlobalLogger.info() << "Received SIGUSR2 -- reset log level to normal ("
378 << I2n::Logger::get_log_level_string() << ")";
383 { // interrupt infinite loop in main and asio event loop
384 signal_data.stopped = true;
385 signal_data.io_service->stop();
388 { // re-schedule timer
389 signal_data.check_timer->expires_from_now( SIGNAL_CHECK_INTERVAL );
390 signal_data.check_timer->async_wait( signal_checker );
394 /// register own signal handlers; see reset_signal_handlers for undo
395 void install_signal_handlers( const IoServiceItem io_service, const int config_log_level )
397 signal_data.signaled_flag_int = 0;
398 signal_data.signaled_flag_term = 0;
399 signal_data.signaled_flag_usr1 = 0;
400 signal_data.signaled_flag_usr2 = 0;
401 signal_data.config_log_level = config_log_level;
403 // install own signal handlers
404 signal_data.old_handler_int = signal(SIGINT, signal_handler_int);
405 signal_data.old_handler_term = signal(SIGTERM, signal_handler_term);
406 signal_data.old_handler_usr1 = signal(SIGUSR1, signal_handler_usr1);
407 signal_data.old_handler_usr2 = signal(SIGUSR2, signal_handler_usr2);
408 if ( signal_data.old_handler_int == SIG_ERR ||
409 signal_data.old_handler_term == SIG_ERR ||
410 signal_data.old_handler_usr1 == SIG_ERR ||
411 signal_data.old_handler_usr2 == SIG_ERR )
412 throw runtime_error( string("Failed to install signal handler: ") + string(strerror(errno)) );
414 // create a timer and a shared pointer to it, so it does not get out of scope
415 TimerItem check_timer( new boost::asio::deadline_timer( *io_service ) );
417 // remember the io_service and the timer
418 signal_data.io_service = io_service;
419 signal_data.check_timer = check_timer;
422 check_timer->expires_from_now( SIGNAL_CHECK_INTERVAL );
423 check_timer->async_wait( signal_checker );
424 GlobalLogger.debug() << "signal timer set" << endl;
427 /// reset handlers to the ones saved in install_signal_handlers
428 void reset_signal_handlers()
430 void (*old_handler_int)(int) = 0;
431 void (*old_handler_term)(int) = 0;
432 void (*old_handler_usr1)(int) = 0;
433 void (*old_handler_usr2)(int) = 0;
434 if (signal_data.old_handler_int != 0 )
435 old_handler_int = signal(SIGINT , signal_data.old_handler_int);
436 if (signal_data.old_handler_term != 0 )
437 old_handler_term = signal(SIGTERM, signal_data.old_handler_term);
438 if (signal_data.old_handler_usr1 != 0 )
439 old_handler_usr1 = signal(SIGUSR1, signal_data.old_handler_usr1);
440 if (signal_data.old_handler_usr2 != 0 )
441 old_handler_usr2 = signal(SIGUSR2, signal_data.old_handler_usr2);
443 if ( old_handler_int == SIG_ERR ||
444 old_handler_term == SIG_ERR ||
445 old_handler_usr1 == SIG_ERR ||
446 old_handler_usr2 == SIG_ERR )
447 throw runtime_error( string("Failed to reset signal handler: ") + string(strerror(errno)) );
451 int main( int argc, const char *argv[] )
454 GlobalLogger.debug() << "logger initiated with default config";
456 PingSchedulerList scheduler_list;
457 IoServiceItem io_service;
459 unsigned n_exceptions = 0;
460 unsigned max_exceptions = 1;
464 GetConfigReturnType success_and_config = get_configuration( argc, argv );
465 ConfigurationItem configuration = success_and_config.second;
467 if ( configuration->get_print_version() ) // do this even if parsing of config failed
469 GlobalLogger.debug() << "Printing version info (" << VERSION_STRING << ") and exit" << endl;
470 cout << PROJECT_NAME << " version " << VERSION_STRING << "."
471 << VERSION_REVISION_STRING << " build " << __DATE__ << endl;
475 if ( ! success_and_config.first )
477 GlobalLogger.error() << "Could not read/parse configuration!";
478 GlobalLogger.debug() << "Return 1 immediately" << endl;
481 GlobalLogger.debug() << "Start setup" << endl;
483 int log_level = configuration->get_log_level();
484 I2n::Logger::set_log_level( log_level );
485 GlobalLogger.info() << "Set LogLevel to " << I2n::Logger::get_log_level_string() << endl;
487 set_log_output( configuration );
488 GlobalLogger.notice() << "started";
490 bool daemon_mode = configuration->get_daemon();
493 I2n::Daemon::daemonize();
496 LinkStatusItem status_notifier = get_status_notifier( configuration );
498 IoServiceItem io_service_temp( new boost::asio::io_service() );
499 io_service_temp.swap( io_service );
500 io_service_temp.reset();
503 boost::asio::ip::address name_server_ip =
504 boost::asio::ip::address::from_string(
505 configuration->get_nameserver() );
507 DnsMaster::create_master(
510 configuration->get_resolved_ip_ttl_threshold(),
511 configuration->get_max_address_resolution_attempts(),
512 configuration->get_dns_cache_file() );
514 init_pingers( io_service, configuration, status_notifier, &scheduler_list );
516 install_signal_handlers( io_service, log_level );
518 start_pingers( scheduler_list );
520 catch ( const std::exception &ex )
522 GlobalLogger.error() << "Uncaught exception. " << ex.what() << endl;
527 GlobalLogger.error() << "Caught unknown exception!" << endl;
534 GlobalLogger.info() << "starting io_service main loop" << endl;
536 if (max_exceptions > 0)
537 GlobalLogger.warning() << "Limited number of acceptable exceptions,"
538 << " this is a debugging option!";
540 // call boost::asio main event loop, catching exceptions
541 while ( !signal_data.stopped )
547 catch ( const std::exception &ex )
550 GlobalLogger.error() << "Caught exception, will continue. " << ex.what() << endl;
554 GlobalLogger.error() << "Caught unknown exception, will continue!" << endl;
557 if (max_exceptions > 0 && n_exceptions >= max_exceptions)
559 GlobalLogger.info() << "reached max number of exceptions allowed in main loop" << endl;
561 signal_data.stopped = true;
564 else if ( signal_data.stopped )
565 GlobalLogger.info() << "exiting io_service main loop" << endl;
566 // ( io_service->stop() has been called already in signal handler)
568 GlobalLogger.info() << "continuing io_service main loop" << endl;
575 GlobalLogger.info() << "Cleaning up" << endl;
576 stop_pingers( scheduler_list );
577 reset_signal_handlers();
579 catch ( const std::exception &ex )
581 GlobalLogger.error() << "Uncaught exception while cleaning up: " << ex.what() << endl;
585 GlobalLogger.error() << "Caught unknown exception while cleaning up!" << endl;
589 GlobalLogger.notice() << "Pingcheck done " << endl;
590 GlobalLogger.debug() << "In main loop, had " << n_exceptions << " exception[s]" << endl;