json_config_parser.cc 25.8 KB
Newer Older
1
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6

7 8
#include <config.h>

9
#include <cc/command_interpreter.h>
10
#include <dhcp4/dhcp4_log.h>
11
#include <dhcp4/simple_parser4.h>
12 13
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
14
#include <dhcpsrv/cfg_option.h>
15
#include <dhcpsrv/cfgmgr.h>
16
#include <dhcpsrv/parsers/client_class_def_parser.h>
17
#include <dhcp4/json_config_parser.h>
18 19
#include <dhcpsrv/parsers/dbaccess_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
20
#include <dhcpsrv/parsers/expiration_config_parser.h>
21 22
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
23
#include <dhcpsrv/parsers/ifaces_config_parser.h>
24
#include <dhcpsrv/timer_mgr.h>
25
#include <hooks/hooks_parser.h>
26
#include <config/command_mgr.h>
27
#include <util/encode/hex.h>
28
#include <util/strutil.h>
29

Tomek Mrugalski's avatar
Tomek Mrugalski committed
30 31 32
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
33

Tomek Mrugalski's avatar
Tomek Mrugalski committed
34
#include <limits>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
35
#include <iostream>
36
#include <netinet/in.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
37 38 39
#include <vector>
#include <map>

40
using namespace std;
41 42
using namespace isc;
using namespace isc::dhcp;
43 44
using namespace isc::data;
using namespace isc::asiolink;
45
using namespace isc::hooks;
46

