123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- // Copyright (C) 2016 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/.
- /**
- @page libprocess libkea-process - Controllable Process Layer (CPL)
- @section cpl Controllable Process Layer (CPL)
- During the design and development of D2 (Kea's DHCP-DDNS process), an abstract
- layer for process control, called the Controllable Process Layer or CPL, was
- created. Kea's DHCP servers were initially developed prior to D2 and thus
- before CPL existed.
- Out of short term convenience and the fact that only D2 was using it, the CPL
- was initially developed as part of D2 in src/bin/d2. In order to use CPL for
- new Kea processes, it has since been moved into its own library, libkea-process.
- The following sections describe the architecture of CPL and how it can be used to implement new daemons in Kea.
- The CPL provides the essentials for a controllable, configurable,
- asynchronous process. They are the result of an effort to distill the
- common facets of process control currently duplicated in Kea's
- DHCP servers into a reusable construct. The classes which form this abstract
- base are shown in the following class diagram:
- @image html abstract_app_classes.svg "Controllable Process Layer Classes"
- - isc::process::DControllerBase - provides all of the services necessary to manage
- an application process class derived from isc::d2::DProcess. These services include:
- - Command line argument handling
- - Process instantiation and initialization
- - Support for stand-alone execution
- - Process event loop invocation and shutdown
- It creates and manages an instance of isc::process::DProcessBase. The CPL is
- designed for asynchronous event processing applications. It is constructed
- to use ASIO library for IO processing. @c DControllerBase owns an
- isc::asiolink::IOService instance and it passes this into the @c
- DProcessBase constructor. It is this @c IOService that is used to drive the
- process's event loop. The controller is designed to provide any interfaces
- between the process it controls and the outside world.
- @c DControllerBase provides configuration for its process via a JSON file
- specified as a mandatory command line argument. The file structure is
- expected be as follows:
- { "<module-name>": {<module-config>} }
- where:
- - module-name : is a label which uniquely identifies the
- configuration data for the (i.e. the controlled process.)
- It is the value returned by @ref
- isc::process::DControllerBase::getAppName()
- - module-config: a set of zero or more JSON elements which comprise
- application's configuration values. Element syntax is governed
- by those elements supported in isc::cc.
- The file may contain an arbitrary number of other modules.
- @todo Eventually, some sort of secure socket interface which supports remote
- control operations such as configuration changes or status reporting will
- likely be implemented.
- - isc::process::DProcessBase - defines an asynchronous-event processor (i.e.
- application) which provides a uniform interface to:
- - Instantiate and initialize a process instance
- - "Run" the application by starting its event loop
- - Inject events to control the process
- It owns an instance of @c DCfgMgrBase.
- - isc::process::DCfgMgrBase - provides the mechanisms for managing an application's
- configuration. This includes services for parsing sets of configuration
- values, storing the parsed information in its converted form, and retrieving
- the information on demand. It owns an instance of @c DCfgContextBase, which
- provides a "global" context for information that is accessible before, during,
- and after parsing.
- - isc::process::DCfgContextBase - implements a container for configuration
- information or "context". It provides a single enclosure for the storage of
- configuration parameters or any other information that needs to accessible
- within a given context.
- The following sequence diagram shows how a configuration from file moves
- through the CPL layer:
- @image html config_from_file_sequence.svg "CPL Configuration From File Sequence"
- The CPL classes will likely move into a common library.
- @section cplSignals CPL Signal Handling
- CPL supports interaction with the outside world via OS signals. The default
- implementation supports the following signal driven behavior:
- - SIGHUP receipt of this signal will cause a reloading of the configuration
- file.
- - SIGINT/SIGTERM receipt of either of these signals will initiate an
- orderly shutdown.
- CPL applications wait for for process asynchronous IO events through
- isc::asiolink::IOService::run() or its variants. These calls are not
- interrupted upon signal receipt as is the select() function and while
- boost::asio provides a signal mechanism it requires linking in additional
- libraries. Therefore, CPL provides its own signal handling mechanism to
- propagate an OS signal such as SIGHUP to an IOService as a ready event with a
- callback.
- isc::process::DControllerBase uses two mechanisms to carry out signal handling. It
- uses isc::util::SignalSet to catch OS signals, and isc::process::IOSignalQueue to
- propagate them to its isc::asiolink::IOService as instances of
- isc::process::IOSignal.
- This CPL signaling class hierarchy is illustrated in the following diagram:
- @image html cpl_signal_classes.svg "CPL Signal Classes"
- The mechanics of isc::process::IOSignal are straight forward. Upon construction it
- is given the target isc::asiolink::IOService, the value of the OS signal to
- send (e.g. SIGINT, SIGHUP...), and an isc::process::IOSignalHandler. This handler
- should contain the logic the caller would normally execute in its OS signal
- handler. Each isc::process::IOSignal instance has a unique identifier called its
- sequence_id.
- Internally, IOSignal creates a 1 ms, one-shot timer, on the given
- IOService. When the timer expires its event handler invokes the caller's
- IOSignalHandler passing it the sequence_id of the IOSignal.
- Sending IOSignals is done through an isc::process::IOSignalQueue. This class is
- used to create the signals, house them until they are delivered, and dequeue
- them so they can be been handled. To generate an IOSignal when an OS signal
- arrives, the process's OS signal handler need only call
- isc::process::IOSignalQueue::pushSignal() with the appropriate values.
- To dequeue the IOSignal inside the caller's IOSignalHandler, one simply
- invokes isc::process::IOSignalQueue::popSignal() passing it the sequence_id
- parameter passed to the handler. This method returns a pointer to
- instigating IOSignal from which the value of OS signal (i.e. SIGINT,
- SIGUSR1...) can be obtained. Note that calling popSignal() removes the
- IOSignalPtr from the queue, which should reduce its reference count to
- zero upon exiting the handler (unless a deliberate copy of it is made).
- A typical isc::process::IOSignalHandler might be structured as follows:
- @code
- void processSignal(IOSignalId sequence_id) {
- // Pop the signal instance off the queue.
- IOSignalPtr signal = io_signal_queue_->popSignal(sequence_id);
- int os_signal_value = signal->getSignum();
- :
- // logic based on the signal value
- :
- }
- @endcode
- IOSignal's handler invocation code will catch, log ,and then swallow any
- exceptions thrown by an IOSignalHandler. This is done to protect the integrity
- IOService context.
- CPL integrates the use of the two mechanisms by registering the method,
- isc::process::DControllerBase::osSignalHandler(), as the
- isc::util::SignalSet::onreceipt_handler_. This configures SignalSet's internal
- handler to invoke the method each time a signal arrives. When invoked, this
- method will call isc::process::IOSignalQueue::pushSignal() to create an
- isc::process::IOSignal, passing in the OS signal received and
- isc::process::DControllerBase::ioSignalHandler() to use as the IOSignal's
- ready event handler
- The following sequence diagram depicts the initialization of signal handling
- during startup and the subsequent receipt of a SIGHUP:
- @image html cpl_signal_sequence.svg "CPL Signal Handling Sequence"
- */
|