Fix 'occurred' typo
[bpdyndnsd] / src / updater.cpp
index 6bc1239..7c3b021 100644 (file)
@@ -7,43 +7,37 @@
  * @license GPLv2
 */
 
-#include "updater.h"
+#include "updater.hpp"
+
+#include "serviceholder.hpp"
 
-#include "serviceholder.h"
-#include "dhs.h"
-#include "ods.h"
 
 #include <boost/foreach.hpp>
+#include <boost/serialization/export.hpp>
+
 
+using namespace std;
 
-// Following boost macros are needed for serialization of derived classes through a base class pointer (Service *).
-BOOST_CLASS_EXPORT_GUID(ODS, "ODS")
-BOOST_CLASS_EXPORT_GUID(DHS, "DHS")
 
-namespace fs = boost::filesystem;
+/// 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;
 
-using namespace std;
 
 /**
  * Default constructor which initializes the member Conf.
  */
 Updater::Updater()
-    : IPHelp(new IPHelper)
+    : IPAddrHelp(new IPAddrHelper)
 {
     // Initialize program wide Logger, at this point we don't know where to log and with which loglevel.
-    Logger::Ptr _log(new Logger);
-    Log = _log;
-    _log.reset();
+    Log = Logger::Ptr(new Logger);
 
     // initialize Serviceholder
-    Serviceholder::Ptr _serviceholder(new Serviceholder(Log));
-    ServiceHolder = _serviceholder;
-    _serviceholder.reset();
+    ServiceHolder = Serviceholder::Ptr(new Serviceholder(Log));
 
     // initialize Config
-    Config::Ptr _config(new Config(Log,ServiceHolder));
-    Conf = _config;
-    _config.reset();
+    Conf = Config::Ptr(new Config(Log,ServiceHolder));
 }
 
 
@@ -55,13 +49,67 @@ 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[])
+int Updater::init_config_from_cmd(int argc, char *argv[]) const
 {
     // Load the command line parameters
     if( Conf->parse_cmd_line( argc, argv) != 0)
@@ -78,9 +126,9 @@ int Updater::init_config_from_cmd(int argc, char *argv[])
 
 /**
  * Load the main config and the service definition files in config path.
- * @return 0 if all is fine, 
+ * @return 0 if all is fine,
  */
-int Updater::init_config_from_files()
+int Updater::init_config_from_files() const
 {
     // Load the main and service config files in config path
     if ( Conf->load_config_from_files() != 0 )
@@ -101,22 +149,16 @@ int Updater::init_config_from_files()
 
 
 /**
- * Getter for member Config.
- * @return Member Config.
+ * Init all Helper classes
+ * @return
  */
-Config::Ptr Updater::get_config() const
+int Updater::init_helper_classes()
 {
-    return Conf;
-}
-
+    // Initialize IPHelper
+    if ( init_ip_helper() != 0 )
+        return -1;
 
-/**
- * Getter for member Logger.
- * @return Member Logger.
- */
-Logger::Ptr Updater::get_logger() const
-{
-    return Log;
+    return 0;
 }
 
 
@@ -126,73 +168,147 @@ Logger::Ptr Updater::get_logger() const
  */
 int Updater::init_ip_helper()
 {
-    // initialize IPHelper
-    IPHelper::Ptr _iphelp(new IPHelper(Log,Conf->get_webcheck_ip_url(),Conf->get_webcheck_ip_url_alt(),Conf->get_enable_ipv6()));
-    IPHelp = _iphelp;
-    _iphelp.reset();
+    // 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;
 }
 
 
 /**
- * Reloading the config. Delete all Service objects and then init new Service objects from config files.
+ * Getter for member Config.
+ * @return Member Config.
  */
-int Updater::reload_config()
+Config::Ptr Updater::get_config() const
 {
-    // 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();
+    return Conf;
+}
 
-    // load only config files
-    if ( init_config_from_files() != 0 )
-        return -1;
 
-    return 0;
+/**
+ * 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()
+void Updater::init_log_facility() const
 {
-    Log->set_log_facility(Conf->get_loglevel(),Conf->get_syslog());
+    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()
+void Updater::update_services(bool changed_to_online) const
 {
+    // Get all services from the ServiceHolder.
     list<Service::Ptr> services = ServiceHolder->get_services();
 
-    string ip = IPHelp->get_actual_ip();
+    // 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);
+    }
 
-    if ( !ip.empty() )
+    BOOST_FOREACH(Service::Ptr &service, services )
     {
-        BOOST_FOREACH(Service::Ptr &service, services )
-        {
-            string dns_recheck_ip = ip;
-            int current_time = time(NULL);
+        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);
 
-            int lastupdated = 0;
-            if ( service->get_last_updates()->size() > 0 )
-                lastupdated = service->get_last_updates()->front();
+        // Do a DNS Query for the dynamic hostname.
+        string ip_dns_recheck = IPAddrHelp->dns_query(hostname);
 
-            if ( (lastupdated != 0) && ((lastupdated + service->get_dns_cache_ttl()) < current_time) )
-                dns_recheck_ip = IPHelp->dns_query(service->get_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;
+        }
 
-            if ( (service->get_actual_ip() != ip) || (dns_recheck_ip != ip)  )
-                service->update(ip,current_time);
+        // 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, "<NO IP SET>", 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);
+            }
         }
     }
 }