1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
// Copyright (C) 2016-2021 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.
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"
@section redact Redact Passwords
There are two tools to remove sensitive data as passwords or secrets from logs:
- redactedAccessString for database access strings
- redactConfig for full configurations
The jsonPathsToRedact method must be defined in derived classes following this
procedure:
- Get all possible JSON paths from the root of the configuration to leaves that
fulfill the role of map keys and which contain "password" or "secret".
- For each of these paths, remove the root node and the leaf node.
- Include all the paths in the method. Duplicate subpaths are expected in the
case of common subpaths to different leaves.
There are two special syntaxes:
- "[]" suggests that the searched element is a list. This is required for all
lists and is for performance gain.
- "*" as a last element in a JSON path tells the redacter to look in all
elements that follow for elements that contain "password" and "secret". This is
when the particular configuration that is targeted by the "*" does not have a
well defined structure, such as is the case for "parameters" in the
"hooks-libraries" map in "Dhcp4" and "Dhcp6".
@section cplMTConsiderations Multi-Threading Consideration for Controllable Process Layer
By default this library is not thread safe and currently there is no known
exception.
*/
|