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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
|
// 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 D_CONTROLLER_H
#define D_CONTROLLER_H
#include <asiolink/io_service.h>
#include <asiolink/io_service_signal.h>
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <log/logger_support.h>
#include <process/daemon.h>
#include <process/d_log.h>
#include <process/d_process.h>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <string>
#include <set>
namespace isc {
namespace process {
/// @brief Exception thrown when the command line is invalid.
/// Can be used to transmit negative messages too.
class InvalidUsage : public isc::Exception {
public:
InvalidUsage(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @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, -V or -W is given. Can be used to transmit positive messages too.
class VersionMessage : public isc::Exception {
public:
VersionMessage(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @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) { };
};
/// @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) { };
};
/// @brief Exception thrown when the application process encounters an
/// 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) { };
};
/// @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) { };
};
/// @brief Defines a shared pointer to DControllerBase.
class DControllerBase;
typedef boost::shared_ptr<DControllerBase> DControllerBasePtr;
/// @brief Application Controller
///
/// DControllerBase is an abstract singleton which provides the framework and
/// services for managing an application process that implements the
/// 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.
/// It creates the IOService instance which is used for runtime control
/// events and passes the IOService into the application process at process
/// creation.
/// It provides the callback handlers for command and configuration events
/// which could be triggered by an external source. Such sources are intended
/// to be registered with and monitored by the controller's IOService such that
/// the appropriate handler can be invoked.
///
/// DControllerBase provides dynamic configuration file reloading upon receipt
/// of SIGHUP, and graceful shutdown upon receipt of either SIGINT or SIGTERM.
///
/// NOTE: Derivations must supply their own static singleton instance method(s)
/// for creating and fetching the instance. The base class declares the instance
/// member in order for it to be available for static callback functions.
class DControllerBase : public Daemon {
public:
/// @brief Constructor
///
/// @param app_name is display name of the application under control. This
/// name appears in log statements.
/// @param bin_name is the name of the application executable.
DControllerBase(const char* app_name, const char* bin_name);
/// @brief Destructor
virtual ~DControllerBase();
/// @brief returns Kea version on stdout and exit.
/// redeclaration/redefinition. @ref isc::process::Daemon::getVersion()
std::string getVersion(bool extended);
/// @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
/// 3. load the configuration file
/// 4. record the start timestamp
/// 5. initialize signal handling
/// 6. start and wait on the application process event loop
/// 7. exit to the caller
///
/// It is intended to be called from main() and be given the command line
/// arguments.
///
/// This function can be run in "test mode". It prevents initialization
/// of module logger. This is used in unit tests which initialize logger
/// in their main function. Such a logger uses environmental variables to
/// control severity, verbosity etc.
///
/// @param argc is the number of command line arguments supplied
/// @param argv is the array of string (char *) command line arguments
/// @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.
///
/// @throw throws one of the following exceptions:
/// InvalidUsage - Indicates invalid command line.
/// ProcessInitError - Failed to create and initialize application
/// process object.
/// ProcessRunError - A fatal error occurred while in the application
/// process event loop.
/// @return The value from @c Daemon::getExitValue().
virtual int launch(int argc, char* argv[], const bool test_mode);
/// @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
/// configuration and then invoke the application process' configure method.
///
/// @param new_config is the new configuration
///
/// @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 updateConfig(isc::data::ConstElementPtr
new_config);
/// @brief Instance method invoked by the configuration event handler and
/// which processes the actual configuration check. Provides behavioral
/// path for both integrated and stand-alone modes. The current
/// implementation will merge the configuration update into the existing
/// configuration and then invoke the application process' configure method
/// with a final rollback.
///
/// @param new_config is the new configuration
///
/// @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 checkConfig(isc::data::ConstElementPtr
new_config);
/// @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
/// the application's configuration values
/// @endcode
///
/// To translate the JSON content into Elements, @c parseFile() is called
/// first. This virtual method provides derivations a means to parse the
/// file content using an alternate parser. If it returns an empty pointer
/// than the JSON parsing providing by Element::fromJSONFile() is called.
///
/// Once parsed, the method extracts the set of configuration
/// elements for the module-name that matches the controller's app_name_,
/// looks for the loggers entry and, if present uses it to configure
/// logging. It then passes that set into @c updateConfig() (or
/// @c checkConfig()).
///
/// 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();
/// @brief Fetches the name of the application under control.
///
/// @return returns the controller service name string
std::string getAppName() const {
return (app_name_);
}
/// @brief Fetches the name of the application executable.
///
/// @return returns the controller logger name string
std::string getBinName() const {
return (bin_name_);
}
/// @brief handler for version-get command
///
/// This method handles the version-get command. It returns the basic and
/// extended version.
///
/// @param command (ignored)
/// @param args (ignored)
/// @return answer with version details.
isc::data::ConstElementPtr
versionGetHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for 'build-report' command
///
/// This method handles build-report command. It returns the output printed
/// by configure script which contains most compilation parameters.
///
/// @param command (ignored)
/// @param args (ignored)
/// @return answer with build report
isc::data::ConstElementPtr
buildReportHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for config-get command
///
/// This method handles the config-get command, which retrieves
/// the current configuration and returns it in response.
///
/// @param command (ignored)
/// @param args (ignored)
/// @return current configuration wrapped in a response
isc::data::ConstElementPtr
configGetHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for config-hash-get command
///
/// This method handles the config-hash-get command, which retrieves
/// the current configuration and returns it in response.
///
/// @param command (ignored)
/// @param args (ignored)
/// @return hash of current configuration wrapped in a response
isc::data::ConstElementPtr
configHashGetHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for config-write command
///
/// This handle processes write-config command, which writes the
/// current configuration to disk. This command takes one optional
/// parameter called filename. If specified, the current configuration
/// will be written to that file. If not specified, the file used during
/// Kea start-up will be used. To avoid any exploits, the path is
/// always relative and .. is not allowed in the filename. This is
/// a security measure against exploiting file writes remotely.
///
/// @param command (ignored)
/// @param args may contain optional string argument filename
/// @return status of the configuration file write
isc::data::ConstElementPtr
configWriteHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for config-test command
///
/// This method handles the config-test command, which checks
/// configuration specified in args parameter.
///
/// @param command (ignored)
/// @param args configuration to be checked.
/// @return status of the command
isc::data::ConstElementPtr
configTestHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for config-reload command
///
/// This method handles the config-reload command, which reloads
/// the configuration file.
///
/// @param command (ignored)
/// @param args (ignored)
/// @return status of the command
isc::data::ConstElementPtr
configReloadHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for config-set command
///
/// This method handles the config-set command, which loads
/// configuration specified in args parameter.
///
/// @param command (ignored)
/// @param args configuration to be checked.
/// @return status of the command
isc::data::ConstElementPtr
configSetHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for 'shutdown' command
///
/// This method handles shutdown command. It initiates the shutdown procedure
/// using CPL methods.
/// @param command (ignored)
/// @param args (ignored)
/// @return answer confirming that the shutdown procedure is started
isc::data::ConstElementPtr
shutdownHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for server-tag-get command
///
/// This method handles the server-tag-get command, which retrieves
/// the current server tag and returns it in response.
///
/// @param command (ignored)
/// @param args (ignored)
/// @return current configuration wrapped in a response
isc::data::ConstElementPtr
serverTagGetHandler(const std::string& command,
isc::data::ConstElementPtr args);
/// @brief handler for status-get command
///
/// This method handles the status-get command, which retrieves
/// the server process information i.e. the pid and returns it in
/// response.
///
/// @param command (ignored)
/// @param args (ignored)
/// @return process information wrapped in a response
isc::data::ConstElementPtr
statusGetHandler(const std::string& command,
isc::data::ConstElementPtr args);
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
/// recognized as a stock DControllerBase option.
///
/// @param option is the option "character" from the command line, without
/// any prefixing hyphen(s)
/// @param optarg is the argument value (if one) associated with the option
///
/// @return must return true if the option was valid, false if it is
/// invalid. (Note the default implementation always returns false.)
virtual bool customOption(int option, char *optarg);
/// @brief Abstract method that is responsible for instantiating the
/// application process object. It is invoked by the controller after
/// command line argument parsing as part of the process initialization
/// (see initProcess method).
///
/// @return returns a pointer to the new process object (DProcessBase*)
/// or NULL if the create fails.
/// Note this value is subsequently wrapped in a smart pointer.
virtual DProcessBase* createProcess() = 0;
/// @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.
virtual const std::string getUsageText() const {
return ("");
}
/// @brief Virtual method which returns a string containing the option
/// letters for any custom command line options supported by the derivation.
/// These are added to the stock options of "c", "d", ..., during command
/// line interpretation.
///
/// @return returns a string containing the custom option letters.
virtual const std::string getCustomOpts() const {
return ("");
}
/// @brief Check the configuration
///
/// Called by @c launch() when @c check_only_ mode is enabled
/// @throw VersionMessage when successful but a message should be displayed
/// @throw InvalidUsage when an error was detected
void checkConfigOnly();
/// @brief Application-level signal processing method.
///
/// This method is the last step in processing a OS signal occurrence.
/// It currently supports the following signals as follows:
/// -# SIGHUP - instigates reloading the configuration file
/// -# SIGINT - instigates a graceful shutdown
/// -# SIGTERM - instigates a graceful shutdown
/// If it receives any other signal, it will issue a debug statement and
/// 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())
/// with the signal value.
/// @todo Provide a convenient way for derivations to register additional
/// signals.
virtual void processSignal(int signum);
/// @brief Supplies whether or not verbose logging is enabled.
///
/// @return returns true if verbose logging is enabled.
bool isVerbose() const {
return (verbose_);
}
/// @brief Method for enabling or disabling verbose logging.
///
/// @param value is the new value to assign the flag.
void setVerbose(bool value) {
verbose_ = value;
}
/// @brief Supplies whether or not check only mode is enabled.
///
/// @return returns true if check only is enabled.
bool isCheckOnly() const {
return (check_only_);
}
/// @brief Method for enabling or disabling check only mode.
///
/// @todo this method and @c setVerbose are currently not used.
///
/// @param value is the new value to assign the flag.
void setCheckOnly(bool value) {
check_only_ = value;
}
/// @brief Getter for fetching the controller's IOService
///
/// @return returns a pointer reference to the IOService.
asiolink::IOServicePtr& getIOService() {
return (io_service_);
}
/// @brief Static getter which returns the singleton instance.
///
/// @return returns a pointer reference to the private singleton instance
/// member.
static DControllerBasePtr& getController() {
return (controller_);
}
/// @brief Static setter which sets the singleton instance.
///
/// @param controller is a pointer to the singleton instance.
///
/// @throw throws DControllerBase error if an attempt is made to set the
/// instance a second time.
static void setController(const DControllerBasePtr& controller);
/// @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
/// cstdlib's getopt to loop through the command line.
/// It handles stock options directly, and passes any custom options into
/// the customOption method. Currently there are only some stock options
/// -c/t for specifying the configuration file, -d for verbose logging,
/// and -v/V/W for version reports.
///
/// @param argc is the number of command line arguments supplied
/// @param argv is the array of string (char *) command line arguments
///
/// @throw InvalidUsage when there are usage errors.
/// @throw VersionMessage if the -v, -V or -W arguments is given.
void parseArgs(int argc, char* argv[]);
///@brief Parse a given file into Elements
///
/// This method provides a means for deriving classes to use alternate
/// parsing mechanisms to parse configuration files into the corresponding
/// isc::data::Elements. The elements produced must be equivalent to those
/// which would be produced by the original JSON parsing. Implementations
/// should throw when encountering errors.
///
/// The default implementation returns an empty pointer, signifying to
/// callers that they should submit the file to the original parser.
///
/// @param file_name pathname of the file to parse
///
/// @return pointer to the elements created
///
virtual isc::data::ConstElementPtr parseFile(const std::string& file_name);
///@brief Parse text into Elements
///
/// This method provides a means for deriving classes to use alternate
/// parsing mechanisms to parse configuration text into the corresponding
/// isc::data::Elements. The elements produced must be equivalent to those
/// which would be produced by the original JSON parsing. Implementations
/// should throw when encountering errors.
///
/// The default implementation returns an empty pointer, signifying to
/// callers that they should submit the text to the original parser.
///
/// @param input text to parse
///
/// @return pointer to the elements created
///
virtual isc::data::ConstElementPtr parseText(const std::string& input) {
static_cast<void>(input); // just tu shut up the unused parameter warning
isc::data::ConstElementPtr elements;
return (elements);
}
/// @brief Instantiates the application process and then initializes it.
/// This is the second step taken during launch, following successful
/// command line parsing. It is used to invoke the derivation-specific
/// implementation of createProcess, following by an invoking of the
/// newly instantiated process's init method.
///
/// @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).
/// 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
/// shutdown either in response to the shutdown command or due to an
/// unrecoverable error.
///
// @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
/// 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
/// instantaneous nor synchronous. This method does not "block" waiting
/// 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
/// has actually exited.
///
/// @return returns an Element that contains the results of shutdown
/// command composed of an integer status value (0 means successful,
/// non-zero means failure), and a string explanation of the outcome.
///
/// @param args is a set of derivation-specific arguments (if any)
/// for the shutdown command.
isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args);
/// @brief Initializes signal handling
///
/// This method configures the controller to catch and handle signals.
/// It instantiates a IOSignalSet which listens for SIGHUP, SIGINT, and
/// SIGTERM.
void initSignalHandling();
/// @brief Fetches the current process
///
/// @return a pointer to the current process instance.
DProcessBasePtr getProcess() {
return (process_);
}
/// @brief Prints the program usage text to std error.
///
/// @param text is a string message which will preceded the usage text.
/// This is intended to be used for specific usage violation messages.
void usage(const std::string& text);
/// @brief Fetches text containing additional version specifics
///
/// This method is provided so derivations can append any additional
/// desired information such as library dependencies to the extended
/// version text returned when DControllerBase::getVersion(true) is
/// invoked.
/// @return a string containing additional version info
virtual std::string getVersionAddendum() { return (""); }
/// @brief Deals with other (i.e. not application name) global objects.
///
/// Code shared between configuration handlers:
/// - check obsolete or unknown (aka unsupported) objects.
///
/// @param args Command arguments.
/// @return Error message or empty string.
std::string handleOtherObjects(isc::data::ConstElementPtr args);
private:
/// @brief Name of the service under control.
/// This name is used as the configuration module name and appears in log
/// statements.
std::string app_name_;
/// @brief Name of the service executable.
/// By convention this matches the executable name. It is also used to
/// establish the logger name.
std::string bin_name_;
/// @brief Indicates if the verbose logging mode is enabled.
bool verbose_;
/// @brief Indicates if the check only mode for the configuration
/// is enabled (usually specified by the command line -t argument).
bool check_only_;
/// @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.
isc::asiolink::IOServicePtr io_service_;
/// @brief ASIO signal set.
isc::asiolink::IOSignalSetPtr io_signal_set_;
/// @brief Singleton instance value.
static DControllerBasePtr controller_;
// DControllerTest is named a friend class to facilitate unit testing while
// leaving the intended member scopes intact.
friend class DControllerTest;
/// @brief Structure used in parseArgs() to reset arguments in case parseArgs() is called again.
struct ExhaustOptions {
ExhaustOptions(int argc, char* argv[], std::string opts)
: argc_(argc), argv_(argv), opts_(opts) {
}
~ExhaustOptions() {
while (getopt(argc_, argv_, opts_.c_str()) != -1) {
}
}
private:
int argc_;
char** argv_;
std::string opts_;
};
};
} // namespace process
} // namespace isc
#endif
|