nrd-config: Adding processing of command line arguments and processing of conf file.

refs #1450

Change-Id: I18fa5c1c12ec2367670659be331a9f95e0a920eb
diff --git a/nrd.conf.sample b/nrd.conf.sample
new file mode 100644
index 0000000..35b162f
--- /dev/null
+++ b/nrd.conf.sample
@@ -0,0 +1,70 @@
+security
+{
+  ; Security section defines the trust model that NRD should use. It consists of rules and
+  ; trust-anchors, which are briefly defined in this file. For more details please see the
+  ; following wiki:
+  ; http://redmine.named-data.net/projects/ndn-cpp-dev/wiki/CommandValidatorConf
+  ;
+  ; A trust-anchor is a pre-trusted certificate. It is usually stored in a file in the
+  ; same directory as this config file. You can download the NDN testbed root certificate as the
+  ; trust anchor, or you can dump an existing certificate from your system as a trust anchor:
+  ;   $ ndnsec cert-dump /example/certificate/name > trust-anchor.cert
+  ; or you can generate a self-signed certificate as a trust anchor:
+  ;   $ ndnsec key-gen /example/identity/name > trust-anchor.cert
+  ; See comments in trust-anchor section for configuration details.
+  ;
+  ; A rule defines conditions a valid packet MUST have. A packet must satisfy one of the rules defined
+  ; here. A rule can be broken into two parts: matching & checking. A packet will be matched against
+  ; rules from the first to the last until a matched rule is encountered. The matched rule will be
+  ; used to check the packet. If a packet does not match any rule, it will be treated as invalid.
+  ; The matching part of a rule consists of `for` and `filter` sections. They collectively define
+  ; which packets can be checked with this rule. `for` defines packet type (data or interest),
+  ; while `filter` defines conditions on other properties of a packet. Right now, you can only
+  ; define conditions on packet name, and you can only specify ONLY ONE filter for packet name.
+  ; The checking part of a rule consists of `checker`, which defines the conditions that a VALID
+  ; packet MUST have. See comments in checker section for more details.
+
+  rule
+  {
+    id "NRD Prefix Registration Command Rule" ; rule id
+    for interest                              ; this rule is used to validate interests
+    filter
+    {
+      type name                               ; condition on interest name (w/o signature)
+      regex ^[<localhop><localhost>]<nrd>[<register><unregister>]<>{3}$
+    }
+    checker
+    {
+      type customized
+      sig-type rsa-sha256                     ; interest must have a rsa-sha256 signature
+      key-locator
+      {
+        type name                             ; key locator must be the certificate name of
+                                              ; the signing key
+        regex ^[^<KEY>]*<KEY><>*<ksk-.*><ID-CERT>$
+      }
+    }
+  }
+  rule
+  {
+    id "Testbed Hierarchy Rule"               ; rule id
+    for data                                  ; this rule is used to validate data
+    filter
+    {
+      type name                               ; condition on data name
+      regex ^[^<KEY>]*<KEY><>*<ksk-.*><ID-CERT><>$
+    }
+    checker
+    {
+      type hierarchical                       ; the certificate name of the signing key and
+                                              ; the data name must follow the hierarchical model
+      sig-type rsa-sha256                     ; data must have a rsa-sha256 signature
+    }
+  }
+  trust-anchor
+  {
+    type file                     ; trust anchor is stored in a file
+    file-name "trust-anchor.cert" ; the file name, by default this file should be placed in the
+                                  ; same folder as this config file.
+  }
+}
diff --git a/src/common.hpp b/src/common.hpp
index 650f2de..f520828 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -22,5 +22,24 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/regex_find_format.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/info_parser.hpp>
 
+#include "../build/src/config.hpp"
+
+namespace ndn {
+namespace nrd {
+
+class Error : public std::runtime_error
+{
+public:
+  explicit
+  Error(const std::string& what)
+    : std::runtime_error(what)
+  {
+  }
+};
+
+} //namespace nrd
+} //namespace ndn
 #endif // NRD_COMMON_HPP
diff --git a/src/main.cpp b/src/main.cpp
index e360e5d..5d0e5ed 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,21 +4,97 @@
  * See COPYING for copyright and distribution information.
  */
 
