d_controller.h 21.6 KB
Newer Older
1
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6 7 8 9

#ifndef D_CONTROLLER_H
#define D_CONTROLLER_H

10
#include <asiolink/io_service.h>
11
#include <cc/data.h>
12
#include <dhcpsrv/daemon.h>
13 14
#include <exceptions/exceptions.h>
#include <log/logger_support.h>
15
#include <process/d_log.h>
16 17
#include <process/d_process.h>
#include <process/io_service_signal.h>
18 19 20 21 22 23

#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>


namespace isc {
24
namespace process {
25 26 27 28 29 30 31 32

/// @brief Exception thrown when the command line is invalid.
class InvalidUsage : public isc::Exception {
public:
    InvalidUsage(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

33 34 35 36 37 38 39 40 41 42 43
/// @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 or -V is given.
class VersionMessage : public isc::Exception {
public:
    VersionMessage(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

44 45 46 47 48 49
/// @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) { };
};
50

51 52 53 54 55 56 57
/// @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) { };
};

58
/// @brief Exception thrown when the application process encounters an
59 60 61 62 63 64 65
/// 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) { };
};

66 67 68 69 70 71 72 73
/// @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) { };
};


74
/// @brief Defines a shared pointer to DControllerBase.
75 76 77 78 79 80
class DControllerBase;
typedef boost::shared_ptr<DControllerBase> DControllerBasePtr;

/// @brief Application Controller
///
/// DControllerBase is an abstract singleton which provides the framework and
81
/// services for managing an application process that implements the
82 83 84 85 86
/// 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.
87 88
/// It creates the IOService instance which is used for runtime control
/// events and passes the IOService into the application process at process
89 90 91
/// creation.
/// It provides the callback handlers for command and configuration events
/// which could be triggered by an external source.  Such sources are intended
92
/// to be registered with and monitored by the controller's IOService such that
93
/// the appropriate handler can be invoked.
94
///
95 96 97
/// DControllerBase provides dynamic configuration file reloading upon receipt
/// of SIGHUP, and graceful shutdown upon receipt of either SIGINT or SIGTERM.
///
98 99
/// NOTE: Derivations must supply their own static singleton instance method(s)
/// for creating and fetching the instance. The base class declares the instance
100 101
/// member in order for it to be available for static callback functions.
class DControllerBase : public dhcp::Daemon {
102
public:
103
    /// @brief Constructor
104
    ///
105 106
    /// @param app_name is display name of the application under control. This
    /// name appears in log statements.
107
    /// @param bin_name is the name of the application executable.
108
    DControllerBase(const char* app_name, const char* bin_name);
109 110 111 112

    /// @brief Destructor
    virtual ~DControllerBase();

113
    /// @brief returns Kea version on stdout and exit.
114
    /// redeclaration/redefinition. @ref isc::dhcp::Daemon::getVersion()
115 116
    static std::string getVersion(bool extended);
 
117 118 119 120 121
    /// @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
122 123
    /// 3. load the configuration file
    /// 4. initialize signal handling
124 125
    /// 5. start and wait on the application process event loop
    /// 6. exit to the caller
126 127
    ///
    /// It is intended to be called from main() and be given the command line
128
    /// arguments.
129
    ///
130
    /// This function can be run in "test mode". It prevents initialization
131
    /// of D2 module logger. This is used in unit tests which initialize logger
132 133
    /// in their main function. Such a logger uses environmental variables to
    /// control severity, verbosity etc.
134
    ///
135
    /// @param argc  is the number of command line arguments supplied
136
    /// @param argv  is the array of string (char *) command line arguments
137 138 139 140
    /// @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.
141
    ///
142 143 144 145
    /// @throw throws one of the following exceptions:
    /// InvalidUsage - Indicates invalid command line.
    /// ProcessInitError  - Failed to create and initialize application
    /// process object.
146
    /// ProcessRunError - A fatal error occurred while in the application
147
    /// process event loop.
148
    virtual void launch(int argc, char* argv[], const bool test_mode);
149

150 151 152 153
    /// @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
154 155
    /// configuration and then invoke the application process' configure method.
    ///
156 157
    /// @param  new_config is the new configuration
    ///
158
    /// @return returns an Element that contains the results of configuration
159 160
    /// update composed of an integer status value (0 means successful,
    /// non-zero means failure), and a string explanation of the outcome.
161 162
    virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
                                                    new_config);
163

164 165 166 167 168 169 170 171 172 173 174 175 176
    /// @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
177
    ///                    the application's configuration values
178 179 180 181 182 183 184 185 186 187 188 189
    /// @endcode
    ///
    /// The method extracts the set of configuration elements for the
    /// module-name which matches the controller's app_name_ and passes that
    /// set into @c udpateConfig().
    ///
    /// 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();
190 191

