ccsession.cc 33 KB
Newer Older
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
#include <config.h>
16

JINMEI Tatuya's avatar
JINMEI Tatuya committed
17
#include <stdexcept>
18
#include <stdlib.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
19
#include <string.h>
20
#include <sys/time.h>
21
#include <ctype.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
22

23
#include <algorithm>
24
#include <cerrno>
25 26
#include <fstream>
#include <iostream>
27
#include <set>
28 29
#include <sstream>
#include <string>
30

31
#include <boost/bind.hpp>
32
#include <boost/foreach.hpp>
33

34
#include <cc/data.h>
35
#include <config/module_spec.h>
36
#include <cc/session.h>
Jelte Jansen's avatar
Jelte Jansen committed
37
#include <exceptions/exceptions.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
38

39
#include <config/config_log.h>
40
#include <config/ccsession.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
41

Jelte Jansen's avatar
Jelte Jansen committed
42 43
#include <log/logger_support.h>
#include <log/logger_specification.h>
44
#include <log/logger_manager.h>
45
#include <log/logger_name.h>
Jelte Jansen's avatar
Jelte Jansen committed
46

47
using namespace std;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
48

Jelte Jansen's avatar
Jelte Jansen committed
49
using isc::data::Element;
50
using isc::data::ConstElementPtr;
Jelte Jansen's avatar
Jelte Jansen committed
51
using isc::data::ElementPtr;
Jelte Jansen's avatar
Jelte Jansen committed
52
using isc::data::JSONError;
53 54 55

namespace isc {
namespace config {
56

Jelte Jansen's avatar
Jelte Jansen committed
57
/// Creates a standard config/command protocol answer message
58
ConstElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
59
createAnswer() {
60
    ElementPtr answer = Element::createMap();
61
    ElementPtr answer_content = Element::createList();
62 63
    answer_content->add(Element::create(isc::cc::CC_REPLY_SUCCESS));
    answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
64

JINMEI Tatuya's avatar
JINMEI Tatuya committed
65
    return (answer);
66 67
}

68 69
ConstElementPtr
createAnswer(const int rcode, ConstElementPtr arg) {
70 71 72
    if (rcode != 0 && (!arg || arg->getType() != Element::string)) {
        isc_throw(CCSessionError, "Bad or no argument for rcode != 0");
    }
73
    ElementPtr answer = Element::createMap();
74
    ElementPtr answer_content = Element::createList();
75 76
    answer_content->add(Element::create(rcode));
    answer_content->add(arg);
77
    answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
78

JINMEI Tatuya's avatar
JINMEI Tatuya committed
79
    return (answer);
80 81
}

82
ConstElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
83
createAnswer(const int rcode, const std::string& arg) {
84
    ElementPtr answer = Element::createMap();
85
    ElementPtr answer_content = Element::createList();
86 87
    answer_content->add(Element::create(rcode));
    answer_content->add(Element::create(arg));
88
    answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
89

JINMEI Tatuya's avatar
JINMEI Tatuya committed
90
    return (answer);
91 92
}

93 94
ConstElementPtr
parseAnswer(int &rcode, ConstElementPtr msg) {
95 96
    if (msg &&
        msg->getType() == Element::map &&
97 98
        msg->contains(isc::cc::CC_PAYLOAD_RESULT)) {
        ConstElementPtr result = msg->get(isc::cc::CC_PAYLOAD_RESULT);
99 100 101
        if (result->getType() != Element::list) {
            isc_throw(CCSessionError, "Result element in answer message is not a list");
        } else if (result->get(0)->getType() != Element::integer) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
102
            isc_throw(CCSessionError, "First element of result is not an rcode in answer message");
Jelte Jansen's avatar
Jelte Jansen committed
103 104 105
        }
        rcode = result->get(0)->intValue();
        if (result->size() > 1) {
106
            if (rcode == 0 || result->get(1)->getType() == Element::string) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
107
                return (result->get(1));
108 109 110
            } else {
                isc_throw(CCSessionError, "Error description in result with rcode != 0 is not a string");
            }
Jelte Jansen's avatar
Jelte Jansen committed
111
        } else {
112
            if (rcode == 0) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
113
                return (ElementPtr());
114 115 116
            } else {
                isc_throw(CCSessionError, "Result with rcode != 0 does not have an error description");
            }
Jelte Jansen's avatar
Jelte Jansen committed
117
        }
118 119
    } else {
        isc_throw(CCSessionError, "No result part in answer message");
Jelte Jansen's avatar
Jelte Jansen committed
120 121
    }
}
122

123
ConstElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
124
createCommand(const std::string& command) {
125
    return (createCommand(command, ElementPtr()));
126 127
}

128 129
ConstElementPtr
createCommand(const std::string& command, ConstElementPtr arg) {
130 131
    ElementPtr cmd = Element::createMap();
    ElementPtr cmd_parts = Element::createList();
132 133 134 135
    cmd_parts->add(Element::create(command));
    if (arg) {
        cmd_parts->add(arg);
    }
136
    cmd->set(isc::cc::CC_PAYLOAD_COMMAND, cmd_parts);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
137
    return (cmd);
138 139
}

140 141
std::string
parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
142 143
    if (command &&
        command->getType() == Element::map &&
144 145
        command->contains(isc::cc::CC_PAYLOAD_COMMAND)) {
        ConstElementPtr cmd = command->get(isc::cc::CC_PAYLOAD_COMMAND);
146
        if (cmd->getType() == Element::list &&
147
            !cmd->empty() &&
148 149 150 151
            cmd->get(0)->getType() == Element::string) {
            if (cmd->size() > 1) {
                arg = cmd->get(1);
            } else {
152
                arg = Element::createMap();
153
            }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
154
            return (cmd->get(0)->stringValue());
155 156
        } else {
            isc_throw(CCSessionError, "Command part in command message missing, empty, or not a list");
157
        }
158 159
    } else {
        isc_throw(CCSessionError, "Command Element empty or not a map with \"command\"");
160 161 162
    }
}

163
namespace {
164 165 166 167
// Temporary workaround functions for missing functionality in
// getValue() (main problem described in ticket #993)
// This returns either the value set for the given relative id,
// or its default value
168
// (intentionally defined here so this interface does not get
169 170 171 172 173 174 175 176 177 178 179 180
// included in ConfigData as it is)
ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
                                  const std::string& relative_id,
                                  const ConfigData& config_data,
                                  const std::string& full_id) {
    if (config_part->contains(relative_id)) {
        return config_part->get(relative_id);
    } else {
        return config_data.getDefaultValue(full_id);
    }
}

181 182
// Prefix name with "b10-".
//
183 184 185 186 187 188
// In BIND 10, modules have names taken from the .spec file, which are typically
// names starting with a capital letter (e.g. "Resolver", "Auth" etc.).  The
// names of the associated binaries are derived from the module names, being
// prefixed "b10-" and having the first letter of the module name lower-cased
// (e.g. "b10-resolver", "b10-auth").  (It is a required convention that there
// be this relationship between the names.)
189
//
190 191 192 193 194 195 196
// Within the binaries the root loggers are named after the binaries themselves.
// (The reason for this is that the name of the logger is included in the
// message logged, so making it clear which message comes from which BIND 10
// process.) As logging is configured using module names, the configuration code
// has to match these with the corresponding logger names. This function
// converts a module name to a root logger name by lowercasing the first letter
// of the module name and prepending "b10-".
197
//
198 199
// \param instring String to convert.  (This may be empty, in which case
//        "b10-" will be returned.)
200
//
201
// \return Converted string.
202 203 204 205
std::string
b10Prefix(const std::string& instring) {
    std::string result = instring;
    if (!result.empty()) {
206
        result[0] = tolower(result[0]);
207 208 209 210
    }
    return (std::string("b10-") + result);
}

211 212 213 214 215 216 217 218 219 220 221 222
// Reads a output_option subelement of a logger configuration,
// and sets the values thereing to the given OutputOption struct,
// or defaults values if they are not provided (from config_data).
void
readOutputOptionConf(isc::log::OutputOption& output_option,
                     ConstElementPtr output_option_el,
                     const ConfigData& config_data)
{
    ConstElementPtr destination_el = getValueOrDefault(output_option_el,
                                    "destination", config_data,
                                    "loggers/output_options/destination");
    output_option.destination = isc::log::getDestination(destination_el->stringValue());
223 224 225 226 227 228 229 230 231 232
    ConstElementPtr output_el = getValueOrDefault(output_option_el,
                                    "output", config_data,
                                    "loggers/output_options/output");
    if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) {
        output_option.stream = isc::log::getStream(output_el->stringValue());
    } else if (output_option.destination == isc::log::OutputOption::DEST_FILE) {
        output_option.filename = output_el->stringValue();
    } else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) {
        output_option.facility = output_el->stringValue();
    }
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
    output_option.flush = getValueOrDefault(output_option_el,
                              "flush", config_data,
                              "loggers/output_options/flush")->boolValue();
    output_option.maxsize = getValueOrDefault(output_option_el,
                                "maxsize", config_data,
                                "loggers/output_options/maxsize")->intValue();
    output_option.maxver = getValueOrDefault(output_option_el,
                               "maxver", config_data,
                               "loggers/output_options/maxver")->intValue();
}

// Reads a full 'loggers' configuration, and adds the loggers therein
// to the given vector, fills in blanks with defaults from config_data
void
readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
                ConstElementPtr logger,
                const ConfigData& config_data)
{
251
    // Read name, adding prefix as required.
252 253
    std::string lname = logger->get("name")->stringValue();

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
    ConstElementPtr severity_el = getValueOrDefault(logger,
                                      "severity", config_data,
                                      "loggers/severity");
    isc::log::Severity severity = isc::log::getSeverity(
                                      severity_el->stringValue());
    int dbg_level = getValueOrDefault(logger, "debuglevel",
                                      config_data,
                                      "loggers/debuglevel")->intValue();
    bool additive = getValueOrDefault(logger, "additive", config_data,
                                      "loggers/additive")->boolValue();

    isc::log::LoggerSpecification logger_spec(
        lname, severity, dbg_level, additive
    );

    if (logger->contains("output_options")) {
        BOOST_FOREACH(ConstElementPtr output_option_el,
                      logger->get("output_options")->listValue()) {
            // create outputoptions
            isc::log::OutputOption output_option;
            readOutputOptionConf(output_option,
                                 output_option_el,
                                 config_data);
            logger_spec.addOutputOption(output_option);
278 279
        }
    }
280 281

    specs.push_back(logger_spec);
282 283
}

284 285 286 287 288 289 290 291 292
// Copies the map for a logger, changing the name of the logger in the process.
// This is used because the map being copied is "const", so in order to
// change the name we need to create a new one.
//
// \param cur_logger Logger being copied.
// \param new_name New value of the "name" element at the top level.
//
// \return Pointer to the map with the updated element.
ConstElementPtr
293 294
copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) {

295 296
    // Since we'll only be updating one first-level element and subsequent
    // use won't change the contents of the map, a shallow map copy is enough.
297 298 299 300 301 302 303 304
    ElementPtr new_logger(Element::createMap());
    new_logger->setValue(cur_logger->mapValue());
    new_logger->set("name", Element::create(new_name));

    return (new_logger);
}


305 306
} // end anonymous namespace

307 308 309 310

ConstElementPtr
getRelatedLoggers(ConstElementPtr loggers) {
    // Keep a list of names for easier lookup later
311
    std::set<std::string> our_names;
312 313 314 315 316
    const std::string& root_name = isc::log::getRootLoggerName();

    ElementPtr result = isc::data::Element::createList();

    BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
317
        // Need to add the b10- prefix to names ready from the spec file.
318
        const std::string cur_name = cur_logger->get("name")->stringValue();
319 320 321 322 323 324 325 326 327 328 329 330 331 332
        const std::string mod_name = b10Prefix(cur_name);
        if (mod_name == root_name || mod_name.find(root_name + ".") == 0) {

            // Note this name so that we don't add a wildcard that matches it.
            our_names.insert(mod_name);

            // We want to store the logger with the modified name (i.e. with
            // the b10- prefix).  As we are dealing with const loggers, we
            // store a modified copy of the data.
            result->add(copyLogger(cur_logger, mod_name));
            LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_EXPLICIT)
                      .arg(cur_name);

        } else if (!cur_name.empty() && (cur_name[0] != '*')) {
333
            // Not a wildcard logger and we are ignoring it.
334 335
            LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
                      CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name);
