core, main: make missing config file section behavior configurable

refs: #1499

Change-Id: I01cce3c73214f592d8c342d8aeda4fbafc6804b8
diff --git a/core/config-file.cpp b/core/config-file.cpp
index 436c480..5887c2b 100644
--- a/core/config-file.cpp
+++ b/core/config-file.cpp
@@ -32,7 +32,30 @@
 
 NFD_LOG_INIT("ConfigFile");
 
-ConfigFile::ConfigFile()
+void
+ConfigFile::throwErrorOnUnknownSection(const std::string& filename,
+                                       const std::string& sectionName,
+                                       const ConfigSection& section,
+                                       bool isDryRun)
+{
+  std::string msg = "Error processing configuration file ";
+  msg += filename;
+  msg += ": no module subscribed for section \"" + sectionName + "\"";
+
+  throw ConfigFile::Error(msg);
+}
+
+void
+ConfigFile::ignoreUnknownSection(const std::string& filename,
+                                 const std::string& sectionName,
+                                 const ConfigSection& section,
+                                 bool isDryRun)
+{
+  // do nothing
+}
+
+ConfigFile::ConfigFile(UnknownConfigSectionHandler unknownSectionCallback)
+  : m_unknownSectionCallback(unknownSectionCallback)
 {
 }
 
@@ -93,8 +116,7 @@
 
   if (m_global.begin() == m_global.end())
     {
-      std::string msg = "Error processing configuration file";
-      msg += ": ";
+      std::string msg = "Error processing configuration file: ";
       msg += filename;
       msg += " no data";
       throw Error(msg);
@@ -113,11 +135,7 @@
         }
       else
         {
-          std::string msg = "Error processing configuration file";
-          msg += " ";
-          msg += filename;
-          msg += " no module subscribed for section: " + sectionName;
-          throw Error(msg);
+          m_unknownSectionCallback(filename, sectionName, section, isDryRun);
         }
     }
 }
diff --git a/core/config-file.hpp b/core/config-file.hpp
index f3128c4..b7dd886 100644
--- a/core/config-file.hpp
+++ b/core/config-file.hpp
@@ -34,7 +34,15 @@
 typedef boost::property_tree::ptree ConfigSection;
 
 /// \brief callback for config file sections
-typedef function<void(const ConfigSection&, bool, const std::string&)> ConfigSectionHandler;
+typedef function<void(const ConfigSection& /*section*/,
+                      bool /*isDryRun*/,
+                      const std::string& /*filename*/)> ConfigSectionHandler;
+
+/// \brief callback for config file sections without a subscribed handler
+typedef function<void(const std::string& /*filename*/,
+                      const std::string& /*sectionName*/,
+                      const ConfigSection& /*section*/,
+                      bool /*isDryRun*/)> UnknownConfigSectionHandler;
 
 class ConfigFile : noncopyable
 {
@@ -51,7 +59,19 @@
     }
   };
 
-  ConfigFile();
+  ConfigFile(UnknownConfigSectionHandler unknownSectionCallback = throwErrorOnUnknownSection);
+
+  static void
+  throwErrorOnUnknownSection(const std::string& filename,
+                             const std::string& sectionName,
+                             const ConfigSection& section,
+                             bool isDryRun);
+
+  static void
+  ignoreUnknownSection(const std::string& filename,
+                       const std::string& sectionName,
+                       const ConfigSection& section,
+                       bool isDryRun);
 
   /// \brief setup notification of configuration file sections
   void
@@ -93,6 +113,7 @@
   process(bool isDryRun, const std::string& filename);
 
 private:
+  UnknownConfigSectionHandler m_unknownSectionCallback;
 
   typedef std::map<std::string, ConfigSectionHandler> SubscriptionTable;
 
