util, transport: add configuration file support and make default unix socket configurable
add sample library configuration file
refs: #1364
Change-Id: I3cb36d078aa3f0b0a50d9a83a521e95448df0a93
diff --git a/src/face.cpp b/src/face.cpp
index fa0f24d..2773f07 100644
--- a/src/face.cpp
+++ b/src/face.cpp
@@ -13,6 +13,7 @@
#include "util/time.hpp"
#include "util/random.hpp"
+#include "util/config-file.hpp"
#include <cstdlib>
#include "management/ndnd-controller.hpp"
@@ -23,13 +24,15 @@
Face::Face()
{
- construct(shared_ptr<Transport>(new UnixTransport()),
+ const std::string socketName = UnixTransport::getDefaultSocketName(m_config);
+ construct(shared_ptr<Transport>(new UnixTransport(socketName)),
make_shared<boost::asio::io_service>());
}
Face::Face(const shared_ptr<boost::asio::io_service>& ioService)
{
- construct(shared_ptr<Transport>(new UnixTransport()),
+ const std::string socketName = UnixTransport::getDefaultSocketName(m_config);
+ construct(shared_ptr<Transport>(new UnixTransport(socketName)),
ioService);
}
@@ -68,18 +71,39 @@
m_pitTimeoutCheckTimer = make_shared<monotonic_deadline_timer>(boost::ref(*m_ioService));
m_processEventsTimeoutTimer = make_shared<monotonic_deadline_timer>(boost::ref(*m_ioService));
- if (std::getenv("NFD") != 0)
+ std::string protocol = "nrd-0.1";
+
+ try
{
- if (std::getenv("NRD") != 0)
- m_fwController = make_shared<nrd::Controller>(boost::ref(*this));
- else
- m_fwController = make_shared<nfd::Controller>(boost::ref(*this));
+ protocol = m_config.getParsedConfiguration().get<std::string>("protocol");
+ }
+ catch (const boost::property_tree::ptree_bad_path& error)
+ {
+ // protocol not specified
+ }
+ catch (const boost::property_tree::ptree_bad_data& error)
+ {
+ throw ConfigFile::Error(error.what());
+ }
+
+ if (isSupportedNrdProtocol(protocol))
+ {
+ m_fwController = make_shared<nrd::Controller>(boost::ref(*this));
+ }
+ if (isSupportedNfdProtocol(protocol))
+ {
+ m_fwController = make_shared<nfd::Controller>(boost::ref(*this));
+ }
+ else if (isSupportedNdndProtocol(protocol))
+ {
+ m_fwController = make_shared<ndnd::Controller>(boost::ref(*this));
}
else
- m_fwController = make_shared<ndnd::Controller>(boost::ref(*this));
+ {
+ throw Face::Error("Cannot create controller for unsupported protocol \"" + protocol + "\"");
+ }
}
-
const PendingInterestId*
Face::expressInterest(const Interest& interest, const OnData& onData, const OnTimeout& onTimeout)
{
diff --git a/src/face.hpp b/src/face.hpp
index be1ca06..d168e73 100644
--- a/src/face.hpp
+++ b/src/face.hpp
@@ -58,12 +58,16 @@
/**
* @brief Create a new Face for communication with an NDN Forwarder using the default UnixTransport.
+ * @throws ConfigFile::Error on configuration file parse failure
+ * @throws Face::Error on unsupported protocol
*/
Face();
/**
* @brief Create a new Face for communication with an NDN Forwarder using the default UnixTransport.
* @param ioService A shared pointer to boost::io_service object that should control all IO operations
+ * @throws ConfigFile::Error on configuration file parse failure
+ * @throws Face::Error on unsupported protocol
*/
explicit
Face(const shared_ptr<boost::asio::io_service>& ioService);
@@ -72,6 +76,7 @@
* Create a new Face for communication with an NDN hub at host:port using the default TcpTransport.
* @param host The host of the NDN hub.
* @param port The port or service name of the NDN hub. If omitted. use 6363.
+ * @throws Face::Error on unsupported protocol
*/
Face(const std::string& host, const std::string& port = "6363");
@@ -79,6 +84,7 @@
* Create a new Face for communication with an NDN hub with the given Transport object and connectionInfo.
* @param transport A shared_ptr to a Transport object used for communication.
* @param transport A shared_ptr to a Transport::ConnectionInfo to be used to connect to the transport.
+ * @throws Face::Error on unsupported protocol
*/
explicit
Face(const shared_ptr<Transport>& transport);
@@ -94,6 +100,7 @@
* // Now the following ensures that events on both faces are processed
* face1.processEvents();
* </code>
+ * @throws Face::Error on unsupported protocol
*/
Face(const shared_ptr<Transport>& transport,
const shared_ptr<boost::asio::io_service>& ioService);
@@ -203,9 +210,22 @@
ioService() { return m_ioService; }
private:
+
+ /**
+ * @throws Face::Error on unsupported protocol
+ */
void
construct(const shared_ptr<Transport>& transport,
const shared_ptr<boost::asio::io_service>& ioService);
+
+ bool
+ isSupportedNfdProtocol(const std::string& protocol);
+
+ bool
+ isSupportedNrdProtocol(const std::string& protocol);
+
+ bool
+ isSupportedNdndProtocol(const std::string& protocol);
struct ProcessEventsTimeout {};
typedef std::list<shared_ptr<PendingInterest> > PendingInterestTable;
@@ -255,8 +275,28 @@
RegisteredPrefixTable m_registeredPrefixTable;
shared_ptr<Controller> m_fwController;
+
+ ConfigFile m_config;
};
+inline bool
+Face::isSupportedNfdProtocol(const std::string& protocol)
+{
+ return protocol == "nfd-0.1";
+}
+
+inline bool
+Face::isSupportedNrdProtocol(const std::string& protocol)
+{
+ return protocol == "nrd-0.1";
+}
+
+inline bool
+Face::isSupportedNdndProtocol(const std::string& protocol)
+{
+ return protocol == "ndnd-tlv-0.7";
+}
+
} // namespace ndn
#endif // NDN_FACE_HPP
diff --git a/src/transport/transport.hpp b/src/transport/transport.hpp
index da06da4..e678491 100644
--- a/src/transport/transport.hpp
+++ b/src/transport/transport.hpp
@@ -14,7 +14,12 @@
class Transport {
public:
- struct Error : public std::runtime_error { inline Error(const boost::system::error_code &code, const std::string &msg); };
+ class Error : public std::runtime_error
+ {
+ public:
+ inline Error(const boost::system::error_code &code, const std::string &msg);
+ inline Error(const std::string& msg);
+ };
typedef ptr_lib::function<void (const Block &wire)> ReceiveCallback;
typedef ptr_lib::function<void ()> ErrorCallback;
@@ -89,12 +94,19 @@
{
}
-inline Transport::Error::Error(const boost::system::error_code& code, const std::string& msg)
+inline
+Transport::Error::Error(const boost::system::error_code& code, const std::string& msg)
: std::runtime_error(msg + (code.value() ? " (" + code.category().message(code.value()) + ")" : ""))
{
}
inline
+Transport::Error::Error(const std::string& msg)
+ : std::runtime_error(msg)
+{
+}
+
+inline
Transport::~Transport()
{
}
diff --git a/src/transport/unix-transport.cpp b/src/transport/unix-transport.cpp
index 33fad1c..ad6db2c 100644
--- a/src/transport/unix-transport.cpp
+++ b/src/transport/unix-transport.cpp
@@ -13,14 +13,6 @@
namespace ndn {
-UnixTransport::UnixTransport()
-{
- if (std::getenv("NFD") != 0)
- m_unixSocket = "/var/run/nfd.sock";
- else
- m_unixSocket = "/tmp/.ndnd.sock";
-}
-
UnixTransport::UnixTransport(const std::string& unixSocket)
: m_unixSocket(unixSocket)
{
@@ -30,6 +22,48 @@
{
}
+std::string
+UnixTransport::getDefaultSocketName(const ConfigFile& config)
+{
+ const ConfigFile::Parsed& parsed = config.getParsedConfiguration();
+ try
+ {
+ return parsed.get<std::string>("unix_socket");
+ }
+ catch (const boost::property_tree::ptree_bad_path& error)
+ {
+ // unix_socket not present, continue
+ }
+ catch (const boost::property_tree::ptree_bad_data& error)
+ {
+ throw ConfigFile::Error(error.what());
+ }
+
+ // no unix_socket specified so the default socket name
+ // depends on the protocol we're using
+ try
+ {
+ const std::string protocol = parsed.get<std::string>("protocol");
+ if (protocol == "ndnd-tlv-0.7")
+ {
+ return "/tmp/.ndnd.sock";
+ }
+ }
+ catch (boost::property_tree::ptree_bad_path& error)
+ {
+ return "/var/run/nfd.sock";
+ }
+ catch (boost::property_tree::ptree_bad_data& error)
+ {
+ throw ConfigFile::Error(error.what());
+ }
+
+ // A we made here, then there's no unix_socket specified in the configuration
+ // file. A protocol is present, but it's not ndnd.
+ // Assume the default nfd.sock location.
+ return "/var/run/nfd.sock";
+}
+
void
UnixTransport::connect(boost::asio::io_service& ioService,
const ReceiveCallback& receiveCallback)
diff --git a/src/transport/unix-transport.hpp b/src/transport/unix-transport.hpp
index f8114f5..47636fa 100644
--- a/src/transport/unix-transport.hpp
+++ b/src/transport/unix-transport.hpp
@@ -9,6 +9,7 @@
#include "../common.hpp"
#include "transport.hpp"
+#include "../util/config-file.hpp"
// forward declaration
namespace boost { namespace asio { namespace local { class stream_protocol; } } }
@@ -22,9 +23,15 @@
class UnixTransport : public Transport
{
public:
- UnixTransport();
+ /**
+ * Create Unix transport based on the socket specified
+ * in a well-known configuration file or fallback to /var/run/nfd.sock
+ *
+ * @throws Throws UnixTransport::Error on failure to parse a discovered configuration file
+ */
UnixTransport(const std::string& unixSocket);
+
~UnixTransport();
// from Transport
@@ -46,7 +53,16 @@
virtual void
send(const Block& header, const Block& payload);
-
+
+ /**
+ * Determine the default NFD unix socket
+ *
+ * @returns unix_socket value if present in config, else /var/run/nfd.sock
+ * @throws ConfigFile::Error if fail to parse value of a present "unix_socket" field
+ */
+ static std::string
+ getDefaultSocketName(const ConfigFile& config);
+
private:
std::string m_unixSocket;
diff --git a/src/util/config-file.cpp b/src/util/config-file.cpp
new file mode 100644
index 0000000..031df25
--- /dev/null
+++ b/src/util/config-file.cpp
@@ -0,0 +1,123 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+
+#include "config-file.hpp"
+
+#include <boost/property_tree/ini_parser.hpp>
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+
+ConfigFile::ConfigFile()
+ : m_path(findConfigFile())
+{
+ if (open())
+ {
+ parse();
+ close();
+ }
+}
+
+ConfigFile::~ConfigFile()
+{
+ if (m_input.is_open())
+ {
+ m_input.close();
+ }
+}
+
+boost::filesystem::path
+ConfigFile::findConfigFile()
+{
+ using namespace boost::filesystem;
+
+ path home(std::getenv("HOME"));
+ if (!home.empty())
+ {
+ home /= ".ndn/client.conf";
+ if (exists(home))
+ {
+ return absolute(home);
+ }
+ }
+
+#ifdef NDN_CPP_SYSCONFDIR
+ path sysconfdir(NDN_CPP_SYSCONFDIR);
+ sysconfdir /= "ndn/client.conf";
+
+ if (exists(sysconfdir))
+ {
+ return absolute(sysconfdir);
+ }
+#endif // NDN_CPP_SYSCONFDIR
+
+ path etc("/etc/ndn/client.conf");
+ if (exists(etc))
+ {
+ return absolute(etc);
+ }
+
+ return path();
+}
+
+
+
+bool
+ConfigFile::open()
+{
+ if (m_path.empty())
+ {
+ return false;
+ }
+
+ m_input.open(m_path.c_str());
+ if (!m_input.good() || !m_input.is_open())
+ {
+ return false;
+ }
+ return true;
+}
+
+void
+ConfigFile::close()
+{
+ if (m_input.is_open())
+ {
+ m_input.close();
+ }
+}
+
+
+const ConfigFile::Parsed&
+ConfigFile::parse()
+{
+ if (m_path.empty())
+ {
+ throw Error("Failed to locate configuration file for parsing");
+ }
+ else if (!m_input.is_open() && !open())
+ {
+ throw Error("Failed to open configuration file for parsing");
+ }
+
+ try
+ {
+ boost::property_tree::read_ini(m_input, m_config);
+ }
+ catch (const boost::property_tree::ini_parser_error& error)
+ {
+ std::stringstream msg;
+ msg << "Failed to parse configuration file";
+ msg << " " << m_path;
+ msg << " " << error.message() << " line " << error.line();
+ throw Error(msg.str());
+ }
+ return m_config;
+}
+
+}
+
diff --git a/src/util/config-file.hpp b/src/util/config-file.hpp
new file mode 100644
index 0000000..cecceba
--- /dev/null
+++ b/src/util/config-file.hpp
@@ -0,0 +1,103 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NDN_MANAGEMENT_CONFIG_FILE_HPP
+#define NDN_MANAGEMENT_CONFIG_FILE_HPP
+
+#include "../common.hpp"
+
+#include <boost/property_tree/ptree.hpp>
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+
+class ConfigFile : noncopyable
+{
+public:
+
+ class Error : public std::runtime_error
+ {
+ public:
+ Error(const std::string& what)
+ : std::runtime_error(what)
+ {
+
+ }
+ };
+
+ typedef boost::property_tree::ptree Parsed;
+
+ /**
+ * Locate, open, and parse a library configuration file.
+ *
+ * @throws ConfigFile::Error on parse error
+ */
+ ConfigFile();
+
+ ~ConfigFile();
+
+ const boost::filesystem::path&
+ getPath() const;
+
+ const Parsed&
+ getParsedConfiguration() const;
+
+private:
+
+ bool
+ open();
+
+ void
+ close();
+
+ /**
+ * Parse a previously discovered and opened configuration file.
+ * For convenience this method will attempt to open the file
+ * if it has previously been located, but open() has not been called.
+ *
+ * @throws ConfigFile::Error on parse error
+ * @throws ConfigFile::Error on failure to open previously un-open configuration file
+ * @throws ConfigFile::Error if no configuration file was previously located
+ */
+ const Parsed&
+ parse();
+
+ /**
+ * Looking for the configuration file in these well-known locations:
+ *
+ * 1. $HOME/.ndn/client.conf
+ * 2. @SYSCONFDIR@/ndn/client.conf
+ * 3. /etc/ndn/client.conf
+ *
+ * @return path to preferred configuration (according to above order) or empty path on failure
+ */
+
+ boost::filesystem::path
+ findConfigFile();
+
+private:
+ boost::filesystem::path m_path; // absolute path to active configuration file (if any)
+ std::ifstream m_input;
+ Parsed m_config;
+};
+
+inline const boost::filesystem::path&
+ConfigFile::getPath() const
+{
+ return m_path;
+}
+
+inline const ConfigFile::Parsed&
+ConfigFile::getParsedConfiguration() const
+{
+ return m_config;
+}
+
+} // namespace ndn
+
+
+#endif // NDN_MANAGEMENT_CONFIG_FILE_HPP
+