336 337 338
        }
    }

339
    // Now find the wildcard names (the one that start with "*").
340
    BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
341
        const std::string cur_name = cur_logger->get("name")->stringValue();
342
        // If name is '*', or starts with '*.', replace * with root
343
        // logger name.
344 345
        if (cur_name == "*" || (cur_name.length() > 1 &&
            cur_name[0] == '*' && cur_name[1] == '.')) {
346

347 348 349 350
            // Substitute the "*" with the root name
            std::string mod_name = cur_name;
            mod_name.replace(0, 1, root_name);

351 352
            // Now add it to the result list, but only if a logger with
            // that name was not configured explicitly.
353
            if (our_names.find(mod_name) == our_names.end()) {
354 355 356

                // We substitute the name here, but as we are dealing with
                // consts, we need to copy the data.
357 358 359 360 361
                result->add(copyLogger(cur_logger, mod_name));
                LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
                          CONFIG_LOG_WILD_MATCH).arg(cur_name);

            } else if (!cur_name.empty() && (cur_name[0] == '*')) {
362 363 364
                // Is a wildcard and we are ignoring it (because the wildcard
                // expands to a specification that we already encountered when
                // processing explicit names).
365 366
                LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
                          CONFIG_LOG_IGNORE_WILD).arg(cur_name);
367 368 369
            }
        }
    }
370
    return (result);
371 372
}

Jelte Jansen's avatar
Jelte Jansen committed
373
void
374
default_logconfig_handler(const std::string& module_name,
375 376
                          ConstElementPtr new_config,
                          const ConfigData& config_data) {
377
    config_data.getModuleSpec().validateConfig(new_config, true);
378 379 380

    std::vector<isc::log::LoggerSpecification> specs;

Jelte Jansen's avatar
Jelte Jansen committed
381
    if (new_config->contains("loggers")) {
382
        ConstElementPtr loggers = getRelatedLoggers(new_config->get("loggers"));
Jelte Jansen's avatar
Jelte Jansen committed
383
        BOOST_FOREACH(ConstElementPtr logger,
384
                      loggers->listValue()) {
385
            readLoggersConf(specs, logger, config_data);
Jelte Jansen's avatar
Jelte Jansen committed
386 387
        }
    }
388 389 390

    isc::log::LoggerManager logger_manager;
    logger_manager.process(specs.begin(), specs.end());
Jelte Jansen's avatar
Jelte Jansen committed
391 392 393
}


394
ModuleSpec
395
ModuleCCSession::readModuleSpecification(const std::string& filename) {
396
    std::ifstream file;
397
    ModuleSpec module_spec;
398

399
    // this file should be declared in a @something@ directive
400
    file.open(filename.c_str());
401
    if (!file) {
402
        LOG_ERROR(config_logger, CONFIG_OPEN_FAIL).arg(filename).arg(strerror(errno));
403
        isc_throw(CCSessionInitError, strerror(errno));
404
    }
405

406
    try {
407
        module_spec = moduleSpecFromFile(file, true);
408
    } catch (const JSONError& pe) {
409 410
        LOG_ERROR(config_logger, CONFIG_JSON_PARSE).arg(filename).arg(pe.what());
        isc_throw(CCSessionInitError, pe.what());
411
    } catch (const ModuleSpecError& dde) {
412
        LOG_ERROR(config_logger, CONFIG_MOD_SPEC_FORMAT).arg(filename).arg(dde.what());
413
        isc_throw(CCSessionInitError, dde.what());
414 415
    }
    file.close();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
416
    return (module_spec);
417
}
418

419 420 421 422
void
ModuleCCSession::startCheck() {
    // data available on the command channel.  process it in the synchronous
    // mode.
423
    checkCommand();
424 425 426 427 428 429

    // start asynchronous read again.
    session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
}