    /// @brief Instance method invoked by the command event handler and  which
192 193
    /// processes the actual command directive.
    ///
194 195 196
    /// It supports the execution of:
    ///
    ///   1. Stock controller commands - commands common to all DControllerBase
197
    /// derivations.  Currently there is only one, the shutdown command.
198 199 200
    ///
    ///   2. Custom controller commands - commands that the deriving controller
    /// class implements.  These commands are executed by the deriving
201
    /// controller.
202 203 204
    ///
    ///   3. Custom application commands - commands supported by the application
    /// process implementation.  These commands are executed by the application
205
    /// process.
206 207 208
    ///
    /// @param command is a string label representing the command to execute.
    /// @param args is a set of arguments (if any) required for the given
209
    /// command.
210 211 212 213 214 215 216 217 218
    ///
    /// @return an Element that contains the results of command composed
    /// of an integer status value and a string explanation of the outcome.
    /// The status value is one of the following:
    ///   D2::COMMAND_SUCCESS - Command executed successfully
    ///   D2::COMMAND_ERROR - Command is valid but suffered an operational
    ///   failure.
    ///   D2::COMMAND_INVALID - Command is not recognized as valid be either
    ///   the controller or the application process.
219 220 221 222 223 224 225 226
    virtual isc::data::ConstElementPtr executeCommand(const std::string&
                                                      command,
                                                      isc::data::
                                                      ConstElementPtr args);

    /// @brief Fetches the name of the application under control.
    ///
    /// @return returns the controller service name string
227
    std::string getAppName() const {
228 229 230 231 232 233
        return (app_name_);
    }

    /// @brief Fetches the name of the application executable.
    ///
    /// @return returns the controller logger name string
234
    std::string getBinName() const {
235 236
        return (bin_name_);
    }
237 238 239 240 241

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
242
    /// recognized as a stock DControllerBase option.
243 244
    ///
    /// @param option is the option "character" from the command line, without
245
    /// any prefixing hyphen(s)
246
    /// @param optarg is the argument value (if one) associated with the option
247
    ///
248
    /// @return must return true if the option was valid, false if it is
249
    /// invalid. (Note the default implementation always returns false.)
250 251
    virtual bool customOption(int option, char *optarg);

252
    /// @brief Abstract method that is responsible for instantiating the
253
    /// application process object. It is invoked by the controller after
254
    /// command line argument parsing as part of the process initialization
255
    /// (see initProcess method).
256
    ///
257
    /// @return returns a pointer to the new process object (DProcessBase*)
258
    /// or NULL if the create fails.
259 260 261 262 263
    /// Note this value is subsequently wrapped in a smart pointer.
    virtual DProcessBase* createProcess() = 0;

    /// @brief Virtual method that provides derivations the opportunity to
    /// support custom external commands executed by the controller.  This
264
    /// method is invoked by the processCommand if the received command is
265 266 267 268
    /// not a stock controller command.
    ///
    /// @param command is a string label representing the command to execute.
    /// @param args is a set of arguments (if any) required for the given
269
    /// command.
270 271 272 273 274 275 276
    ///
    /// @return an Element that contains the results of command composed
    /// of an integer status value and a string explanation of the outcome.
    /// The status value is one of the following:
    ///   D2::COMMAND_SUCCESS - Command executed successfully
    ///   D2::COMMAND_ERROR - Command is valid but suffered an operational
    ///   failure.
277 278
    ///   D2::COMMAND_INVALID - Command is not recognized as a valid custom
    ///   controller command.
279 280 281 282 283 284 285 286
    virtual isc::data::ConstElementPtr customControllerCommand(
            const std::string& command, isc::data::ConstElementPtr args);