47 48
namespace {

49
/// @brief Parser for IPv4 pool definitions.
50
///
51 52 53
/// This is the IPv4 derivation of the PoolParser class and handles pool
/// definitions, i.e. a list of entries of one of two syntaxes: min-max and
/// prefix/len for IPv4 pools. Pool4 objects are created and stored in chosen
54
/// PoolStorage container.
55
///
56 57 58 59
/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
class Pool4Parser : public PoolParser {
protected:
    /// @brief Creates a Pool4 object given a IPv4 prefix and the prefix length.
60
    ///
61 62
    /// @param addr is the IPv4 prefix of the pool.
    /// @param len is the prefix length.
63
    /// @param ignored dummy parameter to provide symmetry between the
64
    /// PoolParser derivations. The V6 derivation requires a third value.
65
    /// @return returns a PoolPtr to the new Pool4 object.
66
    PoolPtr poolMaker (IOAddress &addr, uint32_t len, int32_t) {
67 68
        return (PoolPtr(new Pool4(addr, len)));
    }
69

70 71 72 73
    /// @brief Creates a Pool4 object given starting and ending IPv4 addresses.
    ///
    /// @param min is the first IPv4 address in the pool.
    /// @param max is the last IPv4 address in the pool.
74
    /// @param ignored dummy parameter to provide symmetry between the
75
    /// PoolParser derivations. The V6 derivation requires a third value.
76
    /// @return returns a PoolPtr to the new Pool4 object.
77
    PoolPtr poolMaker (IOAddress &min, IOAddress &max, int32_t) {
78
        return (PoolPtr(new Pool4(min, max)));
79
    }
80
};
81

82 83
/// @brief Specialization of the pool list parser for DHCPv4
class Pools4ListParser : PoolsListParser {
84 85
public:

86 87 88 89
    /// @brief parses the actual structure
    ///
    /// This method parses the actual list of pools.
    ///
90
    /// @param pools storage container in which to store the parsed pool.
91 92
    /// @param pools_list a list of pool structures
    /// @throw isc::dhcp::DhcpConfigError when pool parsing fails
93 94
    void parse(PoolStoragePtr pools,
               isc::data::ConstElementPtr pools_list) {
95
        BOOST_FOREACH(ConstElementPtr pool, pools_list->listValue()) {
96 97
            Pool4Parser parser;
            parser.parse(pools, pool, AF_INET);
98
        }
99 100 101
    }
};

102
/// @anchor Subnet4ConfigParser
103 104
/// @brief This class parses a single IPv4 subnet.
///
105 106
/// This is the IPv4 derivation of the SubnetConfigParser class and it parses
/// the whole subnet definition. It creates parsersfor received configuration
107 108 109 110
/// parameters as needed.
class Subnet4ConfigParser : public SubnetConfigParser {
public:
    /// @brief Constructor
111
    ///
Francis Dupont's avatar
Francis Dupont committed
112
    /// stores global scope parameters, options, option definitions.
113
    Subnet4ConfigParser()
114
        :SubnetConfigParser(AF_INET) {
115
    }
116

117 118 119 120
    /// @brief Parses a single IPv4 subnet configuration and adds to the
    /// Configuration Manager.
    ///
    /// @param subnet A new subnet being configured.
121 122
    /// @return a pointer to created Subnet4 object
    Subnet4Ptr parse(ConstElementPtr subnet) {
123 124 125
        /// Parse Pools first.
        ConstElementPtr pools = subnet->get("pools");
        if (pools) {
126 127
            Pools4ListParser parser;
            parser.parse(pools_, pools);
128 129
        }

130
        SubnetPtr generic = SubnetConfigParser::parse(subnet);
131

132 133 134 135 136 137 138 139
        if (!generic) {
            isc_throw(DhcpConfigError,
                      "Failed to create an IPv4 subnet (" <<
                      subnet->getPosition() << ")");
        }

        Subnet4Ptr sn4ptr = boost::dynamic_pointer_cast<Subnet4>(subnet_);
        if (!sn4ptr) {
140 141 142 143
            // If we hit this, it is a programming error.
            isc_throw(Unexpected,
                      "Invalid Subnet4 cast in Subnet4ConfigParser::parse");
        }
144

145 146
        // Set relay information if it was parsed
        if (relay_info_) {
147
            sn4ptr->setRelayInfo(*relay_info_);
148
        }
149 150 151 152

        // Parse Host Reservations for this subnet if any.
        ConstElementPtr reservations = subnet->get("reservations");
        if (reservations) {
153 154
            HostReservationsListParser<HostReservationParser4> parser;
            parser.parse(subnet_->getID(), reservations);
155
        }
156

157
        return (sn4ptr);
158
    }
159

160
protected:
161

162
    /// @brief Instantiates the IPv4 Subnet based on a given IPv4 address
163 164
    /// and prefix length.
    ///
165
    /// @param addr is IPv4 address of the subnet.
166
    /// @param len is the prefix length
167 168
    void initSubnet(isc::data::ConstElementPtr params,
                    isc::asiolink::IOAddress addr, uint8_t len) {
169 170 171
        // The renew-timer and rebind-timer are optional. If not set, the
        // option 58 and 59 will not be sent to a client. In this case the
        // client will use default values based on the valid-lifetime.
172 173 174
        Triplet<uint32_t> t1 = getInteger(params, "renew-timer");
        Triplet<uint32_t> t2 = getInteger(params, "rebind-timer");

175 176 177
        // The valid-lifetime is mandatory. It may be specified for a
        // particular subnet. If not, the global value should be present.
        // If there is no global value, exception is thrown.
178
        Triplet<uint32_t> valid = getInteger(params, "valid-lifetime");
179

180
        // Subnet ID is optional. If it is not supplied the value of 0 is used,
181 182 183
        // which means autogenerate. The value was inserted earlier by calling
        // SimpleParser4::setAllDefaults.
        SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id"));
184

185 186 187 188 189 190 191 192 193 194
        stringstream s;
        s << addr << "/" << static_cast<int>(len) << " with params: ";
        // t1 and t2 are optional may be not specified.
        if (!t1.unspecified()) {
            s << "t1=" << t1 << ", ";
        }
        if (!t2.unspecified()) {
            s << "t2=" << t2 << ", ";
        }
        s <<"valid-lifetime=" << valid;
195

196
        LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(s.str());
197

198
        Subnet4Ptr subnet4(new Subnet4(addr, len, t1, t2, valid, subnet_id));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
199
        subnet_ = subnet4;
200

201
        // Set the match-client-id value for the subnet. It is always present.
Francis Dupont's avatar
Francis Dupont committed
202
        // If not explicitly specified, the default value was filled in when
203 204 205
        // SimpleParser4::setAllDefaults was called.
        bool match_client_id = getBoolean(params, "match-client-id");
        subnet4->setMatchClientId(match_client_id);
206

207 208 209 210
        // Set next-server. The default value is 0.0.0.0. Nevertheless, the
        // user could have messed that up by specifying incorrect value.
        // To avoid using 0.0.0.0, user can specify "".
        string next_server;
211
        try {
212
            next_server = getString(params, "next-server");
Tomek Mrugalski's avatar
Tomek Mrugalski committed
213 214 215
            if (!next_server.empty()) {
                subnet4->setSiaddr(IOAddress(next_server));
            }
216
        } catch (...) {
Francis Dupont's avatar
Francis Dupont committed
217
            ConstElementPtr next = params->get("next-server");
218
            string pos;
219 220
            if (next)
                pos = next->getPosition().str();
221 222
            else
                pos = params->getPosition().str();
223 224
            isc_throw(DhcpConfigError, "invalid parameter next-server : "
                      << next_server << "(" << pos << ")");
225
        }
226

227 228 229
        // 4o6 specific parameter: 4o6-interface. If not explicitly specified,
        // it will have the default value of "".
        string iface4o6 = getString(params, "4o6-interface");
230
        if (!iface4o6.empty()) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
231 232
            subnet4->get4o6().setIface4o6(iface4o6);
            subnet4->get4o6().enabled(true);
233 234
        }

235 236 237
        // 4o6 specific parameter: 4o6-subnet. If not explicitly specified, it
        // will have the default value of "".
        string subnet4o6 = getString(params, "4o6-subnet");
238
        if (!subnet4o6.empty()) {
239 240 241
            size_t slash = subnet4o6.find("/");
            if (slash == std::string::npos) {
                isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
242
                          << subnet4o6 << ", expected format: prefix6/length");
243 244 245 246 247 248 249 250 251
            }
            string prefix = subnet4o6.substr(0, slash);
            string lenstr = subnet4o6.substr(slash + 1);

            uint8_t len = 128;
            try {
                len = boost::lexical_cast<unsigned int>(lenstr.c_str());
            } catch (const boost::bad_lexical_cast &) {
                isc_throw(DhcpConfigError, "Invalid prefix length specified in "
252
                          "4o6-subnet parameter: " << subnet4o6 << ", expected 0..128 value");
253
            }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
254 255
            subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
            subnet4->get4o6().enabled(true);
256
        }
257

Francis Dupont's avatar
Francis Dupont committed
258
        // Try 4o6 specific parameter: 4o6-interface-id
259
        std::string ifaceid = getString(params, "4o6-interface-id");
260
        if (!ifaceid.empty()) {
261 262
            OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
            OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
Tomek Mrugalski's avatar
Tomek Mrugalski committed
263 264
            subnet4->get4o6().setInterfaceId(opt);
            subnet4->get4o6().enabled(true);
265
        }
266

267 268
        /// client-class processing is now generic and handled in the common
        /// code (see @ref isc::data::SubnetConfigParser::createSubnet)
269 270 271
    }
};

272
/// @brief this class parses list of DHCP4 subnets
273 274 275 276
///
/// This is a wrapper parser that handles the whole list of Subnet4
/// definitions. It iterates over all entries and creates Subnet4ConfigParser
/// for each entry.
277
class Subnets4ListConfigParser : public isc::data::SimpleParser {
278 279 280 281
public:

    /// @brief parses contents of the list
    ///
282 283 284
    /// Iterates over all entries on the list, parses its content
    /// (by instantiating Subnet6ConfigParser) and adds to specified
    /// configuration.
285 286
    ///
    /// @param subnets_list pointer to a list of IPv4 subnets
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
    /// @return number of subnets created
    size_t parse(SrvConfigPtr cfg, ConstElementPtr subnets_list) {
        size_t cnt = 0;
        BOOST_FOREACH(ConstElementPtr subnet_json, subnets_list->listValue()) {

            Subnet4ConfigParser parser;
            Subnet4Ptr subnet = parser.parse(subnet_json);
            if (subnet) {

                // Adding a subnet to the Configuration Manager may fail if the
                // subnet id is invalid (duplicate). Thus, we catch exceptions
                // here to append a position in the configuration string.
                try {
                    cfg->getCfgSubnets4()->add(subnet);
                    cnt++;
                } catch (const std::exception& ex) {
                    isc_throw(DhcpConfigError, ex.what() << " ("
                              << subnet_json->getPosition() << ")");
                }
            }
307
        }
308
        return (cnt);
309 310 311
    }
};

312
/// @brief Parser that takes care of global DHCPv4 parameters.
313 314
///
/// See @ref parse method for a list of supported parameters.
315 316 317 318 319 320
class Dhcp4ConfigParser : public isc::data::SimpleParser {
public:

    /// @brief Sets global parameters in staging configuration
    ///
    /// @param global global configuration scope
321
    /// @param cfg Server configuration (parsed parameters will be stored here)
322 323 324 325 326 327 328 329 330
    ///
    /// Currently this method sets the following global parameters:
    ///
    /// - echo-client-id
    /// - decline-probation-period
    /// - dhcp4o6-port
    ///
    /// @throw DhcpConfigError if parameters are missing or
    /// or having incorrect values.
331
    void parse(SrvConfigPtr cfg, ConstElementPtr global) {
332 333 334 335

        // Set whether v4 server is supposed to echo back client-id
        // (yes = RFC6842 compatible, no = backward compatibility)
        bool echo_client_id = getBoolean(global, "echo-client-id");
336
        cfg->setEchoClientId(echo_client_id);
337

338 339 340 341 342 343
        // Set the probation period for decline handling.
        uint32_t probation_period =
            getUint32(global, "decline-probation-period");
        cfg->setDeclinePeriod(probation_period);

        // Set the DHCPv4-over-DHCPv6 interserver port.
344
        uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
345
        cfg->setDhcp4o6Port(dhcp4o6_port);
346 347 348
    }
};

349 350 351 352 353
} // anonymous namespace

namespace isc {
namespace dhcp {

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
/// @brief Initialize the command channel based on the staging configuration
///
/// Only close the current channel, if the new channel configuration is
/// different.  This avoids disconnecting a client and hence not sending them
/// a command result, unless they specifically alter the channel configuration.
/// In that case the user simply has to accept they'll be disconnected.
///
void configureCommandChannel() {
    // Get new socket configuration.
    ConstElementPtr sock_cfg =
        CfgMgr::instance().getStagingCfg()->getControlSocketInfo();

    // Get current socket configuration.
    ConstElementPtr current_sock_cfg =
            CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();

    // Determine if the socket configuration has changed. It has if
    // both old and new configuration is specified but respective
    // data elements are't equal.
    bool sock_changed = (sock_cfg && current_sock_cfg &&
                         !sock_cfg->equals(*current_sock_cfg));

    // If the previous or new socket configuration doesn't exist or
    // the new configuration differs from the old configuration we
    // close the exisitng socket and open a new socket as appropriate.
    // Note that closing an existing socket means the clien will not
    // receive the configuration result.
    if (!sock_cfg || !current_sock_cfg || sock_changed) {
        // Close the existing socket (if any).
        isc::config::CommandMgr::instance().closeCommandSocket();

        if (sock_cfg) {
            // This will create a control socket and install the external
            // socket in IfaceMgr. That socket will be monitored when
            // Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
            // callback in CommandMgr will be called, if necessary.
            isc::config::CommandMgr::instance().openCommandSocket(sock_cfg);
        }
    }
}

395
isc::data::ConstElementPtr
396 397
configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set,
                     bool check_only) {
398
    if (!config_set) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
399 400 401
        ConstElementPtr answer = isc::config::createAnswer(1,
                                 string("Can't parse NULL config"));
        return (answer);
402 403
    }

404
    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND,
405
              DHCP4_CONFIG_START).arg(config_set->str());
406

407 408 409 410
    // Before starting any subnet operations, let's reset the subnet-id counter,
    // so newly recreated configuration starts with first subnet-id equal 1.
    Subnet::resetSubnetID();

411 412 413
    // Remove any existing timers.
    TimerMgr::instance()->unregisterTimers();

414 415
    // Revert any runtime option definitions configured so far and not committed.
    LibDHCP::revertRuntimeOptionDefs();
416 417 418
    // Let's set empty container in case a user hasn't specified any configuration
    // for option definitions. This is equivalent to commiting empty container.
    LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
419

420
    // Answer will hold the result.
421
    ConstElementPtr answer;
Francis Dupont's avatar
Francis Dupont committed
422
    // Rollback informs whether error occurred and original data
423 424
    // have to be restored to global storages.
    bool rollback = false;
425
    // config_pair holds the details of the current parser when iterating over
426 427
    // the parsers.  It is declared outside the loops so in case of an error,
    // the name of the failing parser can be retrieved in the "catch" clause.
428
    ConfigPair config_pair;
