Kea  2.3.3-git
lib/cc/simple_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-2022 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <cc/simple_parser.h>
10 #include <asiolink/io_address.h>
11 #include <boost/foreach.hpp>
12 #include <boost/lexical_cast.hpp>
13 #include <cc/data.h>
14 #include <string>
15 
16 using namespace std;
17 using namespace isc::asiolink;
18 using namespace isc::dhcp;
20 
21 namespace isc {
22 namespace data {
23 
24 void
25 SimpleParser::checkRequired(const SimpleRequiredKeywords& required,
26  ConstElementPtr scope) {
27  for (auto name : required) {
28  if (scope->contains(name)) {
29  continue;
30  }
31  isc_throw(DhcpConfigError, "missing '" << name << "' parameter");
32  }
33 }
34 
35 void
36 SimpleParser::checkKeywords(const SimpleKeywords& keywords,
37  ConstElementPtr scope) {
38  string spurious;
39  for (auto entry : scope->mapValue()) {
40  if (keywords.count(entry.first) == 0) {
41  if (spurious.empty()) {
42  spurious = entry.first;
43  }
44  continue;
45  }
46  Element::types expected = keywords.at(entry.first);
47  if ((expected == Element::any) ||
48  (entry.second->getType() == expected)) {
49  continue;
50  }
51  isc_throw(DhcpConfigError, "'" << entry.first << "' parameter is not "
52  << (expected == Element::integer ? "an " : "a ")
53  << Element::typeToName(expected));
54  }
55  if (!spurious.empty()) {
56  isc_throw(DhcpConfigError, "spurious '" << spurious << "' parameter");
57  }
58 }
59 
60 std::string
61 SimpleParser::getString(ConstElementPtr scope, const std::string& name) {
62  ConstElementPtr x = scope->get(name);
63  if (!x) {
65  "missing parameter '" << name << "' ("
66  << scope->getPosition() << ")");
67  }
68  if (x->getType() != Element::string) {
70  "invalid type specified for parameter '" << name
71  << "' (" << x->getPosition() << ")");
72  }
73 
74  return (x->stringValue());
75 }
76 
77 int64_t
78 SimpleParser::getInteger(ConstElementPtr scope, const std::string& name) {
79  ConstElementPtr x = scope->get(name);
80  if (!x) {
82  "missing parameter '" << name << "' ("
83  << scope->getPosition() << ")");
84  }
85  if (x->getType() != Element::integer) {
87  "invalid type specified for parameter '" << name
88  << "' (" << x->getPosition() << ")");
89  }
90 
91  return (x->intValue());
92 }
93 
94 int64_t
95 SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& name,
96  int64_t min, int64_t max) {
97  int64_t tmp = getInteger(scope, name);
98  if (tmp < min || tmp > max) {
100  "The '" << name << "' value (" << tmp
101  << ") is not within expected range: (" << min << " - " << max
102  << ")");
103  }
104 
105  return (tmp);
106 }
107 
108 bool
109 SimpleParser::getBoolean(ConstElementPtr scope, const std::string& name) {
110  ConstElementPtr x = scope->get(name);
111  if (!x) {
113  "missing parameter '" << name << "' ("
114  << scope->getPosition() << ")");
115  }
116  if (x->getType() != Element::boolean) {
118  "invalid type specified for parameter '" << name
119  << "' (" << x->getPosition() << ")");
120  }
121 
122  return (x->boolValue());
123 }
124 
125 IOAddress
126 SimpleParser::getAddress(const ConstElementPtr& scope,
127  const std::string& name) {
128  std::string str = getString(scope, name);
129  try {
130  return (IOAddress(str));
131  } catch (const std::exception& e) {
132  isc_throw(DhcpConfigError, "Failed to convert '" << str
133  << "' to address: " << e.what() << "("
134  << getPosition(name, scope) << ")");
135  }
136 }
137 
138 double
139 SimpleParser::getDouble(const ConstElementPtr& scope,
140  const std::string& name) {
141  ConstElementPtr x = scope->get(name);
142  if (!x) {
144  "missing parameter '" << name << "' ("
145  << scope->getPosition() << ")");
146  }
147 
148  if (x->getType() != Element::real) {
150  "invalid type specified for parameter '" << name
151  << "' (" << x->getPosition() << ")");
152  }
153 
154  return (x->doubleValue());
155 }
156 
157 
159 SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr parent) {
160  if (!parent) {
161  return (data::Element::ZERO_POSITION());
162  }
163  ConstElementPtr elem = parent->get(name);
164  if (!elem) {
165  return (parent->getPosition());
166  }
167 
168  return (elem->getPosition());
169 }
170 
171 size_t SimpleParser::setDefaults(ElementPtr scope,
172  const SimpleDefaults& default_values) {
173  size_t cnt = 0;
174 
175  // This is the position representing a default value. As the values
176  // we're inserting here are not present in whatever the config file
177  // came from, we need to make sure it's clearly labeled as default.
178  const Element::Position pos("<default-value>", 0, 0);
179 
180  // Let's go over all parameters we have defaults for.
181  BOOST_FOREACH(SimpleDefault def_value, default_values) {
182 
183  // Try if such a parameter is there. If it is, let's
184  // skip it, because user knows best *cough*.
185  ConstElementPtr x = scope->get(string(def_value.name_));
186  if (x) {
187  // There is such a value already, skip it.
188  continue;
189  }
190 
191  // There isn't such a value defined, let's create the default
192  // value...
193  switch (def_value.type_) {
194  case Element::string: {
195  x.reset(new StringElement(def_value.value_, pos));
196  break;
197  }
198  case Element::integer: {
199  try {
200  int int_value = boost::lexical_cast<int>(def_value.value_);
201  x.reset(new IntElement(int_value, pos));
202  }
203  catch (const std::exception& ex) {
204  isc_throw(BadValue, "Internal error. Integer value expected for: "
205  << def_value.name_ << ", value is: "
206  << def_value.value_ );
207  }
208 
209  break;
210  }
211  case Element::boolean: {
212  bool bool_value;
213  if (def_value.value_ == string("true")) {
214  bool_value = true;
215  } else if (def_value.value_ == string("false")) {
216  bool_value = false;
217  } else {
219  "Internal error. Boolean value specified as "
220  << def_value.value_ << ", expected true or false");
221  }
222  x.reset(new BoolElement(bool_value, pos));
223  break;
224  }
225  case Element::real: {
226  double dbl_value = boost::lexical_cast<double>(def_value.value_);
227  x.reset(new DoubleElement(dbl_value, pos));
228  break;
229  }
230  default:
231  // No default values for null, list or map
233  "Internal error. Incorrect default value type.");
234  }
235 
236  // ... and insert it into the provided Element tree.
237  scope->set(def_value.name_, x);
238  ++cnt;
239  }
240 
241  return (cnt);
242 }
243 
244 size_t
245 SimpleParser::setListDefaults(ConstElementPtr list,
246  const SimpleDefaults& default_values) {
247  size_t cnt = 0;
248  BOOST_FOREACH(ElementPtr entry, list->listValue()) {
249  cnt += setDefaults(entry, default_values);
250  }
251  return (cnt);
252 }
253 
254 size_t
255 SimpleParser::deriveParams(ConstElementPtr parent,
256  ElementPtr child,
257  const ParamsList& params) {
258  if ( (parent->getType() != Element::map) ||
259  (child->getType() != Element::map)) {
260  return (0);
261  }
262 
263  size_t cnt = 0;
264  BOOST_FOREACH(string param, params) {
265  ConstElementPtr x = parent->get(param);
266  if (!x) {
267  // Parent doesn't define this parameter, so there's
268  // nothing to derive from
269  continue;
270  }
271 
272  if (child->get(param)) {
273  // Child defines this parameter already. There's
274  // nothing to do here.
275  continue;
276  }
277 
278  // Copy the parameters to the child scope.
279  child->set(param, x);
280  cnt++;
281  }
282 
283  return (cnt);
284 }
285 
287 SimpleParser::parseIntTriplet(const ConstElementPtr& scope,
288  const std::string& name) {
289  // Initialize as some compilers complain otherwise.
290  uint32_t value = 0;
291  bool has_value = false;
292  uint32_t min_value = 0;
293  bool has_min = false;
294  uint32_t max_value = 0;
295  bool has_max = false;
296  if (scope->contains(name)) {
297  value = getInteger(scope, name);
298  has_value = true;
299  }
300  if (scope->contains("min-" + name)) {
301  min_value = getInteger(scope, "min-" + name);
302  has_min = true;
303  }
304  if (scope->contains("max-" + name)) {
305  max_value = getInteger(scope, "max-" + name);
306  has_max = true;
307  }
308  if (!has_value && !has_min && !has_max) {
309  return (util::Triplet<uint32_t>());
310  }
311  if (has_value) {
312  if (!has_min && !has_max) {
313  // default only.
314  min_value = value;
315  max_value = value;
316  } else if (!has_min) {
317  // default and max.
318  min_value = value;
319  } else if (!has_max) {
320  // default and min.
321  max_value = value;
322  }
323  } else if (has_min) {
324  // min only.
325  if (!has_max) {
326  value = min_value;
327  max_value = min_value;
328  } else {
329  // min and max.
330  isc_throw(DhcpConfigError, "have min-" << name << " and max-"
331  << name << " but no " << name << " (default) in "
332  << scope->getPosition());
333  }
334  } else {
335  // max only.
336  min_value = max_value;
337  value = max_value;
338  }
339  // Check that min <= max.
340  if (min_value > max_value) {
341  if (has_min && has_max) {
342  isc_throw(DhcpConfigError, "the value of min-" << name << " ("
343  << min_value << ") is not less than max-" << name << " ("
344  << max_value << ")");
345  } else if (has_min) {
346  // Only min and default so min > default.
347  isc_throw(DhcpConfigError, "the value of min-" << name << " ("
348  << min_value << ") is not less than (default) " << name
349  << " (" << value << ")");
350  } else {
351  // Only default and max so default > max.
352  isc_throw(DhcpConfigError, "the value of (default) " << name
353  << " (" << value << ") is not less than max-" << name
354  << " (" << max_value << ")");
355  }
356  }
357  // Check that value is between min and max.
358  if ((value < min_value) || (value > max_value)) {
359  isc_throw(DhcpConfigError, "the value of (default) " << name << " ("
360  << value << ") is not between min-" << name << " ("
361  << min_value << ") and max-" << name << " ("
362  << max_value << ")");
363  }
364 
365  return (util::Triplet<uint32_t>(min_value, value, max_value));
366 }
367 
368 } // end of isc::dhcp namespace
369 } // end of isc namespace
std::map< std::string, isc::data::Element::types > SimpleKeywords
This specifies all accepted keywords with their types.
std::vector< SimpleDefault > SimpleDefaults
This specifies all default values in a given scope (e.g. a subnet).
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
STL namespace.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const isc::data::Element::types type_
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
To be removed. Please use ConfigError instead.
Notes: IntElement type is changed to int64_t.
Definition: data.h:590
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
std::vector< std::string > SimpleRequiredKeywords
This specifies all required keywords.
This array defines a single entry of default values.
Represents the position of the data element within a configuration string.
Definition: data.h:92
Defines the logger used by the top-level component of kea-lfc.
std::vector< std::string > ParamsList
This defines a list of all parameters that are derived (or inherited) between contexts.
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...