    /// @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.
287
    virtual const std::string getUsageText() const {
288 289 290 291
        return ("");
    }

    /// @brief Virtual method which returns a string containing the option
292
    /// letters for any custom command line options supported by the derivation.
293
    /// These are added to the stock options of "c" and "v" during command
294 295 296
    /// line interpretation.
    ///
    /// @return returns a string containing the custom option letters.
297
    virtual const std::string getCustomOpts() const {
298 299 300
        return ("");
    }

301 302 303 304 305 306 307 308
    /// @brief Application-level signal processing method.
    ///
    /// This method is the last step in processing a OS signal occurrence.  It
    /// is invoked when an IOSignal's internal timer callback is executed by
    /// IOService.  It currently supports the following signals as follows:
    /// -# SIGHUP - instigates reloading the configuration file
    /// -# SIGINT - instigates a graceful shutdown
    /// -# SIGTERM - instigates a graceful shutdown
309
    /// If it receives any other signal, it will issue a debug statement and
310 311 312 313
    /// 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())
314
    /// with the signal value.
315 316 317 318
    /// @todo Provide a convenient way for derivations to register additional
    /// signals.
    virtual void processSignal(int signum);

319
    /// @brief Supplies whether or not verbose logging is enabled.
320 321
    ///
    /// @return returns true if verbose logging is enabled.
322
    bool isVerbose() const {
323 324 325
        return (verbose_);
    }

326
    /// @brief Method for enabling or disabling verbose logging.
327 328 329 330 331 332 333 334 335
    ///
    /// @param value is the new value to assign the flag.
    void setVerbose(bool value) {
        verbose_ = value;
    }

    /// @brief Getter for fetching the controller's IOService
    ///
    /// @return returns a pointer reference to the IOService.
336
    asiolink::IOServicePtr& getIOService() {
337 338 339
        return (io_service_);
    }

340
    /// @brief Getter for fetching the name of the controller's config spec
341 342
    /// file.
    ///
343
    /// @return returns the file name string.
344
    const std::string getSpecFileName() const {
345 346 347
        return (spec_file_name_);
    }

348
    /// @brief Setter for setting the name of the controller's config spec file.
349
    ///
350
    /// @param spec_file_name the file name string.
351 352 353 354 355 356 357 358 359 360 361 362
    void setSpecFileName(const std::string& spec_file_name) {
        spec_file_name_ = spec_file_name;
    }

    /// @brief Static getter which returns the singleton instance.
    ///
    /// @return returns a pointer reference to the private singleton instance
    /// member.
    static DControllerBasePtr& getController() {
        return (controller_);
    }

363 364 365
    /// @brief Static setter which sets the singleton instance.
    ///
    /// @param controller is a pointer to the singleton instance.
366 367 368
    ///
    /// @throw throws DControllerBase error if an attempt is made to set the
    /// instance a second time.
369
    static void setController(const DControllerBasePtr& controller);
370 371 372 373

    /// @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
374
    /// cstdlib's getopt to loop through the command line.
375 376
    /// It handles stock options directly, and passes any custom options into
    /// the customOption method.  Currently there are only two stock options
377
    /// -c for specifying the configuration file, and -v for verbose logging.
378
    ///
379
    /// @param argc  is the number of command line arguments supplied
380 381
    /// @param argv  is the array of string (char *) command line arguments
    ///
382 383
    /// @throw InvalidUsage when there are usage errors.
    /// @throw VersionMessage if the -v or -V arguments is given.
384 385 386
    void parseArgs(int argc, char* argv[]);

