Commit | Line | Data |
---|---|---|
91fcc471 TJ |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
4 | ||
5 | A copy of GNU General Public License (GPL) is included in this distribution, | |
6 | in the file COPYING.GPL. | |
7 | ||
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. | |
13 | ||
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. | |
16 | ||
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. | |
19 | */ | |
88714861 | 20 | #include <signal.h> |
238da857 | 21 | #include <stdint.h> |
88714861 | 22 | |
c5e4bfa1 | 23 | #include <vector> |
a4049623 | 24 | #include <iostream> |
c5e4bfa1 | 25 | |
e39cc3da | 26 | #include <boost/asio.hpp> |
8739a651 | 27 | #include <boost/foreach.hpp> |
c1fff16a | 28 | #include <boost/shared_ptr.hpp> |
59733431 CH |
29 | #include <boost/math/special_functions/round.hpp> |
30 | #include <boost/numeric/conversion/cast.hpp> | |
365036be | 31 | #include <boost/date_time/posix_time/posix_time_types.hpp> |
e4b14784 CH |
32 | #include <boost/random/linear_congruential.hpp> |
33 | #include <boost/random/uniform_real.hpp> | |
34 | #include <boost/random/variate_generator.hpp> | |
4ea9706c | 35 | |
301610ca GMF |
36 | #include <daemonfunc.hpp> |
37 | #include <logfunc.hpp> | |
8ef29e4a | 38 | |
780b0bca | 39 | #include "boost_assert_handler.h" |
9c55ecd3 GMF |
40 | #include "config/configurationreader.h" |
41 | #include "config/host.h" | |
72e54d1c | 42 | #include "link/linkstatus.h" |
3625c0e5 | 43 | #include "host/loglevel.h" |
3fd74a53 | 44 | #include "host/pingprotocol.h" |
8f66f529 | 45 | #include "host/pingscheduler.h" |
41d175b0 | 46 | #include "icmp/icmppinger.h" // contains IcmpPacketDistributor |
c5b4902d | 47 | #include "dns/dnsmaster.h" |
4ea9706c | 48 | |
780b0bca | 49 | |
ad8eb8ab | 50 | using namespace std; |
2bf8720f | 51 | using boost::shared_ptr; |
365036be | 52 | using boost::posix_time::time_duration; |
301610ca GMF |
53 | using I2n::Logger::GlobalLogger; |
54 | ||
59733431 CH |
55 | // a map from interval (in seconds) to delay (in seconds) |
56 | typedef std::pair<int, float> IntervalCountPair; | |
57 | typedef std::map<int, float> DelayMap; | |
365036be CH |
58 | typedef shared_ptr<boost::asio::deadline_timer> TimerItem; |
59 | ||
60 | const boost::posix_time::time_duration SIGNAL_CHECK_INTERVAL = boost::posix_time::seconds(1); | |
59733431 | 61 | |
7cb9a340 GMF |
62 | //----------------------------------------------------------------------------- |
63 | // Declarations | |
64 | //----------------------------------------------------------------------------- | |
65 | ||
b588279a CH |
66 | typedef std::pair<bool, ConfigurationItem> GetConfigReturnType; |
67 | GetConfigReturnType get_configuration(int, const char**); | |
7cb9a340 GMF |
68 | LinkStatusItem get_status_notifier(const ConfigurationItem&); |
69 | void init_logger(); | |
5ba17410 | 70 | void set_log_output(const ConfigurationItem &); |
59733431 | 71 | DelayMap calc_pinger_delays(const HostList &hosts); |
a85f210b | 72 | bool init_pingers(const IoServiceItem, const ConfigurationItem&, |
365036be | 73 | const LinkStatusItem&, PingSchedulerList*); |
7cb9a340 GMF |
74 | void start_pingers(const PingSchedulerList&); |
75 | void stop_pingers(const PingSchedulerList&); | |
7cb9a340 | 76 | |
666388ba CH |
77 | void signal_handler_int(int param); |
78 | void signal_handler_term(int param); | |
79 | void signal_handler_usr1(int param); | |
80 | void signal_handler_usr2(int param); | |
365036be | 81 | void signal_checker( const boost::system::error_code &error ); |
666388ba | 82 | void install_signal_handlers( const IoServiceItem io_service, const int config_log_level ); |
365036be CH |
83 | void reset_signal_handlers(); |
84 | ||
85 | // data required for signal handling (SIGINT, SIGTERM, ... ) | |
86 | struct signal_data_struct | |
87 | { | |
963e98a2 CH |
88 | volatile sig_atomic_t signaled_flag_int; |
89 | volatile sig_atomic_t signaled_flag_term; | |
90 | volatile sig_atomic_t signaled_flag_usr1; | |
91 | volatile sig_atomic_t signaled_flag_usr2; | |
365036be CH |
92 | IoServiceItem io_service; |
93 | void (*old_handler_int )(int); | |
94 | void (*old_handler_term)(int); | |
95 | void (*old_handler_usr1)(int); | |
96 | void (*old_handler_usr2)(int); | |
97 | bool stopped; | |
98 | TimerItem check_timer; | |
99 | int config_log_level; | |
100 | ||
101 | signal_data_struct(): | |
963e98a2 CH |
102 | signaled_flag_int( 0 ), |
103 | signaled_flag_term( 0 ), | |
104 | signaled_flag_usr1( 0 ), | |
105 | signaled_flag_usr2( 0 ), | |
365036be | 106 | io_service(), |
425d0f07 CH |
107 | old_handler_int( 0 ), |
108 | old_handler_term( 0 ), | |
109 | old_handler_usr1( 0 ), | |
110 | old_handler_usr2( 0 ), | |
365036be CH |
111 | stopped( false ), |
112 | check_timer(), | |
113 | config_log_level( I2n::Logger::LogLevel::Notice ) | |
114 | { } | |
115 | ||
116 | }; | |
7cb9a340 GMF |
117 | //----------------------------------------------------------------------------- |
118 | // Definitions | |
119 | //----------------------------------------------------------------------------- | |
35e8afe0 | 120 | |
b588279a | 121 | GetConfigReturnType get_configuration( |
f2c0a5db | 122 | int argc, |
5c3c6449 | 123 | const char *argv[] |
f2c0a5db GMF |
124 | ) |
125 | { | |
f2c0a5db GMF |
126 | ConfigurationReader config_reader; |
127 | bool parsed_success = config_reader.parse( argc, argv ); | |
b588279a | 128 | Configuration config_obj = config_reader.get_configuration(); |
f2c0a5db | 129 | |
b588279a CH |
130 | ConfigurationItem configuration( new Configuration( config_obj ) ); |
131 | GetConfigReturnType return_val( parsed_success, configuration ); | |
132 | return return_val; | |
f2c0a5db GMF |
133 | } |
134 | ||
72e54d1c | 135 | LinkStatusItem get_status_notifier( |
f2c0a5db GMF |
136 | const ConfigurationItem &configuration |
137 | ) | |
138 | { | |
139 | int hosts_down_limit = configuration->get_hosts_down_limit(); | |
97837af8 CH |
140 | int link_up_interval_in_sec = configuration->get_link_up_interval_in_sec(); |
141 | int link_down_interval_in_sec = configuration->get_link_down_interval_in_sec(); | |
f2c0a5db | 142 | string status_notifier_cmd = configuration->get_status_notifier_cmd(); |
72e54d1c GMF |
143 | LinkStatusItem link_analyzer( |
144 | new LinkStatus( | |
f2c0a5db | 145 | hosts_down_limit, |
97837af8 CH |
146 | link_up_interval_in_sec, |
147 | link_down_interval_in_sec, | |
f2c0a5db GMF |
148 | status_notifier_cmd |
149 | ) | |
150 | ); | |
151 | ||
152 | return link_analyzer; | |
153 | } | |
154 | ||
88714861 | 155 | void init_logger() |
301610ca | 156 | { |
0fd358ca CH |
157 | // set default: log at level NOTICE to syslog and stderr |
158 | // to ensure that in case of faulty config, the error is noticed | |
365036be CH |
159 | I2n::Logger::enable_syslog( I2n::Logger::Facility::User ); |
160 | I2n::Logger::enable_stderr_log( true ); | |
0fd358ca | 161 | I2n::Logger::set_log_level( I2n::Logger::LogLevel::Notice ); |
88714861 GMF |
162 | } |
163 | ||
5ba17410 GMF |
164 | void set_log_output( |
165 | const ConfigurationItem &configuration | |
166 | ) | |
167 | { | |
168 | LogOutput log_output = configuration->get_log_output(); | |
fda777ea | 169 | string log_file_name = configuration->get_log_file(); |
5ba17410 GMF |
170 | switch (log_output) |
171 | { | |
a85f210b CH |
172 | case LogOutput_UNDEFINED: |
173 | GlobalLogger.warning() << "Unknown output target -- use syslog"; | |
5ba17410 | 174 | case LogOutput_SYSLOG: |
ea16eb5e | 175 | GlobalLogger.info() << "Setting log output target to syslog" << endl; |
5ba17410 GMF |
176 | I2n::Logger::enable_syslog(true); |
177 | I2n::Logger::enable_stderr_log(false); | |
178 | I2n::Logger::enable_log_file(false); | |
ea16eb5e | 179 | GlobalLogger.info() << "Set log output target to syslog" << endl; |
5ba17410 GMF |
180 | break; |
181 | case LogOutput_TERMINAL: | |
ea16eb5e | 182 | GlobalLogger.info() << "Setting log output target to terminal" << endl; |
5ba17410 GMF |
183 | I2n::Logger::enable_syslog(false); |
184 | I2n::Logger::enable_stderr_log(true); | |
185 | I2n::Logger::enable_log_file(false); | |
ea16eb5e CH |
186 | GlobalLogger.info() << "Set log output target to terminal" << endl; |
187 | GlobalLogger.info() << "(check syslog for earlier messages)" << endl; | |
188 | break; | |
fda777ea CH |
189 | case LogOutput_FILE: |
190 | GlobalLogger.info() << "Setting log output target to file " | |
191 | << log_file_name << endl; | |
192 | I2n::Logger::enable_syslog(false); | |
193 | I2n::Logger::enable_stderr_log(false); | |
194 | I2n::Logger::enable_log_file(log_file_name); | |
195 | GlobalLogger.info() << "Set log output target to file " | |
196 | << log_file_name << endl; | |
197 | GlobalLogger.info() << "(check syslog for earlier messages)" << endl; | |
198 | break; | |
ea16eb5e CH |
199 | default: |
200 | GlobalLogger.error() << "Unknown log output target!" << endl; | |
5ba17410 GMF |
201 | break; |
202 | } | |
203 | } | |
204 | ||
59733431 CH |
205 | /** |
206 | * @brief calculate delay between pingers to evenly distribute them in time | |
207 | * | |
208 | * If there are many pingers with same interval, will get bursts of pings | |
209 | * and none in-between. This function calculates delays for large numbers | |
0fd358ca | 210 | * of hosts with same ping intervals, to distribute them as evenly as |
59733431 CH |
211 | * possible, right from the start (might diverge over time, anyway). |
212 | * | |
e4b14784 CH |
213 | * If interval is chosen at random, will have many pingers with different |
214 | * intervals; to those will assign a random delay in [0, interval]. | |
59733431 CH |
215 | * |
216 | * Called by init_pingers with | |
217 | * @param hosts list of hosts as obtained from configuration | |
218 | * @returns a map from ping interval to delay between pingers of that interval | |
219 | */ | |
220 | DelayMap calc_pinger_delays(const HostList &hosts) | |
221 | { | |
222 | // first step: count number of hosts with same intervals | |
223 | DelayMap delay_shifts; | |
224 | int curr_interval; | |
c086c9e6 | 225 | BOOST_FOREACH( const HostItem &host, hosts ) |
59733431 CH |
226 | { |
227 | curr_interval = host->get_interval_in_sec(); | |
228 | if (! curr_interval) | |
229 | delay_shifts[curr_interval] = 1.0f; | |
230 | else | |
231 | delay_shifts[curr_interval] += 1.0f; | |
232 | } | |
233 | ||
e4b14784 CH |
234 | // create random number generator |
235 | typedef boost::rand48 rand_gen_type; | |
236 | typedef boost::uniform_real<float> rand_dist_type; | |
237 | typedef boost::variate_generator< rand_gen_type&, | |
238 | rand_dist_type > rand_var_type; | |
239 | rand_gen_type random_number_generator( | |
240 | boost::numeric_cast<unsigned int>(time(0)) ); | |
241 | ||
59733431 CH |
242 | // second step: divide intervals by counts, round to int |
243 | // --> for 18 pingers with a 30s interval, get 30s/18 = 1.66667 | |
e4b14784 | 244 | // for random intervals, use random delays within intervals |
59733431 | 245 | BOOST_FOREACH( IntervalCountPair interval_and_count, delay_shifts ) |
e4b14784 | 246 | { |
e25e38b8 | 247 | if ( abs(interval_and_count.second - 1.0f) < 0.0001 ) // == 1.0f |
e4b14784 CH |
248 | { // there is exactly 1 pinger with exactly that interval |
249 | // --> assign a random delay within interval | |
e25e38b8 | 250 | rand_dist_type random_distribution(0.0f, interval_and_count.first); |
e4b14784 CH |
251 | rand_var_type random_variate(random_number_generator, |
252 | random_distribution); | |
253 | delay_shifts[interval_and_count.first] = random_variate(); | |
254 | } | |
255 | else | |
256 | { // there are several pingers with same interval | |
257 | // --> distribute evenly | |
258 | delay_shifts[interval_and_count.first] = | |
259 | boost::numeric_cast<float>(interval_and_count.first) / | |
e25e38b8 | 260 | std::max(1.0f, interval_and_count.second); //max is paranoid |
e4b14784 CH |
261 | } |
262 | } | |
59733431 CH |
263 | |
264 | return delay_shifts; | |
265 | } | |
266 | ||
a85f210b | 267 | bool init_pingers( |
365036be | 268 | const IoServiceItem io_service, |
f2c0a5db | 269 | const ConfigurationItem &configuration, |
72e54d1c | 270 | const LinkStatusItem &status_notifier, |
f2c0a5db GMF |
271 | PingSchedulerList *scheduler_list |
272 | ) | |
2666d1f7 | 273 | { |
035c2305 | 274 | string default_network_interface = configuration->get_source_network_interface(); |
f2c0a5db | 275 | int ping_fail_limit = configuration->get_ping_fail_limit(); |
079d19ab | 276 | int ping_reply_timeout = configuration->get_ping_reply_timeout(); |
301610ca | 277 | |
096b06ef CH |
278 | // remove some hosts at random |
279 | configuration->randomize_hosts(); | |
280 | ||
59733431 CH |
281 | // calculate delays between pingers of same interval |
282 | DelayMap delay_shifts = calc_pinger_delays(configuration->get_hosts()); | |
283 | ||
e4b14784 | 284 | // setup memory for assigned delays; init with delay > 0 |
59733431 CH |
285 | DelayMap delays; |
286 | BOOST_FOREACH( IntervalCountPair interval_and_delay, delay_shifts ) | |
287 | delays[interval_and_delay.first] = 0.0f; | |
288 | ||
f2c0a5db | 289 | HostList hosts = configuration->get_hosts(); |
a85f210b CH |
290 | |
291 | if (hosts.empty()) | |
292 | return false; | |
293 | ||
941b5e25 CH |
294 | // more variables for pingcheck, maybe should move to config? |
295 | int n_parallel_pings = 1; | |
296 | int parallel_ping_delay = 100; // ms | |
297 | int congestion_duration_thresh = 10; // seconds | |
298 | int congestion_percentage_thresh = 75; | |
299 | int congest_caused_by_fail_limit_percentage = 99; | |
300 | int ping_timeout_factor = 5; | |
301 | ||
e01cc130 | 302 | BOOST_FOREACH( const HostItem &host, hosts ) |
2666d1f7 | 303 | { |
f2c0a5db | 304 | string destination_address = host->get_address(); |
238da857 | 305 | uint16_t destination_port = host->get_port(); |
035c2305 | 306 | string host_network_interface = host->get_source_network_interface(); |
27c5a2be GMF |
307 | string network_interface = ( host_network_interface == "default" ) ? |
308 | default_network_interface : | |
309 | host_network_interface; | |
086e2cc0 | 310 | PingProtocolList protocol_list = host->get_ping_protocol_list(); |
f2c0a5db | 311 | int ping_interval_in_sec = host->get_interval_in_sec(); |
59733431 CH |
312 | |
313 | // get delay for this scheduler and update assigned delays | |
59733431 | 314 | delays[ping_interval_in_sec] += delay_shifts[ping_interval_in_sec]; |
e4b14784 | 315 | int current_delay = boost::math::iround(delays[ping_interval_in_sec]); |
51462359 | 316 | GlobalLogger.debug() << "assigning delay of " << current_delay |
e4b14784 | 317 | << "s to pinger with interval " << ping_interval_in_sec << "s"; |
59733431 | 318 | |
f2c0a5db GMF |
319 | PingSchedulerItem scheduler( |
320 | new PingScheduler( | |
fc7ae593 | 321 | io_service, |
035c2305 | 322 | network_interface, |
f2c0a5db | 323 | destination_address, |
1309d0e4 | 324 | destination_port, |
086e2cc0 | 325 | protocol_list, |
f2c0a5db GMF |
326 | ping_interval_in_sec, |
327 | ping_fail_limit, | |
a7b15639 | 328 | congestion_percentage_thresh, |
941b5e25 | 329 | congest_caused_by_fail_limit_percentage, |
a7b15639 | 330 | congestion_duration_thresh, |
079d19ab | 331 | ping_reply_timeout, |
59733431 | 332 | status_notifier, |
91aa83f9 CH |
333 | current_delay, |
334 | n_parallel_pings, | |
87758553 CH |
335 | parallel_ping_delay, |
336 | ping_timeout_factor | |
f1bf3249 | 337 | ) |
c1fff16a | 338 | ); |
f2c0a5db GMF |
339 | scheduler_list->push_back( scheduler ); |
340 | } | |
a85f210b CH |
341 | |
342 | return true; | |
f2c0a5db | 343 | } |
c1fff16a | 344 | |
f2c0a5db GMF |
345 | void start_pingers( |
346 | const PingSchedulerList &scheduler_list | |
347 | ) | |
348 | { | |
88714861 | 349 | // start each ping scheduler |
e01cc130 | 350 | BOOST_FOREACH( const PingSchedulerItem &scheduler, scheduler_list ) |
365036be | 351 | scheduler->start_pinging(); |
88714861 GMF |
352 | } |
353 | ||
354 | void stop_pingers( | |
355 | const PingSchedulerList &scheduler_list | |
356 | ) | |
357 | { | |
1d1ae364 TJ |
358 | // Stop each ping scheduler |
359 | GlobalLogger.info() << "Telling all pingers to stop"; | |
e01cc130 | 360 | BOOST_FOREACH( const PingSchedulerItem &scheduler, scheduler_list ) |
88714861 | 361 | { |
365036be | 362 | scheduler->stop_pinging(); |
88714861 | 363 | } |
0470811e | 364 | |
1ece191b | 365 | IcmpPacketDistributor::clean_up_all(); |
88714861 GMF |
366 | } |
367 | ||
35e8afe0 | 368 | |
365036be CH |
369 | // the one instance of signal_data_struct |
370 | signal_data_struct signal_data; | |
371 | ||
0fd358ca | 372 | |
666388ba CH |
373 | /// registered as signal handler; just sets signal_data.signaled_flag |
374 | void signal_handler_int(int param) | |
365036be | 375 | { |
963e98a2 | 376 | signal_data.signaled_flag_int = 1; |
666388ba CH |
377 | } |
378 | void signal_handler_term(int param) | |
379 | { | |
963e98a2 | 380 | signal_data.signaled_flag_term = 1; |
666388ba CH |
381 | } |
382 | void signal_handler_usr1(int param) | |
383 | { | |
963e98a2 | 384 | signal_data.signaled_flag_usr1 = 1; |
666388ba CH |
385 | } |
386 | void signal_handler_usr2(int param) | |
387 | { | |
963e98a2 | 388 | signal_data.signaled_flag_usr2 = 1; |
35e8afe0 TJ |
389 | } |
390 | ||
0fd358ca | 391 | |
3bd12178 CH |
392 | /** |
393 | * @brief called regularly from io_service; checks signal_data.signal_flag | |
394 | * | |
395 | * this does NOT work if there is a change in system time -- boost asio's | |
396 | * deadline timers seem to operate on fixed time points on a non-monotonic | |
397 | * clock; | |
398 | * | |
399 | * We therefore use external programs to kill pingchecker processes if the | |
400 | * system clock changes; We use SIGHUP for this because it is not handled | |
401 | * here and thus the default signal handler (which just kills the process | |
402 | * without calling any destructors) avoids a freeze in here. SIGTERM would | |
403 | * not work any more after system time change! | |
404 | * | |
405 | * --> do not catch SIGHUP in any asio-related signal handling! | |
406 | */ | |
365036be | 407 | void signal_checker( const boost::system::error_code &error ) |
35e8afe0 | 408 | { |
365036be | 409 | bool want_stop = false; |
35e8afe0 | 410 | |
365036be CH |
411 | if ( error ) |
412 | { // there was an error in the timer | |
413 | if ( error == boost::asio::error::operation_aborted ) | |
35e8afe0 | 414 | { |
365036be CH |
415 | GlobalLogger.error() << "Signal check timer was cancelled! Stopping io_service" << endl; |
416 | want_stop = true; | |
0b920e26 GMF |
417 | } |
418 | else | |
419 | { | |
0fd358ca CH |
420 | GlobalLogger.error() << "Signal check timer handler received error code " << error |
421 | << "! Stopping io_service" << endl; | |
365036be | 422 | want_stop = true; |
0b920e26 | 423 | } |
35e8afe0 | 424 | } |
716deecb CH |
425 | else { |
426 | if ( signal_data.signaled_flag_int ) | |
427 | { | |
963e98a2 | 428 | signal_data.signaled_flag_int = 0; |
716deecb CH |
429 | GlobalLogger.notice() << "Received signal SIGINT --> will stop" << endl; |
430 | want_stop = true; | |
431 | } | |
432 | else if ( signal_data.signaled_flag_term ) | |
433 | { | |
963e98a2 | 434 | signal_data.signaled_flag_term = 0; |
716deecb CH |
435 | GlobalLogger.notice() << "Received signal SIGTERM --> will stop" << endl; |
436 | want_stop = true; | |
437 | } | |
438 | else if ( signal_data.signaled_flag_usr1 ) | |
365036be | 439 | { |
963e98a2 | 440 | signal_data.signaled_flag_usr1 = 0; |
365036be CH |
441 | int new_log_level = I2n::Logger::get_log_level()+1; |
442 | I2n::Logger::set_log_level( new_log_level ); | |
666388ba | 443 | GlobalLogger.info() << "Received SIGUSR1 -- increased log level to " |
365036be CH |
444 | << I2n::Logger::get_log_level_string(); |
445 | } | |
716deecb | 446 | else if ( signal_data.signaled_flag_usr2 ) |
365036be | 447 | { |
963e98a2 | 448 | signal_data.signaled_flag_usr2 = 0; |
365036be | 449 | I2n::Logger::set_log_level( signal_data.config_log_level ); |
666388ba | 450 | GlobalLogger.info() << "Received SIGUSR2 -- reset log level to normal (" |
365036be CH |
451 | << I2n::Logger::get_log_level_string() << ")"; |
452 | } | |
365036be | 453 | } |
fc7ae593 | 454 | |
365036be CH |
455 | if ( want_stop ) |
456 | { // interrupt infinite loop in main and asio event loop | |
457 | signal_data.stopped = true; | |
458 | signal_data.io_service->stop(); | |
fc7ae593 CH |
459 | } |
460 | else | |
365036be | 461 | { // re-schedule timer |
365036be CH |
462 | signal_data.check_timer->expires_from_now( SIGNAL_CHECK_INTERVAL ); |
463 | signal_data.check_timer->async_wait( signal_checker ); | |
fc7ae593 CH |
464 | } |
465 | } | |
466 | ||
365036be | 467 | /// register own signal handlers; see reset_signal_handlers for undo |
666388ba | 468 | void install_signal_handlers( const IoServiceItem io_service, const int config_log_level ) |
fc7ae593 | 469 | { |
963e98a2 CH |
470 | signal_data.signaled_flag_int = 0; |
471 | signal_data.signaled_flag_term = 0; | |
472 | signal_data.signaled_flag_usr1 = 0; | |
473 | signal_data.signaled_flag_usr2 = 0; | |
365036be | 474 | signal_data.config_log_level = config_log_level; |
fc7ae593 CH |
475 | |
476 | // install own signal handlers | |
666388ba CH |
477 | signal_data.old_handler_int = signal(SIGINT, signal_handler_int); |
478 | signal_data.old_handler_term = signal(SIGTERM, signal_handler_term); | |
479 | signal_data.old_handler_usr1 = signal(SIGUSR1, signal_handler_usr1); | |
480 | signal_data.old_handler_usr2 = signal(SIGUSR2, signal_handler_usr2); | |
fc7ae593 CH |
481 | if ( signal_data.old_handler_int == SIG_ERR || |
482 | signal_data.old_handler_term == SIG_ERR || | |
483 | signal_data.old_handler_usr1 == SIG_ERR || | |
484 | signal_data.old_handler_usr2 == SIG_ERR ) | |
365036be CH |
485 | throw runtime_error( string("Failed to install signal handler: ") + string(strerror(errno)) ); |
486 | ||
487 | // create a timer and a shared pointer to it, so it does not get out of scope | |
488 | TimerItem check_timer( new boost::asio::deadline_timer( *io_service ) ); | |
fc7ae593 | 489 | |
365036be | 490 | // remember the io_service and the timer |
fc7ae593 | 491 | signal_data.io_service = io_service; |
365036be | 492 | signal_data.check_timer = check_timer; |
fc7ae593 | 493 | |
365036be CH |
494 | // set the timer |
495 | check_timer->expires_from_now( SIGNAL_CHECK_INTERVAL ); | |
496 | check_timer->async_wait( signal_checker ); | |
497 | GlobalLogger.debug() << "signal timer set" << endl; | |
fc7ae593 CH |
498 | } |
499 | ||
666388ba | 500 | /// reset handlers to the ones saved in install_signal_handlers |
365036be | 501 | void reset_signal_handlers() |
f2c0a5db | 502 | { |
425d0f07 CH |
503 | void (*old_handler_int)(int) = 0; |
504 | void (*old_handler_term)(int) = 0; | |
505 | void (*old_handler_usr1)(int) = 0; | |
506 | void (*old_handler_usr2)(int) = 0; | |
507 | if (signal_data.old_handler_int != 0 ) | |
508 | old_handler_int = signal(SIGINT , signal_data.old_handler_int); | |
509 | if (signal_data.old_handler_term != 0 ) | |
510 | old_handler_term = signal(SIGTERM, signal_data.old_handler_term); | |
511 | if (signal_data.old_handler_usr1 != 0 ) | |
512 | old_handler_usr1 = signal(SIGUSR1, signal_data.old_handler_usr1); | |
513 | if (signal_data.old_handler_usr2 != 0 ) | |
514 | old_handler_usr2 = signal(SIGUSR2, signal_data.old_handler_usr2); | |
365036be CH |
515 | |
516 | if ( old_handler_int == SIG_ERR || | |
517 | old_handler_term == SIG_ERR || | |
518 | old_handler_usr1 == SIG_ERR || | |
519 | old_handler_usr2 == SIG_ERR ) | |
520 | throw runtime_error( string("Failed to reset signal handler: ") + string(strerror(errno)) ); | |
521 | } | |
522 | ||
35e8afe0 | 523 | |
365036be CH |
524 | int main( int argc, const char *argv[] ) |
525 | { | |
88714861 | 526 | init_logger(); |
99ee8244 | 527 | GlobalLogger.debug() << "logger initiated with default config"; |
f2c0a5db | 528 | |
6e8c880d | 529 | PingSchedulerList scheduler_list; |
365036be | 530 | IoServiceItem io_service; |
6e8c880d | 531 | int ret_code = 0; |
ad83004d | 532 | |
6e8c880d | 533 | try |
f2c0a5db | 534 | { |
b588279a CH |
535 | GetConfigReturnType success_and_config = get_configuration( argc, argv ); |
536 | ConfigurationItem configuration = success_and_config.second; | |
537 | ||
538 | if ( configuration->get_print_version() ) // do this even if parsing of config failed | |
539 | { | |
a85f210b CH |
540 | GlobalLogger.debug() << "Printing version info (" |
541 | << VERSION_STRING << "." << VERSION_REVISION_STRING | |
542 | << " build " << __DATE__ | |
543 | << ") and exit" << endl; | |
544 | cout << PROJECT_NAME << " version " | |
545 | << VERSION_STRING << "." << VERSION_REVISION_STRING | |
546 | << " build " << __DATE__ | |
547 | << endl; | |
b588279a CH |
548 | return 0; |
549 | } | |
550 | ||
551 | if ( ! success_and_config.first ) | |
6e8c880d | 552 | { |
b588279a CH |
553 | GlobalLogger.error() << "Could not read/parse configuration!"; |
554 | GlobalLogger.debug() << "Return 1 immediately" << endl; | |
fc7ae593 CH |
555 | return 1; |
556 | } | |
b588279a | 557 | GlobalLogger.debug() << "Start setup" << endl; |
1d1ae364 | 558 | |
fc7ae593 | 559 | int log_level = configuration->get_log_level(); |
365036be | 560 | I2n::Logger::set_log_level( log_level ); |
fc7ae593 | 561 | GlobalLogger.info() << "Set LogLevel to " << I2n::Logger::get_log_level_string() << endl; |
5ba17410 | 562 | |
fc7ae593 | 563 | set_log_output( configuration ); |
a85f210b CH |
564 | GlobalLogger.notice() << "started pingcheck version " |
565 | << VERSION_STRING << "." << VERSION_REVISION_STRING | |
566 | << " build " << __DATE__ | |
567 | << endl; | |
f2c0a5db | 568 | |
fc7ae593 CH |
569 | bool daemon_mode = configuration->get_daemon(); |
570 | if ( daemon_mode ) | |
571 | { | |
572 | I2n::Daemon::daemonize(); | |
573 | } | |
f2c0a5db | 574 | |
fc7ae593 | 575 | LinkStatusItem status_notifier = get_status_notifier( configuration ); |
f2c0a5db | 576 | |
365036be CH |
577 | IoServiceItem io_service_temp( new boost::asio::io_service() ); |
578 | io_service_temp.swap( io_service ); | |
579 | io_service_temp.reset(); | |
fc7ae593 | 580 | |
9490a7bd CH |
581 | // create Dns master |
582 | boost::asio::ip::address name_server_ip = | |
583 | boost::asio::ip::address::from_string( | |
584 | configuration->get_nameserver() ); | |
a85f210b | 585 | int max_recursion_count = 10; // could make a config var some time |
9490a7bd CH |
586 | DnsMaster::create_master( |
587 | io_service, | |
588 | name_server_ip, | |
589 | configuration->get_resolved_ip_ttl_threshold(), | |
f833126b | 590 | configuration->get_min_time_between_resolves(), |
9490a7bd | 591 | configuration->get_max_address_resolution_attempts(), |
cd71d095 | 592 | max_recursion_count, |
9490a7bd CH |
593 | configuration->get_dns_cache_file() ); |
594 | ||
a85f210b CH |
595 | if ( !init_pingers(io_service, configuration, status_notifier, |
596 | &scheduler_list) ) | |
597 | { | |
598 | GlobalLogger.error() << "Could not initialize pingers or no hosts " | |
599 | << "given to ping --> exit"; | |
600 | return 2; | |
601 | } | |
fc7ae593 | 602 | |
666388ba | 603 | install_signal_handlers( io_service, log_level ); |
88714861 | 604 | |
365036be | 605 | start_pingers( scheduler_list ); |
6e8c880d | 606 | } |
aaff53da | 607 | catch ( const std::exception &ex ) |
6e8c880d | 608 | { |
fc7ae593 | 609 | GlobalLogger.error() << "Uncaught exception. " << ex.what() << endl; |
a85f210b | 610 | ret_code = 3; |
6e8c880d CH |
611 | } |
612 | catch (...) { | |
fc7ae593 | 613 | GlobalLogger.error() << "Caught unknown exception!" << endl; |
a85f210b | 614 | ret_code = 4; |
6e8c880d | 615 | } |
35e8afe0 | 616 | |
365036be | 617 | if ( ret_code == 0 ) |
fc7ae593 | 618 | { |
747c13ca | 619 | GlobalLogger.info() << "starting io_service main loop" << endl; |
ad83004d | 620 | |
365036be | 621 | // call boost::asio main event loop, catching exceptions |
7840efba CH |
622 | try |
623 | { | |
624 | io_service->run(); | |
625 | } | |
626 | catch ( const std::exception &ex ) | |
fc7ae593 | 627 | { |
7840efba CH |
628 | GlobalLogger.error() << "Caught exception, will continue. " << ex.what() << endl; |
629 | } | |
630 | catch (...) { | |
631 | GlobalLogger.error() << "Caught unknown exception, will continue!" << endl; | |
fc7ae593 CH |
632 | } |
633 | } | |
634 | ||
6e8c880d CH |
635 | // clean up |
636 | try | |
637 | { | |
638 | GlobalLogger.info() << "Cleaning up" << endl; | |
1d1ae364 | 639 | stop_pingers( scheduler_list ); |
365036be | 640 | reset_signal_handlers(); |
ced28dc7 | 641 | } |
aaff53da | 642 | catch ( const std::exception &ex ) |
6e8c880d | 643 | { |
fc7ae593 | 644 | GlobalLogger.error() << "Uncaught exception while cleaning up: " << ex.what() << endl; |
a85f210b | 645 | ret_code += 16; |
6e8c880d CH |
646 | } |
647 | catch (...) { | |
fc7ae593 | 648 | GlobalLogger.error() << "Caught unknown exception while cleaning up!" << endl; |
a85f210b | 649 | ret_code += 32; |
6e8c880d | 650 | } |
4ea9706c | 651 | |
365036be | 652 | GlobalLogger.notice() << "Pingcheck done " << endl; |
6e8c880d | 653 | return ret_code; |
4ea9706c | 654 | } |