+#include "common.hpp"
 #include "nrd.hpp"
+#include "nrd-config.hpp"
+#include <getopt.h>
+
+struct ProgramOptions
+{
+  bool showUsage;
+  std::string config;
+};
+
+static void
+printUsage(std::ostream& os, const std::string& programName)
+{
+  os << "Usage: \n"
+    << "  " << programName << " [options]\n"
+    << "\n"
+    << "Run NRD daemon\n"
+    << "\n"
+    << "Options:\n"
+    << "  [--help]   - print this help message\n"
+    << "  [--config /path/to/nrd.conf] - path to configuration file "
+    << "(default: " << DEFAULT_CONFIG_FILE << ")\n"
+    ;
+}
+
+static bool
+parseCommandLine(int argc, char** argv, ProgramOptions& options)
+{
+  options.showUsage = false;
+  options.config = DEFAULT_CONFIG_FILE;
+
+  while (true) {
+    int optionIndex = 0;
+    static ::option longOptions[] = {
+      { "help"   , no_argument      , 0, 0 },
+      { "config" , required_argument, 0, 0 },
+      { 0        , 0                , 0, 0 }
+    };
+    int c = getopt_long_only(argc, argv, "", longOptions, &optionIndex);
+    if (c == -1)
+      break;
+
+    switch (c) {
+      case 0:
+        switch (optionIndex) {
+          case 0: // help
+            options.showUsage = true;
+            break;
+          case 1: // config
+            options.config = ::optarg;
+            break;
+          default:
+            return false;
+        }
+        break;
+    }
+  }
+  return true;
+}
 
 int
 main(int argc, char** argv)
 {
-  try {
-    // TODO: the configFilename should be obtained from command line arguments.
-    std::string configFilename("nrd.conf");
+  //processing command line arguments, if available
+  ProgramOptions options;
+  bool isCommandLineValid = parseCommandLine(argc, argv, options);
+  if (!isCommandLineValid) {
+    printUsage(std::cerr, argv[0]);
+    return 1;
+  }
+  if (options.showUsage) {
+    printUsage(std::cout, argv[0]);
+    return 0;
+  }
 
-    ndn::nrd::Nrd nrd(configFilename);
+  //Now read the config file and pass the security section to validator
+  try {
+    std::string configFilename(options.config);
+
+    ndn::nrd::NrdConfig config;
+    config.load(configFilename);
+    ndn::nrd::ConfigSection securitySection = config.getSecuritySection();
+
+    ndn::nrd::Nrd nrd(securitySection, configFilename);
     nrd.enableLocalControlHeader();
     nrd.listen();
   }
   catch (std::exception& e) {
     std::cerr << "ERROR: " << e.what() << std::endl;
   }
+
   return 0;
 }
diff --git a/src/nrd-config.cpp b/src/nrd-config.cpp
new file mode 100644
index 0000000..c6181f9
--- /dev/null
+++ b/src/nrd-config.cpp
@@ -0,0 +1,116 @@
+#include "nrd-config.hpp"
+
+namespace ndn {
+namespace nrd {
+
+NrdConfig::NrdConfig()
+  : m_isSecuritySectionDefined(false)
+{
+}
+
+void
+NrdConfig::load(const std::string& filename)
+{
+  std::ifstream inputFile;
+  inputFile.open(filename.c_str());
+  if (!inputFile.good() || !inputFile.is_open())
+  {
+    std::string msg = "Failed to read configuration file: ";
+    msg += filename;
+    std::cerr << filename << std::endl;
+    throw Error(msg);
+  }
+  load(inputFile, filename);
+  inputFile.close();
+}
+
+void
+NrdConfig::load(const std::string& input, const std::string& filename)
+{
+  std::istringstream inputStream(input);
+  load(inputStream, filename);
+}
+
+void
+NrdConfig::load(std::istream& input, const std::string& filename)
+{
+  BOOST_ASSERT(!filename.empty());
+
+  ConfigSection ptree;
+  try
+  {
+    boost::property_tree::read_info(input, ptree);
+  }
+  catch (const boost::property_tree::info_parser_error& error)
+  {
+    std::stringstream msg;
+    msg << "Failed to parse configuration file";
+    msg << " " << filename;
+    msg << " " << error.message() << " line " << error.line();
+    throw Error(msg.str());
+  }
+  process(ptree, filename);
+}
+
+void
+NrdConfig::process(const ConfigSection& configSection,
+                   const std::string& filename)
+{
+  BOOST_ASSERT(!filename.empty());
+
+  if (configSection.begin() == configSection.end())
+    {
+      std::string msg = "Error processing configuration file";
+      msg += ": ";
+      msg += filename;
+      msg += " no data";
+      throw Error(msg);
+    }
+
+  for (ConfigSection::const_iterator i = configSection.begin();
+       i != configSection.end(); ++i)
+    {
+      const std::string& sectionName = i->first;
+      const ConfigSection& section = i->second;
+
+      if (boost::iequals(sectionName, "security"))
+        {
+          onSectionSecurity(section, filename);
+        }
+      //Add more sections here as needed
+      //else if (boost::iequals(sectionName, "another-section"))
+        //{
+          //onSectionAnotherSection(section, filename);
+        //}
+      else
+        {
+          std::string msg = "Error processing configuration file";
+          msg += " ";
+          msg += filename;
+          msg += " unrecognized section: " + sectionName;
+          throw Error(msg);
+        }
+    }
+}
+
+void
+NrdConfig::onSectionSecurity(const ConfigSection& section,
+                             const std::string& filename)
+{
+  if (!m_isSecuritySectionDefined) {
+    //setSecturitySection(section);
+    m_securitySection = section;
+    m_filename = filename;
+    m_isSecuritySectionDefined = true;
+  }
+  else {
+    std::string msg = "Error processing configuration file";
+    msg += " ";
+    msg += filename;
+    msg += " security section can appear only once";
+    throw Error(msg);
+  }
+}
+
+} //namespace nrd
+} //namespace ndn
diff --git a/src/nrd-config.hpp b/src/nrd-config.hpp
new file mode 100644
index 0000000..ee119f6
--- /dev/null
+++ b/src/nrd-config.hpp
@@ -0,0 +1,61 @@
+#ifndef NRD_CONFIG_HPP
+#define NRD_CONFIG_HPP
+
+#include "common.hpp"
+
+namespace ndn {
+namespace nrd {
+
+typedef boost::property_tree::ptree ConfigSection;
+
+class NrdConfig
+{
+
+public:
+  NrdConfig();
+
+  virtual
+  ~NrdConfig()
+  {
+  }
+
+  void
+  load(const std::string& filename);
+
+  void
+  load(const std::string& input, const std::string& filename);
+
+  void
+  load(std::istream& input, const std::string& filename);
+
+  const ConfigSection&
+  getSecuritySection() const
+  {
+    return m_securitySection;
+  }
+
+private:
+  void
+  process(const ConfigSection& configSection,
+          const std::string& filename);
+
+  void
+  onSectionSecurity(const ConfigSection& section,
+                    const std::string& filename);
+
+  const void
+  setSecturitySection(const ConfigSection& securitySection)
+  {
+    m_securitySection = securitySection;
+  }
+
+private:
+  bool m_isSecuritySectionDefined;
+  ConfigSection m_securitySection;
+  std::string m_filename;
+};
+
+}//namespace nrd
+}//namespace ndn
+
+#endif // NRD_CONFIG_HPP
diff --git a/src/nrd.cpp b/src/nrd.cpp
index 09db6b2..492f9c2 100644
--- a/src/nrd.cpp
+++ b/src/nrd.cpp
@@ -34,7 +34,8 @@
                      ),
   };
 