ModuleCCSession::ModuleCCSession(
430
    const std::string& spec_file_name,
JINMEI Tatuya's avatar
JINMEI Tatuya committed
431
    isc::cc::AbstractSession& session,
432 433 434
    isc::data::ConstElementPtr(*config_handler)(
        isc::data::ConstElementPtr new_config),
    isc::data::ConstElementPtr(*command_handler)(
JINMEI Tatuya's avatar
JINMEI Tatuya committed
435
        const std::string& command, isc::data::ConstElementPtr args),
Jelte Jansen's avatar
Jelte Jansen committed
436 437
    bool start_immediately,
    bool handle_logging
438
    ) :
JINMEI Tatuya's avatar
JINMEI Tatuya committed
439
    started_(false),
JINMEI Tatuya's avatar
JINMEI Tatuya committed
440
    session_(session)
JINMEI Tatuya's avatar
JINMEI Tatuya committed
441
{
442
    module_specification_ = readModuleSpecification(spec_file_name);
443
    setModuleSpec(module_specification_);
444

445
    module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
446 447 448
    config_handler_ = config_handler;
    command_handler_ = command_handler;

449
    session_.establish(NULL);
450
    session_.subscribe(module_name_, "*");
451

Jelte Jansen's avatar
Jelte Jansen committed
452
    // send the data specification
453 454
    ConstElementPtr spec_msg = createCommand("module_spec",
                                             module_specification_.getFullSpec());
Jelte Jansen's avatar
Jelte Jansen committed
455
    unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
456 457

    ConstElementPtr answer, env;
Jelte Jansen's avatar
Jelte Jansen committed
458
    session_.group_recvmsg(env, answer, false, seq);
Mukund Sivaraman's avatar
Mukund Sivaraman committed
459
    int rcode = -1;
460
    ConstElementPtr err = parseAnswer(rcode, answer);
461
    if (rcode != 0) {
462
        LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str());
463
        isc_throw(CCSessionInitError, answer->str());
464
    }
465

466
    setLocalConfig(Element::createMap());
Jelte Jansen's avatar
Jelte Jansen committed
467 468
    // get any stored configuration from the manager
    if (config_handler_) {
469 470 471 472
        ConstElementPtr cmd =
            createCommand("get_config",
                          Element::fromJSON("{\"module_name\":\"" +
                                            module_name_ + "\"}"));
Jelte Jansen's avatar
Jelte Jansen committed
473 474
        seq = session_.group_sendmsg(cmd, "ConfigManager");
        session_.group_recvmsg(env, answer, false, seq);
475
        ConstElementPtr new_config = parseAnswer(rcode, answer);
476 477 478
        if (rcode == 0) {
            handleConfigUpdate(new_config);
        } else {
479
            LOG_ERROR(config_logger, CONFIG_GET_FAIL).arg(new_config->str());
480
            isc_throw(CCSessionInitError, answer->str());
481
        }
Jelte Jansen's avatar
Jelte Jansen committed
482
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
483

Jelte Jansen's avatar
Jelte Jansen committed
484 485
    // Keep track of logging settings automatically
    if (handle_logging) {
486
        addRemoteConfig("Logging", default_logconfig_handler, false);
Jelte Jansen's avatar
Jelte Jansen committed
487
    }
488 489 490 491 492

    if (start_immediately) {
        start();
    }

JINMEI Tatuya's avatar
JINMEI Tatuya committed
493 494
}

495 496
ModuleCCSession::~ModuleCCSession() {
    try {
497 498 499 500
        sendStopping();
    } catch (const std::exception& exc) {
        LOG_ERROR(config_logger,
                  CONFIG_CCSESSION_STOPPING).arg(exc.what());
501
    } catch (...) {
502 503
        LOG_ERROR(config_logger,
                  CONFIG_CCSESSION_STOPPING_UNKNOWN);
504 505 506
    }
};

