// 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. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using isc::data::Element; using isc::data::ConstElementPtr; using isc::data::ElementPtr; using isc::data::JSONError; namespace isc { namespace config { /// Creates a standard config/command protocol answer message ConstElementPtr createAnswer() { ElementPtr answer = Element::createMap(); ElementPtr answer_content = Element::createList(); answer_content->add(Element::create(isc::cc::CC_REPLY_SUCCESS)); answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content); return (answer); } ConstElementPtr createAnswer(const int rcode, ConstElementPtr arg) { if (rcode != 0 && (!arg || arg->getType() != Element::string)) { isc_throw(CCSessionError, "Bad or no argument for rcode != 0"); } ElementPtr answer = Element::createMap(); ElementPtr answer_content = Element::createList(); answer_content->add(Element::create(rcode)); answer_content->add(arg); answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content); return (answer); } ConstElementPtr createAnswer(const int rcode, const std::string& arg) { ElementPtr answer = Element::createMap(); ElementPtr answer_content = Element::createList(); answer_content->add(Element::create(rcode)); answer_content->add(Element::create(arg)); answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content); return (answer); } ConstElementPtr parseAnswer(int &rcode, ConstElementPtr msg) { if (msg && msg->getType() == Element::map && msg->contains(isc::cc::CC_PAYLOAD_RESULT)) { ConstElementPtr result = msg->get(isc::cc::CC_PAYLOAD_RESULT); 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) { isc_throw(CCSessionError, "First element of result is not an rcode in answer message"); } rcode = result->get(0)->intValue(); if (result->size() > 1) { if (rcode == 0 || result->get(1)->getType() == Element::string) { return (result->get(1)); } else { isc_throw(CCSessionError, "Error description in result with rcode != 0 is not a string"); } } else { if (rcode == 0) { return (ElementPtr()); } else { isc_throw(CCSessionError, "Result with rcode != 0 does not have an error description"); } } } else { isc_throw(CCSessionError, "No result part in answer message"); } } ConstElementPtr createCommand(const std::string& command) { return (createCommand(command, ElementPtr())); } ConstElementPtr createCommand(const std::string& command, ConstElementPtr arg) { ElementPtr cmd = Element::createMap(); ElementPtr cmd_parts = Element::createList(); cmd_parts->add(Element::create(command)); if (arg) { cmd_parts->add(arg); } cmd->set(isc::cc::CC_PAYLOAD_COMMAND, cmd_parts); return (cmd); } std::string parseCommand(ConstElementPtr& arg, ConstElementPtr command) { if (command && command->getType() == Element::map && command->contains(isc::cc::CC_PAYLOAD_COMMAND)) { ConstElementPtr cmd = command->get(isc::cc::CC_PAYLOAD_COMMAND); if (cmd->getType() == Element::list && cmd->size() > 0 && cmd->get(0)->getType() == Element::string) { if (cmd->size() > 1) { arg = cmd->get(1); } else { arg = Element::createMap(); } return (cmd->get(0)->stringValue()); } else { isc_throw(CCSessionError, "Command part in command message missing, empty, or not a list"); } } else { isc_throw(CCSessionError, "Command Element empty or not a map with \"command\""); } } namespace { // 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 // (intentially defined here so this interface does not get // 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); } } // Prefix name with "b10-". // // 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.) // // 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-". // // \param instring String to convert. (This may be empty, in which case // "b10-" will be returned.) // // \return Converted string. std::string b10Prefix(const std::string& instring) { std::string result = instring; if (!result.empty()) { result[0] = tolower(result[0]); } return (std::string("b10-") + result); } // 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()); 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(); } 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& specs, ConstElementPtr logger, const ConfigData& config_data) { // Read name, adding prefix as required. std::string lname = logger->get("name")->stringValue(); 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); } } specs.push_back(logger_spec); } // 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 copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) { // 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. ElementPtr new_logger(Element::createMap()); new_logger->setValue(cur_logger->mapValue()); new_logger->set("name", Element::create(new_name)); return (new_logger); } } // end anonymous namespace ConstElementPtr getRelatedLoggers(ConstElementPtr loggers) { // Keep a list of names for easier lookup later std::set our_names; const std::string& root_name = isc::log::getRootLoggerName(); ElementPtr result = isc::data::Element::createList(); BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { // Need to add the b10- prefix to names ready from the spec file. const std::string cur_name = cur_logger->get("name")->stringValue(); 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] != '*')) { // Not a wildcard logger and we are ignoring it. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name); } } // Now find the wildcard names (the one that start with "*"). BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { const std::string cur_name = cur_logger->get("name")->stringValue(); // If name is '*', or starts with '*.', replace * with root // logger name. if (cur_name == "*" || (cur_name.length() > 1 && cur_name[0] == '*' && cur_name[1] == '.')) { // Substitute the "*" with the root name std::string mod_name = cur_name; mod_name.replace(0, 1, root_name); // Now add it to the result list, but only if a logger with // that name was not configured explicitly. if (our_names.find(mod_name) == our_names.end()) { // We substitute the name here, but as we are dealing with // consts, we need to copy the data. 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] == '*')) { // Is a wildcard and we are ignoring it (because the wildcard // expands to a specification that we already encountered when // processing explicit names). LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_IGNORE_WILD).arg(cur_name); } } } return (result); } void default_logconfig_handler(const std::string& module_name, ConstElementPtr new_config, const ConfigData& config_data) { config_data.getModuleSpec().validateConfig(new_config, true); std::vector specs; if (new_config->contains("loggers")) { ConstElementPtr loggers = getRelatedLoggers(new_config->get("loggers")); BOOST_FOREACH(ConstElementPtr logger, loggers->listValue()) { readLoggersConf(specs, logger, config_data); } } isc::log::LoggerManager logger_manager; logger_manager.process(specs.begin(), specs.end()); } ModuleSpec ModuleCCSession::readModuleSpecification(const std::string& filename) { std::ifstream file; ModuleSpec module_spec; // this file should be declared in a @something@ directive file.open(filename.c_str()); if (!file) { LOG_ERROR(config_logger, CONFIG_OPEN_FAIL).arg(filename).arg(strerror(errno)); isc_throw(CCSessionInitError, strerror(errno)); } try { module_spec = moduleSpecFromFile(file, true); } catch (const JSONError& pe) { LOG_ERROR(config_logger, CONFIG_JSON_PARSE).arg(filename).arg(pe.what()); isc_throw(CCSessionInitError, pe.what()); } catch (const ModuleSpecError& dde) { LOG_ERROR(config_logger, CONFIG_MOD_SPEC_FORMAT).arg(filename).arg(dde.what()); isc_throw(CCSessionInitError, dde.what()); } file.close(); return (module_spec); } void ModuleCCSession::startCheck() { // data available on the command channel. process it in the synchronous // mode. checkCommand(); // start asynchronous read again. session_.startRead(boost::bind(&ModuleCCSession::startCheck, this)); } ModuleCCSession::ModuleCCSession( const std::string& spec_file_name, isc::cc::AbstractSession& session, isc::data::ConstElementPtr(*config_handler)( isc::data::ConstElementPtr new_config), isc::data::ConstElementPtr(*command_handler)( const std::string& command, isc::data::ConstElementPtr args), bool start_immediately, bool handle_logging ) : started_(false), session_(session) { module_specification_ = readModuleSpecification(spec_file_name); setModuleSpec(module_specification_); module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue(); config_handler_ = config_handler; command_handler_ = command_handler; session_.establish(NULL); session_.subscribe(module_name_, "*"); // send the data specification ConstElementPtr spec_msg = createCommand("module_spec", module_specification_.getFullSpec()); unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager"); ConstElementPtr answer, env; session_.group_recvmsg(env, answer, false, seq); int rcode = -1; ConstElementPtr err = parseAnswer(rcode, answer); if (rcode != 0) { LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str()); isc_throw(CCSessionInitError, answer->str()); } setLocalConfig(Element::createMap()); // get any stored configuration from the manager if (config_handler_) { ConstElementPtr cmd = createCommand("get_config", Element::fromJSON("{\"module_name\":\"" + module_name_ + "\"}")); seq = session_.group_sendmsg(cmd, "ConfigManager"); session_.group_recvmsg(env, answer, false, seq); ConstElementPtr new_config = parseAnswer(rcode, answer); if (rcode == 0) { handleConfigUpdate(new_config); } else { LOG_ERROR(config_logger, CONFIG_GET_FAIL).arg(new_config->str()); isc_throw(CCSessionInitError, answer->str()); } } // Keep track of logging settings automatically if (handle_logging) { addRemoteConfig("Logging", default_logconfig_handler, false); } if (start_immediately) { start(); } } ModuleCCSession::~ModuleCCSession() { try { sendStopping(); } catch (const std::exception& exc) { LOG_ERROR(config_logger, CONFIG_CCSESSION_STOPPING).arg(exc.what()); } catch (...) { LOG_ERROR(config_logger, CONFIG_CCSESSION_STOPPING_UNKNOWN); } }; void ModuleCCSession::start() { if (started_) { isc_throw(CCSessionError, "Module CC session already started"); } // register callback for asynchronous read session_.startRead(boost::bind(&ModuleCCSession::startCheck, this)); started_ = true; } /// Validates the new config values, if they are correct, /// call the config handler with the values that have changed /// If that results in success, store the new config ConstElementPtr ModuleCCSession::handleConfigUpdate(ConstElementPtr new_config) { ConstElementPtr answer; ElementPtr errors = Element::createList(); if (!config_handler_) { answer = createAnswer(1, module_name_ + " does not have a config handler"); } else if (!module_specification_.validateConfig(new_config, false, errors)) { std::stringstream ss; ss << "Error in config validation: "; BOOST_FOREACH(ConstElementPtr error, errors->listValue()) { ss << error->stringValue(); } answer = createAnswer(2, ss.str()); } else { // remove the values that have not changed ConstElementPtr diff = removeIdentical(new_config, getLocalConfig()); // handle config update answer = config_handler_(diff); int rcode = -1; parseAnswer(rcode, answer); if (rcode == 0) { ElementPtr local_config = getLocalConfig(); isc::data::merge(local_config, diff); setLocalConfig(local_config); } } return (answer); } bool ModuleCCSession::hasQueuedMsgs() const { return (session_.hasQueuedMsgs()); } ConstElementPtr ModuleCCSession::checkConfigUpdateCommand(const std::string& target_module, ConstElementPtr arg) { if (target_module == module_name_) { return (handleConfigUpdate(arg)); } 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 return (ElementPtr()); } } ConstElementPtr ModuleCCSession::checkModuleCommand(const std::string& cmd_str, const std::string& target_module, ConstElementPtr arg) const { if (target_module == module_name_) { if (command_handler_) { ElementPtr errors = Element::createList(); if (module_specification_.validateCommand(cmd_str, arg, errors)) { return (command_handler_(cmd_str, arg)); } else { std::stringstream ss; ss << "Error in command validation: "; BOOST_FOREACH(ConstElementPtr error, errors->listValue()) { ss << error->stringValue(); } return (createAnswer(3, ss.str())); } } else { return (createAnswer(1, "Command given but no " "command handler for module")); } } return (ElementPtr()); } int ModuleCCSession::checkCommand() { ConstElementPtr cmd, routing, data; if (session_.group_recvmsg(routing, data, true)) { // In case the message is wanted asynchronously, it gets used. if (checkAsyncRecv(routing, data)) { return (0); } /* ignore result messages (in case we're out of sync, to prevent * pingpongs */ if (data->getType() != Element::map || data->contains(isc::cc::CC_PAYLOAD_RESULT)) { return (0); } ConstElementPtr arg; ConstElementPtr answer; try { std::string cmd_str = parseCommand(arg, data); std::string target_module = routing->get(isc::cc::CC_HEADER_GROUP)->stringValue(); if (cmd_str == "config_update") { answer = checkConfigUpdateCommand(target_module, arg); } else { answer = checkModuleCommand(cmd_str, target_module, arg); } } catch (const CCSessionError& re) { LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what()); } catch (const std::exception& stde) { // No matter what unexpected error happens, we do not want // 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()); } if (!isNull(answer)) { session_.reply(routing, answer); } } return (0); } ModuleSpec ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) { if (is_filename) { // It is a filename, simply load it. return (readModuleSpecification(module)); } else { // It's module name, request it from config manager // Send the command ConstElementPtr cmd(createCommand("get_module_spec", Element::fromJSON("{\"module_name\": \"" + module + "\"}"))); unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager"); ConstElementPtr env, answer; session_.group_recvmsg(env, answer, false, seq); int rcode = -1; ConstElementPtr spec_data = parseAnswer(rcode, answer); if (rcode == 0 && spec_data) { // received OK, construct the spec out of it ModuleSpec spec = ModuleSpec(spec_data); if (module != spec.getModuleName()) { // It's a different module! isc_throw(CCSessionError, "Module name mismatch"); } return (spec); } else { isc_throw(CCSessionError, "Error getting config for " + module + ": " + answer->str()); } } } std::string ModuleCCSession::addRemoteConfig(const std::string& spec_name, RemoteHandler handler, bool spec_is_filename) { // First get the module name, specification and default config const ModuleSpec rmod_spec(fetchRemoteSpec(spec_name, spec_is_filename)); const std::string module_name(rmod_spec.getModuleName()); ConfigData rmod_config(rmod_spec); // Get the current configuration values from config manager ConstElementPtr cmd(createCommand("get_config", Element::fromJSON("{\"module_name\": \"" + module_name + "\"}"))); const unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager"); ConstElementPtr env, answer; session_.group_recvmsg(env, answer, false, seq); int rcode = -1; ConstElementPtr new_config = parseAnswer(rcode, answer); ElementPtr local_config; if (rcode == 0 && new_config) { // Merge the received config into existing local config local_config = rmod_config.getLocalConfig(); isc::data::merge(local_config, new_config); rmod_config.setLocalConfig(local_config); } else { isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str()); } // all ok, add it remote_module_configs_[module_name] = rmod_config; if (handler) { remote_module_handlers_[module_name] = handler; handler(module_name, local_config, rmod_config); } // Make sure we get updates in future session_.subscribe(module_name); return (module_name); } void ModuleCCSession::removeRemoteConfig(const std::string& module_name) { std::map::iterator it; it = remote_module_configs_.find(module_name); if (it != remote_module_configs_.end()) { remote_module_configs_.erase(it); remote_module_handlers_.erase(module_name); session_.unsubscribe(module_name); } } ConstElementPtr ModuleCCSession::getRemoteConfigValue(const std::string& module_name, const std::string& identifier) const { std::map::const_iterator it = remote_module_configs_.find(module_name); if (it != remote_module_configs_.end()) { return ((*it).second.getValue(identifier)); } else { isc_throw(CCSessionError, "Remote module " + module_name + " not found."); } } void ModuleCCSession::updateRemoteConfig(const std::string& module_name, ConstElementPtr new_config) { std::map::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); std::map::iterator hit = remote_module_handlers_.find(module_name); if (hit != remote_module_handlers_.end()) { hit->second(module_name, new_config, it->second); } } } void ModuleCCSession::sendStopping() { // Inform the configuration manager that this module is stopping ConstElementPtr cmd(createCommand("stopping", Element::fromJSON( "{\"module_name\": \"" + module_name_ + "\"}"))); // It's just an FYI, configmanager is not expected to respond. session_.group_sendmsg(cmd, "ConfigManager"); } class ModuleCCSession::AsyncRecvRequest { public: // Everything is public here, as the definition is hidden anyway 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; }; 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() // push_back would be simpler, but it does not return the iterator we need return (async_recv_requests_.insert(async_recv_requests_.end(), AsyncRecvRequest(callback, recipient, seq, is_reply))); } bool ModuleCCSession::checkAsyncRecv(const ConstElementPtr& envelope, const ConstElementPtr& msg) { for (AsyncRecvRequestID request(async_recv_requests_.begin()); request != async_recv_requests_.end(); ++request) { // Just go through all the requests and look for a matching one if (requestMatch(*request, envelope)) { // We want the request to be still alive at the time we // call the callback. But we need to remove it on an exception // too, so we use the class. If just C++ had the finally keyword. class RequestDeleter { public: RequestDeleter(AsyncRecvRequests& requests, AsyncRecvRequestID& request) : requests_(requests), request_(request) { } ~RequestDeleter() { requests_.erase(request_); } private: AsyncRecvRequests& requests_; AsyncRecvRequestID& request_; }; RequestDeleter deleter(async_recv_requests_, request); // Call the callback request->callback(envelope, msg, request); return (true); } } return (false); } bool ModuleCCSession::requestMatch(const AsyncRecvRequest& request, const ConstElementPtr& envelope) const { if (request.is_reply != envelope->contains(isc::cc::CC_HEADER_REPLY)) { // Wrong type of message return (false); } if (request.is_reply && (request.seq == -1 || request.seq == envelope->get(isc::cc::CC_HEADER_REPLY)->intValue())) { // This is the correct reply return (true); } if (!request.is_reply && (request.recipient.empty() || request.recipient == envelope->get(isc::cc::CC_HEADER_GROUP)->stringValue())) { // This is the correct command return (true); } // If nothing from the above, we don't want it return (false); } void ModuleCCSession::cancelAsyncRecv(const AsyncRecvRequestID& id) { async_recv_requests_.erase(id); } ConstElementPtr ModuleCCSession::rpcCall(const std::string &command, const std::string &group, const std::string &instance, const std::string &to, const ConstElementPtr ¶ms) { ConstElementPtr command_el(createCommand(command, params)); const int seq = groupSendMsg(command_el, group, instance, to, true); ConstElementPtr env, answer; LOG_DEBUG(config_logger, DBGLVL_TRACE_DETAIL, CONFIG_RPC_SEQ).arg(command). arg(group).arg(seq); groupRecvMsg(env, answer, true, seq); int rcode; const ConstElementPtr result(parseAnswer(rcode, answer)); if (rcode == isc::cc::CC_REPLY_NO_RECPT) { isc_throw(RPCRecipientMissing, result); } else if (rcode != isc::cc::CC_REPLY_SUCCESS) { isc_throw_1(RPCError, result, rcode); } else { return (result); } } } }