-Nrd::Nrd(const std::string& validatorConfig)
+Nrd::Nrd(const ndn::nrd::ConfigSection& securitySection,
+         const std::string& validatorConfig)
   : m_face(new Face())
   , m_nfdController(new nfd::Controller(*m_face))
   , m_validator(m_face)
@@ -45,7 +46,8 @@
   //check whether the components of localhop and localhost prefixes are same
   BOOST_ASSERT(COMMAND_PREFIX.size() == REMOTE_COMMAND_PREFIX.size());
 
-  m_validator.load(validatorConfig);
+  //m_validator.load(validatorConfig);
+  m_validator.load(securitySection, validatorConfig);
 
   std::cerr << "Setting interest filter on: " << COMMAND_PREFIX.toUri() << std::endl;
   m_face->setController(m_nfdController);
@@ -70,7 +72,6 @@
   m_face->shutdown();
 }
 
-
 void
 Nrd::sendResponse(const Name& name,
                   const nfd::ControlResponse& response)
@@ -230,7 +231,6 @@
   sendResponse(request.getName(), response);
 }
 
-
 void
 Nrd::insertEntry(const Interest& request, const PrefixRegOptions& options)
 {
@@ -248,7 +248,6 @@
     bind(&Nrd::onCommandError, this, _1, _2, request, options));
 }
 
