diff --git a/lib/base/perfdatavalue.cpp b/lib/base/perfdatavalue.cpp index 2871f5501..6268ac1bb 100644 --- a/lib/base/perfdatavalue.cpp +++ b/lib/base/perfdatavalue.cpp @@ -243,23 +243,28 @@ PerfdataValue::Ptr PerfdataValue::Parse(const String& perfdata) spq = perfdata.GetLength(); String valueStr = perfdata.SubStr(eqp + 1, spq - eqp - 1); + std::vector tokens = valueStr.Split(";"); - size_t pos = valueStr.FindFirstNotOf("+-0123456789.eE"); - - if (pos != String::NPos && valueStr[pos] == ',') { + if (valueStr.FindFirstOf(',') != String::NPos || tokens.empty()) { BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid performance data value: " + perfdata)); } - double value = Convert::ToDouble(valueStr.SubStr(0, pos)); + // Find the position where to split value and unit. Possible values of tokens[0] include: + // "1000", "1.0", "1.", "-.1", "+1", "1e10", "1GB", "1e10GB", "1e10EB", "1E10EB", "1.5GB", "1.GB", "+1.E-1EW" + // Consider everything up to and including the last digit or decimal point as part of the value. + size_t pos = tokens[0].FindLastOf("0123456789."); + if (pos != String::NPos) { + pos++; + } - std::vector tokens = valueStr.Split(";"); + double value = Convert::ToDouble(tokens[0].SubStr(0, pos)); bool counter = false; String unit; Value warn, crit, min, max; if (pos != String::NPos) - unit = valueStr.SubStr(pos, tokens[0].GetLength() - pos); + unit = tokens[0].SubStr(pos, String::NPos); double base; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dc8b19893..75c8dc4be 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -147,6 +147,7 @@ add_boost_test(base icinga_perfdata/invalid icinga_perfdata/multi icinga_perfdata/scientificnotation + icinga_perfdata/parse_edgecases remote_url/id_and_path remote_url/parameters remote_url/get_and_set diff --git a/test/icinga-perfdata.cpp b/test/icinga-perfdata.cpp index 3decfc2a9..288161486 100644 --- a/test/icinga-perfdata.cpp +++ b/test/icinga-perfdata.cpp @@ -296,6 +296,13 @@ BOOST_AUTO_TEST_CASE(invalid) { BOOST_CHECK_THROW(PerfdataValue::Parse("123456"), boost::exception); BOOST_CHECK_THROW(PerfdataValue::Parse("test=1,23456"), boost::exception); + BOOST_CHECK_THROW(PerfdataValue::Parse("test=123_456"), boost::exception); + BOOST_CHECK_THROW(PerfdataValue::Parse("test="), boost::exception); + BOOST_CHECK_THROW(PerfdataValue::Parse("test=123,456;1;1;1;1"), boost::exception); + BOOST_CHECK_THROW(PerfdataValue::Parse("test=1;123,456;1;1;1"), boost::exception); + BOOST_CHECK_THROW(PerfdataValue::Parse("test=1;1;123,456;1;1"), boost::exception); + BOOST_CHECK_THROW(PerfdataValue::Parse("test=1;1;1;123,456;1"), boost::exception); + BOOST_CHECK_THROW(PerfdataValue::Parse("test=1;1;1;1;123,456"), boost::exception); } BOOST_AUTO_TEST_CASE(multi) @@ -354,4 +361,23 @@ BOOST_AUTO_TEST_CASE(scientificnotation) BOOST_CHECK(str == "test=0.110000;12;0.130000;0.014000;150"); } +BOOST_AUTO_TEST_CASE(parse_edgecases) +{ + // Trailing decimal point + PerfdataValue::Ptr pv = PerfdataValue::Parse("test=23."); + BOOST_CHECK(pv); + BOOST_CHECK(pv->GetValue() == 23.0); + + // Leading decimal point + pv = PerfdataValue::Parse("test=.42"); + BOOST_CHECK(pv); + BOOST_CHECK(pv->GetValue() == 0.42); + + // E both as exponent and unit prefix + pv = PerfdataValue::Parse("test=+1.5E-15EB"); + BOOST_CHECK(pv); + BOOST_CHECK(pv->GetValue() == 1.5e3); + BOOST_CHECK(pv->GetUnit() == "bytes"); +} + BOOST_AUTO_TEST_SUITE_END()