    /// @brief Instantiates the application process and then initializes it.
387
    /// This is the second step taken during launch, following successful
388
    /// command line parsing. It is used to invoke the derivation-specific
389 390
    /// implementation of createProcess, following by an invoking of the
    /// newly instantiated process's init method.
391 392 393 394 395 396
    ///
    /// @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).
397 398 399 400
    /// 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
401
    /// shutdown either in response to the shutdown command or due to an
402
    /// unrecoverable error.
403 404 405 406 407 408
    ///
    // @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
409 410 411 412
    /// 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
413
    /// instantaneous nor synchronous.  This method does not "block" waiting
414 415 416
    /// 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
417
    /// has actually exited.
418
    ///
419
    /// @return returns an Element that contains the results of shutdown
420
    /// command composed of an integer status value (0 means successful,
421
    /// non-zero means failure), and a string explanation of the outcome.
422 423 424
    ///
    /// @param args is a set of derivation-specific arguments (if any)
    /// for the shutdown command.
425 426
    isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args);

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
    /// @brief Initializes signal handling
    ///
    /// This method configures the controller to catch and handle signals.
    /// It instantiates an IOSignalQueue, registers @c osSignalHandler() as
    /// the SignalSet "on-receipt" handler, and lastly instantiates a SignalSet
    /// which listens for SIGHUP, SIGINT, and SIGTERM.
    void initSignalHandling();

    /// @brief Handler for processing OS-level signals
    ///
    /// This method is installed as the SignalSet "on-receipt" handler. Upon
    /// invocation, it uses the controller's IOSignalQueue to schedule an
    /// IOSignal with for the given signal value.
    ///
    /// @param signum OS signal value (e.g. SIGINT, SIGUSR1 ...) to received
    ///
    /// @return SignalSet "on-receipt" handlers are required to return a
    /// boolean indicating if the OS signal has been processed (true) or if it
    /// should be saved for deferred processing (false).  Currently this
    /// method processes all received signals, so it always returns true.
    bool osSignalHandler(int signum);

    /// @brief Handler for processing IOSignals
    ///
    /// This method is supplied as the callback when IOSignals are scheduled.
    /// It fetches the IOSignal for the given sequence_id and then invokes
    /// the virtual method, @c processSignal() passing it the signal value
    /// obtained from the IOSignal.  This allows derivations to supply a
    /// custom signal processing method, while ensuring IOSignalQueue
    /// integrity.
457 458
    ///
    /// @param sequence_id id of the IOSignal instance "received"
459 460
    void ioSignalHandler(IOSignalId sequence_id);

461 462
    /// @brief Fetches the current process
    ///
463
    /// @return a pointer to the current process instance.
464 465 466
    DProcessBasePtr getProcess() {
        return (process_);
    }
467 468

    /// @brief Prints the program usage text to std error.
469
    ///
470 471
    /// @param text is a string message which will preceded the usage text.
    /// This is intended to be used for specific usage violation messages.
472
    void usage(const std::string& text);
473 474

private:
475 476 477
    /// @brief Name of the service under control.
    /// This name is used as the configuration module name and appears in log
    /// statements.
478 479
    std::string app_name_;

480
    /// @brief Name of the service executable.
481
    /// By convention this matches the executable name. It is also used to
482
    /// establish the logger name.
483
    std::string bin_name_;
484

485
    /// @brief Indicates if the verbose logging mode is enabled.
486
    bool verbose_;
487

488
    /// @brief The absolute file name of the JSON spec file.
489 490 491 492 493 494 495 496 497
    std::string spec_file_name_;

    /// @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.
498
    asiolink::IOServicePtr io_service_;
499

500 501 502
    /// @brief Queue for propagating caught signals to the IOService.
    IOSignalQueuePtr io_signal_queue_;

503
    /// @brief Singleton instance value.
504 505
    static DControllerBasePtr controller_;

506
// DControllerTest is named a friend class to facilitate unit testing while
507 508 509 510
// leaving the intended member scopes intact.
friend class DControllerTest;
};

511
}; // namespace isc::process
512 513 514
}; // namespace isc

#endif