diff --git a/daemon/main.cpp b/daemon/main.cpp
index 5fd159f..a13dfe1 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -64,22 +64,38 @@
   void
   initializeLogging(const std::string& configFile)
   {
-    ConfigFile config;
+    ConfigFile config(&ConfigFile::ignoreUnknownSection);
     LoggerFactory::getInstance().setConfigFile(config);
 
-    for (size_t i = 0; i < N_SUPPORTED_CONFIG_SECTIONS; ++i)
-      {
-        if (SUPPORTED_CONFIG_SECTIONS[i] != "log")
-          {
-            config.addSectionHandler(SUPPORTED_CONFIG_SECTIONS[i],
-                                     bind(std::plus<int>(), 0, 0)); // no-op.
-          }
-      }
-
     config.parse(configFile, true);
     config.parse(configFile, false);
   }
 
+  class IgnoreRibAndLogSections
+  {
+  public:
+    void
+    operator()(const std::string& filename,
+               const std::string& sectionName,
+               const ConfigSection& section,
+               bool isDryRun)
+
+    {
+      // Ignore "log" and sections beginning with "rib_" (intended for rib manager),
+      // but raise an error if we're missing a handler for an NFD section.
+
+      if (sectionName.find("rib_") == 0 || sectionName == "log")
+        {
+          // do nothing
+        }
+      else
+        {
+          // missing NFD section
+          ConfigFile::throwErrorOnUnknownSection(filename, sectionName, section, isDryRun);
+        }
+    }
+  };
+
   void
   initializeManagement(const std::string& configFile)
   {
@@ -99,15 +115,13 @@
     m_statusServer = make_shared<StatusServer>(m_internalFace,
                                                boost::ref(*m_forwarder));
 
-    ConfigFile config;
+    ConfigFile config((IgnoreRibAndLogSections()));
     m_internalFace->getValidator().setConfigFile(config);
 
     m_forwarder->addFace(m_internalFace);
 
     m_faceManager->setConfigFile(config);
 
-    config.addSectionHandler("log", bind(std::plus<int>(), 0, 0)); // no-op
-
     // parse config file
     config.parse(configFile, true);
     config.parse(configFile, false);
@@ -217,22 +231,8 @@
   shared_ptr<FaceManager>           m_faceManager;
   shared_ptr<StrategyChoiceManager> m_strategyChoiceManager;
   shared_ptr<StatusServer>          m_statusServer;
-
-  static const std::string SUPPORTED_CONFIG_SECTIONS[];
-  static const size_t      N_SUPPORTED_CONFIG_SECTIONS;
 };
 
-const std::string Nfd::SUPPORTED_CONFIG_SECTIONS[] =
-  {
-    "log",
-    "face_system",
-    "authorizations",
-    "rib_security",
-  };
-
-const size_t Nfd::N_SUPPORTED_CONFIG_SECTIONS =
-  sizeof(SUPPORTED_CONFIG_SECTIONS) / sizeof(std::string);
-
 } // namespace nfd
 
 int