JINMEI Tatuya's avatar
JINMEI Tatuya committed
507 508 509 510 511 512
void
ModuleCCSession::start() {
    if (started_) {
        isc_throw(CCSessionError, "Module CC session already started");
    }

JINMEI Tatuya's avatar
JINMEI Tatuya committed
513 514
    // register callback for asynchronous read
    session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
515 516

    started_ = true;
Jelte Jansen's avatar
Jelte Jansen committed
517 518 519
}

/// Validates the new config values, if they are correct,
520
/// call the config handler with the values that have changed
Jelte Jansen's avatar
Jelte Jansen committed
521
/// If that results in success, store the new config
522 523 524
ConstElementPtr
ModuleCCSession::handleConfigUpdate(ConstElementPtr new_config) {
    ConstElementPtr answer;
525
    ElementPtr errors = Element::createList();
Jelte Jansen's avatar
Jelte Jansen committed
526 527
    if (!config_handler_) {
        answer = createAnswer(1, module_name_ + " does not have a config handler");
528
    } else if (!module_specification_.validateConfig(new_config, false,
529
                                                      errors)) {
530 531
        std::stringstream ss;
        ss << "Error in config validation: ";
532
        BOOST_FOREACH(ConstElementPtr error, errors->listValue()) {
533 534 535
            ss << error->stringValue();
        }
        answer = createAnswer(2, ss.str());
Jelte Jansen's avatar
Jelte Jansen committed
536
    } else {
537
        // remove the values that have not changed
538
        ConstElementPtr diff = removeIdentical(new_config, getLocalConfig());
Jelte Jansen's avatar
Jelte Jansen committed
539
        // handle config update
540
        answer = config_handler_(diff);
Mukund Sivaraman's avatar
Mukund Sivaraman committed
541
        int rcode = -1;
Jelte Jansen's avatar
Jelte Jansen committed
542 543
        parseAnswer(rcode, answer);
        if (rcode == 0) {
544
            ElementPtr local_config = getLocalConfig();
545
            isc::data::merge(local_config, diff);
546
            setLocalConfig(local_config);
547
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
548
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
549
    return (answer);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
550 551
}

Jelte Jansen's avatar
Jelte Jansen committed
552
bool
553
ModuleCCSession::hasQueuedMsgs() const {
Jelte Jansen's avatar
Jelte Jansen committed
554 555 556
    return (session_.hasQueuedMsgs());
}

Jelte Jansen's avatar
Jelte Jansen committed
557 558
ConstElementPtr
ModuleCCSession::checkConfigUpdateCommand(const std::string& target_module,
559 560
                                          ConstElementPtr arg)
{
Jelte Jansen's avatar
Jelte Jansen committed
561
    if (target_module == module_name_) {
562
        return (handleConfigUpdate(arg));
Jelte Jansen's avatar
Jelte Jansen committed
563 564 565 566 567
    } else {
        // ok this update is not for us, if we have this module
        // in our remote config list, update that
        updateRemoteConfig(target_module, arg);
        // we're not supposed to answer to this, so return
568
        return (ElementPtr());
Jelte Jansen's avatar
Jelte Jansen committed
569 570 571 572 573 574
    }
}

ConstElementPtr
ModuleCCSession::checkModuleCommand(const std::string& cmd_str,
                                    const std::string& target_module,
575 576
                                    ConstElementPtr arg) const
{
Jelte Jansen's avatar
Jelte Jansen committed
577 578 579
    if (target_module == module_name_) {
        if (command_handler_) {
            ElementPtr errors = Element::createList();
580
            if (module_specification_.validateCommand(cmd_str,
Jelte Jansen's avatar
Jelte Jansen committed
581 582
                                                       arg,
                                                       errors)) {
583
                return (command_handler_(cmd_str, arg));
Jelte Jansen's avatar
Jelte Jansen committed
584 585 586 587 588 589 590
            } else {
                std::stringstream ss;
                ss << "Error in command validation: ";
                BOOST_FOREACH(ConstElementPtr error,
                              errors->listValue()) {
                    ss << error->stringValue();
                }
591
                return (createAnswer(3, ss.str()));
Jelte Jansen's avatar
Jelte Jansen committed
592 593
            }
        } else {
594 595 596
            return (createAnswer(1,
                                 "Command given but no "
                                 "command handler for module"));
Jelte Jansen's avatar
Jelte Jansen committed
597
        }
598 599
    } else if (unhandled_callback_) {
        unhandled_callback_(cmd_str, target_module, arg);
Jelte Jansen's avatar
Jelte Jansen committed
600
    }
601
    return (ElementPtr());
Jelte Jansen's avatar
Jelte Jansen committed
602 603
}

604
int
605 606
ModuleCCSession::checkCommand() {
    ConstElementPtr cmd, routing, data;
607
    if (session_.group_recvmsg(routing, data, true)) {
608

609 610 611 612 613
        // In case the message is wanted asynchronously, it gets used.
        if (checkAsyncRecv(routing, data)) {
            return (0);
        }

614 615
        /* ignore result messages (in case we're out of sync, to prevent
         * pingpongs */
616 617
        if (data->getType() != Element::map ||
            data->contains(isc::cc::CC_PAYLOAD_RESULT)) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
618
            return (0);
619
        }
620 621
        ConstElementPtr arg;
        ConstElementPtr answer;
622 623
        try {
            std::string cmd_str = parseCommand(arg, data);
624 625
            std::string target_module =
                routing->get(isc::cc::CC_HEADER_GROUP)->stringValue();
626
            if (cmd_str == "config_update") {
Jelte Jansen's avatar
Jelte Jansen committed
627
                answer = checkConfigUpdateCommand(target_module, arg);
628
            } else {
Jelte Jansen's avatar
Jelte Jansen committed
629
                answer = checkModuleCommand(cmd_str, target_module, arg);
630
            }
631
        } catch (const CCSessionError& re) {
632
            LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what());
633 634
        } catch (const std::exception& stde) {
            // No matter what unexpected error happens, we do not want
635 636 637
            // to crash because of an incoming event, so we log the
            // exception and continue to run
            LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG_INTERNAL).arg(stde.what());
638
        }
639 640 641
        if (!isNull(answer)) {
            session_.reply(routing, answer);
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
642
    }
643

JINMEI Tatuya's avatar
JINMEI Tatuya committed
644
    return (0);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
645
}
646

