main.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. // Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <dhcp6/ctrl_dhcp6_srv.h>
  8. #include <dhcp6/dhcp6_log.h>
  9. #include <dhcp6/parser_context.h>
  10. #include <dhcp6/json_config_parser.h>
  11. #include <dhcpsrv/cfgmgr.h>
  12. #include <log/logger_support.h>
  13. #include <log/logger_manager.h>
  14. #include <exceptions/exceptions.h>
  15. #include <cfgrpt/config_report.h>
  16. #include <boost/lexical_cast.hpp>
  17. #include <iostream>
  18. using namespace isc::data;
  19. using namespace isc::dhcp;
  20. using namespace std;
  21. /// This file contains entry point (main() function) for standard DHCPv6 server
  22. /// component of Kea software suite. It parses command-line arguments and
  23. /// instantiates ControlledDhcpv6Srv class that is responsible for establishing
  24. /// connection with msgq (receiving commands and configuration) and also
  25. /// creating Dhcpv6 server object as well.
  26. ///
  27. /// For detailed explanation or relations between main(), ControlledDhcpv6Srv,
  28. /// Dhcpv6Srv and other classes, see \ref dhcpv6Session.
  29. namespace {
  30. const char* const DHCP6_NAME = "kea-dhcp6";
  31. const char* const DHCP6_LOGGER_NAME = "kea-dhcp6";
  32. /// @brief Prints Kea Usage and exits
  33. ///
  34. /// Note: This function never returns. It terminates the process.
  35. void
  36. usage() {
  37. cerr << "Kea DHCPv6 server, version " << VERSION << endl;
  38. cerr << endl;
  39. cerr << "Usage: " << DHCP6_NAME
  40. << " -[v|V|W] [-d] [-{c|t} cfgfile] [-p port_number]" << endl;
  41. cerr << " -v: print version number and exit." << endl;
  42. cerr << " -V: print extended version and exit" << endl;
  43. cerr << " -W: display the configuration report and exit" << endl;
  44. cerr << " -d: debug mode with extra verbosity (former -v)" << endl;
  45. cerr << " -c file: specify configuration file" << endl;
  46. cerr << " -t file: check the configuration file syntax and exit" << endl;
  47. cerr << " -p number: specify non-standard port number 1-65535 "
  48. << "(useful for testing only)" << endl;
  49. exit(EXIT_FAILURE);
  50. }
  51. } // end of anonymous namespace
  52. int
  53. main(int argc, char* argv[]) {
  54. int ch;
  55. int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
  56. // useful for testing only.
  57. bool verbose_mode = false; // Should server be verbose?
  58. bool check_mode = false; // Check syntax
  59. // The standard config file
  60. std::string config_file("");
  61. while ((ch = getopt(argc, argv, "dvVWc:p:t:")) != -1) {
  62. switch (ch) {
  63. case 'd':
  64. verbose_mode = true;
  65. break;
  66. case 'v':
  67. cout << Dhcpv6Srv::getVersion(false) << endl;
  68. return (EXIT_SUCCESS);
  69. case 'V':
  70. cout << Dhcpv6Srv::getVersion(true) << endl;
  71. return (EXIT_SUCCESS);
  72. case 'W':
  73. cout << isc::detail::getConfigReport() << endl;
  74. return (EXIT_SUCCESS);
  75. case 't':
  76. check_mode = true;
  77. // falls through
  78. case 'c': // config file
  79. config_file = optarg;
  80. break;
  81. case 'p': // port number
  82. try {
  83. port_number = boost::lexical_cast<int>(optarg);
  84. } catch (const boost::bad_lexical_cast &) {
  85. cerr << "Failed to parse port number: [" << optarg
  86. << "], 1-65535 allowed." << endl;
  87. usage();
  88. }
  89. if (port_number <= 0 || port_number > 65535) {
  90. cerr << "Failed to parse port number: [" << optarg
  91. << "], 1-65535 allowed." << endl;
  92. usage();
  93. }
  94. break;
  95. default:
  96. usage();
  97. }
  98. }
  99. // Check for extraneous parameters.
  100. if (argc > optind) {
  101. usage();
  102. }
  103. // Configuration file is required.
  104. if (config_file.empty()) {
  105. cerr << "Configuration file not specified." << endl;
  106. usage();
  107. }
  108. if (check_mode) {
  109. try {
  110. // We need to initialize logging, in case any error messages are to be printed.
  111. // This is just a test, so we don't care about lockfile.
  112. setenv("KEA_LOCKFILE_DIR", "none", 0);
  113. CfgMgr::instance().setDefaultLoggerName(DHCP6_ROOT_LOGGER_NAME);
  114. Daemon::loggerInit(DHCP6_ROOT_LOGGER_NAME, verbose_mode);
  115. // Check the syntax first.
  116. Parser6Context parser;
  117. ConstElementPtr json;
  118. json = parser.parseFile(config_file, Parser6Context::PARSER_DHCP6);
  119. if (!json) {
  120. cerr << "No configuration found" << endl;
  121. return (EXIT_FAILURE);
  122. }
  123. if (verbose_mode) {
  124. cerr << "Syntax check OK" << endl;
  125. }
  126. // Check the logic next.
  127. ConstElementPtr dhcp6 = json->get("Dhcp6");
  128. if (!dhcp6) {
  129. cerr << "Missing mandatory Dhcp6 element" << endl;
  130. return (EXIT_FAILURE);
  131. }
  132. ControlledDhcpv6Srv server(0);
  133. ConstElementPtr answer;
  134. // Now we pass the Dhcp6 configuration to the server, but
  135. // tell it to check the configuration only (check_only = true)
  136. answer = configureDhcp6Server(server, dhcp6, true);
  137. int status_code = 0;
  138. answer = isc::config::parseAnswer(status_code, answer);
  139. if (status_code == 0) {
  140. return (EXIT_SUCCESS);
  141. } else {
  142. cerr << "Error encountered: " << answer->stringValue() << endl;
  143. return (EXIT_FAILURE);
  144. }
  145. return (EXIT_SUCCESS);
  146. } catch (const std::exception& ex) {
  147. cerr << "Syntax check failed with " << ex.what() << endl;
  148. }
  149. return (EXIT_FAILURE);
  150. }
  151. int ret = EXIT_SUCCESS;
  152. try {
  153. // It is important that we set a default logger name because this name
  154. // will be used when the user doesn't provide the logging configuration
  155. // in the Kea configuration file.
  156. CfgMgr::instance().setDefaultLoggerName(DHCP6_LOGGER_NAME);
  157. // Initialize logging. If verbose, we'll use maximum verbosity.
  158. Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode);
  159. LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
  160. .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no");
  161. LOG_INFO(dhcp6_logger, DHCP6_STARTING).arg(VERSION);
  162. // Create the server instance.
  163. ControlledDhcpv6Srv server(port_number);
  164. // Remember verbose-mode
  165. server.setVerbose(verbose_mode);
  166. // Create our PID file
  167. server.setProcName(DHCP6_NAME);
  168. server.setConfigFile(config_file);
  169. server.createPIDFile();
  170. try {
  171. // Initialize the server, e.g. establish control session
  172. // Read a configuration file
  173. server.init(config_file);
  174. } catch (const std::exception& ex) {
  175. try {
  176. // Let's log out what went wrong.
  177. isc::log::LoggerManager log_manager;
  178. log_manager.process();
  179. LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what());
  180. } catch (...) {
  181. // The exception thrown during the initialization could
  182. // originate from logger subsystem. Therefore LOG_ERROR() may
  183. // fail as well.
  184. cerr << "Failed to initialize server: " << ex.what() << endl;
  185. }
  186. return (EXIT_FAILURE);
  187. }
  188. // Tell the admin we are ready to process packets
  189. LOG_INFO(dhcp6_logger, DHCP6_STARTED).arg(VERSION);
  190. // And run the main loop of the server.
  191. server.run();
  192. LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN);
  193. } catch (const isc::dhcp::DaemonPIDExists& ex) {
  194. // First, we print the error on stderr (that should always work)
  195. cerr << DHCP6_NAME << " already running? " << ex.what()
  196. << endl;
  197. // Let's also try to log it using logging system, but we're not
  198. // sure if it's usable (the exception may have been thrown from
  199. // the logger subsystem)
  200. try {
  201. LOG_FATAL(dhcp6_logger, DHCP6_ALREADY_RUNNING)
  202. .arg(DHCP6_NAME).arg(ex.what());
  203. } catch (...) {
  204. // Already logged so ignore
  205. }
  206. ret = EXIT_FAILURE;
  207. } catch (const std::exception& ex) {
  208. // First, we print the error on stderr (that should always work)
  209. cerr << DHCP6_NAME << "Fatal error during start up: " << ex.what()
  210. << endl;
  211. // Let's also try to log it using logging system, but we're not
  212. // sure if it's usable (the exception may have been thrown from
  213. // the logger subsystem)
  214. try {
  215. LOG_FATAL(dhcp6_logger, DHCP6_SERVER_FAILED).arg(ex.what());
  216. } catch (...) {
  217. // Already logged so ignore
  218. }
  219. ret = EXIT_FAILURE;
  220. }
  221. return (ret);
  222. }