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-2024, 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/asio/signal_set.hpp>
37 #include <boost/config.hpp>
38 #include <boost/exception/diagnostic_information.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/system/system_error.hpp>
43 #include <boost/version.hpp>
44 
45 #include <atomic>
46 #include <condition_variable>
47 #include <iostream>
48 #include <system_error>
49 #include <thread>
50 
51 #include <ndn-cxx/util/logging.hpp>
52 #include <ndn-cxx/util/ostream-joiner.hpp>
53 #include <ndn-cxx/version.hpp>
54 
55 #ifdef NFD_HAVE_LIBPCAP
56 #include <pcap/pcap.h>
57 #endif
58 #ifdef NFD_HAVE_SYSTEMD
59 #include <systemd/sd-daemon.h>
60 #endif
61 #ifdef NFD_HAVE_WEBSOCKET
62 #include <websocketpp/version.hpp>
63 #endif
64 
65 namespace po = boost::program_options;
66 
67 NFD_LOG_INIT(Main);
68 
69 namespace nfd {
70 
80 class NfdRunner : noncopyable
81 {
82 public:
83  explicit
84  NfdRunner(const std::string& configFile)
85  : m_nfd(configFile, m_nfdKeyChain)
86  , m_configFile(configFile)
87  , m_terminateSignals(getGlobalIoService(), SIGINT, SIGTERM)
88  , m_reloadSignals(getGlobalIoService(), SIGHUP)
89  {
90  m_terminateSignals.async_wait([this] (auto&&... args) {
91  terminate(std::forward<decltype(args)>(args)...);
92  });
93  m_reloadSignals.async_wait([this] (auto&&... args) {
94  reload(std::forward<decltype(args)>(args)...);
95  });
96  }
97 
98  void
99  initialize()
100  {
101  m_nfd.initialize();
102  }
103 
104  int
105  run()
106  {
107  // Return value: a non-zero value is assigned when either NFD or RIB manager (running in
108  // a separate thread) fails.
109  std::atomic_int retval(0);
110 
111  boost::asio::io_context* const mainIo = &getGlobalIoService();
112  setMainIoService(mainIo);
113  boost::asio::io_context* ribIo = nullptr;
114 
115  // Mutex and conditional variable to implement synchronization between main and RIB manager
116  // threads:
117  // - to block main thread until RIB manager thread starts and initializes ribIo (to allow
118  // stopping it later)
119  std::mutex m;
120  std::condition_variable cv;
121 
122  std::thread ribThread([configFile = m_configFile, &retval, &ribIo, mainIo, &cv, &m] {
123  {
124  std::lock_guard<std::mutex> lock(m);
125  ribIo = &getGlobalIoService();
126  BOOST_ASSERT(ribIo != mainIo);
127  setRibIoService(ribIo);
128  }
129  cv.notify_all(); // notify that ribIo has been assigned
130 
131  try {
132  ndn::KeyChain ribKeyChain;
133  // must be created inside a separate thread
134  rib::Service ribService(configFile, ribKeyChain);
135  getGlobalIoService().run(); // ribIo is not thread-safe to use here
136  }
137  catch (const std::exception& e) {
138  NFD_LOG_FATAL(boost::diagnostic_information(e));
139  retval = 1;
140  mainIo->stop();
141  }
142 
143  {
144  std::lock_guard<std::mutex> lock(m);
145  ribIo = nullptr;
146  }
147  });
148 
149  {
150  // Wait to guarantee that ribIo is properly initialized, so it can be used to terminate
151  // RIB manager thread.
152  std::unique_lock<std::mutex> lock(m);
153  cv.wait(lock, [&ribIo] { return ribIo != nullptr; });
154  }
155 
156  try {
157  systemdNotify("READY=1");
158  mainIo->run();
159  }
160  catch (const std::exception& e) {
161  NFD_LOG_FATAL(boost::diagnostic_information(e));
162  retval = 1;
163  }
164  catch (const PrivilegeHelper::Error& e) {
165  NFD_LOG_FATAL(e.what());
166  retval = 4;
167  }
168 
169  {
170  // ribIo is guaranteed to be alive at this point
171  std::lock_guard<std::mutex> lock(m);
172  if (ribIo != nullptr) {
173  ribIo->stop();
174  ribIo = nullptr;
175  }
176  }
177  ribThread.join();
178 
179  return retval;
180  }
181 
182  static void
183  systemdNotify(const char* state)
184  {
185 #ifdef NFD_HAVE_SYSTEMD
186  sd_notify(0, state);
187 #endif
188  }
189 
190 private:
191  void
192  terminate(const boost::system::error_code& error, int signalNo)
193  {
194  if (error)
195  return;
196 
197  NFD_LOG_INFO("Caught signal " << signalNo << " (" << ::strsignal(signalNo) << "), exiting...");
198 
199  systemdNotify("STOPPING=1");
200  getGlobalIoService().stop();
201  }
202 
203  void
204  reload(const boost::system::error_code& error, int signalNo)
205  {
206  if (error)
207  return;
208 
209  NFD_LOG_INFO("Caught signal " << signalNo << " (" << ::strsignal(signalNo) << "), reloading...");
210 
211  systemdNotify("RELOADING=1");
212  m_nfd.reloadConfigFile();
213  systemdNotify("READY=1");
214 
215  m_reloadSignals.async_wait([this] (auto&&... args) {
216  reload(std::forward<decltype(args)>(args)...);
217  });
218  }
219 
220 private:
221  ndn::KeyChain m_nfdKeyChain;
222  Nfd m_nfd;
223  std::string m_configFile;
224 
225  boost::asio::signal_set m_terminateSignals;
226  boost::asio::signal_set m_reloadSignals;
227 };
228 
229 static void
230 printUsage(std::ostream& os, const char* programName, const po::options_description& opts)
231 {
232  os << "Usage: " << programName << " [options]\n"
233  << "\n"
234  << "Run the NDN Forwarding Daemon (NFD)\n"
235  << "\n"
236  << opts;
237 }
238 
239 static void
240 printLogModules(std::ostream& os)
241 {
242  const auto& modules = ndn::util::Logging::getLoggerNames();
243  std::copy(modules.begin(), modules.end(), ndn::make_ostream_joiner(os, "\n"));
244  os << std::endl;
245 }
246 
247 } // namespace nfd
248 
249 int
250 main(int argc, char** argv)
251 {
252  using namespace nfd;
253 
254  std::string configFile = NFD_DEFAULT_CONFIG_FILE;
255 
256  po::options_description description("Options");
257  description.add_options()
258  ("help,h", "print this message and exit")
259  ("version,V", "show version information and exit")
260  ("config,c", po::value<std::string>(&configFile),
261  "path to configuration file (default: " NFD_DEFAULT_CONFIG_FILE ")")
262  ("modules,m", "list available logging modules")
263  ;
264 
265  po::variables_map vm;
266  try {
267  po::store(po::parse_command_line(argc, argv, description), vm);
268  po::notify(vm);
269  }
270  catch (const std::exception& e) {
271  // Cannot use NFD_LOG_* macros here, because the logging subsystem is not initialized yet
272  // at this point. Moreover, we don't want to clutter error messages related to command-line
273  // parsing with timestamps and other useless text added by the macros.
274  std::cerr << "ERROR: " << e.what() << "\n\n";
275  printUsage(std::cerr, argv[0], description);
276  return 2;
277  }
278 
279  if (vm.count("help") > 0) {
280  printUsage(std::cout, argv[0], description);
281  return 0;
282  }
283 
284  if (vm.count("version") > 0) {
285  std::cout << NFD_VERSION_BUILD_STRING << std::endl;
286  return 0;
287  }
288 
289  if (vm.count("modules") > 0) {
290  printLogModules(std::cout);
291  return 0;
292  }
293 
294  const std::string boostBuildInfo =
295  "with Boost version " + std::to_string(BOOST_VERSION / 100000) +
296  "." + std::to_string(BOOST_VERSION / 100 % 1000) +
297  "." + std::to_string(BOOST_VERSION % 100);
298  const std::string pcapBuildInfo =
299 #ifdef NFD_HAVE_LIBPCAP
300  "with " + std::string(pcap_lib_version());
301 #else
302  "without libpcap";
303 #endif
304  const std::string wsBuildInfo =
305 #ifdef NFD_HAVE_WEBSOCKET
306  "with WebSocket++ version " + std::to_string(websocketpp::major_version) +
307  "." + std::to_string(websocketpp::minor_version) +
308  "." + std::to_string(websocketpp::patch_version);
309 #else
310  "without WebSocket++";
311 #endif
312 
313  std::clog << "NFD version " << NFD_VERSION_BUILD_STRING << " starting\n"
314  << "Built with " BOOST_COMPILER ", with " BOOST_STDLIB
315  ", " << boostBuildInfo <<
316  ", " << pcapBuildInfo <<
317  ", " << wsBuildInfo <<
318  ", with ndn-cxx version " NDN_CXX_VERSION_BUILD_STRING
319  << std::endl;
320 
321  auto flush = ndn::make_scope_exit([] { ndn::util::Logging::flush(); });
322 
323  NfdRunner runner(configFile);
324  try {
325  runner.initialize();
326  }
327  catch (const boost::system::system_error& e) {
328  NFD_LOG_FATAL(boost::diagnostic_information(e));
329  if (e.code() == boost::system::errc::operation_not_permitted ||
330  e.code() == boost::system::errc::permission_denied) {
331  return 4;
332  }
333  return 1;
334  }
335  catch (const std::system_error& e) {
336  NFD_LOG_FATAL(boost::diagnostic_information(e));
337  if (e.code() == std::errc::operation_not_permitted ||
338  e.code() == std::errc::permission_denied) {
339  return 4;
340  }
341  return 1;
342  }
343  catch (const std::exception& e) {
344  NFD_LOG_FATAL(boost::diagnostic_information(e));
345  return 1;
346  }
347  catch (const PrivilegeHelper::Error& e) {
348  // PrivilegeHelper::Errors do not inherit from std::exception
349  // and represent seteuid/gid failures
350  NFD_LOG_FATAL(e.what());
351  return 4;
352  }
353 
354  return runner.run();
355 }
int main(int argc, char **argv)
Definition: main.cpp:250
#define NFD_LOG_INFO
Definition: logger.hpp:39
#define NFD_LOG_INIT(name)
Definition: logger.hpp:31
#define NFD_LOG_FATAL
Definition: logger.hpp:42
-status-http-server
Definition: common.hpp:71
static void printUsage(std::ostream &os, const char *programName, const po::options_description &opts)
Definition: main.cpp:230
void setMainIoService(boost::asio::io_context *mainIo)
Definition: global.cpp:77
void setRibIoService(boost::asio::io_context *ribIo)
Definition: global.cpp:83
static void printLogModules(std::ostream &os)
Definition: main.cpp:240
boost::asio::io_context & getGlobalIoService()
Returns the global io_context instance for the calling thread.
Definition: global.cpp:36
const char NFD_VERSION_BUILD_STRING[]
NFD version string, including git commit information if NFD is build from a specific git commit.