28a58fe59a53dae0c770871cfe36ff41d75c1362
[bpdyndnsd] / src / updater.cpp
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
10 #include "updater.hpp"
11
12 #include "serviceholder.hpp"
13
14
15 #include <boost/foreach.hpp>
16 #include <boost/serialization/export.hpp>
17
18
19 using namespace std;
20
21
22 /// Server error threshold: When the host IP is not current, the DNS TTL expired
23 /// and we already sent an update.
24 const int ServerErrorTTLExpiredThreshold = 15 * 60;
25
26
27 /**
28  * Default constructor which initializes the member Conf.
29  */
30 Updater::Updater()
31     : IPAddrHelp(new IPAddrHelper)
32 {
33     // Initialize program wide Logger, at this point we don't know where to log and with which loglevel.
34     Log = Logger::Ptr(new Logger);
35
36     // initialize Serviceholder
37     ServiceHolder = Serviceholder::Ptr(new Serviceholder(Log));
38
39     // initialize Config
40     Conf = Config::Ptr(new Config(Log,ServiceHolder));
41 }
42
43
44 /**
45  * Default destructor.
46  */
47 Updater::~Updater()
48 {
49 }
50
51
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  */
58 int 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  */
79 int 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
91     // clear the external send messages.
92     Log->clear_external_send_messages();
93
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
106 /**
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.
111  */
112 int Updater::init_config_from_cmd(int argc, char *argv[]) const
113 {
114     // Load the command line parameters
115     if( Conf->parse_cmd_line( argc, argv) != 0)
116         return -1;
117
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
121     // successful parsed
122     Log->print_cmd_parsed();
123     return 0;
124 }
125
126
127 /**
128  * Load the main config and the service definition files in config path.
129  * @return 0 if all is fine,
130  */
131 int Updater::init_config_from_files() const
132 {
133     // Load the main and service config files in config path
134     if ( Conf->load_config_from_files() != 0 )
135         return -1;
136
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
141     // Here we are. All Service Objects should be initialized, so it is time to deserialize the old service objects and compare them.
142     if ( ServiceHolder->deserialize_services() != 0 )
143         return -1;
144
145     // successful loaded
146     Log->print_conf_files_parsed();
147     return 0;
148 }
149
150
151 /**
152  * Init all Helper classes
153  * @return
154  */
155 int Updater::init_helper_classes()
156 {
157     // Initialize IPHelper
158     if ( init_ip_helper() != 0 )
159         return -1;
160
161     return 0;
162 }
163
164
165 /**
166  * Init the IPHelp member with needed values.
167  * @return 0 if all is fine, -1 otherwise.
168  */
169 int Updater::init_ip_helper()
170 {
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
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() ) );
177     }
178     else
179     {
180         // IPAddrHelper from ServiceHolder was not declared, so init oen with LastWebcheck 0
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() ) );
182     }
183
184     // Put the IPAddrHelper into ServiceHolder, so the LastWebcheck state will be serialized too.
185     ServiceHolder->set_ip_addr_helper(IPAddrHelp);
186
187     return 0;
188 }
189
190
191 /**
192  * Getter for member Config.
193  * @return Member Config.
194  */
195 Config::Ptr Updater::get_config() const
196 {
197     return Conf;
198 }
199
200
201 /**
202  * Getter for member Logger.
203  * @return Member Logger.
204  */
205 Logger::Ptr Updater::get_logger() const
206 {
207     return Log;
208 }
209
210
211 /**
212  * Initialize the logging facility with loglevel and syslog.
213  */
214 void Updater::init_log_facility() const
215 {
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());
217     Log->print_init_log_facility();
218 }
219
220
221 /**
222  * Update all configured services.
223  * @param changed_to_online True if we just changed to online, false if we were already online
224  */
225 void Updater::update_services(bool changed_to_online) const
226 {
227     // Get all services from the ServiceHolder.
228     list<Service::Ptr> services = ServiceHolder->get_services();
229
230     // Get the actual IP of this host.
231     string ip_host = IPAddrHelp->get_actual_ip(Conf->get_webcheck_enabled(), changed_to_online, Conf->get_wan_ip_override());
232     if ( ip_host.empty() )
233     {
234         Log->print_no_wan_ip(changed_to_online);
235         return;
236     } else
237     {
238         Log->print_external_wan_ip(changed_to_online, ip_host);
239     }
240
241     BOOST_FOREACH(Service::Ptr &service, services )
242     {
243         string ip_last_update = service->get_actual_ip();
244         string hostname = service->get_hostname();
245         time_t current_time = time(NULL);
246
247         // Get the last update time of the service.
248         time_t lastupdated = service->get_last_update_time();
249
250         Log->print_check_service_update(hostname, current_time, lastupdated);
251
252         // Do a DNS Query for the dynamic hostname.
253         string ip_dns_recheck = IPAddrHelp->dns_query(hostname);
254
255         Log->print_cached_dns_entry(hostname, ip_dns_recheck, ip_last_update, ip_host);
256
257         // Test if the DNS-Record could not be found.
258         // If DNS-Record was not found but service was not activated, the hostname could be deactivated, offline or not existent. In this case try a ordinary update.
259         if ( ip_dns_recheck.empty() && service->get_activated() )
260         {
261             Log->print_dns_lookup_failed(changed_to_online, hostname);
262             continue;
263         }
264
265         // Test if the actual DNS-Record differs from the host's IP.
266         if (ip_host == ip_dns_recheck )
267         {
268             Log->print_no_update_needed(changed_to_online, hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated);
269             // No update needed
270             continue;
271         }
272
273         // Check if the IP set in last update differs from the actual host's ip.
274         if ( ip_last_update != ip_host )
275         {
276             // Update
277             if ( !ip_dns_recheck.empty() )
278             {
279                 Log->print_update_service(hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated);
280             }
281             else
282             {
283                 // Service gets updated the first time and no DNS-Record was found. This is either a initial update or the hostname is really not available.
284                 Log->print_update_service(hostname, "<NO IP SET>", ip_last_update, ip_host, lastupdated);
285             }
286
287             service->update(ip_host, current_time, changed_to_online);
288         }
289         else
290         {
291             int dns_cache_ttl = service->get_dns_cache_ttl();
292
293             // Add server error threshold so we don't jam the server with updates for the same IP
294             // in case the server doesn't hand out the already sent IP via DNS
295             // (might indicate a server error).
296             dns_cache_ttl += ServerErrorTTLExpiredThreshold;
297
298             // Check if DNS Cache TTL is expired, if so, then update the same IP again.
299             if ( (lastupdated + dns_cache_ttl) < current_time )
300             {
301                 // Update
302                 Log->print_update_service_ttl_expired(hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated, dns_cache_ttl, current_time);
303                 service->update(ip_host, current_time, changed_to_online);
304             }
305             else
306             {
307                 // DNS cache TTL isn't expired
308                 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);
309             }
310         }
311     }
312 }
313
314
315 /**
316  * Getter for member ServiceHolder.
317  * @return ServiceHolder
318  */
319 Serviceholder::Ptr Updater::get_service_holder() const
320 {
321     return ServiceHolder;
322 }