-
 void
 Nrd::deleteEntry(const Interest& request, const PrefixRegOptions& options)
 {
@@ -260,7 +259,6 @@
     bind(&Nrd::onCommandError, this, _1, _2, request, options));
 }
 
-
 void
 Nrd::listen()
 {
@@ -268,14 +266,12 @@
   m_face->processEvents();
 }
 
-
 void
 Nrd::onControlHeaderSuccess()
 {
   std::cout << "Local control header enabled" << std::endl;
 }
 
-
 void
 Nrd::onControlHeaderError(uint32_t code, const std::string& reason)
 {
@@ -298,9 +294,10 @@
 Nrd::onNotification(const nfd::FaceEventNotification& notification)
 {
   /// \todo A notification can be missed, in this case check Facelist
-  if (notification.getKind() == 2) { //face destroyed
-      m_managedRib.erase(notification.getFaceId());
-    }
+  std::cerr << "Notification Rcvd: " << notification << std::endl;
+  if (notification.getKind() == ndn::nfd::FACE_EVENT_DESTROYED) { //face destroyed
+    m_managedRib.erase(notification.getFaceId());
+  }
 }
 
 } // namespace nrd
diff --git a/src/nrd.hpp b/src/nrd.hpp
index b441de8..d6007d7 100644
--- a/src/nrd.hpp
+++ b/src/nrd.hpp
@@ -9,15 +9,16 @@
 
 #include "rib.hpp"
 #include "face-monitor.hpp"
+#include "nrd-config.hpp"
 
 namespace ndn {
 namespace nrd {
 
-class Nrd
+class Nrd : noncopyable
 {
 public:
-  explicit
-  Nrd(const std::string& validatorConfig);
+  Nrd(const ndn::nrd::ConfigSection& securitySection,
+      const std::string& validatorConfig);
 
   void
   onRibRequest(const Interest& request);
diff --git a/validator.conf.sample b/validator.conf.sample
deleted file mode 100644
index 224d3d8..0000000
--- a/validator.conf.sample
+++ /dev/null
@@ -1,40 +0,0 @@
-rule
-{
-  id "NRD Prefix Registration Command Rule"
-  for interest
-  filter
-  {
-    type name
-    regex ^<localhost><nrd>[<register><unregister>]<>{3}$
-  }
-  checker
-  {
-    type customized
-    sig-type rsa-sha256
-    key-locator
-    {
-      type name
-      regex ^[^<KEY>]*<KEY><>*<ksk-.*><ID-CERT>$
-    }
-  }
-}
-rule
-{
-  id "Testbed Hierarchy Rule"
-  for data
-  filter
-  {
-    type name
-    regex ^[^<KEY>]*<KEY><>*<ksk-.*><ID-CERT><>$
-  }
-  checker
-  {
-    type hierarchical
-    sig-type rsa-sha256
-  }
-}
-trust-anchor
-{
-  type file
-  file-name "trust-anchor.cert"
-}
\ No newline at end of file
diff --git a/wscript b/wscript
index 89c8f40..f25aebf 100644
--- a/wscript
+++ b/wscript
@@ -39,6 +39,7 @@
     # except:
     #     pass
 
+    conf.define('DEFAULT_CONFIG_FILE', '%s/ndn/nrd.conf' % conf.env['SYSCONFDIR'])
     conf.write_config_header('src/config.hpp')
 
 def build (bld):
@@ -65,4 +66,7 @@
             use='nrd-objects',
             includes=['.', 'src'],
             install_prefix=None,
-          )
+            )
+
+    bld.install_files('${SYSCONFDIR}/ndn', 'nrd.conf.sample')
+