/** @file * @brief The updater class implementation. This class implements the updater logic. * * * * @copyright Intra2net AG * @license GPLv2 */ #include "updater.hpp" #include "serviceholder.hpp" #include #include using namespace std; /// Server error threshold: When the host IP is not current, the DNS TTL expired /// and we already sent an update. const int ServerErrorTTLExpiredThreshold = 15 * 60; /** * Default constructor which initializes the member Conf. */ Updater::Updater() : IPAddrHelp(new IPAddrHelper) { // Initialize program wide Logger, at this point we don't know where to log and with which loglevel. Log = Logger::Ptr(new Logger); // initialize Serviceholder ServiceHolder = Serviceholder::Ptr(new Serviceholder(Log)); // initialize Config Conf = Config::Ptr(new Config(Log,ServiceHolder)); } /** * Default destructor. */ Updater::~Updater() { } /** * Load config and initialize helper classes. * @param argc Number of arguments in array * @param argv Array with cmd options */ int Updater::load_config(int argc, char *argv[]) { // load the cmd options if ( init_config_from_cmd(argc,argv) != 0 ) return -1; // load the config and service files if ( init_config_from_files() != 0 ) return -1; // init all helper classes if ( init_helper_classes() != 0 ) return -1; return 0; } /** * Reloading the config. Delete all Service objects and then init new Service objects from config files. */ int Updater::reload_config() { // serialize all service objects if ( ServiceHolder->serialize_services() != 0 ) return -1; // delete all service objects ServiceHolder->delete_services(); // delete the actual Variables_map, perhaps with old cmd options which would overwrite new config file options. Conf->delete_variables_map(); // clear the external send messages. Log->clear_external_send_messages(); // load only config files if ( init_config_from_files() != 0 ) return -1; // init all helper classes if ( init_helper_classes() != 0 ) return -1; return 0; } /** * Parse the command line arguments and initialize corresponding options. * @param argc Number command line arguments. * @param argv[] Array with arguments. * @return 0 if cmd options successfully parsed, 1 if usage or version. */ int Updater::init_config_from_cmd(int argc, char *argv[]) const { // Load the command line parameters if( Conf->parse_cmd_line( argc, argv) != 0) return -1; // If we have loaded the cmd options we need to init the log facility immediately in case debugging is enabled from cmd. init_log_facility(); // successful parsed Log->print_cmd_parsed(); return 0; } /** * Load the main config and the service definition files in config path. * @return 0 if all is fine, */ int Updater::init_config_from_files() const { // Load the main and service config files in config path if ( Conf->load_config_from_files() != 0 ) return -1; // Re-init log facility, perhaps new config file options for logger are set. // These config file options will only overwrite the cmd options if the SIGHUP (reload config) is caught. init_log_facility(); // Here we are. All Service Objects should be initialized, so it is time to deserialize the old service objects and compare them. if ( ServiceHolder->deserialize_services() != 0 ) return -1; // successful loaded Log->print_conf_files_parsed(); return 0; } /** * Init all Helper classes * @return */ int Updater::init_helper_classes() { // Initialize IPHelper if ( init_ip_helper() != 0 ) return -1; return 0; } /** * Init the IPHelp member with needed values. * @return 0 if all is fine, -1 otherwise. */ int Updater::init_ip_helper() { // Try to get deserialized IPAddrHelper from ServiceHolder IPAddrHelper::Ptr _ip_addr_help = ServiceHolder->get_ip_addr_helper(); if ( _ip_addr_help.use_count() != 0 ) { // Initialize IPHelper 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() ) ); } else { // IPAddrHelper from ServiceHolder was not declared, so init oen with LastWebcheck 0 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() ) ); } // Put the IPAddrHelper into ServiceHolder, so the LastWebcheck state will be serialized too. ServiceHolder->set_ip_addr_helper(IPAddrHelp); return 0; } /** * Getter for member Config. * @return Member Config. */ Config::Ptr Updater::get_config() const { return Conf; } /** * Getter for member Logger. * @return Member Logger. */ Logger::Ptr Updater::get_logger() const { return Log; } /** * Initialize the logging facility with loglevel and syslog. */ void Updater::init_log_facility() const { 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()); Log->print_init_log_facility(); Log->set_log_passwords(Conf->get_log_passwords()); } /** * Update all configured services. * @param changed_to_online True if we just changed to online, false if we were already online */ void Updater::update_services(bool changed_to_online) const { // Get all services from the ServiceHolder. list services = ServiceHolder->get_services(); // Get the actual IP of this host. string ip_host = IPAddrHelp->get_actual_ip(Conf->get_webcheck_enabled(), changed_to_online, Conf->get_wan_ip_override()); if ( ip_host.empty() ) { Log->print_no_wan_ip(changed_to_online); return; } else { Log->print_external_wan_ip(changed_to_online, ip_host); } BOOST_FOREACH(Service::Ptr &service, services ) { string ip_last_update = service->get_actual_ip(); string hostname = service->get_hostname(); time_t current_time = time(NULL); // Get the last update time of the service. time_t lastupdated = service->get_last_update_time(); Log->print_check_service_update(hostname, current_time, lastupdated); // Do a DNS Query for the dynamic hostname. string ip_dns_recheck = IPAddrHelp->dns_query(hostname); Log->print_cached_dns_entry(hostname, ip_dns_recheck, ip_last_update, ip_host); // Test if the DNS-Record could not be found. // 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. if ( ip_dns_recheck.empty() && service->get_activated() ) { Log->print_dns_lookup_failed(changed_to_online, hostname); continue; } // Test if the actual DNS-Record differs from the host's IP. if (ip_host == ip_dns_recheck ) { Log->print_no_update_needed(changed_to_online, hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated); // No update needed continue; } // Check if the IP set in last update differs from the actual host's ip. if ( ip_last_update != ip_host ) { // Update if ( !ip_dns_recheck.empty() ) { Log->print_update_service(hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated); } else { // 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. Log->print_update_service(hostname, "", ip_last_update, ip_host, lastupdated); } service->update(ip_host, current_time, changed_to_online); } else { int dns_cache_ttl = service->get_dns_cache_ttl(); // Add server error threshold so we don't jam the server with updates for the same IP // in case the server doesn't hand out the already sent IP via DNS // (might indicate a server error). dns_cache_ttl += ServerErrorTTLExpiredThreshold; // Check if DNS Cache TTL is expired, if so, then update the same IP again. if ( (lastupdated + dns_cache_ttl) < current_time ) { // Update Log->print_update_service_ttl_expired(hostname, ip_dns_recheck, ip_last_update, ip_host, lastupdated, dns_cache_ttl, current_time); service->update(ip_host, current_time, changed_to_online); } else { // DNS cache TTL isn't expired 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); } } } } /** * Getter for member ServiceHolder. * @return ServiceHolder */ Serviceholder::Ptr Updater::get_service_holder() const { return ServiceHolder; }