Branch data Line data Source code
1 : : // Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <config.h>
16 : :
17 : : #include <stdexcept>
18 : : #include <stdlib.h>
19 : : #include <string.h>
20 : : #include <sys/time.h>
21 : : #include <ctype.h>
22 : :
23 : : #include <algorithm>
24 : : #include <cerrno>
25 : : #include <fstream>
26 : : #include <iostream>
27 : : #include <set>
28 : : #include <sstream>
29 : : #include <string>
30 : :
31 : : #include <boost/bind.hpp>
32 : : #include <boost/foreach.hpp>
33 : :
34 : : #include <cc/data.h>
35 : : #include <module_spec.h>
36 : : #include <cc/session.h>
37 : : #include <exceptions/exceptions.h>
38 : :
39 : : #include <config/config_log.h>
40 : : #include <config/ccsession.h>
41 : :
42 : : #include <log/logger_support.h>
43 : : #include <log/logger_specification.h>
44 : : #include <log/logger_manager.h>
45 : : #include <log/logger_name.h>
46 : :
47 : : using namespace std;
48 : :
49 : : using isc::data::Element;
50 : : using isc::data::ConstElementPtr;
51 : : using isc::data::ElementPtr;
52 : : using isc::data::JSONError;
53 : :
54 : : namespace isc {
55 : : namespace config {
56 : :
57 : : /// Creates a standard config/command protocol answer message
58 : : ConstElementPtr
59 : 146 : createAnswer() {
60 [ + - ]: 146 : ElementPtr answer = Element::fromJSON("{\"result\": [] }");
61 [ + - ]: 146 : ElementPtr answer_content = Element::createList();
62 [ + - ]: 292 : answer_content->add(Element::create(0));
63 [ + - ][ + - ]: 292 : answer->set("result", answer_content);
64 : :
65 : 146 : return (answer);
66 : : }
67 : :
68 : : ConstElementPtr
69 : 63 : createAnswer(const int rcode, ConstElementPtr arg) {
70 [ + + ][ + + ]: 63 : if (rcode != 0 && (!arg || arg->getType() != Element::string)) {
[ + + ][ + + ]
71 [ + - ][ + - ]: 4 : isc_throw(CCSessionError, "Bad or no argument for rcode != 0");
72 : : }
73 [ + - ]: 61 : ElementPtr answer = Element::fromJSON("{\"result\": [] }");
74 [ + - ]: 61 : ElementPtr answer_content = Element::createList();
75 [ + - ]: 122 : answer_content->add(Element::create(rcode));
76 [ + - ]: 122 : answer_content->add(arg);
77 [ + - ][ + - ]: 122 : answer->set("result", answer_content);
78 : :
79 : 61 : return (answer);
80 : : }
81 : :
82 : : ConstElementPtr
83 : 75 : createAnswer(const int rcode, const std::string& arg) {
84 [ + - ]: 75 : ElementPtr answer = Element::fromJSON("{\"result\": [] }");
85 [ + - ]: 75 : ElementPtr answer_content = Element::createList();
86 [ + - ]: 150 : answer_content->add(Element::create(rcode));
87 [ + - ][ + - ]: 150 : answer_content->add(Element::create(arg));
88 [ + - ][ + - ]: 150 : answer->set("result", answer_content);
89 : :
90 : 75 : return (answer);
91 : : }
92 : :
93 : : ConstElementPtr
94 : 169 : parseAnswer(int &rcode, ConstElementPtr msg) {
95 [ + + + + ]: 672 : if (msg &&
[ + + ][ + + ]
96 : 168 : msg->getType() == Element::map &&
97 [ + - ][ + - ]: 335 : msg->contains("result")) {
[ + + ][ # # ]
98 [ + - ]: 162 : ConstElementPtr result = msg->get("result");
99 [ + + ]: 162 : if (result->getType() != Element::list) {
100 [ + - ][ + - ]: 4 : isc_throw(CCSessionError, "Result element in answer message is not a list");
[ + - ]
101 [ + - ][ - + ]: 320 : } else if (result->get(0)->getType() != Element::integer) {
102 [ # # ][ # # ]: 0 : isc_throw(CCSessionError, "First element of result is not an rcode in answer message");
[ # # ]
103 : : }
104 [ + - ][ + - ]: 160 : rcode = result->get(0)->intValue();
105 [ + - ][ + + ]: 160 : if (result->size() > 1) {
106 [ + + ][ + - ]: 148 : if (rcode == 0 || result->get(1)->getType() == Element::string) {
[ + + ][ + + ]
[ + + ]
107 [ + - ]: 104 : return (result->get(1));
108 : : } else {
109 [ + - ][ + - ]: 2 : isc_throw(CCSessionError, "Error description in result with rcode != 0 is not a string");
[ + - ]
110 : : }
111 : : } else {
112 [ + + ]: 55 : if (rcode == 0) {
113 : : return (ElementPtr());
114 : : } else {
115 [ + - ][ + - ]: 2 : isc_throw(CCSessionError, "Result with rcode != 0 does not have an error description");
[ + - ]
116 : : }
117 : : }
118 : : } else {
119 [ + - ][ + - ]: 14 : isc_throw(CCSessionError, "No result part in answer message");
120 : : }
121 : : }
122 : :
123 : : ConstElementPtr
124 : 1 : createCommand(const std::string& command) {
125 [ + - ]: 2 : return (createCommand(command, ElementPtr()));
126 : : }
127 : :
128 : : ConstElementPtr
129 : 176 : createCommand(const std::string& command, ConstElementPtr arg) {
130 : 176 : ElementPtr cmd = Element::createMap();
131 [ + - ]: 176 : ElementPtr cmd_parts = Element::createList();
132 [ + - ][ + - ]: 352 : cmd_parts->add(Element::create(command));
133 [ + + ]: 176 : if (arg) {
134 [ + - ]: 350 : cmd_parts->add(arg);
135 : : }
136 [ + - ][ + - ]: 352 : cmd->set("command", cmd_parts);
137 : 176 : return (cmd);
138 : : }
139 : :
140 : : std::string
141 : 27 : parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
142 [ + + + + ]: 105 : if (command &&
[ + + ][ + + ]
143 : 26 : command->getType() == Element::map &&
144 [ + - ][ + - ]: 52 : command->contains("command")) {
[ + + ][ # # ]
145 [ + - ]: 23 : ConstElementPtr cmd = command->get("command");
146 [ + + ]: 87 : if (cmd->getType() == Element::list &&
[ + + + + ]
[ + + ]
147 [ + - ]: 21 : cmd->size() > 0 &&
148 [ + - ][ + + ]: 43 : cmd->get(0)->getType() == Element::string) {
149 [ + - ][ + + ]: 19 : if (cmd->size() > 1) {
150 [ + - ][ + - ]: 12 : arg = cmd->get(1);
151 : : } else {
152 [ + - ]: 7 : arg = Element::createMap();
153 : : }
154 [ + - ][ + - ]: 38 : return (cmd->get(0)->stringValue());
155 : : } else {
156 [ + - ][ + - ]: 8 : isc_throw(CCSessionError, "Command part in command message missing, empty, or not a list");
[ + - ]
157 : : }
158 : : } else {
159 [ + - ][ + - ]: 8 : isc_throw(CCSessionError, "Command Element empty or not a map with \"command\"");
160 : : }
161 : : }
162 : :
163 : : namespace {
164 : : // Temporary workaround functions for missing functionality in
165 : : // getValue() (main problem described in ticket #993)
166 : : // This returns either the value set for the given relative id,
167 : : // or its default value
168 : : // (intentially defined here so this interface does not get
169 : : // included in ConfigData as it is)
170 : 0 : ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
171 : : const std::string& relative_id,
172 : : const ConfigData& config_data,
173 : : const std::string& full_id) {
174 [ # # ]: 0 : if (config_part->contains(relative_id)) {
175 : 0 : return config_part->get(relative_id);
176 : : } else {
177 : 0 : return config_data.getDefaultValue(full_id);
178 : : }
179 : : }
180 : :
181 : : // Prefix name with "b10-".
182 : : //
183 : : // In BIND 10, modules have names taken from the .spec file, which are typically
184 : : // names starting with a capital letter (e.g. "Resolver", "Auth" etc.). The
185 : : // names of the associated binaries are derived from the module names, being
186 : : // prefixed "b10-" and having the first letter of the module name lower-cased
187 : : // (e.g. "b10-resolver", "b10-auth"). (It is a required convention that there
188 : : // be this relationship between the names.)
189 : : //
190 : : // Within the binaries the root loggers are named after the binaries themselves.
191 : : // (The reason for this is that the name of the logger is included in the
192 : : // message logged, so making it clear which message comes from which BIND 10
193 : : // process.) As logging is configured using module names, the configuration code
194 : : // has to match these with the corresponding logger names. This function
195 : : // converts a module name to a root logger name by lowercasing the first letter
196 : : // of the module name and prepending "b10-".
197 : : //
198 : : // \param instring String to convert. (This may be empty, in which case
199 : : // "b10-" will be returned.)
200 : : //
201 : : // \return Converted string.
202 : : std::string
203 : 27 : b10Prefix(const std::string& instring) {
204 : 54 : std::string result = instring;
205 [ + - ]: 27 : if (!result.empty()) {
206 : 27 : result[0] = tolower(result[0]);
207 : : }
208 [ + - ][ + - ]: 54 : return (std::string("b10-") + result);
209 : : }
210 : :
211 : : // Reads a output_option subelement of a logger configuration,
212 : : // and sets the values thereing to the given OutputOption struct,
213 : : // or defaults values if they are not provided (from config_data).
214 : : void
215 : 0 : readOutputOptionConf(isc::log::OutputOption& output_option,
216 : : ConstElementPtr output_option_el,
217 : : const ConfigData& config_data)
218 : : {
219 : : ConstElementPtr destination_el = getValueOrDefault(output_option_el,
220 : : "destination", config_data,
221 [ # # ][ # # ]: 0 : "loggers/output_options/destination");
222 [ # # ][ # # ]: 0 : output_option.destination = isc::log::getDestination(destination_el->stringValue());
223 : : ConstElementPtr output_el = getValueOrDefault(output_option_el,
224 : : "output", config_data,
225 [ # # ][ # # ]: 0 : "loggers/output_options/output");
[ # # ]
226 [ # # ]: 0 : if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) {
227 [ # # ][ # # ]: 0 : output_option.stream = isc::log::getStream(output_el->stringValue());
228 [ # # ]: 0 : } else if (output_option.destination == isc::log::OutputOption::DEST_FILE) {
229 [ # # ]: 0 : output_option.filename = output_el->stringValue();
230 [ # # ]: 0 : } else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) {
231 [ # # ]: 0 : output_option.facility = output_el->stringValue();
232 : : }
233 : : output_option.flush = getValueOrDefault(output_option_el,
234 : : "flush", config_data,
235 [ # # ][ # # ]: 0 : "loggers/output_options/flush")->boolValue();
[ # # ][ # # ]
236 : : output_option.maxsize = getValueOrDefault(output_option_el,
237 : : "maxsize", config_data,
238 [ # # ][ # # ]: 0 : "loggers/output_options/maxsize")->intValue();
[ # # ][ # # ]
239 : : output_option.maxver = getValueOrDefault(output_option_el,
240 : : "maxver", config_data,
241 [ # # ][ # # ]: 0 : "loggers/output_options/maxver")->intValue();
[ # # ][ # # ]
242 : 0 : }
243 : :
244 : : // Reads a full 'loggers' configuration, and adds the loggers therein
245 : : // to the given vector, fills in blanks with defaults from config_data
246 : : void
247 : 0 : readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
248 : : ConstElementPtr logger,
249 : : const ConfigData& config_data)
250 : : {
251 : : // Read name, adding prefix as required.
252 [ # # ][ # # ]: 0 : std::string lname = logger->get("name")->stringValue();
253 : :
254 : : ConstElementPtr severity_el = getValueOrDefault(logger,
255 : : "severity", config_data,
256 [ # # ][ # # ]: 0 : "loggers/severity");
[ # # ]
257 : : isc::log::Severity severity = isc::log::getSeverity(
258 [ # # ][ # # ]: 0 : severity_el->stringValue());
259 : : int dbg_level = getValueOrDefault(logger, "debuglevel",
260 : : config_data,
261 [ # # ][ # # ]: 0 : "loggers/debuglevel")->intValue();
[ # # ][ # # ]
262 : : bool additive = getValueOrDefault(logger, "additive", config_data,
263 [ # # ][ # # ]: 0 : "loggers/additive")->boolValue();
[ # # ][ # # ]
264 : :
265 : : isc::log::LoggerSpecification logger_spec(
266 : : lname, severity, dbg_level, additive
267 : 0 : );
268 : :
269 [ # # ][ # # ]: 0 : if (logger->contains("output_options")) {
[ # # ]
270 [ # # ][ # # ]: 0 : BOOST_FOREACH(ConstElementPtr output_option_el,
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
271 : : logger->get("output_options")->listValue()) {
272 : : // create outputoptions
273 : 0 : isc::log::OutputOption output_option;
274 : : readOutputOptionConf(output_option,
275 : : output_option_el,
276 [ # # ]: 0 : config_data);
277 : : logger_spec.addOutputOption(output_option);
278 : : }
279 : : }
280 : :
281 : : specs.push_back(logger_spec);
282 : 0 : }
283 : :
284 : : // Copies the map for a logger, changing the name of the logger in the process.
285 : : // This is used because the map being copied is "const", so in order to
286 : : // change the name we need to create a new one.
287 : : //
288 : : // \param cur_logger Logger being copied.
289 : : // \param new_name New value of the "name" element at the top level.
290 : : //
291 : : // \return Pointer to the map with the updated element.
292 : : ConstElementPtr
293 : 11 : copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) {
294 : :
295 : : // Since we'll only be updating one first-level element and subsequent
296 : : // use won't change the contents of the map, a shallow map copy is enough.
297 : 11 : ElementPtr new_logger(Element::createMap());
298 [ + - ][ + - ]: 11 : new_logger->setValue(cur_logger->mapValue());
299 [ + - ][ + - ]: 22 : new_logger->set("name", Element::create(new_name));
[ + - ]
300 : :
301 : 11 : return (new_logger);
302 : : }
303 : :
304 : :
305 : : } // end anonymous namespace
306 : :
307 : :
308 : : ConstElementPtr
309 : 19 : getRelatedLoggers(ConstElementPtr loggers) {
310 : : // Keep a list of names for easier lookup later
311 : : std::set<std::string> our_names;
312 [ + - ]: 19 : const std::string& root_name = isc::log::getRootLoggerName();
313 : :
314 [ + - ]: 19 : ElementPtr result = isc::data::Element::createList();
315 : :
316 [ + - ][ + + ]: 127 : BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
[ + - ][ + - ]
[ + + ][ + + ]
317 : : // Need to add the b10- prefix to names ready from the spec file.
318 [ + - ][ + - ]: 54 : const std::string cur_name = cur_logger->get("name")->stringValue();
[ + - ]
319 [ + - ]: 54 : const std::string mod_name = b10Prefix(cur_name);
320 [ + + ][ + - ]: 27 : if (mod_name == root_name || mod_name.find(root_name + ".") == 0) {
[ + - ][ + + ]
[ + + ][ + + ]
[ # # ]
321 : :
322 : : // Note this name so that we don't add a wildcard that matches it.
323 : : our_names.insert(mod_name);
324 : :
325 : : // We want to store the logger with the modified name (i.e. with
326 : : // the b10- prefix). As we are dealing with const loggers, we
327 : : // store a modified copy of the data.
328 [ + - ][ + - ]: 8 : result->add(copyLogger(cur_logger, mod_name));
329 [ + - ][ - + ]: 8 : LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_EXPLICIT)
[ # # ]
330 [ # # ][ # # ]: 0 : .arg(cur_name);
331 : :
332 [ + - ][ + + ]: 19 : } else if (!cur_name.empty() && (cur_name[0] != '*')) {
[ + + ]
333 : : // Not a wildcard logger and we are ignoring it.
334 [ + - ][ - + ]: 11 : LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
[ # # ]
335 [ # # ][ # # ]: 0 : CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name);
336 : : }
337 : : }
338 : :
339 : : // Now find the wildcard names (the one that start with "*").
340 [ + - ][ + + ]: 127 : BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
[ + - ][ + - ]
[ + + ][ + + ]
341 [ + - ][ + - ]: 54 : std::string cur_name = cur_logger->get("name")->stringValue();
[ + - ]
342 : : // If name is '*', or starts with '*.', replace * with root
343 : : // logger name.
344 [ + + ][ + - ]: 56 : if (cur_name == "*" || cur_name.length() > 1 &&
[ + + ][ + + ]
[ + + ]
345 : : cur_name[0] == '*' && cur_name[1] == '.') {
346 : :
347 : : // Substitute the "*" with the root name
348 [ + - ]: 8 : std::string mod_name = cur_name;
349 [ + - ]: 4 : mod_name.replace(0, 1, root_name);
350 : :
351 : : // Now add it to the result list, but only if a logger with
352 : : // that name was not configured explicitly.
353 [ + + ]: 4 : if (our_names.find(mod_name) == our_names.end()) {
354 : :
355 : : // We substitute the name here, but as we are dealing with
356 : : // consts, we need to copy the data.
357 [ + - ][ + - ]: 3 : result->add(copyLogger(cur_logger, mod_name));
358 [ + - ][ - + ]: 3 : LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
[ # # ]
359 [ # # ][ # # ]: 0 : CONFIG_LOG_WILD_MATCH).arg(cur_name);
360 : :
361 [ + - ][ - + ]: 2 : } else if (!cur_name.empty() && (cur_name[0] == '*')) {
[ + - ]
362 : : // Is a wildcard and we are ignoring it (because the wildcard
363 : : // expands to a specification that we already encountered when
364 : : // processing explicit names).
365 [ + - ][ - + ]: 1 : LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
[ # # ]
366 [ # # ][ # # ]: 0 : CONFIG_LOG_IGNORE_WILD).arg(cur_name);
367 : : }
368 : : }
369 : : }
370 : 19 : return (result);
371 : : }
372 : :
373 : : void
374 : 139 : default_logconfig_handler(const std::string& module_name,
375 : : ConstElementPtr new_config,
376 : : const ConfigData& config_data) {
377 [ + - ]: 139 : config_data.getModuleSpec().validateConfig(new_config, true);
378 : :
379 : 139 : std::vector<isc::log::LoggerSpecification> specs;
380 : :
381 [ + - ][ + - ]: 139 : if (new_config->contains("loggers")) {
[ + + ]
382 [ + - ][ + - ]: 4 : ConstElementPtr loggers = getRelatedLoggers(new_config->get("loggers"));
[ + - ]
383 [ + - ][ # # ]: 2 : BOOST_FOREACH(ConstElementPtr logger,
[ # # ][ + - ]
[ + - ][ - + ]
384 : : loggers->listValue()) {
385 [ # # ]: 0 : readLoggersConf(specs, logger, config_data);
386 : : }
387 : : }
388 : :
389 [ + - ][ + - ]: 278 : isc::log::LoggerManager logger_manager;
390 : : logger_manager.process(specs.begin(), specs.end());
391 : 139 : }
392 : :
393 : :
394 : : ModuleSpec
395 : 39 : ModuleCCSession::readModuleSpecification(const std::string& filename) {
396 : 78 : std::ifstream file;
397 : : ModuleSpec module_spec;
398 : :
399 : : // this file should be declared in a @something@ directive
400 [ + - ]: 39 : file.open(filename.c_str());
401 [ + + ]: 39 : if (!file) {
402 [ + - ][ + - ]: 1 : LOG_ERROR(config_logger, CONFIG_OPEN_FAIL).arg(filename).arg(strerror(errno));
[ + - ][ + - ]
[ + - ][ + - ]
403 [ + - ][ + - ]: 2 : isc_throw(CCSessionInitError, strerror(errno));
404 : : }
405 : :
406 : : try {
407 [ + + ]: 38 : module_spec = moduleSpecFromFile(file, true);
408 : 0 : } catch (const JSONError& pe) {
409 [ # # ][ # # ]: 0 : LOG_ERROR(config_logger, CONFIG_JSON_PARSE).arg(filename).arg(pe.what());
[ # # ][ # # ]
[ # # ][ # # ]
410 [ # # ][ # # ]: 0 : isc_throw(CCSessionInitError, pe.what());
411 [ - - + ]: 2 : } catch (const ModuleSpecError& dde) {
412 [ - + ][ + - ]: 1 : LOG_ERROR(config_logger, CONFIG_MOD_SPEC_FORMAT).arg(filename).arg(dde.what());
[ - + ][ - + ]
[ - + ][ - + ]
413 [ - + ][ - + ]: 2 : isc_throw(CCSessionInitError, dde.what());
414 : : }
415 [ + - ]: 37 : file.close();
416 : 37 : return (module_spec);
417 : : }
418 : :
419 : : void
420 : 0 : ModuleCCSession::startCheck() {
421 : : // data available on the command channel. process it in the synchronous
422 : : // mode.
423 : 0 : checkCommand();
424 : :
425 : : // start asynchronous read again.
426 [ # # ]: 0 : session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
427 : 0 : }
428 : :
429 : 31 : ModuleCCSession::ModuleCCSession(
430 : : const std::string& spec_file_name,
431 : : isc::cc::AbstractSession& session,
432 : : isc::data::ConstElementPtr(*config_handler)(
433 : : isc::data::ConstElementPtr new_config),
434 : : isc::data::ConstElementPtr(*command_handler)(
435 : : const std::string& command, isc::data::ConstElementPtr args),
436 : : bool start_immediately,
437 : : bool handle_logging
438 : : ) :
439 : : started_(false),
440 : 35 : session_(session)
441 : : {
442 [ + + ]: 31 : module_specification_ = readModuleSpecification(spec_file_name);
443 : 58 : setModuleSpec(module_specification_);
444 : :
445 [ + - ][ + - ]: 87 : module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
[ + - ]
446 : 29 : config_handler_ = config_handler;
447 : 29 : command_handler_ = command_handler;
448 : :
449 [ + - ]: 29 : session_.establish(NULL);
450 [ + - ][ + - ]: 29 : session_.subscribe(module_name_, "*");
[ + - ]
451 : :
452 : : // send the data specification
453 : : ConstElementPtr spec_msg = createCommand("module_spec",
454 [ + - ][ + - ]: 29 : module_specification_.getFullSpec());
455 [ + - ][ + - ]: 87 : unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
[ + - ][ + - ]
456 : :
457 : : ConstElementPtr answer, env;
458 [ + - ]: 29 : session_.group_recvmsg(env, answer, false, seq);
459 : : int rcode;
460 [ + - ]: 29 : ConstElementPtr err = parseAnswer(rcode, answer);
461 [ - + ]: 29 : if (rcode != 0) {
462 [ # # ][ # # ]: 0 : LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str());
[ # # ][ # # ]
[ # # ][ # # ]
463 [ # # ][ # # ]: 0 : isc_throw(CCSessionInitError, answer->str());
[ # # ]
464 : : }
465 : :
466 [ + - ][ + - ]: 58 : setLocalConfig(Element::fromJSON("{}"));
467 : : // get any stored configuration from the manager
468 [ + + ]: 29 : if (config_handler_) {
469 [ + - ][ + - ]: 10 : ConstElementPtr cmd = Element::fromJSON("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
[ + - ]
470 [ + - ][ + - ]: 15 : seq = session_.group_sendmsg(cmd, "ConfigManager");
[ + - ][ + - ]
471 [ + - ]: 5 : session_.group_recvmsg(env, answer, false, seq);
472 [ + - ]: 5 : ConstElementPtr new_config = parseAnswer(rcode, answer);
473 [ + + ]: 5 : if (rcode == 0) {
474 [ + - ]: 4 : handleConfigUpdate(new_config);
475 : : } else {
476 [ + - ][ + - ]: 1 : LOG_ERROR(config_logger, CONFIG_GET_FAIL).arg(new_config->str());
[ + - ][ + - ]
[ + - ][ + - ]
477 [ + - ][ + - ]: 2 : isc_throw(CCSessionInitError, answer->str());
[ + - ]
478 : : }
479 : : }
480 : :
481 : : // Keep track of logging settings automatically
482 [ + + ]: 28 : if (handle_logging) {
483 [ + - ][ + + ]: 2 : addRemoteConfig("Logging", default_logconfig_handler, false);
484 : : }
485 : :
486 [ + + ]: 27 : if (start_immediately) {
487 [ + - ]: 10 : start();
488 : : }
489 : :
490 : 27 : }
491 : :
492 : 27 : ModuleCCSession::~ModuleCCSession() {
493 : : try {
494 [ + + ]: 27 : sendStopping();
495 : 2 : } catch (const std::exception& exc) {
496 [ - + ][ + - ]: 2 : LOG_ERROR(config_logger,
[ - + ]
497 [ - + ][ - + ]: 1 : CONFIG_CCSESSION_STOPPING).arg(exc.what());
498 [ + - ][ # # ]: 1 : } catch (...) {
499 [ # # ][ # # ]: 0 : LOG_ERROR(config_logger,
[ # # ]
500 [ # # ]: 0 : CONFIG_CCSESSION_STOPPING_UNKNOWN);
501 : : }
502 : 41 : };
503 : :
504 : : void
505 : 14 : ModuleCCSession::start() {
506 [ + + ]: 14 : if (started_) {
507 [ + - ][ + - ]: 4 : isc_throw(CCSessionError, "Module CC session already started");
508 : : }
509 : :
510 : : // register callback for asynchronous read
511 [ + - ]: 12 : session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
512 : :
513 : 12 : started_ = true;
514 : 12 : }
515 : :
516 : : /// Validates the new config values, if they are correct,
517 : : /// call the config handler with the values that have changed
518 : : /// If that results in success, store the new config
519 : : ConstElementPtr
520 : 7 : ModuleCCSession::handleConfigUpdate(ConstElementPtr new_config) {
521 : : ConstElementPtr answer;
522 [ + - ]: 7 : ElementPtr errors = Element::createList();
523 [ - + ]: 7 : if (!config_handler_) {
524 [ # # ][ # # ]: 0 : answer = createAnswer(1, module_name_ + " does not have a config handler");
[ # # ]
525 [ + + ]: 7 : } else if (!module_specification_.validateConfig(new_config, false,
526 [ + - ]: 7 : errors)) {
527 [ + - + - ]: 2 : std::stringstream ss;
528 [ + - ]: 1 : ss << "Error in config validation: ";
529 [ + - ][ + + ]: 5 : BOOST_FOREACH(ConstElementPtr error, errors->listValue()) {
[ + - ][ + - ]
[ + + ][ + + ]
530 [ + - ][ + - ]: 1 : ss << error->stringValue();
531 : : }
532 [ + - ][ + - ]: 2 : answer = createAnswer(2, ss.str());
533 : : } else {
534 : : // remove the values that have not changed
535 [ + - ]: 6 : ConstElementPtr diff = removeIdentical(new_config, getLocalConfig());
536 : : // handle config update
537 [ + - ][ + - ]: 6 : answer = config_handler_(diff);
538 : : int rcode;
539 [ + - ]: 6 : parseAnswer(rcode, answer);
540 [ + + ]: 6 : if (rcode == 0) {
541 : 5 : ElementPtr local_config = getLocalConfig();
542 [ + - ]: 5 : isc::data::merge(local_config, diff);
543 : 5 : setLocalConfig(local_config);
544 : : }
545 : : }
546 : 7 : return (answer);
547 : : }
548 : :
549 : : bool
550 : 0 : ModuleCCSession::hasQueuedMsgs() const {
551 : 0 : return (session_.hasQueuedMsgs());
552 : : }
553 : :
554 : : ConstElementPtr
555 : 8 : ModuleCCSession::checkConfigUpdateCommand(const std::string& target_module,
556 : : ConstElementPtr arg)
557 : : {
558 [ + + ]: 8 : if (target_module == module_name_) {
559 [ + - ]: 3 : return (handleConfigUpdate(arg));
560 : : } else {
561 : : // ok this update is not for us, if we have this module
562 : : // in our remote config list, update that
563 [ + - ]: 5 : updateRemoteConfig(target_module, arg);
564 : : // we're not supposed to answer to this, so return
565 : : return (ElementPtr());
566 : : }
567 : : }
568 : :
569 : : ConstElementPtr
570 : 8 : ModuleCCSession::checkModuleCommand(const std::string& cmd_str,
571 : : const std::string& target_module,
572 : : ConstElementPtr arg) const
573 : : {
574 [ + + ]: 8 : if (target_module == module_name_) {
575 [ + + ]: 7 : if (command_handler_) {
576 : 6 : ElementPtr errors = Element::createList();
577 [ + - ][ + + ]: 12 : if (module_specification_.validateCommand(cmd_str,
578 : : arg,
579 : 6 : errors)) {
580 [ + - ]: 5 : return (command_handler_(cmd_str, arg));
581 : : } else {
582 [ + - + - ]: 2 : std::stringstream ss;
583 [ + - ]: 1 : ss << "Error in command validation: ";
584 [ + - ][ + + ]: 5 : BOOST_FOREACH(ConstElementPtr error,
[ + - ][ + - ]
[ + + ][ + + ]
585 : : errors->listValue()) {
586 [ + - ][ + - ]: 1 : ss << error->stringValue();
587 : : }
588 [ + - ]: 1 : return (createAnswer(3, ss.str()));
589 : : }
590 : : } else {
591 : : return (createAnswer(1,
592 : : "Command given but no "
593 [ + - ]: 1 : "command handler for module"));
594 : : }
595 : : }
596 : : return (ElementPtr());
597 : : }
598 : :
599 : : int
600 : 19 : ModuleCCSession::checkCommand() {
601 : : ConstElementPtr cmd, routing, data;
602 [ + - ][ + + ]: 19 : if (session_.group_recvmsg(routing, data, true)) {
603 : :
604 : : /* ignore result messages (in case we're out of sync, to prevent
605 : : * pingpongs */
606 [ + + ][ + - ]: 18 : if (data->getType() != Element::map || data->contains("result")) {
[ + - ][ - + ]
[ + + ][ + + ]
[ # # ]
607 : : return (0);
608 : : }
609 : : ConstElementPtr arg;
610 : : ConstElementPtr answer;
611 : : try {
612 [ + + ]: 33 : std::string cmd_str = parseCommand(arg, data);
613 [ + - ][ + - ]: 48 : std::string target_module = routing->get("group")->stringValue();
[ + - ]
614 [ + + ]: 16 : if (cmd_str == "config_update") {
615 [ + - ][ + - ]: 8 : answer = checkConfigUpdateCommand(target_module, arg);
616 : : } else {
617 [ + - ][ + - ]: 8 : answer = checkModuleCommand(cmd_str, target_module, arg);
618 : : }
619 [ + - ]: 2 : } catch (const CCSessionError& re) {
620 [ - + ][ + - ]: 1 : LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what());
[ - + ][ - + ]
[ - + ]
621 [ - + - ]: 1 : } catch (const std::exception& stde) {
622 : : // No matter what unexpected error happens, we do not want
623 : : // to crash because of an incoming event, so we log the
624 : : // exception and continue to run
625 [ # # ][ # # ]: 0 : LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG_INTERNAL).arg(stde.what());
[ # # ][ # # ]
[ # # ]
626 : : }
627 [ + - ][ + + ]: 34 : if (!isNull(answer)) {
628 [ + - ]: 10 : session_.reply(routing, answer);
629 : : }
630 : : }
631 : :
632 : : return (0);
633 : : }
634 : :
635 : : ModuleSpec
636 : 21 : ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
637 [ + + ]: 21 : if (is_filename) {
638 : : // It is a filename, simply load it.
639 : 8 : return (readModuleSpecification(module));
640 : : } else {
641 : : // It's module name, request it from config manager
642 : :
643 : : // Send the command
644 : : ConstElementPtr cmd(createCommand("get_module_spec",
645 : 13 : Element::fromJSON("{\"module_name\": \"" + module +
646 [ + - ][ + - ]: 39 : "\"}")));
[ + - ][ + - ]
647 [ + - ][ + - ]: 39 : unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
[ + - ][ + - ]
648 : : ConstElementPtr env, answer;
649 [ + - ]: 13 : session_.group_recvmsg(env, answer, false, seq);
650 : : int rcode;
651 [ + - ]: 13 : ConstElementPtr spec_data = parseAnswer(rcode, answer);
652 [ + - ][ - + ]: 26 : if (rcode == 0 && spec_data) {
[ + - ]
653 : : // received OK, construct the spec out of it
654 [ + + ]: 13 : ModuleSpec spec = ModuleSpec(spec_data);
655 [ + - + + ]: 24 : if (module != spec.getModuleName()) {
656 : : // It's a different module!
657 [ + - ][ + - ]: 2 : isc_throw(CCSessionError, "Module name mismatch");
[ + - ]
658 : : }
659 : : return (spec);
660 : : } else {
661 [ # # ][ # # ]: 0 : isc_throw(CCSessionError, "Error getting config for " +
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
662 : : module + ": " + answer->str());
663 : : }
664 : : }
665 : : }
666 : :
667 : : std::string
668 : 21 : ModuleCCSession::addRemoteConfig(const std::string& spec_name,
669 : : void (*handler)(const std::string& module,
670 : : ConstElementPtr,
671 : : const ConfigData&),
672 : : bool spec_is_filename)
673 : : {
674 : : // First get the module name, specification and default config
675 : 21 : const ModuleSpec rmod_spec(fetchRemoteSpec(spec_name, spec_is_filename));
676 [ + - ]: 23 : const std::string module_name(rmod_spec.getModuleName());
677 [ + - ]: 19 : ConfigData rmod_config(rmod_spec);
678 : :
679 : : // Get the current configuration values from config manager
680 : : ConstElementPtr cmd(createCommand("get_config",
681 : : Element::fromJSON("{\"module_name\": \"" +
682 [ + - ][ + - ]: 57 : module_name + "\"}")));
[ + - ][ + - ]
[ + - ]
683 [ + - ][ + - ]: 57 : const unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
[ + - ][ + - ]
684 : :
685 : : ConstElementPtr env, answer;
686 [ + + ]: 19 : session_.group_recvmsg(env, answer, false, seq);
687 : : int rcode;
688 [ + + ]: 18 : ConstElementPtr new_config = parseAnswer(rcode, answer);
689 : : ElementPtr local_config;
690 [ + + ][ + + ]: 33 : if (rcode == 0 && new_config) {
[ + + ]
691 : : // Merge the received config into existing local config
692 [ + - ]: 15 : local_config = rmod_config.getLocalConfig();
693 [ + - ]: 15 : isc::data::merge(local_config, new_config);
694 : : rmod_config.setLocalConfig(local_config);
695 : : } else {
696 [ + - ][ + - ]: 4 : isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
697 : : }
698 : :
699 : : // all ok, add it
700 [ + - ]: 15 : remote_module_configs_[module_name] = rmod_config;
701 [ + + ]: 15 : if (handler) {
702 : 22 : remote_module_handlers_[module_name] = handler;
703 [ + - ]: 11 : handler(module_name, local_config, rmod_config);
704 : : }
705 : :
706 : : // Make sure we get updates in future
707 [ + - ][ + - ]: 15 : session_.subscribe(module_name);
[ + - ]
708 : 15 : return (module_name);
709 : : }
710 : :
711 : : void
712 : 13 : ModuleCCSession::removeRemoteConfig(const std::string& module_name) {
713 : : std::map<std::string, ConfigData>::iterator it;
714 : :
715 : 13 : it = remote_module_configs_.find(module_name);
716 [ + - ]: 13 : if (it != remote_module_configs_.end()) {
717 : 13 : remote_module_configs_.erase(it);
718 : 13 : remote_module_handlers_.erase(module_name);
719 [ + - ][ + - ]: 13 : session_.unsubscribe(module_name);
720 : : }
721 : 13 : }
722 : :
723 : : ConstElementPtr
724 : 9 : ModuleCCSession::getRemoteConfigValue(const std::string& module_name,
725 : : const std::string& identifier) const
726 : : {
727 : : std::map<std::string, ConfigData>::const_iterator it =
728 : 18 : remote_module_configs_.find(module_name);
729 : :
730 [ + + ]: 9 : if (it != remote_module_configs_.end()) {
731 : 8 : return ((*it).second.getValue(identifier));
732 : : } else {
733 [ + - ][ + - ]: 2 : isc_throw(CCSessionError,
[ + - ][ + - ]
734 : : "Remote module " + module_name + " not found.");
735 : : }
736 : : }
737 : :
738 : : void
739 : 5 : ModuleCCSession::updateRemoteConfig(const std::string& module_name,
740 : : ConstElementPtr new_config)
741 : : {
742 : : std::map<std::string, ConfigData>::iterator it;
743 : :
744 : 5 : it = remote_module_configs_.find(module_name);
745 [ + - ]: 5 : if (it != remote_module_configs_.end()) {
746 : 5 : ElementPtr rconf = (*it).second.getLocalConfig();
747 [ + - ]: 5 : isc::data::merge(rconf, new_config);
748 : : std::map<std::string, RemoteHandler>::iterator hit =
749 : 10 : remote_module_handlers_.find(module_name);
750 [ + + ]: 5 : if (hit != remote_module_handlers_.end()) {
751 [ + - ]: 4 : hit->second(module_name, new_config, it->second);
752 : : }
753 : : }
754 : 5 : }
755 : :
756 : : void
757 : 27 : ModuleCCSession::sendStopping() {
758 : : // Inform the configuration manager that this module is stopping
759 : : ConstElementPtr cmd(createCommand("stopping",
760 : : Element::fromJSON(
761 : : "{\"module_name\": \"" +
762 [ + - ][ + - ]: 81 : module_name_ + "\"}")));
[ + - ][ + - ]
763 : : // It's just an FYI, configmanager is not expected to respond.
764 [ + - ][ + - ]: 81 : session_.group_sendmsg(cmd, "ConfigManager");
[ + - ][ + + ]
765 : 26 : }
766 : :
767 : : }
768 : 111 : }
|