647 648
ModuleSpec
ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
649 650
    if (is_filename) {
        // It is a filename, simply load it.
651
        return (readModuleSpecification(module));
652 653
    } else {
        // It's module name, request it from config manager
Jelte Jansen's avatar
Jelte Jansen committed
654 655 656 657 658

        // Send the command
        ConstElementPtr cmd(createCommand("get_module_spec",
                            Element::fromJSON("{\"module_name\": \"" + module +
                                              "\"}")));
659
        unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
660 661
        ConstElementPtr env, answer;
        session_.group_recvmsg(env, answer, false, seq);
Mukund Sivaraman's avatar
Mukund Sivaraman committed
662
        int rcode = -1;
663 664
        ConstElementPtr spec_data = parseAnswer(rcode, answer);
        if (rcode == 0 && spec_data) {
665
            // received OK, construct the spec out of it
666
            ModuleSpec spec = ModuleSpec(spec_data);
667 668
            if (module != spec.getModuleName()) {
                // It's a different module!
669 670
                isc_throw(CCSessionError, "Module name mismatch");
            }
671
            return (spec);
672
        } else {
673 674
            isc_throw(CCSessionError, "Error getting config for " +
                      module + ": " + answer->str());
675 676
        }
    }
677
}
678

679 680
std::string
ModuleCCSession::addRemoteConfig(const std::string& spec_name,
681
                                 RemoteHandler handler,
682 683 684
                                 bool spec_is_filename)
{
    // First get the module name, specification and default config
685 686
    const ModuleSpec rmod_spec(fetchRemoteSpec(spec_name, spec_is_filename));
    const std::string module_name(rmod_spec.getModuleName());
687 688 689 690 691 692
    ConfigData rmod_config(rmod_spec);

    // Get the current configuration values from config manager
    ConstElementPtr cmd(createCommand("get_config",
                        Element::fromJSON("{\"module_name\": \"" +
                                          module_name + "\"}")));
693
    const unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
694 695

    ConstElementPtr env, answer;
Jelte Jansen's avatar
Jelte Jansen committed
696
    session_.group_recvmsg(env, answer, false, seq);
Mukund Sivaraman's avatar
Mukund Sivaraman committed
697
    int rcode = -1;
698
    ConstElementPtr new_config = parseAnswer(rcode, answer);
699
    ElementPtr local_config;
700
    if (rcode == 0 && new_config) {
701
        // Merge the received config into existing local config
702
        local_config = rmod_config.getLocalConfig();
703 704
        isc::data::merge(local_config, new_config);
        rmod_config.setLocalConfig(local_config);
705 706 707 708 709 710
    } else {
        isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
    }

    // all ok, add it
    remote_module_configs_[module_name] = rmod_config;
711 712
    if (handler) {
        remote_module_handlers_[module_name] = handler;
713
        handler(module_name, local_config, rmod_config);
714
    }
715 716

    // Make sure we get updates in future
717
    session_.subscribe(module_name);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
718
    return (module_name);
719 720 721
}

