diff options
Diffstat (limited to 'src/bin/dhcp6/main.cc')
-rw-r--r-- | src/bin/dhcp6/main.cc | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/src/bin/dhcp6/main.cc b/src/bin/dhcp6/main.cc new file mode 100644 index 0000000..fcfff7d --- /dev/null +++ b/src/bin/dhcp6/main.cc @@ -0,0 +1,322 @@ +// Copyright (C) 2011-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <kea_version.h> + +#include <dhcp6/ctrl_dhcp6_srv.h> +#include <dhcp6/dhcp6_log.h> +#include <dhcp6/parser_context.h> +#include <dhcp6/json_config_parser.h> +#include <dhcpsrv/cfgmgr.h> +#include <exceptions/exceptions.h> +#include <log/logger_support.h> +#include <log/logger_manager.h> +#include <log/output_option.h> +#include <process/cfgrpt/config_report.h> +#include <process/daemon.h> + +#include <boost/lexical_cast.hpp> + +#include <iostream> + +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::process; +using namespace std; + +/// This file contains entry point (main() function) for standard DHCPv6 server +/// component of Kea software suite. It parses command-line arguments and +/// instantiates ControlledDhcpv6Srv class that is responsible for establishing +/// connection with msgq (receiving commands and configuration) and also +/// creating Dhcpv6 server object as well. +/// +/// For detailed explanation or relations between main(), ControlledDhcpv6Srv, +/// Dhcpv6Srv and other classes, see \ref dhcpv6Session. + +namespace { + +const char* const DHCP6_NAME = "kea-dhcp6"; + +/// @brief Prints Kea Usage and exits +/// +/// Note: This function never returns. It terminates the process. +void +usage() { + cerr << "Kea DHCPv6 server, " + << "version " << VERSION + << " (" << PACKAGE_VERSION_TYPE << ")" + << endl; + cerr << endl; + cerr << "Usage: " << DHCP6_NAME + << " -[v|V|W] [-d] [-{c|t|T} cfgfile] [-p number] [-P number]" << endl; + cerr << " -v: print version number and exit" << endl; + cerr << " -V: print extended version and exit" << endl; + cerr << " -W: display the configuration report and exit" << endl; + cerr << " -d: debug mode with extra verbosity (former -v)" << endl; + cerr << " -c file: specify configuration file" << endl; + cerr << " -t file: check the configuration file syntax and exit" << endl; + cerr << " -T file: check the configuration file doing hooks load and extra " + << "checks and exit" << endl; + cerr << " -p number: specify non-standard server port number 1-65535 " + << "(useful for testing only)" << endl; + cerr << " -P number: specify non-standard client port number 1-65535 " + << "(useful for testing only)" << endl; + exit(EXIT_FAILURE); +} +} // namespace + +int +main(int argc, char* argv[]) { + int ch; + // The default. Any other values are useful for testing only. + int server_port_number = DHCP6_SERVER_PORT; + // Not zero values are useful for testing only. + int client_port_number = 0; + bool verbose_mode = false; // Should server be verbose? + bool check_mode = false; // Check syntax + bool load_hooks = false; // Check hooks config + + // The standard config file + std::string config_file(""); + + while ((ch = getopt(argc, argv, "dvVWc:p:P:t:T:")) != -1) { + switch (ch) { + case 'd': + verbose_mode = true; + break; + + case 'v': + cout << Dhcpv6Srv::getVersion(false) << endl; + return (EXIT_SUCCESS); + + case 'V': + cout << Dhcpv6Srv::getVersion(true) << endl; + return (EXIT_SUCCESS); + + case 'W': + cout << isc::detail::getConfigReport() << endl; + return (EXIT_SUCCESS); + + case 'T': + load_hooks = true; + check_mode = true; + config_file = optarg; + break; + + case 't': + check_mode = true; + config_file = optarg; + break; + + case 'c': // config file + config_file = optarg; + break; + + case 'p': // server port number + try { + server_port_number = boost::lexical_cast<int>(optarg); + } catch (const boost::bad_lexical_cast &) { + cerr << "Failed to parse server port number: [" << optarg + << "], 1-65535 allowed." << endl; + usage(); + } + if (server_port_number <= 0 || server_port_number > 65535) { + cerr << "Failed to parse server port number: [" << optarg + << "], 1-65535 allowed." << endl; + usage(); + } + break; + + case 'P': // client port number + try { + client_port_number = boost::lexical_cast<int>(optarg); + } catch (const boost::bad_lexical_cast &) { + cerr << "Failed to parse client port number: [" << optarg + << "], 1-65535 allowed." << endl; + usage(); + } + if (client_port_number <= 0 || client_port_number > 65535) { + cerr << "Failed to parse client port number: [" << optarg + << "], 1-65535 allowed." << endl; + usage(); + } + break; + + default: + usage(); + } + } + + // Check for extraneous parameters. + if (argc > optind) { + usage(); + } + + // Configuration file is required. + if (config_file.empty()) { + cerr << "Configuration file not specified." << endl; + usage(); + } + + // This is the DHCPv6 server + CfgMgr::instance().setFamily(AF_INET6); + + if (check_mode) { + try { + // We need to initialize logging, in case any error messages are to be printed. + // This is just a test, so we don't care about lockfile. + setenv("KEA_LOCKFILE_DIR", "none", 0); + Daemon::setDefaultLoggerName(DHCP6_ROOT_LOGGER_NAME); + Daemon::loggerInit(DHCP6_ROOT_LOGGER_NAME, verbose_mode); + + // Check the syntax first. + Parser6Context parser; + ConstElementPtr json; + json = parser.parseFile(config_file, Parser6Context::PARSER_DHCP6); + if (!json) { + cerr << "No configuration found" << endl; + return (EXIT_FAILURE); + } + if (verbose_mode) { + cerr << "Syntax check OK" << endl; + } + + // Check the logic next. + ConstElementPtr dhcp6 = json->get("Dhcp6"); + if (!dhcp6) { + cerr << "Missing mandatory Dhcp6 element" << endl; + return (EXIT_FAILURE); + } + ControlledDhcpv6Srv server(0); + ConstElementPtr answer; + + server.setProcName(DHCP6_NAME); + + // Now we pass the Dhcp6 configuration to the server, but + // tell it to check the configuration only (check_only = true) + answer = configureDhcp6Server(server, dhcp6, true, load_hooks); + + int status_code = 0; + answer = isc::config::parseAnswer(status_code, answer); + if (status_code == 0) { + return (EXIT_SUCCESS); + } else { + cerr << "Error encountered: " << answer->stringValue() << endl; + return (EXIT_FAILURE); + } + } catch (const std::exception& ex) { + cerr << "Syntax check failed with: " << ex.what() << endl; + } + return (EXIT_FAILURE); + } + + int ret = EXIT_SUCCESS; + try { + // It is important that we set a default logger name because this name + // will be used when the user doesn't provide the logging configuration + // in the Kea configuration file. + Daemon::setDefaultLoggerName(DHCP6_ROOT_LOGGER_NAME); + + // Initialize logging. If verbose, we'll use maximum verbosity. + Daemon::loggerInit(DHCP6_ROOT_LOGGER_NAME, verbose_mode); + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO) + .arg(getpid()) + .arg(server_port_number) + .arg(client_port_number) + .arg(verbose_mode ? "yes" : "no"); + + LOG_INFO(dhcp6_logger, DHCP6_STARTING) + .arg(VERSION) + .arg(PACKAGE_VERSION_TYPE); + + if (string(PACKAGE_VERSION_TYPE) == "development") { + LOG_WARN(dhcp6_logger, DHCP6_DEVELOPMENT_VERSION); + } + + // Create the server instance. + ControlledDhcpv6Srv server(server_port_number, client_port_number); + + // Remember verbose-mode + server.setVerbose(verbose_mode); + + // Create our PID file. + server.setProcName(DHCP6_NAME); + server.setConfigFile(config_file); + server.createPIDFile(); + + try { + // Initialize the server. + server.init(config_file); + } catch (const std::exception& ex) { + + // Let's log out what went wrong. + try { + // Log with the current logger, but only if it's not + // configured with console output so as to not log twice. + if (!dhcp6_logger.hasAppender(isc::log::OutputOption::DEST_CONSOLE)) { + LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what()); + } + + // Log on the console as well. + isc::log::LoggerManager log_manager; + log_manager.process(); + LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what()); + } catch (...) { + // The exception thrown during the initialization could + // originate from logger subsystem. Therefore LOG_ERROR() + // may fail as well. + cerr << "Failed to initialize server: " << ex.what() << endl; + } + + return (EXIT_FAILURE); + } + + // Tell the admin we are ready to process packets + LOG_INFO(dhcp6_logger, DHCP6_STARTED).arg(VERSION); + + // And run the main loop of the server. + ret = server.run(); + + LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN); + + } catch (const isc::process::DaemonPIDExists& ex) { + // First, we print the error on stderr (that should always work) + cerr << DHCP6_NAME << " already running? " << ex.what() + << endl; + + // Let's also try to log it using logging system, but we're not + // sure if it's usable (the exception may have been thrown from + // the logger subsystem) + try { + LOG_FATAL(dhcp6_logger, DHCP6_ALREADY_RUNNING) + .arg(DHCP6_NAME).arg(ex.what()); + } catch (...) { + // Already logged so ignore + } + ret = EXIT_FAILURE; + } catch (const std::exception& ex) { + // First, we print the error on stderr (that should always work) + cerr << DHCP6_NAME << ": Fatal error during start up: " << ex.what() + << endl; + + // Let's also try to log it using logging system, but we're not + // sure if it's usable (the exception may have been thrown from + // the logger subsystem) + try { + LOG_FATAL(dhcp6_logger, DHCP6_SERVER_FAILED).arg(ex.what()); + } catch (...) { + // Already logged so ignore + } + ret = EXIT_FAILURE; + } catch (...) { + cerr << DHCP6_NAME << ": Fatal error during start up" + << endl; + ret = EXIT_FAILURE; + } + + return (ret); +} |