/* // Copyright (c) 2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ /// \file utils.cpp #include "utils.hpp" #include "expression.hpp" #include "variant_visitors.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include constexpr const char* templateChar = "$"; namespace fs = std::filesystem; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static bool powerStatusOn = false; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static std::unique_ptr powerMatch = nullptr; bool findFiles(const fs::path& dirPath, const std::string& matchString, std::vector& foundPaths) { if (!fs::exists(dirPath)) { return false; } std::regex search(matchString); std::smatch match; for (const auto& p : fs::directory_iterator(dirPath)) { std::string path = p.path().string(); if (std::regex_search(path, match, search)) { foundPaths.emplace_back(p.path()); } } return true; } bool findFiles(const std::vector&& dirPaths, const std::string& matchString, std::vector& foundPaths) { std::map paths; std::regex search(matchString); std::smatch match; for (const auto& dirPath : dirPaths) { if (!fs::exists(dirPath)) { continue; } for (const auto& p : fs::directory_iterator(dirPath)) { std::string path = p.path().string(); if (std::regex_search(path, match, search)) { paths[p.path().filename()] = p.path(); } } } for (const auto& [key, value] : paths) { foundPaths.emplace_back(value); } return !foundPaths.empty(); } bool getI2cDevicePaths(const fs::path& dirPath, boost::container::flat_map& busPaths) { if (!fs::exists(dirPath)) { return false; } // Regex for matching the path std::regex searchPath(std::string(R"(i2c-\d+$)")); // Regex for matching the bus numbers std::regex searchBus(std::string(R"(\w[^-]*$)")); std::smatch matchPath; std::smatch matchBus; for (const auto& p : fs::directory_iterator(dirPath)) { std::string path = p.path().string(); if (std::regex_search(path, matchPath, searchPath)) { if (std::regex_search(path, matchBus, searchBus)) { size_t bus = stoul(*matchBus.begin()); busPaths.insert(std::pair(bus, p.path())); } } } return true; } bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input) { valijson::Schema schema; valijson::SchemaParser parser; valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile); parser.populateSchema(schemaAdapter, schema); valijson::Validator validator; valijson::adapters::NlohmannJsonAdapter targetAdapter(input); return validator.validate(schema, targetAdapter, nullptr); } bool isPowerOn() { if (!powerMatch) { throw std::runtime_error("Power Match Not Created"); } return powerStatusOn; } void setupPowerMatch(const std::shared_ptr& conn) { powerMatch = std::make_unique( static_cast(*conn), "type='signal',interface='" + std::string(properties::interface) + "',path='" + std::string(power::path) + "',arg0='" + std::string(power::interface) + "'", [](sdbusplus::message_t& message) { std::string objectName; boost::container::flat_map> values; message.read(objectName, values); auto findState = values.find(power::property); if (findState != values.end()) { powerStatusOn = boost::ends_with( std::get(findState->second), "Running"); } }); conn->async_method_call( [](boost::system::error_code ec, const std::variant& state) { if (ec) { return; } powerStatusOn = boost::ends_with(std::get(state), "Running"); }, power::busname, power::path, properties::interface, properties::get, power::interface, power::property); } // Replaces the template character like the other version of this function, // but checks all properties on all interfaces provided to do the substitution // with. std::optional templateCharReplace(nlohmann::json::iterator& keyPair, const DBusObject& object, const size_t index, const std::optional& replaceStr) { for (const auto& [_, interface] : object) { auto ret = templateCharReplace(keyPair, interface, index, replaceStr); if (ret) { return ret; } } return std::nullopt; } // finds the template character (currently set to $) and replaces the value with // the field found in a dbus object i.e. $ADDRESS would get populated with the // ADDRESS field from a object on dbus std::optional templateCharReplace(nlohmann::json::iterator& keyPair, const DBusInterface& interface, const size_t index, const std::optional& replaceStr) { std::optional ret = std::nullopt; if (keyPair.value().type() == nlohmann::json::value_t::object || keyPair.value().type() == nlohmann::json::value_t::array) { for (auto nextLayer = keyPair.value().begin(); nextLayer != keyPair.value().end(); nextLayer++) { templateCharReplace(nextLayer, interface, index, replaceStr); } return ret; } std::string* strPtr = keyPair.value().get_ptr(); if (strPtr == nullptr) { return ret; } boost::replace_all(*strPtr, std::string(templateChar) + "index", std::to_string(index)); if (replaceStr) { boost::replace_all(*strPtr, *replaceStr, std::to_string(index)); } for (const auto& [propName, propValue] : interface) { std::string templateName = templateChar + propName; boost::iterator_range find = boost::ifind_first(*strPtr, templateName); if (!find) { continue; } size_t start = find.begin() - strPtr->begin(); // check for additional operations if ((start == 0U) && find.end() == strPtr->end()) { std::visit([&](auto&& val) { keyPair.value() = val; }, propValue); return ret; } constexpr const std::array mathChars = {'+', '-', '%', '*', '/'}; size_t nextItemIdx = start + templateName.size() + 1; if (nextItemIdx > strPtr->size() || std::find(mathChars.begin(), mathChars.end(), strPtr->at(nextItemIdx)) == mathChars.end()) { std::string val = std::visit(VariantToStringVisitor(), propValue); boost::ireplace_all(*strPtr, templateName, val); continue; } // save the prefix std::string prefix = strPtr->substr(0, start); // operate on the rest std::string end = strPtr->substr(nextItemIdx); std::vector split; boost::split(split, end, boost::is_any_of(" ")); // need at least 1 operation and number if (split.size() < 2) { std::cerr << "Syntax error on template replacement of " << *strPtr << "\n"; for (const std::string& data : split) { std::cerr << data << " "; } std::cerr << "\n"; continue; } // we assume that the replacement is a number, because we can // only do math on numbers.. we might concatenate strings in the // future, but thats later int number = std::visit(VariantToIntVisitor(), propValue); auto exprBegin = split.begin(); auto exprEnd = split.end(); number = expression::evaluate(number, exprBegin, exprEnd); std::string replaced(find.begin(), find.end()); while (exprBegin != exprEnd) { replaced.append(" ").append(*exprBegin++); } ret = replaced; std::string result = prefix + std::to_string(number); while (exprEnd != split.end()) { result.append(" ").append(*exprEnd++); } keyPair.value() = result; // We probably just invalidated the pointer abovei, // reset and continue to handle multiple templates strPtr = keyPair.value().get_ptr(); if (strPtr == nullptr) { break; } } strPtr = keyPair.value().get_ptr(); if (strPtr == nullptr) { return ret; } std::string_view strView = *strPtr; int base = 10; if (boost::starts_with(strView, "0x")) { strView.remove_prefix(2); base = 16; } uint64_t temp = 0; const char* strDataEndPtr = strView.data() + strView.size(); const std::from_chars_result res = std::from_chars(strView.data(), strDataEndPtr, temp, base); if (res.ec == std::errc{} && res.ptr == strDataEndPtr) { keyPair.value() = temp; } return ret; } /// \brief JSON/DBus matching Callable for std::variant (visitor) /// /// Default match JSON/DBus match implementation /// \tparam T The concrete DBus value type from DBusValueVariant template struct MatchProbe { /// \param probe the probe statement to match against /// \param value the property value being matched to a probe /// \return true if the dbusValue matched the probe otherwise false static bool match(const nlohmann::json& probe, const T& value) { return probe == value; } }; /// \brief JSON/DBus matching Callable for std::variant (visitor) /// /// std::string specialization of MatchProbe enabling regex matching template <> struct MatchProbe { /// \param probe the probe statement to match against /// \param value the string value being matched to a probe /// \return true if the dbusValue matched the probe otherwise false static bool match(const nlohmann::json& probe, const std::string& value) { if (probe.is_string()) { try { std::regex search(probe); std::smatch regMatch; return std::regex_search(value, regMatch, search); } catch (const std::regex_error&) { std::cerr << "Syntax error in regular expression: " << probe << " will never match"; } } // Skip calling nlohmann here, since it will never match a non-string // to a std::string return false; } }; /// \brief Forwarding JSON/DBus matching Callable for std::variant (visitor) /// /// Forward calls to the correct template instantiation of MatchProbe struct MatchProbeForwarder { explicit MatchProbeForwarder(const nlohmann::json& probe) : probeRef(probe) {} const nlohmann::json& probeRef; template bool operator()(const T& dbusValue) const { return MatchProbe::match(probeRef, dbusValue); } }; bool matchProbe(const nlohmann::json& probe, const DBusValueVariant& dbusValue) { return std::visit(MatchProbeForwarder(probe), dbusValue); }