main.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014-2019, Regents of the University of California,
4  * Arizona Board of Regents,
5  * Colorado State University,
6  * University Pierre & Marie Curie, Sorbonne University,
7  * Washington University in St. Louis,
8  * Beijing Institute of Technology,
9  * The University of Memphis.
10  *
11  * This file is part of NFD (Named Data Networking Forwarding Daemon).
12  * See AUTHORS.md for complete list of NFD authors and contributors.
13  *
14  * NFD is free software: you can redistribute it and/or modify it under the terms
15  * of the GNU General Public License as published by the Free Software Foundation,
16  * either version 3 of the License, or (at your option) any later version.
17  *
18  * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20  * PURPOSE. See the GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along with
23  * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "nfd.hpp"
27 #include "rib/service.hpp"
28 
29 #include "common/global.hpp"
30 #include "common/logger.hpp"
32 #include "core/version.hpp"
33 
34 #include <string.h> // for strsignal()
35 
36 #include <boost/config.hpp>
37 #include <boost/exception/diagnostic_information.hpp>
38 #include <boost/filesystem.hpp>
39 #include <boost/program_options/options_description.hpp>
40 #include <boost/program_options/parsers.hpp>
41 #include <boost/program_options/variables_map.hpp>
42 #include <boost/version.hpp>
43 
44 #include <atomic>
45 #include <condition_variable>
46 #include <iostream>
47 #include <thread>
48 
49 #include <ndn-cxx/util/logging.hpp>
50 #include <ndn-cxx/version.hpp>
51 
52 #ifdef HAVE_LIBPCAP
53 #include <pcap/pcap.h>
54 #endif
55 #ifdef HAVE_SYSTEMD
56 #include <systemd/sd-daemon.h>
57 #endif
58 #ifdef HAVE_WEBSOCKET
59 #include <websocketpp/version.hpp>
60 #endif
61 
62 namespace po = boost::program_options;
63 
64 NFD_LOG_INIT(Main);
65 
66 namespace nfd {
67 
77 class NfdRunner : noncopyable
78 {
79 public:
80  explicit
81  NfdRunner(const std::string& configFile)
82  : m_nfd(configFile, m_nfdKeyChain)
83  , m_configFile(configFile)
84  , m_terminationSignalSet(getGlobalIoService())
85  , m_reloadSignalSet(getGlobalIoService())
86  {
87  m_terminationSignalSet.add(SIGINT);
88  m_terminationSignalSet.add(SIGTERM);
89  m_terminationSignalSet.async_wait(bind(&NfdRunner::terminate, this, _1, _2));
90 
91  m_reloadSignalSet.add(SIGHUP);
92  m_reloadSignalSet.async_wait(bind(&NfdRunner::reload, this, _1, _2));
93  }
94 
95  void
96  initialize()
97  {
98  m_nfd.initialize();
99  }
100 
101  int
102  run()
103  {
104  // Return value: a non-zero value is assigned when either NFD or RIB manager (running in
105  // a separate thread) fails.
106  std::atomic_int retval(0);
107 
108  boost::asio::io_service* const mainIo = &getGlobalIoService();
109  setMainIoService(mainIo);
110  boost::asio::io_service* ribIo = nullptr;
111 
112  // Mutex and conditional variable to implement synchronization between main and RIB manager
113  // threads:
114  // - to block main thread until RIB manager thread starts and initializes ribIo (to allow
115  // stopping it later)
116  std::mutex m;
117  std::condition_variable cv;
118 
119  std::thread ribThread([configFile = m_configFile, &retval, &ribIo, mainIo, &cv, &m] {
120  {
121  std::lock_guard<std::mutex> lock(m);
122  ribIo = &getGlobalIoService();
123  BOOST_ASSERT(ribIo != mainIo);
124  setRibIoService(ribIo);
125  }
126  cv.notify_all(); // notify that ribIo has been assigned
127 
128  try {
129  ndn::KeyChain ribKeyChain;
130  // must be created inside a separate thread
131  rib::Service ribService(configFile, ribKeyChain);
132  getGlobalIoService().run(); // ribIo is not thread-safe to use here
133  }
134  catch (const std::exception& e) {
135  NFD_LOG_FATAL(boost::diagnostic_information(e));
136  retval = 1;
137  mainIo->stop();
138  }
139 
140  {
141  std::lock_guard<std::mutex> lock(m);
142  ribIo = nullptr;
143  }
144  });
145 
146  {
147  // Wait to guarantee that ribIo is properly initialized, so it can be used to terminate
148  // RIB manager thread.
149  std::unique_lock<std::mutex> lock(m);
150  cv.wait(lock, [&ribIo] { return ribIo != nullptr; });
151  }
152 
153  try {
154  systemdNotify("READY=1");
155  mainIo->run();
156  }
157  catch (const std::exception& e) {
158  NFD_LOG_FATAL(boost::diagnostic_information(e));
159  retval = 1;
160  }
161  catch (const PrivilegeHelper::Error& e) {
162  NFD_LOG_FATAL(e.what());
163  retval = 4;
164  }
165 
166  {
167  // ribIo is guaranteed to be alive at this point
168  std::lock_guard<std::mutex> lock(m);
169  if (ribIo != nullptr) {
170  ribIo->stop();
171  ribIo = nullptr;
172  }
173  }
174  ribThread.join();
175 
176  return retval;
177  }
178 
179  static void
180  systemdNotify(const char* state)
181  {
182 #ifdef HAVE_SYSTEMD
183  sd_notify(0, state);
184 #endif
185  }
186 
187 private:
188  void
189  terminate(const boost::system::error_code& error, int signalNo)
190  {
191  if (error)
192  return;
193 
194  NFD_LOG_INFO("Caught signal " << signalNo << " (" << ::strsignal(signalNo) << "), exiting...");
195 
196  systemdNotify("STOPPING=1");
197  getGlobalIoService().stop();
198  }
199 
200  void
201  reload(const boost::system::error_code& error, int signalNo)
202  {
203  if (error)
204  return;
205 
206  NFD_LOG_INFO("Caught signal " << signalNo << " (" << ::strsignal(signalNo) << "), reloading...");
207 
208  systemdNotify("RELOADING=1");
209  m_nfd.reloadConfigFile();
210  systemdNotify("READY=1");
211 
212  m_reloadSignalSet.async_wait(bind(&NfdRunner::reload, this, _1, _2));
213  }
214 
215 private:
216  ndn::KeyChain m_nfdKeyChain;
217  Nfd m_nfd;
218  std::string m_configFile;
219 
220  boost::asio::signal_set m_terminationSignalSet;
221  boost::asio::signal_set m_reloadSignalSet;
222 };
223 
224 static void
225 printUsage(std::ostream& os, const char* programName, const po::options_description& opts)
226 {
227  os << "Usage: " << programName << " [options]\n"
228  << "\n"
229  << "Run the NDN Forwarding Daemon (NFD)\n"
230  << "\n"
231  << opts;
232 }
233 
234 static void
235 printLogModules(std::ostream& os)
236 {
237  const auto& modules = ndn::util::Logging::getLoggerNames();
238  std::copy(modules.begin(), modules.end(), ndn::make_ostream_joiner(os, "\n"));
239  os << std::endl;
240 }
241 
242 } // namespace nfd
243 
244 int
245 main(int argc, char** argv)
246 {
247  using namespace nfd;
248 
249  std::string configFile = DEFAULT_CONFIG_FILE;
250 
251  po::options_description description("Options");
252  description.add_options()
253  ("help,h", "print this message and exit")
254  ("version,V", "show version information and exit")
255  ("config,c", po::value<std::string>(&configFile),
256  "path to configuration file (default: " DEFAULT_CONFIG_FILE ")")
257  ("modules,m", "list available logging modules")
258  ;
259 
260  po::variables_map vm;
261  try {
262  po::store(po::parse_command_line(argc, argv, description), vm);
263  po::notify(vm);
264  }
265  catch (const std::exception& e) {
266  // Cannot use NFD_LOG_* macros here, because the logging subsystem is not initialized yet
267  // at this point. Moreover, we don't want to clutter error messages related to command-line
268  // parsing with timestamps and other useless text added by the macros.
269  std::cerr << "ERROR: " << e.what() << "\n\n";
270  printUsage(std::cerr, argv[0], description);
271  return 2;
272  }
273 
274  if (vm.count("help") > 0) {
275  printUsage(std::cout, argv[0], description);
276  return 0;
277  }
278 
279  if (vm.count("version") > 0) {
280  std::cout << NFD_VERSION_BUILD_STRING << std::endl;
281  return 0;
282  }
283 
284  if (vm.count("modules") > 0) {
285  printLogModules(std::cout);
286  return 0;
287  }
288 
289  const std::string boostBuildInfo =
290  "with Boost version " + to_string(BOOST_VERSION / 100000) +
291  "." + to_string(BOOST_VERSION / 100 % 1000) +
292  "." + to_string(BOOST_VERSION % 100);
293  const std::string pcapBuildInfo =
294 #ifdef HAVE_LIBPCAP
295  "with " + std::string(pcap_lib_version());
296 #else
297  "without libpcap";
298 #endif
299  const std::string wsBuildInfo =
300 #ifdef HAVE_WEBSOCKET
301  "with WebSocket++ version " + to_string(websocketpp::major_version) +
302  "." + to_string(websocketpp::minor_version) +
303  "." + to_string(websocketpp::patch_version);
304 #else
305  "without WebSocket++";
306 #endif
307 
308  std::clog << "NFD version " << NFD_VERSION_BUILD_STRING << " starting\n"
309  << "Built with " BOOST_COMPILER ", with " BOOST_STDLIB
310  ", " << boostBuildInfo <<
311  ", " << pcapBuildInfo <<
312  ", " << wsBuildInfo <<
313  ", with ndn-cxx version " NDN_CXX_VERSION_BUILD_STRING
314  << std::endl;
315 
316  NfdRunner runner(configFile);
317  try {
318  runner.initialize();
319  }
320  catch (const boost::filesystem::filesystem_error& e) {
321  NFD_LOG_FATAL(boost::diagnostic_information(e));
322  return e.code() == boost::system::errc::permission_denied ? 4 : 1;
323  }
324  catch (const std::exception& e) {
325  NFD_LOG_FATAL(boost::diagnostic_information(e));
326  return 1;
327  }
328  catch (const PrivilegeHelper::Error& e) {
329  // PrivilegeHelper::Errors do not inherit from std::exception
330  // and represent seteuid/gid failures
331  NFD_LOG_FATAL(e.what());
332  return 4;
333  }
334 
335  return runner.run();
336 }
static void printLogModules(std::ostream &os)
Definition: main.cpp:235
int main(int argc, char **argv)
Definition: main.cpp:245
void setMainIoService(boost::asio::io_service *mainIo)
Definition: global.cpp:77
Copyright (c) 2014-2015, Regents of the University of California, Arizona Board of Regents...
Definition: algorithm.hpp:32
const char NFD_VERSION_BUILD_STRING[]
NFD version string, including git commit information, if NFD is build from specific git commit...
#define NFD_LOG_INFO
Definition: logger.hpp:39
#define NFD_LOG_INIT(name)
Definition: logger.hpp:31
static void printUsage(std::ostream &os, const char *programName, const po::options_description &opts)
Definition: main.cpp:225
#define NFD_LOG_FATAL
Definition: logger.hpp:42
void setRibIoService(boost::asio::io_service *ribIo)
Definition: global.cpp:83
boost::asio::io_service & getGlobalIoService()
Returns the global io_service instance for the calling thread.
Definition: global.cpp:36