lfc_controller.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. // Copyright (C) 2015-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 <lfc/lfc_controller.h>
  8. #include <lfc/lfc_log.h>
  9. #include <util/pid_file.h>
  10. #include <exceptions/exceptions.h>
  11. #include <dhcpsrv/csv_lease_file4.h>
  12. #include <dhcpsrv/csv_lease_file6.h>
  13. #include <dhcpsrv/memfile_lease_mgr.h>
  14. #include <dhcpsrv/memfile_lease_storage.h>
  15. #include <dhcpsrv/lease_mgr.h>
  16. #include <dhcpsrv/lease_file_loader.h>
  17. #include <log/logger_manager.h>
  18. #include <log/logger_name.h>
  19. #include <cfgrpt/config_report.h>
  20. #include <iostream>
  21. #include <sstream>
  22. #include <unistd.h>
  23. #include <stdlib.h>
  24. #include <cerrno>
  25. using namespace std;
  26. using namespace isc::util;
  27. using namespace isc::dhcp;
  28. using namespace isc::log;
  29. namespace {
  30. /// @brief Maximum number of errors to allow when reading leases from the file.
  31. const uint32_t MAX_LEASE_ERRORS = 100;
  32. }; // namespace anonymous
  33. namespace isc {
  34. namespace lfc {
  35. // Refer to config_report so it will be embedded in the binary
  36. const char* const* lfc_config_report = isc::detail::config_report;
  37. /// @brief Defines the application name, it may be used to locate
  38. /// configuration data and appears in log statements.
  39. const char* LFCController::lfc_app_name_ = "DhcpLFC";
  40. /// @brief Defines the executable name.
  41. const char* LFCController::lfc_bin_name_ = "kea-lfc";
  42. LFCController::LFCController()
  43. : protocol_version_(0), verbose_(false), config_file_(""), previous_file_(""),
  44. copy_file_(""), output_file_(""), finish_file_(""), pid_file_("") {
  45. }
  46. LFCController::~LFCController() {
  47. }
  48. void
  49. LFCController::launch(int argc, char* argv[], const bool test_mode) {
  50. bool do_rotate = true;
  51. // It would be nice to set up the logger as the first step
  52. // in the process, but we don't know where to send logging
  53. // info until after we have parsed our arguments. As we
  54. // don't currently log anything when trying to parse the
  55. // arguments we do the parse before the logging setup. If
  56. // we do decide to log something then the code will need
  57. // to move around a bit.
  58. try {
  59. parseArgs(argc, argv);
  60. } catch (const InvalidUsage& ex) {
  61. usage(ex.what());
  62. throw; // rethrow it
  63. }
  64. // Start up the logging system.
  65. startLogger(test_mode);
  66. LOG_INFO(lfc_logger, LFC_START);
  67. // verify we are the only instance
  68. PIDFile pid_file(pid_file_);
  69. try {
  70. if (pid_file.check()) {
  71. // Already running instance, bail out
  72. LOG_FATAL(lfc_logger, LFC_RUNNING);
  73. return;
  74. }
  75. // create the pid file for this instance
  76. pid_file.write();
  77. } catch (const PIDFileError& pid_ex) {
  78. LOG_FATAL(lfc_logger, LFC_FAIL_PID_CREATE).arg(pid_ex.what());
  79. return;
  80. }
  81. // If we don't have a finish file do the processing. We
  82. // don't know the exact type of the finish file here but
  83. // all we care about is if it exists so that's okay
  84. CSVFile lf_finish(getFinishFile());
  85. if (!lf_finish.exists()) {
  86. LOG_INFO(lfc_logger, LFC_PROCESSING)
  87. .arg(previous_file_)
  88. .arg(copy_file_);
  89. try {
  90. if (getProtocolVersion() == 4) {
  91. processLeases<Lease4, CSVLeaseFile4, Lease4Storage>();
  92. } else {
  93. processLeases<Lease6, CSVLeaseFile6, Lease6Storage>();
  94. }
  95. } catch (const std::exception& proc_ex) {
  96. // We don't want to do the cleanup but do want to get rid of the pid
  97. do_rotate = false;
  98. LOG_FATAL(lfc_logger, LFC_FAIL_PROCESS).arg(proc_ex.what());
  99. }
  100. }
  101. // If do_rotate is true We either already had a finish file or
  102. // were able to create one. We now want to do the file cleanup,
  103. // we don't want to return after the catch as we
  104. // still need to cleanup the pid file
  105. if (do_rotate) {
  106. LOG_INFO(lfc_logger, LFC_ROTATING);
  107. try {
  108. fileRotate();
  109. } catch (const RunTimeFail& run_ex) {
  110. LOG_FATAL(lfc_logger, LFC_FAIL_ROTATE).arg(run_ex.what());
  111. }
  112. }
  113. // delete the pid file for this instance
  114. try {
  115. pid_file.deleteFile();
  116. } catch (const PIDFileError& pid_ex) {
  117. LOG_FATAL(lfc_logger, LFC_FAIL_PID_DEL).arg(pid_ex.what());
  118. }
  119. LOG_INFO(lfc_logger, LFC_TERMINATE);
  120. }
  121. void
  122. LFCController::parseArgs(int argc, char* argv[]) {
  123. int ch;
  124. opterr = 0;
  125. optind = 1;
  126. while ((ch = getopt(argc, argv, ":46dhvVWp:x:i:o:c:f:")) != -1) {
  127. switch (ch) {
  128. case '4':
  129. // Process DHCPv4 lease files.
  130. protocol_version_ = 4;
  131. break;
  132. case '6':
  133. // Process DHCPv6 lease files.
  134. protocol_version_ = 6;
  135. break;
  136. case 'v':
  137. // Print just Kea version and exit.
  138. std::cout << getVersion(false) << std::endl;
  139. exit(EXIT_SUCCESS);
  140. case 'V':
  141. // Print extended Kea version and exit.
  142. std::cout << getVersion(true) << std::endl;
  143. exit(EXIT_SUCCESS);
  144. case 'W':
  145. // Display the configuration report and exit.
  146. std::cout << isc::detail::getConfigReport() << std::endl;
  147. exit(EXIT_SUCCESS);
  148. case 'd':
  149. // Verbose output.
  150. verbose_ = true;
  151. break;
  152. case 'p':
  153. // PID file name.
  154. if (optarg == NULL) {
  155. isc_throw(InvalidUsage, "PID file name missing");
  156. }
  157. pid_file_ = optarg;
  158. break;
  159. case 'x':
  160. // Previous (or ex) file name.
  161. if (optarg == NULL) {
  162. isc_throw(InvalidUsage, "Previous (ex) file name missing");
  163. }
  164. previous_file_ = optarg;
  165. break;
  166. case 'i':
  167. // Copy file name.
  168. if (optarg == NULL) {
  169. isc_throw(InvalidUsage, "Copy file name missing");
  170. }
  171. copy_file_ = optarg;
  172. break;
  173. case 'o':
  174. // Output file name.
  175. if (optarg == NULL) {
  176. isc_throw(InvalidUsage, "Output file name missing");
  177. }
  178. output_file_ = optarg;
  179. break;
  180. case 'f':
  181. // Finish file name.
  182. if (optarg == NULL) {
  183. isc_throw(InvalidUsage, "Finish file name missing");
  184. }
  185. finish_file_ = optarg;
  186. break;
  187. case 'c':
  188. // Configuration file name
  189. if (optarg == NULL) {
  190. isc_throw(InvalidUsage, "Configuration file name missing");
  191. }
  192. config_file_ = optarg;
  193. break;
  194. case 'h':
  195. usage("");
  196. exit(EXIT_SUCCESS);
  197. case '?':
  198. // Unknown argument
  199. // note this will catch all the previous ... name missing
  200. isc_throw(InvalidUsage, "Unknown argument");
  201. case ':':
  202. // Missing option argument
  203. isc_throw(InvalidUsage, "Missing option argument");
  204. default:
  205. // I don't think we should get here as the unknown arguments
  206. // and missing options cases should cover everything else
  207. isc_throw(InvalidUsage, "Invalid command line");
  208. }
  209. }
  210. // Check for extraneous parameters.
  211. if (argc > optind) {
  212. isc_throw(InvalidUsage, "Extraneous parameters.");
  213. }
  214. if (protocol_version_ == 0) {
  215. isc_throw(InvalidUsage, "DHCP version required");
  216. }
  217. if (pid_file_.empty()) {
  218. isc_throw(InvalidUsage, "PID file not specified");
  219. }
  220. if (previous_file_.empty()) {
  221. isc_throw(InvalidUsage, "Previous file not specified");
  222. }
  223. if (copy_file_.empty()) {
  224. isc_throw(InvalidUsage, "Copy file not specified");
  225. }
  226. if (output_file_.empty()) {
  227. isc_throw(InvalidUsage, "Output file not specified");
  228. }
  229. if (finish_file_.empty()) {
  230. isc_throw(InvalidUsage, "Finish file not specified");
  231. }
  232. if (config_file_.empty()) {
  233. isc_throw(InvalidUsage, "Config file not specified");
  234. }
  235. // If verbose is set echo the input information
  236. if (verbose_) {
  237. std::cout << "Protocol version: DHCPv" << protocol_version_ << std::endl
  238. << "Previous or ex lease file: " << previous_file_ << std::endl
  239. << "Copy lease file: " << copy_file_ << std::endl
  240. << "Output lease file: " << output_file_ << std::endl
  241. << "Finish file: " << finish_file_ << std::endl
  242. << "Config file: " << config_file_ << std::endl
  243. << "PID file: " << pid_file_ << std::endl
  244. << std::endl;
  245. }
  246. }
  247. void
  248. LFCController::usage(const std::string& text) {
  249. if (!text.empty()) {
  250. std::cerr << "Usage error: " << text << std::endl;
  251. }
  252. std::cerr << "Usage: " << lfc_bin_name_ << std::endl
  253. << " [-4|-6] -p file -x file -i file -o file -f file -c file" << std::endl
  254. << " -4 or -6 clean a set of v4 or v6 lease files" << std::endl
  255. << " -p <file>: PID file" << std::endl
  256. << " -x <file>: previous or ex lease file" << std::endl
  257. << " -i <file>: copy of lease file" << std::endl
  258. << " -o <file>: output lease file" << std::endl
  259. << " -f <file>: finish file" << std::endl
  260. << " -c <file>: configuration file" << std::endl
  261. << " -v: print version number and exit" << std::endl
  262. << " -V: print extended version information and exit" << std::endl
  263. << " -d: optional, verbose output " << std::endl
  264. << " -h: print this message " << std::endl
  265. << std::endl;
  266. }
  267. std::string
  268. LFCController::getVersion(const bool extended) const{
  269. std::stringstream version_stream;
  270. version_stream << VERSION;
  271. if (extended) {
  272. version_stream << std::endl << EXTENDED_VERSION << std::endl
  273. << "database: " << isc::dhcp::Memfile_LeaseMgr::getDBVersion();
  274. }
  275. return (version_stream.str());
  276. }
  277. template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
  278. void
  279. LFCController::processLeases() const {
  280. StorageType storage;
  281. // If a previous file exists read the entries into storage
  282. LeaseFileType lf_prev(getPreviousFile());
  283. if (lf_prev.exists()) {
  284. LeaseFileLoader::load<LeaseObjectType>(lf_prev, storage,
  285. MAX_LEASE_ERRORS);
  286. }
  287. // Follow that with the copy of the current lease file
  288. LeaseFileType lf_copy(getCopyFile());
  289. if (lf_copy.exists()) {
  290. LeaseFileLoader::load<LeaseObjectType>(lf_copy, storage,
  291. MAX_LEASE_ERRORS);
  292. }
  293. // Write the result out to the output file
  294. LeaseFileType lf_output(getOutputFile());
  295. LeaseFileLoader::write<LeaseObjectType>(lf_output, storage);
  296. // If desired log the stats
  297. LOG_INFO(lfc_logger, LFC_READ_STATS)
  298. .arg(lf_prev.getReadLeases() + lf_copy.getReadLeases())
  299. .arg(lf_prev.getReads() + lf_copy.getReads())
  300. .arg(lf_prev.getReadErrs() + lf_copy.getReadErrs());
  301. LOG_INFO(lfc_logger, LFC_WRITE_STATS)
  302. .arg(lf_output.getWriteLeases())
  303. .arg(lf_output.getWrites())
  304. .arg(lf_output.getWriteErrs());
  305. // Once we've finished the output file move it to the complete file
  306. if (rename(getOutputFile().c_str(), getFinishFile().c_str()) != 0) {
  307. isc_throw(RunTimeFail, "Unable to move output (" << output_file_
  308. << ") to complete (" << finish_file_
  309. << ") error: " << strerror(errno));
  310. }
  311. }
  312. void
  313. LFCController::fileRotate() const {
  314. // Remove the old previous file
  315. if ((remove(getPreviousFile().c_str()) != 0) &&
  316. (errno != ENOENT)) {
  317. isc_throw(RunTimeFail, "Unable to delete previous file '"
  318. << previous_file_ << "' error: " << strerror(errno));
  319. }
  320. // Remove the copy file
  321. if ((remove(getCopyFile().c_str()) != 0) &&
  322. (errno != ENOENT)) {
  323. isc_throw(RunTimeFail, "Unable to delete copy file '"
  324. << copy_file_ << "' error: " << strerror(errno));
  325. }
  326. // Rename the finish file to be the previous file
  327. if (rename(finish_file_.c_str(), previous_file_.c_str()) != 0) {
  328. isc_throw(RunTimeFail, "Unable to move finish (" << finish_file_
  329. << ") to previous (" << previous_file_
  330. << ") error: " << strerror(errno));
  331. }
  332. }
  333. void
  334. LFCController::startLogger(const bool test_mode) const {
  335. // If we are running in test mode use the environment variables
  336. // else use our defaults
  337. if (test_mode) {
  338. initLogger();
  339. }
  340. else {
  341. OutputOption option;
  342. LoggerManager manager;
  343. initLogger(lfc_app_name_, INFO, 0, NULL, false);
  344. // Prepare the objects to define the logging specification
  345. LoggerSpecification spec(getRootLoggerName(),
  346. keaLoggerSeverity(INFO),
  347. keaLoggerDbglevel(0));
  348. // If we are running in verbose (debugging) mode
  349. // we send the output to the console, otherwise
  350. // by default we send it to the SYSLOG
  351. if (verbose_) {
  352. option.destination = OutputOption::DEST_CONSOLE;
  353. } else {
  354. option.destination = OutputOption::DEST_SYSLOG;
  355. }
  356. // ... and set the destination
  357. spec.addOutputOption(option);
  358. manager.process(spec);
  359. }
  360. }
  361. }; // namespace isc::lfc
  362. }; // namespace isc