diff --git a/rib/main.cpp b/rib/main.cpp
index 7598d6c..452b8a9 100644
--- a/rib/main.cpp
+++ b/rib/main.cpp
@@ -45,6 +45,30 @@
 class Nrd : noncopyable
 {
 public:
+  class IgnoreNfdAndLogSections
+  {
+  public:
+    void
+    operator()(const std::string& filename,
+               const std::string& sectionName,
+               const ConfigSection& section,
+               bool isDryRun)
+    {
+      // Ignore "log" and sections belonging to NFD,
+      // but raise an error if we're missing a handler for an "rib_" section.
+
+      if (sectionName.find("rib_") != 0 || sectionName == "log")
+        {
+          // do nothing
+        }
+      else
+        {
+          // missing NRD section
+          ConfigFile::throwErrorOnUnknownSection(filename, sectionName, section, isDryRun);
+        }
+    }
+  };
+
   void
   initialize(const std::string& configFile)
   {
@@ -52,18 +76,9 @@
 
     m_ribManager = make_shared<RibManager>();
 
-    ConfigFile config;
+    ConfigFile config((IgnoreNfdAndLogSections()));
     m_ribManager->setConfigFile(config);
 
-    for (size_t i = 0; i < N_SUPPORTED_CONFIG_SECTIONS; ++i)
-      {
-        if (SUPPORTED_CONFIG_SECTIONS[i] != "rib_security")
-          {
-            config.addSectionHandler(SUPPORTED_CONFIG_SECTIONS[i],
-                                     bind(std::plus<int>(), 0, 0)); // no-op.
-          }
-      }
-
     // parse config file
     config.parse(configFile, true);
     config.parse(configFile, false);
@@ -75,18 +90,9 @@
   void
   initializeLogging(const std::string& configFile)
   {
-    ConfigFile config;
+    ConfigFile config(&ConfigFile::ignoreUnknownSection);
     LoggerFactory::getInstance().setConfigFile(config);
 
-    for (size_t i = 0; i < N_SUPPORTED_CONFIG_SECTIONS; ++i)
-      {
-        if (SUPPORTED_CONFIG_SECTIONS[i] != "log")
-          {
-            config.addSectionHandler(SUPPORTED_CONFIG_SECTIONS[i],
-                                     bind(std::plus<int>(), 0, 0)); // no-op.
-          }
-      }
-
     config.parse(configFile, true);
     config.parse(configFile, false);
   }
@@ -191,22 +197,8 @@
 
 private:
   shared_ptr<RibManager> m_ribManager;
-
-  static const std::string SUPPORTED_CONFIG_SECTIONS[];
-  static const size_t      N_SUPPORTED_CONFIG_SECTIONS;
 };
 
-const std::string Nrd::SUPPORTED_CONFIG_SECTIONS[] =
-  {
-    "log",
-    "face_system",
-    "authorizations",
-    "rib_security",
-  };
-
-const size_t Nrd::N_SUPPORTED_CONFIG_SECTIONS =
-  sizeof(SUPPORTED_CONFIG_SECTIONS) / sizeof(std::string);
-
 } // namespace rib
 } // namespace nfd
 
diff --git a/rib/rib-manager.cpp b/rib/rib-manager.cpp
index d5f26f2..27f7f3c 100644
--- a/rib/rib-manager.cpp
+++ b/rib/rib-manager.cpp
@@ -91,7 +91,7 @@
 void
 RibManager::setConfigFile(ConfigFile& configFile)
 {
-  configFile.addSectionHandler("security",
+  configFile.addSectionHandler("rib_security",
                                bind(&RibManager::onConfig, this, _1, _2, _3));
 }
 
diff --git a/tests/core/config-file.cpp b/tests/core/config-file.cpp
index bb75b77..29689dd 100644
--- a/tests/core/config-file.cpp
+++ b/tests/core/config-file.cpp
@@ -342,11 +342,43 @@
   BOOST_CHECK(sub2.allCallbacksFired());
 }
 
-BOOST_AUTO_TEST_CASE(OnConfigUncoveredSections)
+class MissingCallbackFixture : public BaseFixture
+{
+public:
+  MissingCallbackFixture()
+    : m_missingFired(false)
+  {
+  }
+
+  void
+  checkMissingHandler(const std::string& filename,
+                      const std::string& sectionName,
+                      const ConfigSection& section,
+                      bool isDryRun)
+  {
+    m_missingFired = true;
+  }
+
+protected:
+  bool m_missingFired;
+};
+
+
+
+BOOST_FIXTURE_TEST_CASE(OnConfigUncoveredSections, MissingCallbackFixture)
 {
   ConfigFile file;
 
-  BOOST_CHECK_THROW(file.parse(CONFIG, false, "dummy-config"), ConfigFile::Error);
+  BOOST_REQUIRE_THROW(file.parse(CONFIG, false, "dummy-config"), ConfigFile::Error);
+
+  ConfigFile permissiveFile(bind(&MissingCallbackFixture::checkMissingHandler,
+                                 this, _1, _2, _3, _4));
+
+  DummyOneSubscriber subA(permissiveFile, "a");
+
+  BOOST_REQUIRE_NO_THROW(permissiveFile.parse(CONFIG, false, "dummy-config"));
+  BOOST_CHECK(subA.allCallbacksFired());
+  BOOST_CHECK(m_missingFired);
 }
 
 BOOST_AUTO_TEST_CASE(OnConfigCoveredByPartialSubscribers)