unix-stream-channel.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
26 #include "unix-stream-channel.hpp"
27 #include "generic-link-service.hpp"
29 #include "core/global-io.hpp"
30 
31 #include <boost/filesystem.hpp>
32 #include <sys/stat.h> // for chmod()
33 
34 namespace nfd {
35 namespace face {
36 
37 NFD_LOG_INIT("UnixStreamChannel");
38 
40  : m_endpoint(endpoint)
41  , m_acceptor(getGlobalIoService())
42  , m_socket(getGlobalIoService())
43  , m_size(0)
44 {
45  setUri(FaceUri(m_endpoint));
46  NFD_LOG_CHAN_INFO("Creating channel");
47 }
48 
50 {
51  if (isListening()) {
52  // use the non-throwing variants during destruction
53  // and ignore any errors
54  boost::system::error_code error;
55  m_acceptor.close(error);
56  NFD_LOG_CHAN_DEBUG("Removing socket file");
57  boost::filesystem::remove(m_endpoint.path(), error);
58  }
59 }
60 
61 void
63  const FaceCreationFailedCallback& onAcceptFailed,
64  int backlog/* = acceptor::max_connections*/)
65 {
66  if (isListening()) {
67  NFD_LOG_CHAN_WARN("Already listening");
68  return;
69  }
70 
71  namespace fs = boost::filesystem;
72 
73  fs::path socketPath(m_endpoint.path());
74  fs::file_type type = fs::symlink_status(socketPath).type();
75 
76  if (type == fs::socket_file) {
77  boost::system::error_code error;
78  boost::asio::local::stream_protocol::socket socket(getGlobalIoService());
79  socket.connect(m_endpoint, error);
80  NFD_LOG_CHAN_TRACE("connect() on existing socket file returned: " << error.message());
81  if (!error) {
82  // someone answered, leave the socket alone
83  BOOST_THROW_EXCEPTION(Error("Socket file at " + m_endpoint.path()
84  + " belongs to another NFD process"));
85  }
86  else if (error == boost::asio::error::connection_refused ||
87  error == boost::asio::error::timed_out) {
88  // no one is listening on the remote side,
89  // we can safely remove the stale socket
90  NFD_LOG_CHAN_DEBUG("Removing stale socket file");
91  fs::remove(socketPath);
92  }
93  }
94  else if (type != fs::file_not_found) {
95  BOOST_THROW_EXCEPTION(Error(m_endpoint.path() + " already exists and is not a socket file"));
96  }
97 
98  m_acceptor.open();
99  m_acceptor.bind(m_endpoint);
100  m_acceptor.listen(backlog);
101 
102  if (::chmod(m_endpoint.path().c_str(), 0666) < 0) {
103  BOOST_THROW_EXCEPTION(Error("chmod(" + m_endpoint.path() + ") failed: " + std::strerror(errno)));
104  }
105 
106  accept(onFaceCreated, onAcceptFailed);
107  NFD_LOG_CHAN_DEBUG("Started listening");
108 }
109 
110 void
111 UnixStreamChannel::accept(const FaceCreatedCallback& onFaceCreated,
112  const FaceCreationFailedCallback& onAcceptFailed)
113 {
114  m_acceptor.async_accept(m_socket, bind(&UnixStreamChannel::handleAccept, this,
115  boost::asio::placeholders::error,
116  onFaceCreated, onAcceptFailed));
117 }
118 
119 void
120 UnixStreamChannel::handleAccept(const boost::system::error_code& error,
121  const FaceCreatedCallback& onFaceCreated,
122  const FaceCreationFailedCallback& onAcceptFailed)
123 {
124  if (error) {
125  if (error != boost::asio::error::operation_aborted) {
126  NFD_LOG_CHAN_DEBUG("Accept failed: " << error.message());
127  if (onAcceptFailed)
128  onAcceptFailed(500, "Accept failed: " + error.message());
129  }
130  return;
131  }
132 
133  NFD_LOG_CHAN_TRACE("Incoming connection via fd " << m_socket.native_handle());
134 
135  auto linkService = make_unique<GenericLinkService>();
136  auto transport = make_unique<UnixStreamTransport>(std::move(m_socket));
137  auto face = make_shared<Face>(std::move(linkService), std::move(transport));
138 
139  ++m_size;
140  connectFaceClosedSignal(*face, [this] { --m_size; });
141 
142  onFaceCreated(face);
143 
144  // prepare accepting the next connection
145  accept(onFaceCreated, onAcceptFailed);
146 }
147 
148 } // namespace face
149 } // namespace nfd
UnixStreamChannel(const unix_stream::Endpoint &endpoint)
Create UnixStream channel for the specified endpoint.
void connectFaceClosedSignal(Face &face, const std::function< void()> &f)
invokes a callback when the face is closed
Definition: channel.cpp:40
#define NFD_LOG_CHAN_INFO(msg)
Log a message at INFO level.
Definition: channel-log.hpp:52
void listen(const FaceCreatedCallback &onFaceCreated, const FaceCreationFailedCallback &onAcceptFailed, int backlog=boost::asio::local::stream_protocol::acceptor::max_connections)
Start listening.
Copyright (c) 2014-2015, Regents of the University of California, Arizona Board of Regents...
Definition: algorithm.hpp:32
UnixStreamChannel-related error.
function< void(uint32_t status, const std::string &reason)> FaceCreationFailedCallback
Prototype for the callback that is invoked when a face fails to be created.
Definition: channel.hpp:44
void setUri(const FaceUri &uri)
Definition: channel.cpp:34
bool isListening() const override
Returns whether the channel is listening.
#define NFD_LOG_INIT(name)
Definition: logger.hpp:122
#define NFD_LOG_CHAN_TRACE(msg)
Log a message at TRACE level.
Definition: channel-log.hpp:46
boost::asio::local::stream_protocol::endpoint Endpoint
#define NFD_LOG_CHAN_WARN(msg)
Log a message at WARN level.
Definition: channel-log.hpp:55
#define NFD_LOG_CHAN_DEBUG(msg)
Log a message at DEBUG level.
Definition: channel-log.hpp:49
function< void(const shared_ptr< Face > &newFace)> FaceCreatedCallback
Prototype for the callback that is invoked when a face is created (in response to an incoming connect...
Definition: channel.hpp:35
boost::asio::io_service & getGlobalIoService()
Definition: global-io.cpp:41