429
    try {
430

431 432
        SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg();

433 434 435
        // This is a way to convert ConstElementPtr to ElementPtr.
        // We need a config that can be edited, because we will insert
        // default values and will insert derived values as well.
436
        ElementPtr mutable_cfg = boost::const_pointer_cast<Element>(config_set);
437 438

        // Set all default values if not specified by the user.
439
        SimpleParser4::setAllDefaults(mutable_cfg);
440

441 442 443
        // And now derive (inherit) global parameters to subnets, if not specified.
        SimpleParser4::deriveParameters(mutable_cfg);

444 445 446
        // We need definitions first
        ConstElementPtr option_defs = mutable_cfg->get("option-def");
        if (option_defs) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
447
            OptionDefListParser parser;
448
            CfgOptionDefPtr cfg_option_def = srv_cfg->getCfgOptionDef();
449 450 451
            parser.parse(cfg_option_def, option_defs);
        }

452
        // Make parsers grouping.
453
        const std::map<std::string, ConstElementPtr>& values_map =
454
                                                        mutable_cfg->mapValue();
455
        BOOST_FOREACH(config_pair, values_map) {
456 457 458
            // In principle we could have the following code structured as a series
            // of long if else if clauses. That would give a marginal performance
            // boost, but would make the code less readable. We had serious issues
Tomek Mrugalski's avatar
Tomek Mrugalski committed
459
            // with the parser code debugability, so I decided to keep it as a
460
            // series of independent ifs.
461 462 463 464 465 466 467
            if (config_pair.first == "option-def") {
                // This is converted to SimpleParser and is handled already above.
                continue;
            }

            if (config_pair.first == "option-data") {
                OptionDataListParser parser(AF_INET);
468
                CfgOptionPtr cfg_option = srv_cfg->getCfgOption();
469 470 471 472
                parser.parse(cfg_option, config_pair.second);
                continue;
            }

473 474 475 476 477 478
            if (config_pair.first == "control-socket") {
                ControlSocketParser parser;
                parser.parse(*srv_cfg, config_pair.second);
                continue;
            }

479 480 481 482 483 484
            if (config_pair.first == "host-reservation-identifiers") {
                HostReservationIdsParser4 parser;
                parser.parse(config_pair.second);
                continue;
            }

485 486
            if (config_pair.first == "interfaces-config") {
                IfacesConfigParser parser(AF_INET);
487
                CfgIfacePtr cfg_iface = srv_cfg->getCfgIface();
488 489 490 491
                parser.parse(cfg_iface, config_pair.second);
                continue;
            }

Francis Dupont's avatar
Francis Dupont committed
492 493 494 495 496 497
            if (config_pair.first == "expired-leases-processing") {
                ExpirationConfigParser parser;
                parser.parse(config_pair.second);
                continue;
            }

498
            if (config_pair.first == "hooks-libraries") {
499 500 501 502
                HooksLibrariesParser hooks_parser;
                HooksConfig& libraries = srv_cfg->getHooksConfig();
                hooks_parser.parse(libraries, config_pair.second);
                libraries.verifyLibraries(config_pair.second->getPosition());
503 504
                continue;
            }
505

506
            // Legacy DhcpConfigParser stuff below
507
            if (config_pair.first == "dhcp-ddns") {
508 509
                // Apply defaults
                D2ClientConfigParser::setAllDefaults(config_pair.second);
510 511
                D2ClientConfigParser parser;
                D2ClientConfigPtr cfg = parser.parse(config_pair.second);
512
                srv_cfg->setD2ClientConfig(cfg);
513 514 515
                continue;
            }

516 517
            if (config_pair.first == "client-classes") {
                ClientClassDefListParser parser;
518 519
                ClientClassDictionaryPtr dictionary =
                    parser.parse(config_pair.second, AF_INET);
520 521 522 523
                srv_cfg->setClientClassDictionary(dictionary);
                continue;
            }

524 525 526 527 528 529 530 531
            // Please move at the end when migration will be finished.
            if (config_pair.first == "lease-database") {
                DbAccessParser parser(DbAccessParser::LEASE_DB);
                CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess();
                parser.parse(cfg_db_access, config_pair.second);
                continue;
            }

532
            if (config_pair.first == "hosts-database") {
533 534 535
                DbAccessParser parser(DbAccessParser::HOSTS_DB);
                CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess();
                parser.parse(cfg_db_access, config_pair.second);
536 537 538
                continue;
            }

539 540 541 542 543 544 545 546
            if (config_pair.first == "subnet4") {
                SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg();
                Subnets4ListConfigParser subnets_parser;
                // parse() returns number of subnets parsed. We may log it one day.
                subnets_parser.parse(srv_cfg, config_pair.second);
                continue;
            }

547 548 549
            // Timers are not used in the global scope. Their values are derived
            // to specific subnets (see SimpleParser6::deriveParameters).
            // decline-probation-period, dhcp4o6-port, echo-client-id are
550
            // handled in global_parser.parse() which sets global parameters.
551 552 553 554 555 556 557 558 559 560 561 562
            // match-client-id is derived to subnet scope level.
            if ( (config_pair.first == "renew-timer") ||
                 (config_pair.first == "rebind-timer") ||
                 (config_pair.first == "valid-lifetime") ||
                 (config_pair.first == "decline-probation-period") ||
                 (config_pair.first == "dhcp4o6-port") ||
                 (config_pair.first == "echo-client-id") ||
                 (config_pair.first == "match-client-id") ||
                 (config_pair.first == "next-server")) {
                continue;
            }

563 564 565 566
            // If we got here, no code handled this parameter, so we bail out.
            isc_throw(DhcpConfigError,
                      "unsupported global configuration parameter: " << config_pair.first
                      << " (" << config_pair.second->getPosition() << ")");
567
        }
