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