mgmt: add sample configuration file and install default configuration

refs: #1332

Change-Id: Ic50aac57382b9760aa3b1c22b7dd2a9fec589cf9
diff --git a/daemon/main.cpp b/daemon/main.cpp
index 9f7bb75..f80a4bd 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -13,11 +13,8 @@
 #include "mgmt/local-control-header-manager.hpp"
 #include "mgmt/strategy-choice-manager.hpp"
 #include "mgmt/config-file.hpp"
-#include "face/tcp-factory.hpp"
 
-#ifdef HAVE_UNIX_SOCKETS
-#include "face/unix-stream-factory.hpp"
-#endif
+#include <boost/filesystem.hpp>
 
 namespace nfd {
 
@@ -25,21 +22,7 @@
 
 struct ProgramOptions
 {
-  struct TcpOutgoing
-  {
-    TcpOutgoing(std::pair<std::string, std::string> endpoint)
-      : m_endpoint(endpoint)
-    {
-    }
-
-    std::pair<std::string, std::string> m_endpoint;
-    std::vector<Name> m_prefixes;
-  };
-
   bool m_showUsage;
-  std::pair<std::string, std::string> m_tcpListen;
-  std::vector<TcpOutgoing> m_tcpOutgoings;
-  std::string m_unixListen;
   std::string m_config;
 };
 
@@ -49,70 +32,33 @@
 static FaceManager* g_faceManager;
 static LocalControlHeaderManager* g_localControlHeaderManager;
 static StrategyChoiceManager* g_strategyChoiceManager;
-static TcpFactory* g_tcpFactory;
-static shared_ptr<TcpChannel> g_tcpChannel;
 static shared_ptr<InternalFace> g_internalFace;
 
-#ifdef HAVE_UNIX_SOCKETS
-static UnixStreamFactory* g_unixFactory;
-static shared_ptr<UnixStreamChannel> g_unixChannel;
-#endif
-
 
 void
 usage(char* programName)
 {
   printf(
     "%s --help\n\tshow this help and exit\n"
-    "%s [--tcp-listen \"0.0.0.0:6363\"] "
-#ifdef HAVE_UNIX_SOCKETS
-       "[--unix-listen \"/var/run/nfd.sock\"] "
-#endif
-       "[--tcp-connect \"192.0.2.1:6363\" "
-            "[--prefix </example>]] "
+    "%s "
        "[--config /path/to/nfd.conf]\n"
       "\trun forwarding daemon\n"
-      "\t--tcp-listen <ip:port>: listen on IP and port\n"
-#ifdef HAVE_UNIX_SOCKETS
-      "\t--unix-listen <unix-socket-path>: listen on Unix socket\n"
-#endif
-      "\t--tcp-connect <ip:port>: connect to IP and port (can occur multiple times)\n"
-      "\t--prefix <NDN name>: add this face as nexthop to FIB entry "
-        "(must appear after --tcp-connect, can occur multiple times)\n"
        "\t--config <configuration file>]: path to configuration file\n"
     "\n",
     programName, programName
   );
 }
 
-inline std::pair<std::string, std::string>
-parseIpPortPair(const std::string& s)
-{
-  size_t pos = s.rfind(":");
-  if (pos == std::string::npos) {
-    throw std::invalid_argument("ip:port");
-  }
-
-  return std::make_pair(s.substr(0, pos), s.substr(pos+1));
-}
-
 bool
 parseCommandLine(int argc, char** argv)
 {
   g_options.m_showUsage = false;
-  g_options.m_tcpListen = std::make_pair("0.0.0.0", "6363");
-  g_options.m_unixListen = "/var/run/nfd.sock";
-  g_options.m_tcpOutgoings.clear();
   g_options.m_config = DEFAULT_CONFIG_FILE;
 
   while (1) {
     int option_index = 0;
     static ::option long_options[] = {
       { "help"          , no_argument      , 0, 0 },
-      { "tcp-listen"    , required_argument, 0, 0 },
-      { "tcp-connect"   , required_argument, 0, 0 },
-      { "prefix"        , required_argument, 0, 0 },
-      { "unix-listen"   , required_argument, 0, 0 },
       { "config"        , required_argument, 0, 0 },
       { 0               , 0                , 0, 0 }
     };
@@ -125,22 +71,7 @@
           case 0://help
             g_options.m_showUsage = true;
             break;
-          case 1://tcp-listen
-            g_options.m_tcpListen = parseIpPortPair(::optarg);
-            break;
-          case 2://tcp-connect
-            g_options.m_tcpOutgoings.push_back(parseIpPortPair(::optarg));
-            break;
-          case 3://prefix
-            if (g_options.m_tcpOutgoings.empty()) {
-              return false;
-            }
-            g_options.m_tcpOutgoings.back().m_prefixes.push_back(Name(::optarg));
-            break;
-          case 4://unix-listen
-            g_options.m_unixListen = ::optarg;
-            break;
-          case 5://config
+          case 1://config
             g_options.m_config = ::optarg;
             break;
         }
@@ -152,68 +83,6 @@
 }
 
 void
-onFaceFail(shared_ptr<Face> face, const std::string& reason)
-{
-  g_forwarder->removeFace(face);
-}
-
-void
-onFaceEstablish(shared_ptr<Face> newFace, std::vector<Name>* prefixes)
-{
-  g_forwarder->addFace(newFace);
-  newFace->onFail += bind(&onFaceFail, newFace, _1);
-
-  // add nexthop on prefixes
-  if (prefixes != 0) {
-    Fib& fib = g_forwarder->getFib();
-    for (std::vector<Name>::iterator it = prefixes->begin();
-        it != prefixes->end(); ++it) {
-      std::pair<shared_ptr<fib::Entry>, bool> fibInsertResult =
-        fib.insert(*it);
-      shared_ptr<fib::Entry> fibEntry = fibInsertResult.first;
-      fibEntry->addNextHop(newFace, 0);
-    }
-  }
-}
-
-void
-onFaceError(const std::string& reason)
-{
-  throw std::runtime_error(reason);
-}
-
-void
-initializeTcp()
-{
-  g_tcpFactory = new TcpFactory();
-  g_tcpChannel = g_tcpFactory->createChannel(g_options.m_tcpListen.first,
-                                             g_options.m_tcpListen.second);
-  g_tcpChannel->listen(
-    bind(&onFaceEstablish, _1, static_cast<std::vector<Name>*>(0)),
-    &onFaceError);
-
-  for (std::vector<ProgramOptions::TcpOutgoing>::iterator it =
-      g_options.m_tcpOutgoings.begin();
-      it != g_options.m_tcpOutgoings.end(); ++it) {
-    g_tcpChannel->connect(it->m_endpoint.first, it->m_endpoint.second,
-      bind(&onFaceEstablish, _1, &(it->m_prefixes)), &onFaceError);
-  }
-}
-
-#ifdef HAVE_UNIX_SOCKETS
-void
-initializeUnix()
-{
-  g_unixFactory = new UnixStreamFactory();
-  g_unixChannel = g_unixFactory->createChannel(g_options.m_unixListen);
-
-  g_unixChannel->listen(
-    bind(&onFaceEstablish, _1, static_cast<std::vector<Name>*>(0)),
-    &onFaceError);
-}
-#endif
-
-void
 initializeMgmt()
 {
   ConfigFile config;
@@ -238,8 +107,8 @@
   g_strategyChoiceManager = new StrategyChoiceManager(g_forwarder->getStrategyChoice(),
                                                       g_internalFace);
 
-  /// \todo add parsing back when there is an official default config file
-  // config.parse(g_options.m_config);
+  config.parse(g_options.m_config, true);
+  config.parse(g_options.m_config, false);
 
   shared_ptr<fib::Entry> entry = g_forwarder->getFib().insert("/localhost/nfd").first;
   entry->addNextHop(g_internalFace, 0);
@@ -248,29 +117,36 @@
 int
 main(int argc, char** argv)
 {
-  bool isCommandLineValid = parseCommandLine(argc, argv);
-  if (!isCommandLineValid) {
-    usage(argv[0]);
-    return 1;
-  }
-  if (g_options.m_showUsage) {
-    usage(argv[0]);
-    return 0;
-  }
-
-  g_forwarder = new Forwarder();
-  initializeTcp();
-#ifdef HAVE_UNIX_SOCKETS
-  initializeUnix();
-#endif
-  initializeMgmt();
-
-  /// \todo Add signal processing to gracefully terminate the app
-
   try {
+    bool isCommandLineValid = parseCommandLine(argc, argv);
+    if (!isCommandLineValid) {
+      usage(argv[0]);
+      return 1;
+    }
+    if (g_options.m_showUsage) {
+      usage(argv[0]);
+      return 0;
+    }
+
+    g_forwarder = new Forwarder();
+    initializeMgmt();
+
+    /// \todo Add signal processing to gracefully terminate the app
+
     getGlobalIoService().run();
-  } catch(std::exception& ex) {
-    NFD_LOG_ERROR(ex.what());
+  // } catch(ConfigFile::Error& error) {
+  //   NFD_LOG_ERROR("Error: " << error.what());
+  //   NFD_LOG_ERROR("You should either specify --config option or copy sample configuration into "
+  //                 << DEFAULT_CONFIG_FILE);
+  } catch(boost::filesystem::filesystem_error& error) {
+    if (error.code() == boost::system::errc::permission_denied) {
+      NFD_LOG_ERROR("Error: Permissions denied for " << error.path1());
+      NFD_LOG_ERROR(argv[0] << " should be run as superuser");
+    } else {
+      NFD_LOG_ERROR("Error: " << error.what());
+    }
+  } catch(std::exception& exception) {
+    NFD_LOG_ERROR("Error: " << exception.what());
     return 1;
   }
 
diff --git a/daemon/mgmt/config-file.cpp b/daemon/mgmt/config-file.cpp
index 8018310..e5bbb9d 100644
--- a/daemon/mgmt/config-file.cpp
+++ b/daemon/mgmt/config-file.cpp
@@ -26,11 +26,11 @@
 }
 
 void
-ConfigFile::parse(const char* filename, bool isDryRun)
+ConfigFile::parse(const std::string& filename, bool isDryRun)
 {
   std::ifstream inputFile;
-  inputFile.open(filename);
-  if (!inputFile.is_open())
+  inputFile.open(filename.c_str());
+  if (!inputFile.good() || !inputFile.is_open())
     {
       std::string msg = "Failed to read configuration file: ";
       msg += filename;
@@ -41,7 +41,7 @@
 }
 
 void
-ConfigFile::parse(const std::string& input, bool isDryRun, const char* filename)
+ConfigFile::parse(const std::string& input, bool isDryRun, const std::string& filename)
 {
   std::istringstream inputStream(input);
   parse(inputStream, isDryRun, filename);
@@ -49,7 +49,7 @@
 
 
 void
-ConfigFile::parse(std::istream& input, bool isDryRun, const char* filename)
+ConfigFile::parse(std::istream& input, bool isDryRun, const std::string& filename)
 {
   try
     {
@@ -59,10 +59,7 @@
     {
       std::stringstream msg;
       msg << "Failed to parse configuration file";
-      if (filename != 0)
-        {
-          msg << " " << filename;
-        }
+      msg << " " << filename;
       msg << " " << error.message() << " line " << error.line();
       throw Error(msg.str());
     }
@@ -71,18 +68,16 @@
 }
 
 void
-ConfigFile::process(bool isDryRun, const char* filename)
+ConfigFile::process(bool isDryRun, const std::string& filename)
 {
+  BOOST_ASSERT(!filename.empty());
   // NFD_LOG_DEBUG("processing..." << ((isDryRun)?("dry run"):("")));
 
   if (m_global.begin() == m_global.end())
     {
       std::string msg = "Error processing configuration file";
-      if (filename != 0)
-        {
-          msg += ": ";
-          msg += filename;
-        }
+      msg += ": ";
+      msg += filename;
       msg += " no data";
       throw Error(msg);
     }
@@ -101,11 +96,8 @@
       else
         {
           std::string msg = "Error processing configuration file";
-          if (filename != 0)
-            {
-              msg += " ";
-              msg += filename;
-            }
+          msg += " ";
+          msg += filename;
           msg += " no module subscribed for section: " + sectionName;
           throw Error(msg);
         }
diff --git a/daemon/mgmt/config-file.hpp b/daemon/mgmt/config-file.hpp
index 2b72e7f..2a94fac 100644
--- a/daemon/mgmt/config-file.hpp
+++ b/daemon/mgmt/config-file.hpp
@@ -47,31 +47,31 @@
    * \throws ConfigFile::Error if parse error
    */
   void
-  parse(const char* filename, bool isDryRun=false);
+  parse(const std::string& filename, bool isDryRun);
 
   /**
    * \param input configuration (as a string) to parse
    * \param isDryRun true if performing a dry run of configuration, false otherwise
-   * \param filename optional convenience argument to provide more detailed error messages (if available)
+   * \param filename optional convenience argument to provide more detailed error messages
    * \throws ConfigFile::Error if file not found
    * \throws ConfigFile::Error if parse error
    */
   void
-  parse(const std::string& input, bool isDryRun=false, const char* filename=0);
+  parse(const std::string& input, bool isDryRun, const std::string& filename);
 
   /**
    * \param input stream to parse
    * \param isDryRun true if performing a dry run of configuration, false otherwise
-   * \param filename optional convenience argument to provide more detailed error messages (if available)
+   * \param filename optional convenience argument to provide more detailed error messages
    * \throws ConfigFile::Error if parse error
    */
   void
-  parse(std::istream& input, bool isDryRun=false, const char* filename=0);
+  parse(std::istream& input, bool isDryRun, const std::string& filename);
 
 private:
 
   void
-  process(bool isDryRun, const char* filename);
+  process(bool isDryRun, const std::string& filename);
 
 private:
 
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
new file mode 100644
index 0000000..c7710a1
--- /dev/null
+++ b/nfd.conf.sample.in
@@ -0,0 +1,66 @@
+; the general section contains settings of nfd process
+; general
+; {
+; }
+
+; the face_system section defines what faces and channels are created
+face_system
+{
+  ; the unix section contains settings of UNIX stream faces and channels
+  unix
+  {
+    listen yes ; set to 'no' to disable UNIX stream listener, default 'yes'
+    path /var/run/nfd.sock ; UNIX stream listener path
+  }
+
+  ; the tcp section contains settings of TCP faces and channels
+  tcp
+  {
+    listen yes ; set to 'no' to disable TCP listener, default 'yes'
+    port 6363 ; TCP listener port number
+  }
+
+  ; the udp section contains settings of UDP faces and channels
+  udp
+  {
+    port 6363 ; UDP unicast port number
+    idle_timeout 600 ; idle time (seconds) before closing a UDP unicast face
+    keep_alive_interval 25; interval (seconds) between keep-alive refreshes
+
+    mcast no
+
+    ; Example multicast settings
+    ; NFD creates one UDP multicast face per NIC
+    ; mcast yes ; set to 'no' to disable UDP multicast, default 'yes'
+    ; mcast_port 56363 ; UDP multicast port number
+    ; mcast_group 224.0.23.170 ; UDP multicast group (IPv4 only)
+  }
+
+  ; the ether section contains settings of Ethernet faces and channels
+  ether
+  {
+    mcast no
+    ; Example multicast settings
+    ; NFD creates one Ethernet multicast face per NIC
+    ; mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
+    ; mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
+  }
+}
+
+; the authorizations section grants privileges to authorized keys
+authorizations
+{
+  ; an authorize section grants privileges to a key
+  authorize
+  {
+    keyfile @SYSCONFDIR@/ndn/keys/default.pub ; public key file
+    privileges ; set of privileges granted to this public key
+    {
+      control-header
+      faces
+      fib
+      ; stats
+      strategy-choice
+    }
+  }
+}
diff --git a/tests/mgmt/command-validator.cpp b/tests/mgmt/command-validator.cpp
index 7a6d39d..4e53322 100644
--- a/tests/mgmt/command-validator.cpp
+++ b/tests/mgmt/command-validator.cpp
@@ -207,7 +207,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  config.parse(CONFIG, false);
+  config.parse(CONFIG, false, "dummy-config");
 
   validator.validate(*fibCommand,
                      bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
@@ -267,7 +267,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  config.parse(CONFIG, true);
+  config.parse(CONFIG, true, "dummy-config");
 
   validator.validate(*fibCommand,
                      bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
@@ -312,7 +312,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  BOOST_CHECK_THROW(config.parse(NO_AUTHORIZE_CONFIG, false), ConfigFile::Error);
+  BOOST_CHECK_THROW(config.parse(NO_AUTHORIZE_CONFIG, false, "dummy-config"), ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_CASE(NoPrivilegesSections)
@@ -331,7 +331,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  BOOST_CHECK_THROW(config.parse(NO_PRIVILEGES_CONFIG, false), ConfigFile::Error);
+  BOOST_CHECK_THROW(config.parse(NO_PRIVILEGES_CONFIG, false, "dummy-config"), ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_CASE(InvalidKeyFile)
@@ -355,7 +355,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  BOOST_CHECK_THROW(config.parse(INVALID_KEY_CONFIG, false), ConfigFile::Error);
+  BOOST_CHECK_THROW(config.parse(INVALID_KEY_CONFIG, false, "dummy-config"), ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_CASE(NoKeyFile)
@@ -379,7 +379,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  BOOST_CHECK_THROW(config.parse(NO_KEY_CONFIG, false), ConfigFile::Error);
+  BOOST_CHECK_THROW(config.parse(NO_KEY_CONFIG, false, "dummy-config"), ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_CASE(MalformedKey)
@@ -404,7 +404,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  BOOST_CHECK_THROW(config.parse(MALFORMED_KEY_CONFIG, false), ConfigFile::Error);
+  BOOST_CHECK_THROW(config.parse(MALFORMED_KEY_CONFIG, false, "dummy-config"), ConfigFile::Error);
 }
 
 bool
@@ -431,7 +431,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  BOOST_CHECK_EXCEPTION(config.parse(NO_AUTHORIZE_CONFIG, true),
+  BOOST_CHECK_EXCEPTION(config.parse(NO_AUTHORIZE_CONFIG, true, "dummy-config"),
                         ConfigFile::Error,
                         bind(&validateErrorMessage,
                              "No authorize sections found", _1));
@@ -472,7 +472,7 @@
                 << "No privileges section found for key file tests/mgmt/key2.pub "
                 << "(" << m_tester2.getPublicKeyName().toUri() << ")";
 
-  BOOST_CHECK_EXCEPTION(config.parse(NO_PRIVILEGES_CONFIG, true),
+  BOOST_CHECK_EXCEPTION(config.parse(NO_PRIVILEGES_CONFIG, true, "dummy-config"),
                         ConfigFile::Error,
                         bind(&validateErrorMessage, expectedError.str(), _1));
 }
@@ -506,7 +506,7 @@
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
 
-  BOOST_CHECK_EXCEPTION(config.parse(INVALID_KEY_CONFIG, true),
+  BOOST_CHECK_EXCEPTION(config.parse(INVALID_KEY_CONFIG, true, "dummy-config"),
                         ConfigFile::Error,
                         bind(&validateErrorMessage,
                              "Unable to open key file tests/mgmt/notakeyfile.pub\n"
@@ -537,7 +537,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  BOOST_CHECK_EXCEPTION(config.parse(NO_KEY_CONFIG, true),
+  BOOST_CHECK_EXCEPTION(config.parse(NO_KEY_CONFIG, true, "dummy-config"),
                         ConfigFile::Error,
                         bind(&validateErrorMessage,
                              "No keyfile specified\n"
@@ -570,7 +570,7 @@
 
   config.addSectionHandler("authorizations",
                            bind(&CommandValidator::onConfig, boost::ref(validator), _1, _2));
-  BOOST_CHECK_EXCEPTION(config.parse(MALFORMED_KEY_CONFIG, true),
+  BOOST_CHECK_EXCEPTION(config.parse(MALFORMED_KEY_CONFIG, true, "dummy-config"),
                         ConfigFile::Error,
                         bind(&validateErrorMessage,
                              "Malformed key file tests/mgmt/malformedkey.pub\n"
diff --git a/tests/mgmt/config-file.cpp b/tests/mgmt/config-file.cpp
index ca89aec..830ebbb 100644
--- a/tests/mgmt/config-file.cpp
+++ b/tests/mgmt/config-file.cpp
@@ -202,8 +202,7 @@
   input.open("tests/mgmt/config_example.info");
   BOOST_REQUIRE(input.is_open());
 
-  file.parse(input);
-
+  file.parse(input, false, "config_example.info");
   BOOST_CHECK(sub.allCallbacksFired());
 }
 
@@ -214,7 +213,7 @@
 
   std::ifstream input;
 
-  BOOST_CHECK_THROW(file.parse(input), ConfigFile::Error);
+  BOOST_CHECK_THROW(file.parse(input, false, "unknown"), ConfigFile::Error);
   BOOST_CHECK(sub.noCallbacksFired());
 }
 
@@ -224,7 +223,7 @@
   ConfigFile file;
   DummyAllSubscriber sub(file);
 
-  file.parse(CONFIG);
+  file.parse(CONFIG, false, "dummy-config");
 
   BOOST_CHECK(sub.allCallbacksFired());
 }
@@ -234,7 +233,7 @@
   ConfigFile file;
   DummyAllSubscriber sub(file);
 
-  BOOST_CHECK_THROW(file.parse(std::string()), ConfigFile::Error);
+  BOOST_CHECK_THROW(file.parse(std::string(), false, "dummy-config"), ConfigFile::Error);
   BOOST_CHECK(sub.noCallbacksFired());
 }
 
@@ -243,7 +242,7 @@
   ConfigFile file;
   DummyAllSubscriber sub(file);
 
-  BOOST_CHECK_THROW(file.parse(MALFORMED_CONFIG), ConfigFile::Error);
+  BOOST_CHECK_THROW(file.parse(MALFORMED_CONFIG, false, "dummy-config"), ConfigFile::Error);
   BOOST_CHECK(sub.noCallbacksFired());
 }
 
@@ -252,7 +251,7 @@
   ConfigFile file;
   DummyAllSubscriber sub(file, true);
 
-  file.parse(CONFIG, true);
+  file.parse(CONFIG, true, "dummy-config");
 
   BOOST_CHECK(sub.allCallbacksFired());
 }
@@ -262,7 +261,7 @@
   ConfigFile file;
   DummyAllSubscriber sub(file);
 
-  file.parse("tests/mgmt/config_example.info");
+  file.parse("tests/mgmt/config_example.info", false);
 
   BOOST_CHECK(sub.allCallbacksFired());
 }
@@ -272,7 +271,7 @@
   ConfigFile file;
   DummyAllSubscriber sub(file);
 
-  BOOST_CHECK_THROW(file.parse("i_made_this_up.info"), ConfigFile::Error);
+  BOOST_CHECK_THROW(file.parse("i_made_this_up.info", false), ConfigFile::Error);
 
   BOOST_CHECK(sub.noCallbacksFired());
 }
@@ -282,7 +281,7 @@
   ConfigFile file;
   DummyAllSubscriber sub(file);
 
-  BOOST_CHECK_THROW(file.parse("tests/mgmt/config_malformed.info"), ConfigFile::Error);
+  BOOST_CHECK_THROW(file.parse("tests/mgmt/config_malformed.info", false), ConfigFile::Error);
 
   BOOST_CHECK(sub.noCallbacksFired());
 }
@@ -296,7 +295,7 @@
   input.open("tests/mgmt/config_example.info");
   BOOST_REQUIRE(input.is_open());
 
-  file.parse(input, true);
+  file.parse(input, true, "tests/mgmt/config_example.info");
 
   BOOST_CHECK(sub.allCallbacksFired());
 
@@ -318,7 +317,7 @@
   DummyAllSubscriber sub1(file);
   DummyAllSubscriber sub2(file);
 
-  file.parse(CONFIG);
+  file.parse(CONFIG, false, "dummy-config");
 
   BOOST_CHECK(sub1.noCallbacksFired());
   BOOST_CHECK(sub2.allCallbacksFired());
@@ -328,7 +327,7 @@
 {
   ConfigFile file;
 
-  BOOST_CHECK_THROW(file.parse(CONFIG), ConfigFile::Error);
+  BOOST_CHECK_THROW(file.parse(CONFIG, false, "dummy-config"), ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_CASE(OnConfigCoveredByPartialSubscribers)
@@ -337,7 +336,7 @@
   DummyOneSubscriber subA(file, "a");
   DummyOneSubscriber subB(file, "b");
 
-  file.parse(CONFIG);
+  file.parse(CONFIG, false, "dummy-config");
 
   BOOST_CHECK(subA.allCallbacksFired());
   BOOST_CHECK(subB.allCallbacksFired());
diff --git a/tests/mgmt/face-manager.cpp b/tests/mgmt/face-manager.cpp
index e4e7954..191de04 100644
--- a/tests/mgmt/face-manager.cpp
+++ b/tests/mgmt/face-manager.cpp
@@ -260,7 +260,7 @@
   void
   parseConfig(const std::string configuration, bool isDryRun)
   {
-    m_config.parse(configuration, isDryRun);
+    m_config.parse(configuration, isDryRun, "dummy-config");
   }
 
   virtual
diff --git a/wscript b/wscript
index dbdff3e..ea5ef35 100644
--- a/wscript
+++ b/wscript
@@ -13,7 +13,7 @@
     nfdopt.add_option('--with-tests', action='store_true',default=False,dest='with_tests',help='''Build unit tests''')
     nfdopt.add_option('--with-ndn-cpp',action='store',type='string',default=None,dest='ndn_cpp_dir',
                       help='''Use NDN-CPP library from the specified path''')
-    
+
 def configure(conf):
     conf.load("compiler_cxx boost gnu_dirs")
     try:
@@ -40,7 +40,7 @@
     if not conf.options.ndn_cpp_dir:
         conf.check_cfg(package='libndn-cpp-dev', args=['--cflags', '--libs'], uselib_store='NDN_CPP', mandatory=True)
     else:
-        conf.check_cxx(lib='ndn-cpp-dev', uselib_store='NDN_CPP', 
+        conf.check_cxx(lib='ndn-cpp-dev', uselib_store='NDN_CPP',
                        cxxflags="-I%s/include" % conf.options.ndn_cpp_dir,
                        linkflags="-L%s/lib" % conf.options.ndn_cpp_dir,
                        mandatory=True)
@@ -52,7 +52,7 @@
         boost_libs+=' unit_test_framework'
 
     conf.check_boost(lib=boost_libs)
-        
+
     if conf.env.BOOST_VERSION_NUMBER < 104800:
         Logs.error ("Minimum required boost version is 1.48.0")
         Logs.error ("Please upgrade your distribution or install custom boost libraries" +
@@ -96,7 +96,7 @@
         use = 'nfd-objects',
         includes = [".", "daemon"],
         )
-        
+
     for app in bld.path.ant_glob('tools/*.cpp'):
         bld(features=['cxx', 'cxxprogram'],
             target = 'bin/%s' % (str(app.change_ext(''))),
@@ -123,6 +123,11 @@
         if bld.env['HAVE_UNIX_SOCKETS']:
             unit_tests.source += bld.path.ant_glob('tests/face/unix-*.cpp')
 
+    bld(features = "subst",
+        source = 'nfd.conf.sample.in',
+        target = 'nfd.conf.sample',
+        install_path = "${SYSCONFDIR}/ndn")
+
 @Configure.conf
 def add_supported_cxxflags(self, cxxflags):
     """