2 * @brief Config class implementation. This class represents the actual configuration.
6 * @copyright Intra2net AG
12 #include "service_dhs.h"
13 #include "service_ods.h"
14 #include "service_dyndns.h"
15 #include "service_dyns.h"
16 #include "service_easydns.h"
17 #include "service_tzo.h"
18 #include "service_zoneedit.h"
19 #include "service_gnudip.h"
25 #include <boost/foreach.hpp>
26 #include <boost/filesystem.hpp>
27 #include <boost/regex.hpp>
28 #include <boost/algorithm/string.hpp>
31 namespace po = boost::program_options;
32 namespace fs = boost::filesystem;
33 namespace ba = boost::algorithm;
35 typedef boost::shared_ptr<boost::program_options::options_description> Options_descriptionPtr;
41 * Default Constructor. Available command line and config file options with their default values are defined here.
45 , ServiceHolder(new Serviceholder)
50 , ConfigPath("/etc/bpdyndnsd")
53 , ExternalWarningLog("")
54 , ExternalWarningLevel(0)
56 // Available service description config options
57 po::options_description opt_desc_service("Service description options");
58 opt_desc_service.add_options()
59 ("protocol",po::value<string>(),"The service protocol.")
60 ("server",po::value<string>(),"Servername needed for gnudip protocol.")
61 ("host",po::value<string>(),"The hostname to update.")
62 ("login",po::value<string>(),"Login name.")
63 ("password",po::value<string>(),"Corresponding password.")
64 ("update_interval",po::value<int>()->default_value(-1),"Update interval in minutes.")
65 ("max_updates_within_interval",po::value<int>()->default_value(-1),"How many updates can be made in one interval.")
66 ("dns_cache_ttl",po::value<int>()->default_value(-1),"How long a dns record is valid.")
69 // Available command line only options
70 po::options_description opt_desc_cmd_only("Command line only options");
71 opt_desc_cmd_only.add_options()
72 ("help,?","Show help.")
73 ("version,v","Show version.")
74 ("config,c",po::value<string>()->default_value("/etc/bpdyndnsd"),"Set the config path.")
77 // Available generic options. Valid on cmd or in config file.
78 po::options_description opt_desc_generic("Generic config options");
79 opt_desc_generic.add_options()
80 ("daemon_mode",po::value<bool>()->default_value(false),"Run as system daemon.")
81 ("loglevel",po::value<int>()->default_value(0),"Loglevel.")
82 ("syslog",po::value<bool>()->default_value(false),"Use syslog facility.")
83 ("enable_ipv6",po::value<bool>()->default_value(false),"Try to use IPv6.")
84 ("webcheck_url",po::value<string>()->default_value(""),"Use this URL to determine IP.")
85 ("webcheck_url_alt",po::value<string>()->default_value(""),"Use this alternative URL to determine IP.")
86 ("webcheck_interval",po::value<int>()->default_value(10),"The webcheck interval in minutes.")
87 ("http_proxy",po::value<string>(),"Use this proxy for all http requests.")
88 ("http_proxy_port",po::value<int>(),"Port of the proxy.")
89 ("external_warning_log",po::value<string>()->default_value(""),"External programm to pass warning log messages to.")
90 ("external_warning_level",po::value<int>()->default_value(0),"Warning messages of which loglevel should be passed to external programm.")
93 // Define valid command line parameters
94 Options_descriptionPtr _opt_desc_cmd(new po::options_description("Command line options"));
95 OptDescCmd.swap(_opt_desc_cmd);
96 OptDescCmd->add(opt_desc_cmd_only);
97 OptDescCmd->add(opt_desc_generic);
98 OptDescCmd->add(opt_desc_service);
100 // Define valid config file options
101 Options_descriptionPtr _opt_desc_conf_main(new po::options_description("Config file options"));
102 OptDescConfMain.swap(_opt_desc_conf_main);
103 OptDescConfMain->add(opt_desc_generic);
105 // Define valid service file options
106 Options_descriptionPtr _opt_desc_conf_service(new po::options_description("Service file options"));
107 OptDescConfService.swap(_opt_desc_conf_service);
108 OptDescConfService->add(opt_desc_service);
113 * Constructor with Logger and Serviceholder objects. Available command line and config file options with their default values are defined here.
115 Config::Config(Logger::Ptr _log, Serviceholder::Ptr _serviceholder)
117 , ServiceHolder(_serviceholder)
122 , ConfigPath("/etc/bpdyndnsd")
123 , WebcheckInterval(0)
125 , ExternalWarningLog("")
126 , ExternalWarningLevel(0)
128 // Available service description config options
129 po::options_description opt_desc_service("Service description options");
130 opt_desc_service.add_options()
131 ("protocol",po::value<string>(),"The service protocol.")
132 ("server",po::value<string>(),"Servername needed for gnudip protocol.")
133 ("host",po::value<string>(),"The hostname to update.")
134 ("login",po::value<string>(),"Login name.")
135 ("password",po::value<string>(),"Corresponding password.")
136 ("update_interval",po::value<int>()->default_value(-1),"Update interval in minutes.")
137 ("max_updates_within_interval",po::value<int>()->default_value(-1),"How many updates can be made in one interval.")
138 ("dns_cache_ttl",po::value<int>()->default_value(-1),"How long a dns record is valid.")
141 // Available command line only options
142 po::options_description opt_desc_cmd_only("Command line only options");
143 opt_desc_cmd_only.add_options()
144 ("help,?","Show help.")
145 ("version,v","Show version.")
146 ("config,c",po::value<string>()->default_value("/etc/bpdyndnsd"),"Set the config path.")
149 // Available generic options. Valid on cmd or in config file.
150 po::options_description opt_desc_generic("Generic config options");
151 opt_desc_generic.add_options()
152 ("daemon_mode",po::value<bool>()->default_value(false),"Run as system daemon.")
153 ("loglevel",po::value<int>()->default_value(0),"Loglevel.")
154 ("syslog",po::value<bool>()->default_value(false),"Use syslog facility.")
155 ("enable_ipv6",po::value<bool>()->default_value(false),"Try to use IPv6.")
156 ("webcheck_url",po::value<string>()->default_value(""),"Use this URL to determine IP.")
157 ("webcheck_url_alt",po::value<string>()->default_value(""),"Use this alternative URL to determine IP.")
158 ("webcheck_interval",po::value<int>()->default_value(10),"The webcheck interval in minutes.")
159 ("http_proxy",po::value<string>(),"Use this proxy for all http requests.")
160 ("http_proxy_port",po::value<int>(),"Port of the proxy.")
161 ("external_warning_log",po::value<string>()->default_value(""),"External programm to pass warning log messages to.")
162 ("external_warning_level",po::value<int>()->default_value(0),"Warning messages of which loglevel should be passed to external programm.")
165 // Define valid command line parameters
166 Options_descriptionPtr _opt_desc_cmd(new po::options_description("Command line options"));
167 OptDescCmd.swap(_opt_desc_cmd);
168 OptDescCmd->add(opt_desc_cmd_only);
169 OptDescCmd->add(opt_desc_generic);
170 OptDescCmd->add(opt_desc_service);
172 // Define valid config file options
173 Options_descriptionPtr _opt_desc_conf_main(new po::options_description("Config file options"));
174 OptDescConfMain.swap(_opt_desc_conf_main);
175 OptDescConfMain->add(opt_desc_generic);
177 // Define valid service file options
178 Options_descriptionPtr _opt_desc_conf_service(new po::options_description("Service file options"));
179 OptDescConfService.swap(_opt_desc_conf_service);
180 OptDescConfService->add(opt_desc_service);
193 * Parses the command line arguments and does the needed actions.
194 * @param argc Command line argument number given to main.
195 * @param argv[] Pointer to command line argument array given to main.
196 * @return 0 if all is fine, -1 if not.
198 int Config::parse_cmd_line(int argc, char *argv[])
202 po::store(po::parse_command_line(argc, argv, *this->OptDescCmd), VariablesMap);
203 po::notify(VariablesMap);
205 if ( VariablesMap.count("help") )
207 Log->print_usage(OptDescCmd);
210 else if ( VariablesMap.count("version") )
212 Log->print_version();
216 // Create a service object if all needed options are set on the command line
217 if ( VariablesMap.count("protocol") && VariablesMap.count("host") && VariablesMap.count("login") && VariablesMap.count("password") )
219 // Get the cmd parameter values for protocol host login and password
220 string protocol = VariablesMap["protocol"].as<string>();
221 string host = VariablesMap["host"].as<string>();
222 string login = VariablesMap["login"].as<string>();
223 string password = VariablesMap["password"].as<string>();
225 protocol = ba::to_lower_copy(protocol);
228 if ( VariablesMap.count("server") )
229 server = VariablesMap["server"].as<string>();
231 int update_interval = 0;
232 if ( VariablesMap.count("update_interval") )
233 update_interval = VariablesMap["update_interval"].as<int>();
235 int max_updates_within_interval = 0;
236 if ( VariablesMap.count("max_updates_within_interval") )
237 max_updates_within_interval = VariablesMap["max_updates_within_interval"].as<int>();
239 int dns_cache_ttl = 0;
240 if ( VariablesMap.count("dns_cache_ttl") )
241 dns_cache_ttl = VariablesMap["dns_cache_ttl"].as<int>();
243 Service::Ptr service = create_service(protocol,server,host,login,password,update_interval,max_updates_within_interval,dns_cache_ttl);
246 ServiceHolder->add_service(service);
247 Log->print_service_object("New Service object from command line options:", service->get_protocol(), service->get_hostname(), service->get_login() ,service->get_password(), service->get_update_interval(), service->get_max_updates_within_interval(), service->get_dns_cache_ttl() , service->get_actual_ip(), service->get_last_updates());
252 else if ( VariablesMap.count("protocol") || VariablesMap.count("host") || VariablesMap.count("login") || VariablesMap.count("password") )
254 Log->print_missing_cmd_service_option();
255 Log->print_usage(OptDescCmd);
259 if ( VariablesMap.count("config") )
261 fs::path full_config_path = fs::system_complete(fs::path(VariablesMap["config"].as<string>()));
262 ConfigPath = full_config_path.string();
263 if ( !fs::exists(full_config_path) || !fs::is_directory(full_config_path) )
265 // Config path doesn't exist or is not a directory
266 Log->print_error_config_path(ConfigPath);
271 if ( VariablesMap.count("daemon_mode") )
272 DaemonMode = VariablesMap["daemon_mode"].as<bool>();
274 if ( VariablesMap.count("loglevel") )
275 Loglevel = VariablesMap["loglevel"].as<int>();
277 if ( VariablesMap.count("syslog") )
278 Syslog = VariablesMap["syslog"].as<bool>();
280 if ( VariablesMap.count("enable_ipv6") )
281 EnableIPv6 = VariablesMap["enable_ipv6"].as<bool>();
283 if ( VariablesMap.count("webcheck_url") )
284 WebcheckIpUrl = VariablesMap["webcheck_url"].as<string>();
286 if ( VariablesMap.count("webcheck_url_alt") )
287 WebcheckIpUrlAlt = VariablesMap["webcheck_url_alt"].as<string>();
289 if ( VariablesMap.count("webcheck_interval") )
290 WebcheckInterval = VariablesMap["webcheck_interval"].as<int>();
292 if ( VariablesMap.count("http_proxy") && VariablesMap.count("http_proxy_port") )
294 Proxy = VariablesMap["http_proxy"].as<string>();
295 ProxyPort = VariablesMap["http_proxy_port"].as<int>();
297 else if ( VariablesMap.count("http_proxy") || VariablesMap.count("http_proxy_port") )
299 Log->print_missing_cmd_proxy_option();
300 Log->print_usage(OptDescCmd);
304 if ( VariablesMap.count("external_warning_log") )
305 ExternalWarningLog = VariablesMap["external_warning_log"].as<string>();
307 if ( VariablesMap.count("external_warning_level") )
308 ExternalWarningLevel = VariablesMap["external_warning_level"].as<int>();
311 catch( po::unknown_option& e )
313 Log->print_unknown_cmd_option(e.what());
314 Log->print_usage(OptDescCmd);
317 catch( po::multiple_occurrences& e )
319 Log->print_multiple_cmd_option(e.what());
320 Log->print_usage(OptDescCmd);
323 catch( po::error& e )
325 Log->print_error_parsing_cmd(e.what());
326 Log->print_usage(OptDescCmd);
334 * Creates a Service object from the given parameters.
335 * @param protocol Protocol to use.
336 * @param host Hostname to update.
337 * @param login Login.
338 * @param password Password.
339 * @return A pointer to the created Service object.
341 Service::Ptr Config::create_service(const string &protocol, const string& server, const string& hostname, const string& login, const string& password, const int update_interval, const int max_updates_within_interval, const int dns_cache_ttl)
343 // Test for valid hostname. Must contain 3 parts minimum.
344 list<string> fqhn_parts;
345 ba::split(fqhn_parts,hostname,boost::is_any_of("."));
346 if ( fqhn_parts.size() < 3 )
348 Log->print_invalid_hostname(protocol);
349 Service::Ptr service;
353 if(protocol == "dhs")
355 Service::Ptr service_dhs(new ServiceDhs(protocol,hostname,login,password,Log,update_interval,max_updates_within_interval,dns_cache_ttl,Proxy,ProxyPort));
358 else if(protocol == "ods")
360 Service::Ptr service_ods(new ServiceOds(protocol,hostname,login,password,Log,update_interval,max_updates_within_interval,dns_cache_ttl));
363 else if(protocol == "dyndns")
365 Service::Ptr service_dyndns(new ServiceDyndns(protocol,hostname,login,password,Log,update_interval,max_updates_within_interval,dns_cache_ttl,Proxy,ProxyPort));
366 return service_dyndns;
368 else if(protocol == "dyns")
370 Service::Ptr service_dyns(new ServiceDyns(protocol,hostname,login,password,Log,update_interval,max_updates_within_interval,dns_cache_ttl,Proxy,ProxyPort));
373 else if(protocol == "easydns")
375 Service::Ptr service_easydns(new ServiceEasydns(protocol,hostname,login,password,Log,update_interval,max_updates_within_interval,dns_cache_ttl,Proxy,ProxyPort));
376 return service_easydns;
378 else if(protocol == "tzo")
380 Service::Ptr service_tzo(new ServiceTzo(protocol,hostname,login,password,Log,update_interval,max_updates_within_interval,dns_cache_ttl,Proxy,ProxyPort));
383 else if(protocol == "zoneedit")
385 Service::Ptr service_zoneedit(new ServiceZoneedit(protocol,hostname,login,password,Log,update_interval,max_updates_within_interval,dns_cache_ttl,Proxy,ProxyPort));
386 return service_zoneedit;
388 else if(protocol == "gnudip")
390 cout << "Server: " << server << endl;
391 if ( !server.empty() )
393 Service::Ptr service_gnudip(new ServiceGnudip(protocol,server,hostname,login,password,Log,update_interval,max_updates_within_interval,dns_cache_ttl,Proxy,ProxyPort));
394 return service_gnudip;
398 Log->print_gnudip_requires_servername();
399 Service::Ptr service;
405 Log->print_unknown_protocol(protocol);
406 Service::Ptr service;
413 * Loads a service config file, invoked by load_config_from_files.
414 * @param full_filename Filename of the service config file to load.
415 * @return 0 if all is fine, -1 otherwise.
417 int Config::load_service_config_file(const string& full_filename)
419 Log->print_load_service_conf(full_filename);
421 ifstream service_config_file(full_filename.c_str(),ifstream::in);
422 if(service_config_file.is_open())
426 po::variables_map vm;
427 po::parsed_options parsed_service_options = po::parse_config_file(service_config_file,*this->OptDescConfService,true);
428 po::store(parsed_service_options,vm);
431 if(vm.count("protocol") && vm.count("host") && vm.count("login") && vm.count("password"))
433 // create the corresponding service
434 string protocol = vm["protocol"].as<string>();
435 string host = vm["host"].as<string>();
436 string login = vm["login"].as<string>();
437 string password = vm["password"].as<string>();
439 protocol = ba::to_lower_copy(protocol);
442 if ( vm.count("server") )
443 server = vm["server"].as<string>();
445 int update_interval = 0;
446 if ( vm.count("update_interval") )
447 update_interval = vm["update_interval"].as<int>();
449 int max_updates_within_interval = 0;
450 if ( vm.count("max_updates_within_interval") )
451 max_updates_within_interval = vm["max_updates_within_interval"].as<int>();
453 int dns_cache_ttl = 0;
454 if ( vm.count("dns_cache_ttl") )
455 dns_cache_ttl = vm["dns_cache_ttl"].as<int>();
457 Service::Ptr service = create_service(protocol,server,host,login,password,update_interval,max_updates_within_interval,dns_cache_ttl);
460 ServiceHolder->add_service(service);
461 Log->print_service_object("New Service object from config:", service->get_protocol(), service->get_hostname(), service->get_login() ,service->get_password(), service->get_update_interval(), service->get_max_updates_within_interval(), service->get_dns_cache_ttl() , service->get_actual_ip(), service->get_last_updates());
466 else if ( vm.count("protocol") || vm.count("host") || vm.count("login") || vm.count("password") )
468 service_config_file.close();
469 Log->print_missing_service_conf_option(full_filename);
473 catch ( po::unknown_option& e )
475 // unknown option in config file detected
476 service_config_file.close();
477 Log->print_unknown_service_conf_option(full_filename,e.what());
480 catch( po::multiple_occurrences& e )
482 service_config_file.close();
483 Log->print_multiple_service_conf_option(full_filename,e.what());
486 catch( po::error& e )
488 service_config_file.close();
489 Log->print_error_parsing_config_file(full_filename,e.what());
492 service_config_file.close();
496 // error opening service config file for reading
497 Log->print_error_opening_r(full_filename);
505 * Loads the main config file, invoked by load_config_from_files
506 * @param full_filename The full filename of the main config file to load
507 * @return 0 if all is fine, -1 otherwise
509 int Config::load_main_config_file(const string& full_filename)
511 Log->print_load_main_conf(full_filename);
513 ifstream main_config_file(full_filename.c_str(),ifstream::in);
514 if(main_config_file.is_open())
518 po::parsed_options parsed_main_options = po::parse_config_file(main_config_file,*this->OptDescConfMain,true);
519 po::store(parsed_main_options,VariablesMap);
520 po::notify(VariablesMap);
522 if ( VariablesMap.count("daemon_mode") )
523 DaemonMode = VariablesMap["daemon_mode"].as<bool>();
525 if ( VariablesMap.count("loglevel") )
526 Loglevel = VariablesMap["loglevel"].as<int>();
528 if ( VariablesMap.count("syslog") )
529 Syslog = VariablesMap["syslog"].as<bool>();
531 if ( VariablesMap.count("enable_ipv6") )
532 EnableIPv6 = VariablesMap["enable_ipv6"].as<bool>();
534 if ( VariablesMap.count("webcheck_url") )
535 WebcheckIpUrl = VariablesMap["webcheck_url"].as<string>();
537 if ( VariablesMap.count("webcheck_url_alt") )
538 WebcheckIpUrlAlt = VariablesMap["webcheck_url_alt"].as<string>();
540 if ( VariablesMap.count("webcheck_interval") )
541 WebcheckInterval = VariablesMap["webcheck_interval"].as<int>();
543 if ( VariablesMap.count("http_proxy") && VariablesMap.count("http_proxy_port") )
545 Proxy = VariablesMap["http_proxy"].as<string>();
546 ProxyPort = VariablesMap["http_proxy_port"].as<int>();
548 else if ( VariablesMap.count("http_proxy") || VariablesMap.count("http_proxy_port") )
550 main_config_file.close();
551 Log->print_missing_conf_proxy_option(full_filename);
555 if ( VariablesMap.count("external_warning_log") )
556 ExternalWarningLog = VariablesMap["external_warning_log"].as<string>();
558 if ( VariablesMap.count("external_warning_level") )
559 ExternalWarningLevel = VariablesMap["external_warning_level"].as<int>();
562 catch ( po::unknown_option& e ) // at the moment 04-08-2009 this exception is never thrown :-(
564 // unknown option in main config file detected
565 main_config_file.close();
566 Log->print_unknown_main_conf_option(e.what());
569 catch(po::multiple_occurrences& e)
571 main_config_file.close();
572 Log->print_multiple_main_conf_option(full_filename,e.what());
575 main_config_file.close();
579 // error opening main config file for reading
580 Log->print_error_opening_r(full_filename);
588 * Loads the main and the service config file and does the needed action.
589 * @param config_path The path to the config directory.
590 * @return 0 if all is fine, -1 otherwise
592 int Config::load_config_from_files()
594 fs::path full_config_path = fs::path(ConfigPath);
596 fs::directory_iterator end_iter;
597 for ( fs::directory_iterator dir_itr(full_config_path) ; dir_itr != end_iter ; ++dir_itr )
599 if( fs::is_regular_file( dir_itr->status() ) )
601 string actual_file = dir_itr->path().filename();
602 boost::regex expr(".*\\.conf?");
603 // If it is the main config file do the following
604 if ( actual_file == "bpdyndnsd.conf" )
606 // Load the main config file
607 string full_filename = dir_itr->path().string();
608 if ( load_main_config_file(full_filename) != 0 )
611 // If it is a service definition file *.conf, parse it and generate the corresponding service
612 else if ( boost::regex_search( actual_file,expr ) )
614 string full_filename = dir_itr->path().string();
615 if ( load_service_config_file(full_filename) != 0 )
620 // Config file successfully loaded
621 Log->print_conf_loaded(ConfigPath);
627 * Getter method for member OptDescCmd.
628 * @return options_description*.
630 Options_descriptionPtr Config::get_opt_desc_cmd() const
637 * Getter method for member OptDescConfMain.
638 * @return options_description*.
640 Options_descriptionPtr Config::get_opt_desc_conf_main() const
642 return OptDescConfMain;
647 * Getter method for member OptDescConfService.
648 * @return options_description*.
650 Options_descriptionPtr Config::get_opt_desc_conf_service() const
652 return OptDescConfService;
657 * Getter for member Loglevel.
658 * @return Member Loglevel.
660 int Config::get_loglevel() const
667 * Getter for member DaemonMode.
668 * @return TRUE if enabled, FALSE if disabled.
670 bool Config::get_daemon_mode() const
677 * Deletes the map with the previously parsed options.
678 * This is needed in case we reload the config and don't want the old cmd options to overwrite new config file options.
680 void Config::delete_variables_map()
682 VariablesMap.clear();
684 po::variables_map _variables_map;
685 VariablesMap = _variables_map;
690 * Getter for member Syslog.
691 * @return True if logging through syslog is enabled, false otherwise.
693 bool Config::get_syslog() const
700 * Getter for member EnableIPv6
701 * @return Wether IPv6 should be used or not.
703 bool Config::get_enable_ipv6() const
710 * Getter for member WebcheckIpUrl
711 * @return The primary IP Webcheck URL
713 string Config::get_webcheck_ip_url() const
715 return WebcheckIpUrl;
720 * Getter for member WebcheckIpUrlAlt
721 * @return The alternative IP Webcheck URL
723 string Config::get_webcheck_ip_url_alt() const
725 return WebcheckIpUrlAlt;
730 * Get member WebcheckInterval
731 * @return WebcheckInterval
733 int Config::get_webcheck_interval() const
735 return WebcheckInterval;
743 string Config::get_proxy() const
750 * Get member ProxyPort
753 int Config::get_proxy_port() const
760 * Get member ExternalWarningLog
761 * @return ExternalWarningLog
763 string Config::get_external_warning_log() const
765 return ExternalWarningLog;
770 * Get member ExternalWarningLevel
771 * @return ExternalWaringLevel
773 int Config::get_external_warning_level() const
775 return ExternalWarningLevel;