568

569 570
        // Apply global options in the staging config.
        Dhcp4ConfigParser global_parser;
571
        global_parser.parse(srv_cfg, mutable_cfg);
572

573
    } catch (const isc::Exception& ex) {
574
        LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
575
                  .arg(config_pair.first).arg(ex.what());
576
        answer = isc::config::createAnswer(1, ex.what());
577

Francis Dupont's avatar
Francis Dupont committed
578
        // An error occurred, so make sure that we restore original data.
579 580
        rollback = true;

581
    } catch (...) {
582
        // For things like bad_cast in boost::lexical_cast
583
        LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(config_pair.first);
584 585
        answer = isc::config::createAnswer(1, "undefined configuration"
                                           " processing error");
586

Francis Dupont's avatar
Francis Dupont committed
587
        // An error occurred, so make sure that we restore original data.
588
        rollback = true;
589 590
    }

591 592 593 594 595 596 597 598 599
    if (check_only) {
        rollback = true;
        if (!answer) {
            answer = isc::config::createAnswer(0,
            "Configuration seems sane. Control-socket, hook-libraries, and D2 "
            "configuration were sanity checked, but not applied.");
        }
    }

600 601 602 603 604 605
    // So far so good, there was no parsing error so let's commit the
    // configuration. This will add created subnets and option values into
    // the server's configuration.
    // This operation should be exception safe but let's make sure.
    if (!rollback) {
        try {
606 607 608 609

            // Setup the command channel.
            configureCommandChannel();

610 611
            // No need to commit interface names as this is handled by the
            // CfgMgr::commit() function.
612

613 614 615 616
            // Apply the staged D2ClientConfig, used to be done by parser commit
            D2ClientConfigPtr cfg;
            cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
            CfgMgr::instance().setD2ClientConfig(cfg);
617 618 619 620

            // This occurs last as if it succeeds, there is no easy way
            // revert it.  As a result, the failure to commit a subsequent
            // change causes problems when trying to roll back.
621 622 623
            const HooksConfig& libraries =
                CfgMgr::instance().getStagingCfg()->getHooksConfig();
            libraries.loadLibraries();
624 625
        }
        catch (const isc::Exception& ex) {
626
            LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
627
            answer = isc::config::createAnswer(2, ex.what());
628 629
            rollback = true;
        } catch (...) {
630
            // For things like bad_cast in boost::lexical_cast
631
            LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
632 633
            answer = isc::config::createAnswer(2, "undefined configuration"
                                               " parsing error");
634
            rollback = true;
635 636
        }
    }
637 638 639

    // Rollback changes as the configuration parsing failed.
    if (rollback) {
640 641 642
        // Revert to original configuration of runtime option definitions
        // in the libdhcp++.
        LibDHCP::revertRuntimeOptionDefs();
643 644 645
        return (answer);
    }

646
    LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE)
647
        .arg(CfgMgr::instance().getStagingCfg()->
648
             getConfigSummary(SrvConfig::CFGSEL_ALL4));
649

650
    // Everything was fine. Configuration is successful.
651
    answer = isc::config::createAnswer(0, "Configuration successful.");
652 653 654 655 656
    return (answer);
}

}; // end of isc::dhcp namespace
}; // end of isc namespace