Implemented curl error handling. Improved httphelper. Enhanced dhs protocol handling.
[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.h"
11
12 #include "serviceholder.h"
13 #include "dhs.h"
14 #include "ods.h"
15
16 #include <boost/foreach.hpp>
17
18
19 // Following boost macros are needed for serialization of derived classes through a base class pointer (Service *).
20 BOOST_CLASS_EXPORT_GUID(ODS, "ODS")
21 BOOST_CLASS_EXPORT_GUID(DHS, "DHS")
22
23 namespace fs = boost::filesystem;
24
25 using namespace std;
26
27 /**
28  * Default constructor which initializes the member Conf.
29  */
30 Updater::Updater()
31     : IPHelp(new IPHelper)
32 {
33     // Initialize program wide Logger, at this point we don't know where to log and with which loglevel.
34     Logger::Ptr _log(new Logger);
35     Log = _log;
36     _log.reset();
37
38     // initialize Serviceholder
39     Serviceholder::Ptr _serviceholder(new Serviceholder(Log));
40     ServiceHolder = _serviceholder;
41     _serviceholder.reset();
42
43     // initialize Config
44     Config::Ptr _config(new Config(Log,ServiceHolder));
45     Conf = _config;
46     _config.reset();
47 }
48
49
50 /**
51  * Default destructor.
52  */
53 Updater::~Updater()
54 {
55 }
56
57
58
59 /**
60  * Load config and initialize helper classes.
61  * @param argc Number of arguments in array
62  * @param argv Array with cmd options
63  */
64 int Updater::load_config(int argc, char *argv[])
65 {
66     // load the cmd options
67     if ( init_config_from_cmd(argc,argv) != 0 )
68         return -1;
69
70     // load the config and service files
71     if ( init_config_from_files() != 0 )
72         return -1;
73
74     // init all helper classes
75     if ( init_helper_classes() != 0 )
76         return -1;
77
78     return 0;
79 }
80
81
82 /**
83  * Reloading the config. Delete all Service objects and then init new Service objects from config files.
84  */
85 int Updater::reload_config()
86 {
87     // serialize all service objects
88     if ( ServiceHolder->serialize_services() != 0 )
89         return -1;
90
91     // delete all service objects
92     ServiceHolder->delete_services();
93
94     // delete the actual Variables_map, perhaps with old cmd options which would overwrite new config file options.
95     Conf->delete_variables_map();
96
97     // load only config files
98     if ( init_config_from_files() != 0 )
99         return -1;
100
101     // init all helper classes
102     if ( init_helper_classes() != 0 )
103         return -1;
104
105     return 0;
106 }
107
108
109 /**
110  * Parse the command line arguments and initialize corresponding options.
111  * @param argc Number command line arguments.
112  * @param argv[] Array with arguments.
113  * @return 0 if cmd options successfully parsed, 1 if usage or version.
114  */
115 int Updater::init_config_from_cmd(int argc, char *argv[])
116 {
117     // Load the command line parameters
118     if( Conf->parse_cmd_line( argc, argv) != 0)
119         return -1;
120
121     // If we have loaded the cmd options we need to init the log facility immediately in case debugging is enabled from cmd.
122     init_log_facility();
123
124     // successful parsed
125     Log->print_cmd_parsed();
126     return 0;
127 }
128
129
130 /**
131  * Load the main config and the service definition files in config path.
132  * @return 0 if all is fine, 
133  */
134 int Updater::init_config_from_files()
135 {
136     // Load the main and service config files in config path
137     if ( Conf->load_config_from_files() != 0 )
138         return -1;
139
140     // Re-init log facility, perhaps new config file options for logger are set.
141     // These config file options will only overwrite the cmd options if the SIGHUP (reload config) is caught.
142     init_log_facility();
143
144     // Here we are. All Service Objects should be initialized, so it is time to deserialize the old service objects and compare them.
145     if ( ServiceHolder->deserialize_services() != 0 )
146         return -1;
147
148     // successful loaded
149     Log->print_conf_files_parsed();
150     return 0;
151 }
152
153
154 /**
155  * Init all Helper classes
156  * @return 
157  */
158 int Updater::init_helper_classes()
159 {
160     // Initialize IPHelper
161     if ( init_ip_helper() != 0 )
162         return -1;
163
164     return 0;
165 }
166
167
168 /**
169  * Init the IPHelp member with needed values.
170  * @return 0 if all is fine, -1 otherwise.
171  */
172 int Updater::init_ip_helper()
173 {
174     // initialize IPHelper
175     IPHelper::Ptr _iphelp(new IPHelper(Log,Conf->get_webcheck_ip_url(),Conf->get_webcheck_ip_url_alt(),Conf->get_enable_ipv6(),Conf->get_proxy(),Conf->get_proxy_port()));
176     IPHelp = _iphelp;
177     _iphelp.reset();
178
179     return 0;
180 }
181
182
183 /**
184  * Getter for member Config.
185  * @return Member Config.
186  */
187 Config::Ptr Updater::get_config() const
188 {
189     return Conf;
190 }
191
192
193 /**
194  * Getter for member Logger.
195  * @return Member Logger.
196  */
197 Logger::Ptr Updater::get_logger() const
198 {
199     return Log;
200 }
201
202
203 /**
204  * Initialize the logging facility with loglevel and syslog.
205  */
206 void Updater::init_log_facility()
207 {
208     Log->set_log_facility(Conf->get_loglevel(),Conf->get_syslog(),Conf->get_external_warning_log(),Conf->get_external_warning_level());
209     Log->print_init_log_facility();
210 }
211
212
213 /**
214  * Update all configured services.
215  */
216 void Updater::update_services()
217 {
218     list<Service::Ptr> services = ServiceHolder->get_services();
219
220     string ip = IPHelp->get_actual_ip();
221
222     if ( !ip.empty() )
223     {
224         BOOST_FOREACH(Service::Ptr &service, services )
225         {
226             string dns_recheck_ip = ip;
227             int current_time = time(NULL);
228
229             int lastupdated = 0;
230             if ( service->get_last_updates()->size() > 0 )
231                 lastupdated = service->get_last_updates()->front();
232
233             // If the dns cache ttl is expired or the service is updated for the first time, then get the actual ip of the dns record (this should be the IP in the last update)
234             if ( ((lastupdated != 0) && ((lastupdated + service->get_dns_cache_ttl()) < current_time)) || (lastupdated == 0) )
235             {
236                 Log->print_recheck_dns_entry(service->get_hostname(),lastupdated,service->get_dns_cache_ttl(),current_time);
237                 string _dns_recheck_ip = IPHelp->dns_query(service->get_hostname());
238                 Log->print_cached_dns_entry(service->get_hostname(), _dns_recheck_ip, service->get_actual_ip());
239                 if (!_dns_recheck_ip.empty())
240                     dns_recheck_ip = _dns_recheck_ip;
241             }
242
243             // In case the local hosts IP (ip) differ from the IP set in the last update (actual_ip) or
244             // the IP of the dns record (dns_recheck_ip) differs from the IP of the local host (ip)
245             // then perform an update. This implies that the update is not performed if actual_ip == dns_recheck_ip.
246             // Special case when latupdated == 0 then only perform the update if dns_rechek_ip differs from ip
247             if ( ((service->get_actual_ip() != ip) && (lastupdated != 0)) || (dns_recheck_ip != ip)  )
248                 service->update(ip,current_time);
249             else if ( (service->get_actual_ip() != ip) && (lastupdated == 0)  )
250                 service->set_actual_ip(ip);
251         }
252     }
253 }
254
255
256 /**
257  * Getter for member ServiceHolder.
258  * @return ServiceHolder
259  */
260 Serviceholder::Ptr Updater::get_service_holder() const
261 {
262     return ServiceHolder;
263 }