diff --git a/daemon/face/unix-stream-channel.cpp b/daemon/face/unix-stream-channel.cpp index 906d23e9..150fad40 100644 --- a/daemon/face/unix-stream-channel.cpp +++ b/daemon/face/unix-stream-channel.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2023, Regents of the University of California, + * Copyright (c) 2014-2024, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -49,11 +49,10 @@ UnixStreamChannel::UnixStreamChannel(const unix_stream::Endpoint& endpoint, UnixStreamChannel::~UnixStreamChannel() { if (isListening()) { - // use the non-throwing variants during destruction - // and ignore any errors + // use the non-throwing variants during destruction and ignore any errors boost::system::error_code error; m_acceptor.close(error); - NFD_LOG_CHAN_DEBUG("Removing socket file"); + NFD_LOG_CHAN_TRACE("Removing socket file"); boost::filesystem::remove(m_endpoint.path(), error); } } @@ -70,7 +69,7 @@ UnixStreamChannel::listen(const FaceCreatedCallback& onFaceCreated, namespace fs = boost::filesystem; - fs::path socketPath(m_endpoint.path()); + fs::path socketPath = m_endpoint.path(); fs::file_type type = fs::symlink_status(socketPath).type(); if (type == fs::socket_file) { @@ -80,7 +79,7 @@ UnixStreamChannel::listen(const FaceCreatedCallback& onFaceCreated, NFD_LOG_CHAN_TRACE("connect() on existing socket file returned: " << error.message()); if (!error) { // someone answered, leave the socket alone - NDN_THROW(Error("Socket file at " + m_endpoint.path() + " belongs to another NFD process")); + NDN_THROW(Error("Socket file at " + m_endpoint.path() + " belongs to another process")); } else if (error == boost::asio::error::connection_refused || error == boost::asio::error::timed_out) { @@ -94,6 +93,12 @@ UnixStreamChannel::listen(const FaceCreatedCallback& onFaceCreated, NDN_THROW(Error(m_endpoint.path() + " already exists and is not a socket file")); } + // ensure parent directory exists before creating socket + fs::path parent = socketPath.parent_path(); + if (!parent.empty() && fs::create_directories(parent)) { + NFD_LOG_CHAN_TRACE("Created directory " << parent); + } + m_acceptor.open(); m_acceptor.bind(m_endpoint); m_acceptor.listen(backlog); diff --git a/daemon/face/unix-stream-factory.cpp b/daemon/face/unix-stream-factory.cpp index a275fde3..27719eb4 100644 --- a/daemon/face/unix-stream-factory.cpp +++ b/daemon/face/unix-stream-factory.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2022, Regents of the University of California, + * Copyright (c) 2014-2024, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -45,8 +45,8 @@ UnixStreamFactory::doProcessConfig(OptionalConfigSection configSection, { // unix // { - // path /run/nfd.sock ; on Linux - // path /var/run/nfd.sock ; on other platforms + // path /run/nfd/nfd.sock ; on Linux + // path /var/run/nfd/nfd.sock ; on other platforms // } m_wantCongestionMarking = context.generalConfig.wantCongestionMarking; @@ -59,15 +59,12 @@ UnixStreamFactory::doProcessConfig(OptionalConfigSection configSection, } #ifdef __linux__ - std::string path = "/run/nfd.sock"; + std::string path = "/run/nfd/nfd.sock"; #else - std::string path = "/var/run/nfd.sock"; + std::string path = "/var/run/nfd/nfd.sock"; #endif // __linux__ - for (const auto& pair : *configSection) { - const std::string& key = pair.first; - const ConfigSection& value = pair.second; - + for (const auto& [key, value] : *configSection) { if (key == "path") { path = value.get_value(); } @@ -87,11 +84,10 @@ UnixStreamFactory::doProcessConfig(OptionalConfigSection configSection, } shared_ptr -UnixStreamFactory::createChannel(const std::string& unixSocketPath) +UnixStreamFactory::createChannel(const std::string& socketPath) { - boost::filesystem::path p(unixSocketPath); - p = boost::filesystem::canonical(p.parent_path()) / p.filename(); - unix_stream::Endpoint endpoint(p.string()); + auto normalizedPath = boost::filesystem::weakly_canonical(boost::filesystem::absolute(socketPath)); + unix_stream::Endpoint endpoint(normalizedPath.string()); auto it = m_channels.find(endpoint); if (it != m_channels.end()) diff --git a/daemon/face/unix-stream-factory.hpp b/daemon/face/unix-stream-factory.hpp index 76bbfc9c..51bc51d2 100644 --- a/daemon/face/unix-stream-factory.hpp +++ b/daemon/face/unix-stream-factory.hpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2023, Regents of the University of California, + * Copyright (c) 2014-2024, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -53,7 +53,7 @@ class UnixStreamFactory final : public ProtocolFactory * an exception will be thrown if the channel cannot be created. */ shared_ptr - createChannel(const std::string& unixSocketPath); + createChannel(const std::string& socketPath); private: void diff --git a/daemon/rib/service.cpp b/daemon/rib/service.cpp index 1634ef9e..814feb07 100644 --- a/daemon/rib/service.cpp +++ b/daemon/rib/service.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2022, Regents of the University of California, + * Copyright (c) 2014-2024, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -68,9 +68,9 @@ makeLocalNfdTransport(const ConfigSection& config) if (config.get_child_optional("face_system.unix")) { // default socket path should be the same as in UnixStreamFactory::processConfig #ifdef __linux__ - auto path = config.get("face_system.unix.path", "/run/nfd.sock"); + auto path = config.get("face_system.unix.path", "/run/nfd/nfd.sock"); #else - auto path = config.get("face_system.unix.path", "/var/run/nfd.sock"); + auto path = config.get("face_system.unix.path", "/var/run/nfd/nfd.sock"); #endif // __linux__ return make_shared(path); } diff --git a/docs/conf.py b/docs/conf.py index 4ab82aaa..83b4c527 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,7 +10,7 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'Named Data Networking Forwarding Daemon (NFD)' -copyright = 'Copyright © 2014-2023 Named Data Networking Project.' +copyright = 'Copyright © 2014-2024 Named Data Networking Project.' author = 'Named Data Networking Project' # The short X.Y version. diff --git a/docs/manpages/nfdc-face.rst b/docs/manpages/nfdc-face.rst index c09191b3..410b814c 100644 --- a/docs/manpages/nfdc-face.rst +++ b/docs/manpages/nfdc-face.rst @@ -66,7 +66,7 @@ OPTIONS - tcp4://192.0.2.1:6363 - tcp6://[2001:db8::1]:6363 - tcp://example.net - - unix:///var/run/nfd.sock + - unix:///run/nfd/nfd.sock - fd://6 - ether://[08:00:27:01:01:01] - dev://eth0 diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in index 660beb2d..bf7d9ea8 100644 --- a/nfd.conf.sample.in +++ b/nfd.conf.sample.in @@ -101,10 +101,9 @@ face_system ; Unix stream faces and channels. unix { - ; The default transport is unix:///run/nfd.sock (on Linux) or unix:///var/run/nfd.sock (on - ; other platforms). This should match the "transport" field in client.conf for ndn-cxx. If you - ; wish to use TCP instead of Unix sockets with ndn-cxx, change "transport" to an appropriate - ; TCP FaceUri. + ; The default transport is 'unix:///run/nfd/nfd.sock' (on Linux) or 'unix:///var/run/nfd/nfd.sock' (on + ; other platforms). This should match the 'transport' field in client.conf for ndn-cxx. If you wish + ; to use TCP instead of Unix sockets with ndn-cxx, change 'transport' to an appropriate TCP FaceUri. path @UNIX_SOCKET_PATH@ ; Unix stream listener path } @@ -303,12 +302,12 @@ authorizations ; sudo mkdir -p @SYSCONFDIR@/ndn/keys ; sudo mv default.ndncert @SYSCONFDIR@/ndn/keys/default.ndncert ; - ; The "certfile" field below specifies the default key directory for + ; The 'certfile' field below specifies the default key directory for ; your machine. You may move your newly created key to the location it ; specifies or path. ; certfile keys/default.ndncert ; NDN identity certificate file - certfile any ; "any" authorizes command interests signed under any certificate, + certfile any ; 'any' authorizes command interests signed under any certificate, ; i.e., no actual validation. privileges ; set of privileges granted to this identity { diff --git a/tests/daemon/face/unix-stream-channel.t.cpp b/tests/daemon/face/unix-stream-channel.t.cpp index e4ce0cfa..d5836251 100644 --- a/tests/daemon/face/unix-stream-channel.t.cpp +++ b/tests/daemon/face/unix-stream-channel.t.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2022, Regents of the University of California, + * Copyright (c) 2014-2024, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -41,7 +41,8 @@ class UnixStreamChannelFixture : public ChannelFixture @@ -68,11 +69,14 @@ class UnixStreamChannelFixture : public ChannelFixturelisten(nullptr, nullptr), UnixStreamChannel::Error); + + acceptor.close(); + BOOST_CHECK_EQUAL(fs::symlink_status(socketPath).type(), fs::socket_file); + BOOST_CHECK_NO_THROW(channel->listen(nullptr, nullptr)); BOOST_CHECK_EQUAL(fs::symlink_status(socketPath).type(), fs::socket_file); } BOOST_AUTO_TEST_CASE(ExistingRegularFile) { - fs::path socketPath(listenerEp.path()); - fs::remove(socketPath); + auto guard = ndn::make_scope_exit([=] { fs::remove(socketPath); }); - std::ofstream f(listenerEp.path()); + fs::create_directories(socketPath.parent_path()); + std::ofstream f(socketPath.string()); f.close(); + BOOST_CHECK_EQUAL(fs::symlink_status(socketPath).type(), fs::regular_file); auto channel = makeChannel(); BOOST_CHECK_THROW(channel->listen(nullptr, nullptr), UnixStreamChannel::Error); - - fs::remove(socketPath); } BOOST_AUTO_TEST_SUITE_END() // TestUnixStreamChannel diff --git a/tests/daemon/face/unix-stream-factory.t.cpp b/tests/daemon/face/unix-stream-factory.t.cpp index 53a86874..1f0a62fd 100644 --- a/tests/daemon/face/unix-stream-factory.t.cpp +++ b/tests/daemon/face/unix-stream-factory.t.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2023, Regents of the University of California, + * Copyright (c) 2014-2024, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -28,21 +28,18 @@ #include "face-system-fixture.hpp" #include "factory-test-common.hpp" -namespace nfd::tests { +#include -using face::UnixStreamFactory; +namespace nfd::tests { -using UnixStreamFactoryFixture = FaceSystemFactoryFixture; +using UnixStreamFactoryFixture = FaceSystemFactoryFixture; BOOST_AUTO_TEST_SUITE(Face) BOOST_FIXTURE_TEST_SUITE(TestUnixStreamFactory, UnixStreamFactoryFixture) -const std::string CHANNEL_PATH1("unix-stream-test.1.sock"); -const std::string CHANNEL_PATH2("unix-stream-test.2.sock"); - BOOST_AUTO_TEST_SUITE(ProcessConfig) -BOOST_AUTO_TEST_CASE(Normal) +BOOST_AUTO_TEST_CASE(AbsolutePath) { const std::string CONFIG = R"CONFIG( face_system @@ -58,9 +55,40 @@ BOOST_AUTO_TEST_CASE(Normal) parseConfig(CONFIG, false); BOOST_REQUIRE_EQUAL(factory.getChannels().size(), 1); + BOOST_TEST(factory.getChannels().front()->isListening()); + const auto& uri = factory.getChannels().front()->getUri(); - BOOST_CHECK_EQUAL(uri.getScheme(), "unix"); - BOOST_CHECK_NE(uri.getPath().find("nfd-test.sock"), std::string::npos); + BOOST_TEST(uri.getScheme() == "unix"); + boost::filesystem::path path(uri.getPath()); + BOOST_TEST(path.filename() == "nfd-test.sock"); + BOOST_TEST(boost::filesystem::canonical(path) == path); // path should already be in canonical form + BOOST_TEST(boost::filesystem::equivalent(path, "/tmp/nfd-test.sock")); +} + +BOOST_AUTO_TEST_CASE(RelativePath) +{ + const std::string CONFIG = R"CONFIG( + face_system + { + unix + { + path nfd-test.sock + } + } + )CONFIG"; + + parseConfig(CONFIG, true); + parseConfig(CONFIG, false); + + BOOST_REQUIRE_EQUAL(factory.getChannels().size(), 1); + BOOST_TEST(factory.getChannels().front()->isListening()); + + const auto& uri = factory.getChannels().front()->getUri(); + BOOST_TEST(uri.getScheme() == "unix"); + boost::filesystem::path path(uri.getPath()); + BOOST_TEST(path.filename() == "nfd-test.sock"); + BOOST_TEST(boost::filesystem::canonical(path) == path); // path should already be in canonical form + BOOST_TEST(boost::filesystem::equivalent(path, "nfd-test.sock")); } BOOST_AUTO_TEST_CASE(Omitted) @@ -95,6 +123,9 @@ BOOST_AUTO_TEST_CASE(UnknownOption) BOOST_AUTO_TEST_SUITE_END() // ProcessConfig +const std::string CHANNEL_PATH1("unix-stream-test.1.sock"); +const std::string CHANNEL_PATH2("unix-stream-test.2.sock"); + BOOST_AUTO_TEST_CASE(GetChannels) { BOOST_CHECK_EQUAL(factory.getChannels().empty(), true); @@ -107,16 +138,24 @@ BOOST_AUTO_TEST_CASE(GetChannels) BOOST_AUTO_TEST_CASE(CreateChannel) { - auto channel1 = factory.createChannel(CHANNEL_PATH1); + auto channel1 = factory.createChannel("./" + CHANNEL_PATH1); // test path normalization auto channel1a = factory.createChannel(CHANNEL_PATH1); + auto channel1b = factory.createChannel(boost::filesystem::absolute(CHANNEL_PATH1).string()); + auto channel1c = factory.createChannel("foo//../" + CHANNEL_PATH1); BOOST_CHECK_EQUAL(channel1, channel1a); + BOOST_CHECK_EQUAL(channel1, channel1b); + BOOST_CHECK_EQUAL(channel1, channel1c); BOOST_CHECK_EQUAL(factory.getChannels().size(), 1); const auto& uri = channel1->getUri(); + BOOST_TEST_INFO_SCOPE(uri); BOOST_CHECK_EQUAL(uri.getScheme(), "unix"); BOOST_CHECK_EQUAL(uri.getHost(), ""); BOOST_CHECK_EQUAL(uri.getPort(), ""); - BOOST_CHECK_EQUAL(uri.getPath().rfind(CHANNEL_PATH1), uri.getPath().size() - CHANNEL_PATH1.size()); + boost::filesystem::path path1(uri.getPath()); + BOOST_TEST(path1.filename() == CHANNEL_PATH1); + BOOST_TEST(path1.is_absolute()); // path should always be absolute + BOOST_TEST(path1.lexically_normal() == path1); // path should be in normal form auto channel2 = factory.createChannel(CHANNEL_PATH2); BOOST_CHECK_NE(channel1, channel2); diff --git a/wscript b/wscript index 11a1353a..63b8a8a9 100644 --- a/wscript +++ b/wscript @@ -1,6 +1,6 @@ # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- """ -Copyright (c) 2014-2023, Regents of the University of California, +Copyright (c) 2014-2024, Regents of the University of California, Arizona Board of Regents, Colorado State University, University Pierre & Marie Curie, Sorbonne University, @@ -209,7 +209,7 @@ def build(bld): install_path='${SYSCONFDIR}/ndn', IF_HAVE_LIBPCAP='' if bld.env.HAVE_LIBPCAP else '; ', IF_HAVE_WEBSOCKET='' if bld.env.HAVE_WEBSOCKET else '; ', - UNIX_SOCKET_PATH='/run/nfd.sock' if Utils.unversioned_sys_platform() == 'linux' else '/var/run/nfd.sock') + UNIX_SOCKET_PATH='/run/nfd/nfd.sock' if Utils.unversioned_sys_platform() == 'linux' else '/var/run/nfd/nfd.sock') bld.install_files('${SYSCONFDIR}/ndn', 'autoconfig.conf.sample') if bld.env.HAVE_SYSTEMD: