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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
// Copyright (C) 2013-2024 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 D2_PROCESS_H
#define D2_PROCESS_H

#include <d2/d2_queue_mgr.h>
#include <d2/d2_update_mgr.h>
#include <process/d_process.h>

namespace isc {
namespace d2 {

/// @brief DHCP-DDNS Application Process
///
/// D2Process provides the top level application logic for DHCP-driven DDNS
/// update processing.  It provides the asynchronous event processing required
/// to receive DNS mapping change requests and carry them out.
/// It implements the DProcessBase interface, which structures it such that it
/// is a managed "application", controlled by a management layer.
class D2Process : public process::DProcessBase {
public:

    /// @brief Defines the shutdown types supported by D2Process
    ///
    /// * SD_NORMAL - Stops the queue manager and finishes all current
    /// transactions before exiting. This is the default.
    ///
    /// * SD_DRAIN_FIRST - Stops the queue manager but continues processing
    /// requests from the queue until it is empty.
    ///
    /// * SD_NOW - Exits immediately.
    enum ShutdownType {
      SD_NORMAL,
      SD_DRAIN_FIRST,
      SD_NOW
    };

    /// @brief Defines the point at which to resume receiving requests.
    /// If the receive queue has become full, D2Process will "pause" the
    /// reception of requests by putting the queue manager in the stopped
    /// state.  Once the number of entries has decreased to this percentage
    /// of  the maximum allowed, D2Process will "resume" receiving requests
    /// by restarting the queue manager.
    static const unsigned int QUEUE_RESTART_PERCENT;

    /// @brief Constructor
    ///
    /// Construction creates the configuration manager, the queue
    /// manager, and the update manager.
    ///
    /// @param name name is a text label for the process. Generally used
    /// in log statements, but otherwise arbitrary.
    /// @param io_service is the io_service used by the caller for
    /// asynchronous event handling.
    ///
    /// @throw DProcessBaseError if io_service is NULL.
    D2Process(const char* name, const asiolink::IOServicePtr& io_service);

    /// @brief Called after instantiation to perform initialization unique to
    /// D2.
    ///
    /// This is invoked by the controller after command line arguments but
    /// PRIOR to configuration reception.  The base class provides this method
    /// as a place to perform any derivation-specific initialization steps
    /// that are inappropriate for the constructor but necessary prior to
    /// configure.
    /// For D2 it is used to initialize the command manager.
    virtual void init();<--- Function in derived class

    /// @brief Implements the process's event loop.
    ///
    /// Once entered, the main control thread remains inside this method
    /// until shutdown.  The event loop logic is as follows:
    /// @code
    ///    while should not shutdown {
    ///       process queue manager state change
    ///       process completed jobs
    ///       dequeue new jobs
    ///       wait for IO event(s)
    ///
    ///       ON an exception, exit with fatal error
    ///    }
    /// @endcode
    ///
    /// To summarize, each pass through the event loop first checks the state
    /// of the received queue and takes any steps required to ensure it is
    /// operating in the manner necessary.  Next the update manager is given
    /// a chance to clean up any completed transactions and start new
    /// transactions by dequeuing jobs from the request queue.  Lastly, it
    /// allows IOService to process until one or more event handlers are
    /// called.  Note that this last step will block until at least one
    /// ready handler is invoked.  In other words, if no IO events have occurred
    /// since it was last called, the event loop will block at this step until
    /// an IO event occurs.  At that time we return to the top of the loop.
    ///
    /// @throw DProcessBaseError if an error is encountered.  Note that
    /// exceptions thrown at this point are assumed to be FATAL exceptions.
    /// This includes exceptions generated but not caught by IO callbacks.
    /// Services which rely on callbacks are expected to be well behaved and
    /// any errors they encounter handled internally.
    virtual void run();<--- Function in derived class

    /// @brief Initiates the D2Process shutdown process.
    ///
    /// This is last step in the shutdown event callback chain. It is invoked
    /// to notify D2Process that it needs to begin its shutdown procedure.
    /// Note that shutting down may be neither instantaneous nor synchronous,
    /// This method records the request for and the type of shutdown desired.
    /// Generally it will require one or more subsequent events to complete,
    /// dependent on the type of shutdown requested.  The type of shutdown is
    /// specified as an optional argument of the shutdown command. The types
    /// of shutdown supported are:
    ///
    /// * "normal" - Stops the queue manager and finishes all current
    /// transactions before exiting. This is the default.
    ///
    /// * "drain_first" - Stops the queue manager but continues processing
    /// requests from the queue until it is empty.
    ///
    /// * "now" - Exits immediately.
    ///
    /// @param args Specifies the shutdown "type" as "normal", "drain_first",
    /// or "now"
    ///
    /// @return an Element that contains the results of argument processing,
    /// consisting of an integer status value (0 means successful,
    /// non-zero means failure), and a string explanation of the outcome.
    virtual isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr args);<--- Function in derived class

    /// @brief Processes the given configuration.
    ///
    /// This method may be called multiple times during the process lifetime.
    /// Certainly once during process startup, and possibly later if the user
    /// alters the configuration. This method must not throw, it should catch any
    /// processing errors and return a success or failure answer as described
    /// below.
    ///
    /// This method passes the newly received configuration to the configuration
    /// manager instance for parsing.  The configuration manager parses the
    /// configuration and updates the necessary values within the context,
    /// assuming it parses correctly.  If that's the case this method sets the
    /// flag to reconfigure the queue manager and returns a successful response
    /// as described below.
    ///
    /// If the new configuration fails to parse, then the current configuration
    /// is retained and a failure response is returned as described below.
    ///
    /// @param config_set a new configuration (JSON) for the process
    /// @param check_only true if configuration is to be verified only, not applied
    /// @return an Element that contains the results of configuration composed
    /// of an integer status value (0 means successful, non-zero means failure),
    /// and a string explanation of the outcome.
    virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr config_set,<--- Function in derived class
                                                 bool check_only = false);

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

protected:
    /// @brief Monitors current queue manager state, takes action accordingly
    ///
    /// This method ensures that the queue manager transitions to the state
    /// most appropriate to the operational state of the D2Process and any
    /// events that may have occurred since it was last called.  It is called
    /// once for each iteration of the event loop.  It is essentially a
    /// switch statement based on the D2QueueMgr's current state.  The logic
    /// is as follows:
    ///
    /// If the state is D2QueueMgr::RUNNING, and the queue manager needs to be
    /// reconfigured or we have been told to shutdown, then instruct the queue
    /// manager to stop listening. Exit the method.
    ///
    /// If the state is D2QueueMgr::STOPPED_QUEUE_FULL, then check if the
    /// number of entries in the queue has fallen below the "resume threshold".
    /// If it has, then instruct the queue manager to start listening. Exit
    /// the method.
    ///
    /// If the state is D2QueueMgr::STOPPED_RECV_ERROR, then attempt to recover
    /// by calling reconfigureQueueMgr(). Exit the method.
    ///
    /// If the state is D2QueueMgr::STOPPING, simply exit the method. This is
    /// a NOP condition as we are waiting for the IO cancel event
    ///
    /// For any other state, (NOT_INITTED,INITTED,STOPPED), if the reconfigure
    /// queue flag is set, call reconfigureQueueMgr(). Exit the method.
    ///
    /// This method is exception safe.
    virtual void checkQueueStatus();

    /// @brief Initializes then starts the queue manager.
    ///
    /// This method initializes the queue manager with the current
    /// configuration parameters and instructs it to start listening.
    /// Note the existing listener instance (if it exists) is destroyed,
    /// and that a new listener is created during initialization.
    ///
    /// This method is exception safe.
    virtual void reconfigureQueueMgr();