void
722
ModuleCCSession::removeRemoteConfig(const std::string& module_name) {
723 724 725 726 727
    std::map<std::string, ConfigData>::iterator it;

    it = remote_module_configs_.find(module_name);
    if (it != remote_module_configs_.end()) {
        remote_module_configs_.erase(it);
728
        remote_module_handlers_.erase(module_name);
729 730 731 732
        session_.unsubscribe(module_name);
    }
}

733 734 735
ConstElementPtr
ModuleCCSession::getRemoteConfigValue(const std::string& module_name,
                                      const std::string& identifier) const
736
{
737 738
    std::map<std::string, ConfigData>::const_iterator it =
        remote_module_configs_.find(module_name);
739 740

    if (it != remote_module_configs_.end()) {
741
        return ((*it).second.getValue(identifier));
742
    } else {
743 744
        isc_throw(CCSessionError,
                  "Remote module " + module_name + " not found.");
745 746 747 748
    }
}

void
749 750
ModuleCCSession::updateRemoteConfig(const std::string& module_name,
                                    ConstElementPtr new_config)
751 752 753 754 755 756 757
{
    std::map<std::string, ConfigData>::iterator it;

    it = remote_module_configs_.find(module_name);
    if (it != remote_module_configs_.end()) {
        ElementPtr rconf = (*it).second.getLocalConfig();
        isc::data::merge(rconf, new_config);
758 759 760
        std::map<std::string, RemoteHandler>::iterator hit =
            remote_module_handlers_.find(module_name);
        if (hit != remote_module_handlers_.end()) {
761
            hit->second(module_name, new_config, it->second);
762
        }
763 764 765
    }
}

766 767 768 769
void
ModuleCCSession::sendStopping() {
    // Inform the configuration manager that this module is stopping
    ConstElementPtr cmd(createCommand("stopping",
770 771
                                      Element::fromJSON(
                                          "{\"module_name\": \"" +
772 773 774 775 776
                                          module_name_ + "\"}")));
    // It's just an FYI, configmanager is not expected to respond.
    session_.group_sendmsg(cmd, "ConfigManager");
}

777 778
class ModuleCCSession::AsyncRecvRequest {
public: // Everything is public here, as the definition is hidden anyway
779 780 781 782 783 784 785 786 787 788 789
    AsyncRecvRequest(const AsyncRecvCallback& cb, const string& rcp, int sq,
                     bool reply) :
        callback(cb),
        recipient(rcp),
        seq(sq),
        is_reply(reply)
    {}
    const AsyncRecvCallback callback;
    const string recipient;
    const int seq;
    const bool is_reply;
790 791 792 793 794 795 796
};

ModuleCCSession::AsyncRecvRequestID
ModuleCCSession::groupRecvMsgAsync(const AsyncRecvCallback& callback,
                                   bool is_reply, int seq,
                                   const string& recipient) {
    // This just stores the request, the handling is done in checkCommand()
797

798
    // push_back would be simpler, but it does not return the iterator we need
799 800 801
    return (async_recv_requests_.insert(async_recv_requests_.end(),
                                        AsyncRecvRequest(callback, recipient,
                                                         seq, is_reply)));
802 803
}

804 805