Kea 2.7.5
|
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:
isc::process::DControllerBase - provides all of the services necessary to manage an application process class derived from isc::d2::DProcess. These services include:
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. DControllerBase
owns an isc::asiolink::IOService instance and it passes this into the DProcessBase
constructor. It is this 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.
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:
The file may contain an arbitrary number of other modules.
DCfgMgrBase
.DCfgContextBase
, which provides a "global" context for information that is accessible before, during, and after parsing.The following sequence diagram shows how a configuration from file moves through the CPL layer:
The CPL classes will likely move into a common library.
CPL supports interaction with the outside world via OS signals. The default implementation supports the following signal driven behavior:
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:
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:
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.
The following sequence diagram depicts the initialization of signal handling during startup and the subsequent receipt of a SIGHUP:
There are two tools to remove sensitive data as passwords or secrets from logs:
The jsonPathsToRedact method must be defined in derived classes following this procedure:
There are two special syntaxes:
By default this library is not thread safe and currently there is no known exception.