    /// @brief Allows IO processing to run until at least callback is invoked.
    ///
    /// This method is called from within the D2Process main event loop and is
    /// the point at which the D2Process blocks, waiting for IO events to
    /// cause IO event callbacks to be invoked.
    ///
    /// If callbacks are ready to be executed upon entry, the method will
    /// return as soon as these callbacks have completed.  If no callbacks
    /// are ready, then it will wait (indefinitely) until at least one callback
    /// is executed.
    ///
    /// @note: Should become desirable to periodically force an
    /// event, an interval timer could be used to do so.
    ///
    /// @return The number of callback handlers executed, or 0 if the IO
    /// service has been stopped.
    ///
    /// @throw This method does not throw directly, but the execution of
    /// callbacks invoked in response to IO events might.  If so, these
    /// will propagate upward out of this method.
    virtual size_t runIO();

    /// @brief Indicates whether or not the process can perform a shutdown.
    ///
    /// Determines if the process has been instructed to shutdown and if
    /// the criteria for performing the type of shutdown requested has been
    /// met.
    ///
    /// @return Returns true if the criteria has been met, false otherwise.
    virtual bool canShutdown() const;

    /// @brief Sets queue reconfigure indicator to the given value.
    ///
    /// @param value is the new value to assign to the indicator
    ///
    /// @note this method is really only intended for testing purposes.
    void setReconfQueueFlag(const bool value) {
        reconf_queue_flag_ = value;
    }

    /// @brief Sets the shutdown type to the given value.
    ///
    /// @param value is the new value to assign to shutdown type.
    ///
    /// @note this method is really only intended for testing purposes.
    void setShutdownType(const ShutdownType& value) {
        shutdown_type_ = value;
    }

    /// @brief (Re-)Configure the command channel.
    ///
    /// Only close the current channel, if the new channel configuration is
    /// different.  This avoids disconnecting a client and hence not sending
    /// them a command result, unless they specifically alter the channel
    /// configuration. In that case the user simply has to accept they'll
    /// be disconnected.
    void reconfigureCommandChannel();

public:
    /// @brief Returns a pointer to the configuration manager.
    /// Note, this method cannot return a reference as it uses dynamic
    /// pointer casting of the base class configuration manager.
    D2CfgMgrPtr getD2CfgMgr();

    /// @brief Returns a reference to the queue manager.
    const D2QueueMgrPtr& getD2QueueMgr() const {
        return (queue_mgr_);
    }

    /// @brief Returns a reference to the update manager.
    const D2UpdateMgrPtr& getD2UpdateMgr() const {
        return (update_mgr_);
    }

    /// @brief Returns true if the queue manager should be reconfigured.
    bool getReconfQueueFlag() const {
        return (reconf_queue_flag_);
    }

    /// @brief Returns the type of shutdown requested.
    ///
    /// Note, this value is meaningless unless shouldShutdown() returns true.
    ShutdownType getShutdownType() const {
        return (shutdown_type_);
    }

    /// @brief Returns a text label for the given shutdown type.
    ///
    /// @param type the numerical shutdown type for which the label is desired.
    ///
    /// @return A text label corresponding the value or "invalid" if the
    /// value is not a valid value.
    static const char* getShutdownTypeStr(const ShutdownType& type);

private:
    /// @brief Pointer to our queue manager instance.
    D2QueueMgrPtr queue_mgr_;

    /// @brief Pointer to our update manager instance.
    D2UpdateMgrPtr update_mgr_;

    /// @brief Indicates if the queue manager should be reconfigured.
    bool reconf_queue_flag_;

    /// @brief Indicates the type of shutdown requested.
    ShutdownType shutdown_type_;

    /// @brief Current socket control configuration.
    isc::data::ConstElementPtr current_control_socket_;

};

/// @brief Defines a shared pointer to D2Process.
typedef boost::shared_ptr<D2Process> D2ProcessPtr;

} // namespace isc::d2
} // namespace isc

#endif