summaryrefslogtreecommitdiffstats
path: root/src/lib/cc/simple_parser.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/cc/simple_parser.cc369
1 files changed, 369 insertions, 0 deletions
diff --git a/src/lib/cc/simple_parser.cc b/src/lib/cc/simple_parser.cc
new file mode 100644
index 0000000..8140d13
--- /dev/null
+++ b/src/lib/cc/simple_parser.cc
@@ -0,0 +1,369 @@
+// Copyright (C) 2016-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/simple_parser.h>
+#include <asiolink/io_address.h>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <cc/data.h>
+#include <string>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using isc::dhcp::DhcpConfigError;
+
+namespace isc {
+namespace data {
+
+void
+SimpleParser::checkRequired(const SimpleRequiredKeywords& required,
+ ConstElementPtr scope) {
+ for (auto name : required) {
+ if (scope->contains(name)) {
+ continue;
+ }
+ isc_throw(DhcpConfigError, "missing '" << name << "' parameter");
+ }
+}
+
+void
+SimpleParser::checkKeywords(const SimpleKeywords& keywords,
+ ConstElementPtr scope) {
+ string spurious;
+ for (auto entry : scope->mapValue()) {
+ if (keywords.count(entry.first) == 0) {
+ if (spurious.empty()) {
+ spurious = entry.first;
+ }
+ continue;
+ }
+ Element::types expected = keywords.at(entry.first);
+ if ((expected == Element::any) ||
+ (entry.second->getType() == expected)) {
+ continue;
+ }
+ isc_throw(DhcpConfigError, "'" << entry.first << "' parameter is not "
+ << (expected == Element::integer ? "an " : "a ")
+ << Element::typeToName(expected));
+ }
+ if (!spurious.empty()) {
+ isc_throw(DhcpConfigError, "spurious '" << spurious << "' parameter");
+ }
+}
+
+std::string
+SimpleParser::getString(ConstElementPtr scope, const std::string& name) {
+ ConstElementPtr x = scope->get(name);
+ if (!x) {
+ isc_throw(DhcpConfigError,
+ "missing parameter '" << name << "' ("
+ << scope->getPosition() << ")");
+ }
+ if (x->getType() != Element::string) {
+ isc_throw(DhcpConfigError,
+ "invalid type specified for parameter '" << name
+ << "' (" << x->getPosition() << ")");
+ }
+
+ return (x->stringValue());
+}
+
+int64_t
+SimpleParser::getInteger(ConstElementPtr scope, const std::string& name) {
+ ConstElementPtr x = scope->get(name);
+ if (!x) {
+ isc_throw(DhcpConfigError,
+ "missing parameter '" << name << "' ("
+ << scope->getPosition() << ")");
+ }
+ if (x->getType() != Element::integer) {
+ isc_throw(DhcpConfigError,
+ "invalid type specified for parameter '" << name
+ << "' (" << x->getPosition() << ")");
+ }
+
+ return (x->intValue());
+}
+
+int64_t
+SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& name,
+ int64_t min, int64_t max) {
+ int64_t tmp = getInteger(scope, name);
+ if (tmp < min || tmp > max) {
+ isc_throw(OutOfRange,
+ "The '" << name << "' value (" << tmp
+ << ") is not within expected range: (" << min << " - " << max
+ << ")");
+ }
+
+ return (tmp);
+}
+
+bool
+SimpleParser::getBoolean(ConstElementPtr scope, const std::string& name) {
+ ConstElementPtr x = scope->get(name);
+ if (!x) {
+ isc_throw(DhcpConfigError,
+ "missing parameter '" << name << "' ("
+ << scope->getPosition() << ")");
+ }
+ if (x->getType() != Element::boolean) {
+ isc_throw(DhcpConfigError,
+ "invalid type specified for parameter '" << name
+ << "' (" << x->getPosition() << ")");
+ }
+
+ return (x->boolValue());
+}
+
+IOAddress
+SimpleParser::getAddress(const ConstElementPtr& scope,
+ const std::string& name) {
+ std::string str = getString(scope, name);
+ try {
+ return (IOAddress(str));
+ } catch (const std::exception& e) {
+ isc_throw(DhcpConfigError, "Failed to convert '" << str
+ << "' to address: " << e.what() << "("
+ << getPosition(name, scope) << ")");
+ }
+}
+
+double
+SimpleParser::getDouble(const ConstElementPtr& scope,
+ const std::string& name) {
+ ConstElementPtr x = scope->get(name);
+ if (!x) {
+ isc_throw(DhcpConfigError,
+ "missing parameter '" << name << "' ("
+ << scope->getPosition() << ")");
+ }
+
+ if (x->getType() != Element::real) {
+ isc_throw(DhcpConfigError,
+ "invalid type specified for parameter '" << name
+ << "' (" << x->getPosition() << ")");
+ }
+
+ return (x->doubleValue());
+}
+
+
+const data::Element::Position&
+SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr parent) {
+ if (!parent) {
+ return (data::Element::ZERO_POSITION());
+ }
+ ConstElementPtr elem = parent->get(name);
+ if (!elem) {
+ return (parent->getPosition());
+ }
+
+ return (elem->getPosition());
+}
+
+size_t SimpleParser::setDefaults(ElementPtr scope,
+ const SimpleDefaults& default_values) {
+ size_t cnt = 0;
+
+ // This is the position representing a default value. As the values
+ // we're inserting here are not present in whatever the config file
+ // came from, we need to make sure it's clearly labeled as default.
+ const Element::Position pos("<default-value>", 0, 0);
+
+ // Let's go over all parameters we have defaults for.
+ BOOST_FOREACH(SimpleDefault def_value, default_values) {
+
+ // Try if such a parameter is there. If it is, let's
+ // skip it, because user knows best *cough*.
+ ConstElementPtr x = scope->get(string(def_value.name_));
+ if (x) {
+ // There is such a value already, skip it.
+ continue;
+ }
+
+ // There isn't such a value defined, let's create the default
+ // value...
+ switch (def_value.type_) {
+ case Element::string: {
+ x.reset(new StringElement(def_value.value_, pos));
+ break;
+ }
+ case Element::integer: {
+ try {
+ int int_value = boost::lexical_cast<int>(def_value.value_);
+ x.reset(new IntElement(int_value, pos));
+ }
+ catch (const std::exception& ex) {
+ isc_throw(BadValue, "Internal error. Integer value expected for: "
+ << def_value.name_ << ", value is: "
+ << def_value.value_ );
+ }
+
+ break;
+ }
+ case Element::boolean: {
+ bool bool_value;
+ if (def_value.value_ == string("true")) {
+ bool_value = true;
+ } else if (def_value.value_ == string("false")) {
+ bool_value = false;
+ } else {
+ isc_throw(DhcpConfigError,
+ "Internal error. Boolean value specified as "
+ << def_value.value_ << ", expected true or false");
+ }
+ x.reset(new BoolElement(bool_value, pos));
+ break;
+ }
+ case Element::real: {
+ double dbl_value = boost::lexical_cast<double>(def_value.value_);
+ x.reset(new DoubleElement(dbl_value, pos));
+ break;
+ }
+ default:
+ // No default values for null, list or map
+ isc_throw(DhcpConfigError,
+ "Internal error. Incorrect default value type.");
+ }
+
+ // ... and insert it into the provided Element tree.
+ scope->set(def_value.name_, x);
+ ++cnt;
+ }
+
+ return (cnt);
+}
+
+size_t
+SimpleParser::setListDefaults(ConstElementPtr list,
+ const SimpleDefaults& default_values) {
+ size_t cnt = 0;
+ BOOST_FOREACH(ElementPtr entry, list->listValue()) {
+ cnt += setDefaults(entry, default_values);
+ }
+ return (cnt);
+}
+
+size_t
+SimpleParser::deriveParams(ConstElementPtr parent,
+ ElementPtr child,
+ const ParamsList& params) {
+ if ( (parent->getType() != Element::map) ||
+ (child->getType() != Element::map)) {
+ return (0);
+ }
+
+ size_t cnt = 0;
+ BOOST_FOREACH(string param, params) {
+ ConstElementPtr x = parent->get(param);
+ if (!x) {
+ // Parent doesn't define this parameter, so there's
+ // nothing to derive from
+ continue;
+ }
+
+ if (child->get(param)) {
+ // Child defines this parameter already. There's
+ // nothing to do here.
+ continue;
+ }
+
+ // Copy the parameters to the child scope.
+ child->set(param, x);
+ cnt++;
+ }
+
+ return (cnt);
+}
+
+const util::Triplet<uint32_t>
+SimpleParser::parseIntTriplet(const ConstElementPtr& scope,
+ const std::string& name) {
+ // Initialize as some compilers complain otherwise.
+ uint32_t value = 0;
+ bool has_value = false;
+ uint32_t min_value = 0;
+ bool has_min = false;
+ uint32_t max_value = 0;
+ bool has_max = false;
+ if (scope->contains(name)) {
+ value = getInteger(scope, name);
+ has_value = true;
+ }
+ if (scope->contains("min-" + name)) {
+ min_value = getInteger(scope, "min-" + name);
+ has_min = true;
+ }
+ if (scope->contains("max-" + name)) {
+ max_value = getInteger(scope, "max-" + name);
+ has_max = true;
+ }
+ if (!has_value && !has_min && !has_max) {
+ return (util::Triplet<uint32_t>());
+ }
+ if (has_value) {
+ if (!has_min && !has_max) {
+ // default only.
+ min_value = value;
+ max_value = value;
+ } else if (!has_min) {
+ // default and max.
+ min_value = value;
+ } else if (!has_max) {
+ // default and min.
+ max_value = value;
+ }
+ } else if (has_min) {
+ // min only.
+ if (!has_max) {
+ value = min_value;
+ max_value = min_value;
+ } else {
+ // min and max.
+ isc_throw(DhcpConfigError, "have min-" << name << " and max-"
+ << name << " but no " << name << " (default) in "
+ << scope->getPosition());
+ }
+ } else {
+ // max only.
+ min_value = max_value;
+ value = max_value;
+ }
+ // Check that min <= max.
+ if (min_value > max_value) {
+ if (has_min && has_max) {
+ isc_throw(DhcpConfigError, "the value of min-" << name << " ("
+ << min_value << ") is not less than max-" << name << " ("
+ << max_value << ")");
+ } else if (has_min) {
+ // Only min and default so min > default.
+ isc_throw(DhcpConfigError, "the value of min-" << name << " ("
+ << min_value << ") is not less than (default) " << name
+ << " (" << value << ")");
+ } else {
+ // Only default and max so default > max.
+ isc_throw(DhcpConfigError, "the value of (default) " << name
+ << " (" << value << ") is not less than max-" << name
+ << " (" << max_value << ")");
+ }
+ }
+ // Check that value is between min and max.
+ if ((value < min_value) || (value > max_value)) {
+ isc_throw(DhcpConfigError, "the value of (default) " << name << " ("
+ << value << ") is not between min-" << name << " ("
+ << min_value << ") and max-" << name << " ("
+ << max_value << ")");
+ }
+
+ return (util::Triplet<uint32_t>(min_value, value, max_value));
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace