summaryrefslogtreecommitdiffstats
path: root/src/lib/process/d_controller.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/process/d_controller.h663
1 files changed, 663 insertions, 0 deletions
diff --git a/src/lib/process/d_controller.h b/src/lib/process/d_controller.h
new file mode 100644
index 0000000..e16253e
--- /dev/null
+++ b/src/lib/process/d_controller.h
@@ -0,0 +1,663 @@
+// Copyright (C) 2013-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/.
+
+#ifndef D_CONTROLLER_H
+#define D_CONTROLLER_H
+
+#include <asiolink/io_service.h>
+#include <asiolink/io_service_signal.h>
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <log/logger_support.h>
+#include <process/daemon.h>
+#include <process/d_log.h>
+#include <process/d_process.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <string>
+#include <set>
+
+namespace isc {
+namespace process {
+
+/// @brief Exception thrown when the command line is invalid.
+/// Can be used to transmit negative messages too.
+class InvalidUsage : public isc::Exception {
+public:
+ InvalidUsage(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception used to convey version info upwards.
+/// Since command line argument parsing is done as part of
+/// DControllerBase::launch(), it uses this exception to propagate
+/// version information up to main(), when command line argument
+/// -v, -V or -W is given. Can be used to transmit positive messages too.
+class VersionMessage : public isc::Exception {
+public:
+ VersionMessage(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the controller launch fails.
+class LaunchError: public isc::Exception {
+public:
+ LaunchError (const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the application process fails.
+class ProcessInitError: public isc::Exception {
+public:
+ ProcessInitError (const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the application process encounters an
+/// operation in its event loop (i.e. run method).
+class ProcessRunError: public isc::Exception {
+public:
+ ProcessRunError (const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Exception thrown when the controller encounters an operational error.
+class DControllerBaseError : public isc::Exception {
+public:
+ DControllerBaseError (const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Defines a shared pointer to DControllerBase.
+class DControllerBase;
+typedef boost::shared_ptr<DControllerBase> DControllerBasePtr;
+
+/// @brief Application Controller
+///
+/// DControllerBase is an abstract singleton which provides the framework and
+/// services for managing an application process that implements the
+/// DProcessBase interface. It runs the process like a stand-alone, command
+/// line driven executable, which must be supplied a configuration file at
+/// startup. It coordinates command line argument parsing, process
+/// instantiation and initialization, and runtime control through external
+/// command and configuration event handling.
+/// It creates the IOService instance which is used for runtime control
+/// events and passes the IOService into the application process at process
+/// creation.
+/// It provides the callback handlers for command and configuration events
+/// which could be triggered by an external source. Such sources are intended
+/// to be registered with and monitored by the controller's IOService such that
+/// the appropriate handler can be invoked.
+///
+/// DControllerBase provides dynamic configuration file reloading upon receipt
+/// of SIGHUP, and graceful shutdown upon receipt of either SIGINT or SIGTERM.
+///
+/// NOTE: Derivations must supply their own static singleton instance method(s)
+/// for creating and fetching the instance. The base class declares the instance
+/// member in order for it to be available for static callback functions.
+class DControllerBase : public Daemon {
+public:
+ /// @brief Constructor
+ ///
+ /// @param app_name is display name of the application under control. This
+ /// name appears in log statements.
+ /// @param bin_name is the name of the application executable.
+ DControllerBase(const char* app_name, const char* bin_name);
+
+ /// @brief Destructor
+ virtual ~DControllerBase();
+
+ /// @brief returns Kea version on stdout and exit.
+ /// redeclaration/redefinition. @ref isc::process::Daemon::getVersion()
+ std::string getVersion(bool extended);
+
+ /// @brief Acts as the primary entry point into the controller execution
+ /// and provides the outermost application control logic:
+ ///
+ /// 1. parse command line arguments
+ /// 2. instantiate and initialize the application process
+ /// 3. load the configuration file
+ /// 4. record the start timestamp
+ /// 5. initialize signal handling
+ /// 6. start and wait on the application process event loop
+ /// 7. exit to the caller
+ ///
+ /// It is intended to be called from main() and be given the command line
+ /// arguments.
+ ///
+ /// This function can be run in "test mode". It prevents initialization
+ /// of module logger. This is used in unit tests which initialize logger
+ /// in their main function. Such a logger uses environmental variables to
+ /// control severity, verbosity etc.
+ ///
+ /// @param argc is the number of command line arguments supplied
+ /// @param argv is the array of string (char *) command line arguments
+ /// @param test_mode is a bool value which indicates if
+ /// @c DControllerBase::launch should be run in the test mode (if true).
+ /// This parameter doesn't have default value to force test implementers to
+ /// enable test mode explicitly.
+ ///
+ /// @throw throws one of the following exceptions:
+ /// InvalidUsage - Indicates invalid command line.
+ /// ProcessInitError - Failed to create and initialize application
+ /// process object.
+ /// ProcessRunError - A fatal error occurred while in the application
+ /// process event loop.
+ /// @return The value from @c Daemon::getExitValue().
+ virtual int launch(int argc, char* argv[], const bool test_mode);
+
+ /// @brief Instance method invoked by the configuration event handler and
+ /// which processes the actual configuration update. Provides behavioral
+ /// path for both integrated and stand-alone modes. The current
+ /// implementation will merge the configuration update into the existing
+ /// configuration and then invoke the application process' configure method.
+ ///
+ /// @param new_config is the new configuration
+ ///
+ /// @return returns an Element that contains the results of configuration
+ /// update composed of an integer status value (0 means successful,
+ /// non-zero means failure), and a string explanation of the outcome.
+ virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
+ new_config);
+
+ /// @brief Instance method invoked by the configuration event handler and
+ /// which processes the actual configuration check. Provides behavioral
+ /// path for both integrated and stand-alone modes. The current
+ /// implementation will merge the configuration update into the existing
+ /// configuration and then invoke the application process' configure method
+ /// with a final rollback.
+ ///
+ /// @param new_config is the new configuration
+ ///
+ /// @return returns an Element that contains the results of configuration
+ /// update composed of an integer status value (0 means successful,
+ /// non-zero means failure), and a string explanation of the outcome.
+ virtual isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr
+ new_config);
+
+ /// @brief Reconfigures the process from a configuration file
+ ///
+ /// By default the file is assumed to be a JSON text file whose contents
+ /// include at least:
+ ///
+ /// @code
+ /// { "<module-name>": {<module-config>}
+ /// }
+ ///
+ /// where:
+ /// module-name : is a label which uniquely identifies the
+ /// configuration data for this controller's application
+ ///
+ /// module-config: a set of zero or more JSON elements which comprise
+ /// the application's configuration values
+ /// @endcode
+ ///
+ /// To translate the JSON content into Elements, @c parseFile() is called
+ /// first. This virtual method provides derivations a means to parse the
+ /// file content using an alternate parser. If it returns an empty pointer
+ /// than the JSON parsing providing by Element::fromJSONFile() is called.
+ ///
+ /// Once parsed, the method extracts the set of configuration
+ /// elements for the module-name that matches the controller's app_name_,
+ /// looks for the loggers entry and, if present uses it to configure
+ /// logging. It then passes that set into @c updateConfig() (or
+ /// @c checkConfig()).
+ ///
+ /// The file may contain an arbitrary number of other modules.
+ ///
+ /// @return returns an Element that contains the results of configuration
+ /// update composed of an integer status value (0 means successful,
+ /// non-zero means failure), and a string explanation of the outcome.
+ virtual isc::data::ConstElementPtr configFromFile();
+
+ /// @brief Fetches the name of the application under control.
+ ///
+ /// @return returns the controller service name string
+ std::string getAppName() const {
+ return (app_name_);
+ }
+
+ /// @brief Fetches the name of the application executable.
+ ///
+ /// @return returns the controller logger name string
+ std::string getBinName() const {
+ return (bin_name_);
+ }
+
+ /// @brief handler for version-get command
+ ///
+ /// This method handles the version-get command. It returns the basic and
+ /// extended version.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return answer with version details.
+ isc::data::ConstElementPtr
+ versionGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for 'build-report' command
+ ///
+ /// This method handles build-report command. It returns the output printed
+ /// by configure script which contains most compilation parameters.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return answer with build report
+ isc::data::ConstElementPtr
+ buildReportHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for config-get command
+ ///
+ /// This method handles the config-get command, which retrieves
+ /// the current configuration and returns it in response.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return current configuration wrapped in a response
+ isc::data::ConstElementPtr
+ configGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for config-hash-get command
+ ///
+ /// This method handles the config-hash-get command, which retrieves
+ /// the current configuration and returns it in response.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return hash of current configuration wrapped in a response
+ isc::data::ConstElementPtr
+ configHashGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for config-write command
+ ///
+ /// This handle processes write-config command, which writes the
+ /// current configuration to disk. This command takes one optional
+ /// parameter called filename. If specified, the current configuration
+ /// will be written to that file. If not specified, the file used during
+ /// Kea start-up will be used. To avoid any exploits, the path is
+ /// always relative and .. is not allowed in the filename. This is
+ /// a security measure against exploiting file writes remotely.
+ ///
+ /// @param command (ignored)
+ /// @param args may contain optional string argument filename
+ /// @return status of the configuration file write
+ isc::data::ConstElementPtr
+ configWriteHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for config-test command
+ ///
+ /// This method handles the config-test command, which checks
+ /// configuration specified in args parameter.
+ ///
+ /// @param command (ignored)
+ /// @param args configuration to be checked.
+ /// @return status of the command
+ isc::data::ConstElementPtr
+ configTestHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for config-reload command
+ ///
+ /// This method handles the config-reload command, which reloads
+ /// the configuration file.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return status of the command
+ isc::data::ConstElementPtr
+ configReloadHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for config-set command
+ ///
+ /// This method handles the config-set command, which loads
+ /// configuration specified in args parameter.
+ ///
+ /// @param command (ignored)
+ /// @param args configuration to be checked.
+ /// @return status of the command
+ isc::data::ConstElementPtr
+ configSetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for 'shutdown' command
+ ///
+ /// This method handles shutdown command. It initiates the shutdown procedure
+ /// using CPL methods.
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return answer confirming that the shutdown procedure is started
+ isc::data::ConstElementPtr
+ shutdownHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for server-tag-get command
+ ///
+ /// This method handles the server-tag-get command, which retrieves
+ /// the current server tag and returns it in response.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return current configuration wrapped in a response
+ isc::data::ConstElementPtr
+ serverTagGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+ /// @brief handler for status-get command
+ ///
+ /// This method handles the status-get command, which retrieves
+ /// the server process information i.e. the pid and returns it in
+ /// response.
+ ///
+ /// @param command (ignored)
+ /// @param args (ignored)
+ /// @return process information wrapped in a response
+ isc::data::ConstElementPtr
+ statusGetHandler(const std::string& command,
+ isc::data::ConstElementPtr args);
+
+protected:
+ /// @brief Virtual method that provides derivations the opportunity to
+ /// support additional command line options. It is invoked during command
+ /// line argument parsing (see parseArgs method) if the option is not
+ /// recognized as a stock DControllerBase option.
+ ///
+ /// @param option is the option "character" from the command line, without
+ /// any prefixing hyphen(s)
+ /// @param optarg is the argument value (if one) associated with the option
+ ///
+ /// @return must return true if the option was valid, false if it is
+ /// invalid. (Note the default implementation always returns false.)
+ virtual bool customOption(int option, char *optarg);
+
+ /// @brief Abstract method that is responsible for instantiating the
+ /// application process object. It is invoked by the controller after
+ /// command line argument parsing as part of the process initialization
+ /// (see initProcess method).
+ ///
+ /// @return returns a pointer to the new process object (DProcessBase*)
+ /// or NULL if the create fails.
+ /// Note this value is subsequently wrapped in a smart pointer.
+ virtual DProcessBase* createProcess() = 0;
+
+ /// @brief Virtual method which can be used to contribute derivation
+ /// specific usage text. It is invoked by the usage() method under
+ /// invalid usage conditions.
+ ///
+ /// @return returns the desired text.
+ virtual const std::string getUsageText() const {
+ return ("");
+ }
+
+ /// @brief Virtual method which returns a string containing the option
+ /// letters for any custom command line options supported by the derivation.
+ /// These are added to the stock options of "c", "d", ..., during command
+ /// line interpretation.
+ ///
+ /// @return returns a string containing the custom option letters.
+ virtual const std::string getCustomOpts() const {
+ return ("");
+ }
+
+ /// @brief Check the configuration
+ ///
+ /// Called by @c launch() when @c check_only_ mode is enabled
+ /// @throw VersionMessage when successful but a message should be displayed
+ /// @throw InvalidUsage when an error was detected
+ void checkConfigOnly();
+
+ /// @brief Application-level signal processing method.
+ ///
+ /// This method is the last step in processing a OS signal occurrence.
+ /// It currently supports the following signals as follows:
+ /// -# SIGHUP - instigates reloading the configuration file
+ /// -# SIGINT - instigates a graceful shutdown
+ /// -# SIGTERM - instigates a graceful shutdown
+ /// If it receives any other signal, it will issue a debug statement and
+ /// discard it.
+ /// Derivations wishing to support additional signals could override this
+ /// method with one that: processes the signal if it is one of additional
+ /// signals, otherwise invoke this method (DControllerBase::processSignal())
+ /// with the signal value.
+ /// @todo Provide a convenient way for derivations to register additional
+ /// signals.
+ virtual void processSignal(int signum);
+
+ /// @brief Supplies whether or not verbose logging is enabled.
+ ///
+ /// @return returns true if verbose logging is enabled.
+ bool isVerbose() const {
+ return (verbose_);
+ }
+
+ /// @brief Method for enabling or disabling verbose logging.
+ ///
+ /// @param value is the new value to assign the flag.
+ void setVerbose(bool value) {
+ verbose_ = value;
+ }
+
+ /// @brief Supplies whether or not check only mode is enabled.
+ ///
+ /// @return returns true if check only is enabled.
+ bool isCheckOnly() const {
+ return (check_only_);
+ }
+
+ /// @brief Method for enabling or disabling check only mode.
+ ///
+ /// @todo this method and @c setVerbose are currently not used.
+ ///
+ /// @param value is the new value to assign the flag.
+ void setCheckOnly(bool value) {
+ check_only_ = value;
+ }
+
+ /// @brief Getter for fetching the controller's IOService
+ ///
+ /// @return returns a pointer reference to the IOService.
+ asiolink::IOServicePtr& getIOService() {
+ return (io_service_);
+ }
+
+ /// @brief Static getter which returns the singleton instance.
+ ///
+ /// @return returns a pointer reference to the private singleton instance
+ /// member.
+ static DControllerBasePtr& getController() {
+ return (controller_);
+ }
+
+ /// @brief Static setter which sets the singleton instance.
+ ///
+ /// @param controller is a pointer to the singleton instance.
+ ///
+ /// @throw throws DControllerBase error if an attempt is made to set the
+ /// instance a second time.
+ static void setController(const DControllerBasePtr& controller);
+
+ /// @brief Processes the command line arguments. It is the first step
+ /// taken after the controller has been launched. It combines the stock
+ /// list of options with those returned by getCustomOpts(), and uses
+ /// cstdlib's getopt to loop through the command line.
+ /// It handles stock options directly, and passes any custom options into
+ /// the customOption method. Currently there are only some stock options
+ /// -c/t for specifying the configuration file, -d for verbose logging,
+ /// and -v/V/W for version reports.
+ ///
+ /// @param argc is the number of command line arguments supplied
+ /// @param argv is the array of string (char *) command line arguments
+ ///
+ /// @throw InvalidUsage when there are usage errors.
+ /// @throw VersionMessage if the -v, -V or -W arguments is given.
+ void parseArgs(int argc, char* argv[]);
+
+ ///@brief Parse a given file into Elements
+ ///
+ /// This method provides a means for deriving classes to use alternate
+ /// parsing mechanisms to parse configuration files into the corresponding
+ /// isc::data::Elements. The elements produced must be equivalent to those
+ /// which would be produced by the original JSON parsing. Implementations
+ /// should throw when encountering errors.
+ ///
+ /// The default implementation returns an empty pointer, signifying to
+ /// callers that they should submit the file to the original parser.
+ ///
+ /// @param file_name pathname of the file to parse
+ ///
+ /// @return pointer to the elements created
+ ///
+ virtual isc::data::ConstElementPtr parseFile(const std::string& file_name);
+
+ ///@brief Parse text into Elements
+ ///
+ /// This method provides a means for deriving classes to use alternate
+ /// parsing mechanisms to parse configuration text into the corresponding
+ /// isc::data::Elements. The elements produced must be equivalent to those
+ /// which would be produced by the original JSON parsing. Implementations
+ /// should throw when encountering errors.
+ ///
+ /// The default implementation returns an empty pointer, signifying to
+ /// callers that they should submit the text to the original parser.
+ ///
+ /// @param input text to parse
+ ///
+ /// @return pointer to the elements created
+ ///
+ virtual isc::data::ConstElementPtr parseText(const std::string& input) {
+ static_cast<void>(input); // just tu shut up the unused parameter warning
+ isc::data::ConstElementPtr elements;
+ return (elements);
+ }
+
+ /// @brief Instantiates the application process and then initializes it.
+ /// This is the second step taken during launch, following successful
+ /// command line parsing. It is used to invoke the derivation-specific
+ /// implementation of createProcess, following by an invoking of the
+ /// newly instantiated process's init method.
+ ///
+ /// @throw throws DControllerBaseError or indirectly DProcessBaseError
+ /// if there is a failure creating or initializing the application process.
+ void initProcess();
+
+ /// @brief Invokes the application process's event loop,(DBaseProcess::run).
+ /// It is called during launch only after successfully completing the
+ /// requested setup: command line parsing, application initialization,
+ /// and session establishment (if not stand-alone).
+ /// The process event loop is expected to only return upon application
+ /// shutdown either in response to the shutdown command or due to an
+ /// unrecoverable error.
+ ///
+ // @throw throws DControllerBaseError or indirectly DProcessBaseError
+ void runProcess();
+
+ /// @brief Initiates shutdown procedure. This method is invoked
+ /// by executeCommand in response to the shutdown command. It will invoke
+ /// the application process's shutdown method which causes the process to
+ /// to begin its shutdown process.
+ ///
+ /// Note, it is assumed that the process of shutting down is neither
+ /// instantaneous nor synchronous. This method does not "block" waiting
+ /// until the process has halted. Rather it is used to convey the
+ /// need to shutdown. A successful return indicates that the shutdown
+ /// has successfully commenced, but does not indicate that the process
+ /// has actually exited.
+ ///
+ /// @return returns an Element that contains the results of shutdown
+ /// command composed of an integer status value (0 means successful,
+ /// non-zero means failure), and a string explanation of the outcome.
+ ///
+ /// @param args is a set of derivation-specific arguments (if any)
+ /// for the shutdown command.
+ isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args);
+
+ /// @brief Initializes signal handling
+ ///
+ /// This method configures the controller to catch and handle signals.
+ /// It instantiates a IOSignalSet which listens for SIGHUP, SIGINT, and
+ /// SIGTERM.
+ void initSignalHandling();
+
+ /// @brief Fetches the current process
+ ///
+ /// @return a pointer to the current process instance.
+ DProcessBasePtr getProcess() {
+ return (process_);
+ }
+
+ /// @brief Prints the program usage text to std error.
+ ///
+ /// @param text is a string message which will preceded the usage text.
+ /// This is intended to be used for specific usage violation messages.
+ void usage(const std::string& text);
+
+ /// @brief Fetches text containing additional version specifics
+ ///
+ /// This method is provided so derivations can append any additional
+ /// desired information such as library dependencies to the extended
+ /// version text returned when DControllerBase::getVersion(true) is
+ /// invoked.
+ /// @return a string containing additional version info
+ virtual std::string getVersionAddendum() { return (""); }
+
+ /// @brief Deals with other (i.e. not application name) global objects.
+ ///
+ /// Code shared between configuration handlers:
+ /// - check obsolete or unknown (aka unsupported) objects.
+ ///
+ /// @param args Command arguments.
+ /// @return Error message or empty string.
+ std::string handleOtherObjects(isc::data::ConstElementPtr args);
+
+private:
+ /// @brief Name of the service under control.
+ /// This name is used as the configuration module name and appears in log
+ /// statements.
+ std::string app_name_;
+
+ /// @brief Name of the service executable.
+ /// By convention this matches the executable name. It is also used to
+ /// establish the logger name.
+ std::string bin_name_;
+
+ /// @brief Indicates if the verbose logging mode is enabled.
+ bool verbose_;
+
+ /// @brief Indicates if the check only mode for the configuration
+ /// is enabled (usually specified by the command line -t argument).
+ bool check_only_;
+
+ /// @brief Pointer to the instance of the process.
+ ///
+ /// This is required for config and command handlers to gain access to
+ /// the process
+ DProcessBasePtr process_;
+
+ /// @brief Shared pointer to an IOService object, used for ASIO operations.
+ isc::asiolink::IOServicePtr io_service_;
+
+ /// @brief ASIO signal set.
+ isc::asiolink::IOSignalSetPtr io_signal_set_;
+
+ /// @brief Singleton instance value.
+ static DControllerBasePtr controller_;
+
+// DControllerTest is named a friend class to facilitate unit testing while
+// leaving the intended member scopes intact.
+friend class DControllerTest;
+};
+
+} // namespace process
+} // namespace isc
+
+#endif