Cosmetic/documentation changes only
[bpdyndnsd] / src / updater.cpp
CommitLineData
b1be615b
BS
1/** @file
2 * @brief The updater class implementation. This class implements the updater logic.
3 *
4 *
5 *
6 * @copyright Intra2net AG
7 * @license GPLv2
8*/
9
4de6a9b8 10#include "updater.hpp"
4545a371 11
4de6a9b8 12#include "serviceholder.hpp"
e0080b78 13
e0080b78 14
ca5d6889
BS
15#include <boost/foreach.hpp>
16#include <boost/serialization/export.hpp>
b30f392d 17
e0080b78 18
8bca3c5d 19using namespace std;
527536b3 20
ca5d6889 21
ae0e404f
TJ
22/// Server error threshold: When the host IP is not current, the DNS TTL expired
23/// and we already sent an update.
24const int ServerErrorTTLExpiredThreshold = 15 * 60;
25
26
b1be615b
BS
27/**
28 * Default constructor which initializes the member Conf.
29 */
4545a371 30Updater::Updater()
ad0e5016 31 : IPAddrHelp(new IPAddrHelper)
4545a371 32{
2e956a36 33 // Initialize program wide Logger, at this point we don't know where to log and with which loglevel.
c3c84086 34 Log = Logger::Ptr(new Logger);
4545a371 35
e0080b78 36 // initialize Serviceholder
c3c84086 37 ServiceHolder = Serviceholder::Ptr(new Serviceholder(Log));
e0080b78 38
254bbf53 39 // initialize Config
c3c84086 40 Conf = Config::Ptr(new Config(Log,ServiceHolder));
4545a371
BS
41}
42
527536b3 43
b1be615b
BS
44/**
45 * Default destructor.
46 */
4545a371
BS
47Updater::~Updater()
48{
49}
50
527536b3 51
efbde536
BS
52
53/**
54 * Load config and initialize helper classes.
55 * @param argc Number of arguments in array
56 * @param argv Array with cmd options
57 */
58int Updater::load_config(int argc, char *argv[])
59{
60 // load the cmd options
61 if ( init_config_from_cmd(argc,argv) != 0 )
62 return -1;
63
64 // load the config and service files
65 if ( init_config_from_files() != 0 )
66 return -1;
67
68 // init all helper classes
69 if ( init_helper_classes() != 0 )
70 return -1;
71
72 return 0;
73}
74
75
76/**
77 * Reloading the config. Delete all Service objects and then init new Service objects from config files.
78 */
79int Updater::reload_config()
80{
81 // serialize all service objects
82 if ( ServiceHolder->serialize_services() != 0 )
83 return -1;
84
85 // delete all service objects
86 ServiceHolder->delete_services();
87
88 // delete the actual Variables_map, perhaps with old cmd options which would overwrite new config file options.
89 Conf->delete_variables_map();
90
e8787e2e
BS
91 // clear the external send messages.
92 Log->clear_external_send_messages();
93
efbde536
BS
94 // load only config files
95 if ( init_config_from_files() != 0 )
96 return -1;
97
98 // init all helper classes
99 if ( init_helper_classes() != 0 )
100 return -1;
101
102 return 0;
103}
104
105
b1be615b 106/**
254bbf53
BS
107 * Parse the command line arguments and initialize corresponding options.
108 * @param argc Number command line arguments.
109 * @param argv[] Array with arguments.
110 * @return 0 if cmd options successfully parsed, 1 if usage or version.
38060291 111 */
08a5a621 112int Updater::init_config_from_cmd(int argc, char *argv[]) const
38060291
BS
113{
114 // Load the command line parameters
e95a6634 115 if( Conf->parse_cmd_line( argc, argv) != 0)
667c672c 116 return -1;
38060291 117
59c8d63c
BS
118 // If we have loaded the cmd options we need to init the log facility immediately in case debugging is enabled from cmd.
119 init_log_facility();
120
254bbf53
BS
121 // successful parsed
122 Log->print_cmd_parsed();
38060291
BS
123 return 0;
124}
125
126
127/**
254bbf53 128 * Load the main config and the service definition files in config path.
4de6a9b8 129 * @return 0 if all is fine,
b1be615b 130 */
08a5a621 131int Updater::init_config_from_files() const
4545a371 132{
254bbf53 133 // Load the main and service config files in config path
e95a6634 134 if ( Conf->load_config_from_files() != 0 )
667c672c 135 return -1;
4545a371 136
59c8d63c
BS
137 // Re-init log facility, perhaps new config file options for logger are set.
138 // These config file options will only overwrite the cmd options if the SIGHUP (reload config) is caught.
139 init_log_facility();
140
27baf279 141 // Here we are. All Service Objects should be initialized, so it is time to deserialize the old service objects and compare them.
e0080b78 142 if ( ServiceHolder->deserialize_services() != 0 )
667c672c 143 return -1;
27baf279 144
254bbf53 145 // successful loaded
667c672c 146 Log->print_conf_files_parsed();
254bbf53 147 return 0;
4545a371
BS
148}
149
527536b3 150
b1be615b 151/**
efbde536 152 * Init all Helper classes
4de6a9b8 153 * @return
3434b35f 154 */
efbde536 155int Updater::init_helper_classes()
3434b35f 156{
efbde536
BS
157 // Initialize IPHelper
158 if ( init_ip_helper() != 0 )
159 return -1;
3434b35f 160
efbde536 161 return 0;
3434b35f
BS
162}
163
164
165/**
0665b239
BS
166 * Init the IPHelp member with needed values.
167 * @return 0 if all is fine, -1 otherwise.
168 */
169int Updater::init_ip_helper()
170{
20399847
BS
171 // Try to get deserialized IPAddrHelper from ServiceHolder
172 IPAddrHelper::Ptr _ip_addr_help = ServiceHolder->get_ip_addr_helper();
173 if ( _ip_addr_help.use_count() != 0 )
174 {
175 // Initialize IPHelper
c3c84086 176 IPAddrHelp = IPAddrHelper::Ptr( new IPAddrHelper( Log, Conf->get_webcheck_ip_url(), Conf->get_webcheck_ip_url_alt(), Conf->get_webcheck_interval(), _ip_addr_help->get_last_webcheck(), Conf->get_enable_ipv6(), Conf->get_proxy(), Conf->get_proxy_port() ) );
20399847
BS
177 }
178 else
179 {
180 // IPAddrHelper from ServiceHolder was not declared, so init oen with LastWebcheck 0
08a5a621 181 IPAddrHelp = IPAddrHelper::Ptr( new IPAddrHelper( Log, Conf->get_webcheck_ip_url(), Conf->get_webcheck_ip_url_alt(), Conf->get_webcheck_interval(), (size_t)0, Conf->get_enable_ipv6(), Conf->get_proxy(), Conf->get_proxy_port() ) );
20399847
BS
182 }
183
184 // Put the IPAddrHelper into ServiceHolder, so the LastWebcheck state will be serialized too.
185 ServiceHolder->set_ip_addr_helper(IPAddrHelp);
0665b239
BS
186
187 return 0;
188}
189
190
191/**
efbde536
BS
192 * Getter for member Config.
193 * @return Member Config.
194 */
195Config::Ptr Updater::get_config() const
196{
197 return Conf;
198}
667c672c 199
efbde536
BS
200
201/**
202 * Getter for member Logger.
203 * @return Member Logger.
204 */
205Logger::Ptr Updater::get_logger() const
206{
207 return Log;
8bca3c5d
BS
208}
209
210
2bc1878a
BS
211/**
212 * Initialize the logging facility with loglevel and syslog.
213 */
c730deea 214void Updater::init_log_facility() const
8bca3c5d 215{
e8787e2e 216 Log->set_log_facility(Conf->get_loglevel(),Conf->get_syslog(),Conf->get_external_warning_log(),Conf->get_external_warning_level(),Conf->get_external_log_only_once());
8bca3c5d 217 Log->print_init_log_facility();
c5675c01
BS
218}
219
220
221/**
b1be615b 222 * Update all configured services.
d55e13a6 223 * @param changed_to_online True if we just changed to online, false if we were already online
b1be615b 224 */
d55e13a6 225void Updater::update_services(bool changed_to_online) const
4545a371 226{
1af7c124 227 // Get all services from the ServiceHolder.
e0080b78 228 list<Service::Ptr> services = ServiceHolder->get_services();
4545a371 229
1af7c124 230 // Get the actual IP of this host.
6114d87c 231 string ip_host = IPAddrHelp->get_actual_ip(Conf->get_webcheck_enabled(), changed_to_online, Conf->get_wan_ip_override());
a7beeb20
TJ
232 if ( ip_host.empty() )
233 {
f3141675 234 Log->print_no_wan_ip(changed_to_online);
a7beeb20 235 return;
62956d9a
TJ
236 } else
237 {
238 Log->print_external_wan_ip(changed_to_online, ip_host);
a7beeb20 239 }
4545a371 240
a7beeb20 241 BOOST_FOREACH(Service::Ptr &service, services )
4545a371 242 {
a7beeb20
TJ
243 string ip_last_update = service->get_actual_ip();
244 string hostname = service->get_hostname();
a7beeb20 245 time_t current_time = time(NULL);
c3dea5dc 246
20f4aefd 247 // Try to get the last update time of the service if there is one.
4553e833
BS
248 // And check for burnt IP, too.
249 std::map<time_t,std::string> last_updates = service->get_last_updates(); /*lint !e1793 */
20f4aefd 250 time_t lastupdated = 0;
4b5c6574
BS
251
252 int last_updates_size = last_updates.size();
253 int max_equal_updates_in_succession = service->get_max_equal_updates_in_succession();
254
255 Log->print_last_updates(ip_host,max_equal_updates_in_succession,last_updates,service->get_hostname());
256
257 // Only check for burnt IP address if there are at least max_equal_updates_in_succession entries in the last_updates map.
258 if ( (max_equal_updates_in_succession != 0) && (last_updates_size >= max_equal_updates_in_succession) )
4553e833
BS
259 {
260 bool ip_burnt = true;
4553e833 261 int i = 0;
20f4aefd
TJ
262 // Reverse iterate "last_updates" list so the latest update
263 // will be the first entry (map key is the unix timestamp)
264 for ( std::map<time_t,std::string>::reverse_iterator r_iter = last_updates.rbegin();
265 (r_iter != last_updates.rend()) && (i < max_equal_updates_in_succession); r_iter++ )
4553e833
BS
266 {
267 if ( i == 0 )
268 lastupdated = r_iter->first;
269
270 if ( ip_host != r_iter->second )
271 {
272 ip_burnt = false;
273 break;
274 }
275
276 i++;
277 }
278
4553e833
BS
279 if ( ip_burnt )
280 {
281 // IP Address is burnt. Too many updates in succession with the same IP.
282 Log->print_ip_burnt(ip_host,service->get_hostname());
283 continue;
284 }
285 }
c3dea5dc 286
a7beeb20 287 Log->print_check_service_update(hostname, current_time, lastupdated);
1af7c124 288
a7beeb20
TJ
289 // Do a DNS Query for the dynamic hostname.
290 string ip_dns_recheck = IPAddrHelp->dns_query(hostname);
1af7c124 291
a7beeb20 292 Log->print_cached_dns_entry(hostname, ip_dns_recheck, ip_last_update, ip_host);
1af7c124 293
a7beeb20
TJ
294 // Test if the DNS-Record could not be found.
295 if ( ip_dns_recheck.empty() )
f3141675
TJ
296 {
297 Log->print_dns_lookup_failed(changed_to_online, hostname);
3fdee948 298 continue;
f3141675 299 }
3fdee948
TJ
300
301 // Test if the actual DNS-Record differs from the host's IP.
302 if (ip_host == ip_dns_recheck )
a7beeb20 303 {
d55e13a6 304 Log->print_no_update_needed(changed_to_online, hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated);
de2abebd 305 // No update needed
3fdee948 306 continue;
a7beeb20 307 }
3fdee948 308
6643431d 309 // Check if the IP set in last update differs from the actual host's ip.
de2abebd
TJ
310 if ( ip_last_update != ip_host )
311 {
312 // Update
313 Log->print_update_service(hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated);
0ebcd4ef 314 service->update(ip_host, current_time, changed_to_online);
3fdee948
TJ
315 }
316 else
a7beeb20 317 {
de2abebd 318 int dns_cache_ttl = service->get_dns_cache_ttl();
ae0e404f
TJ
319
320 // Add server error threshold so we don't jam the server with updates for the same IP
321 // in case the server doesn't hand out the already sent IP via DNS
322 // (might indicate a server error).
323 dns_cache_ttl += ServerErrorTTLExpiredThreshold;
324
de2abebd
TJ
325 // Check if DNS Cache TTL is expired, if so, then update the same IP again.
326 if ( (lastupdated + dns_cache_ttl) < current_time )
a7c1e271 327 {
3fdee948 328 // Update
de2abebd 329 Log->print_update_service_ttl_expired(hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated, dns_cache_ttl, current_time);
0ebcd4ef 330 service->update(ip_host, current_time, changed_to_online);
a7c1e271 331 }
a7beeb20 332 else
0541cd71 333 {
de2abebd 334 // DNS cache TTL isn't expired
d55e13a6 335 Log->print_update_service_ttl_not_expired(changed_to_online, hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated, dns_cache_ttl, current_time);
1af7c124 336 }
3c0cd271 337 }
1d2e2f56 338 }
b1be615b 339}
e0080b78
BS
340
341
342/**
343 * Getter for member ServiceHolder.
344 * @return ServiceHolder
345 */
346Serviceholder::Ptr Updater::get_service_holder() const
347{
348 return ServiceHolder;
349}