| /* |
| // Copyright (c) 2018 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 perform_probe.cpp |
| #include "entity_manager.hpp" |
| |
| #include <boost/algorithm/string/replace.hpp> |
| #include <phosphor-logging/lg2.hpp> |
| |
| #include <regex> |
| #include <utility> |
| |
| // probes dbus interface dictionary for a key with a value that matches a regex |
| // When an interface passes a probe, also save its D-Bus path with it. |
| bool probeDbus(const std::string& interfaceName, |
| const std::map<std::string, nlohmann::json>& matches, |
| FoundDevices& devices, const std::shared_ptr<PerformScan>& scan, |
| bool& foundProbe) |
| { |
| bool foundMatch = false; |
| foundProbe = false; |
| |
| for (const auto& [path, interfaces] : scan->dbusProbeObjects) |
| { |
| auto it = interfaces.find(interfaceName); |
| if (it == interfaces.end()) |
| { |
| continue; |
| } |
| |
| foundProbe = true; |
| |
| bool deviceMatches = true; |
| const DBusInterface& interface = it->second; |
| |
| for (const auto& [matchProp, matchJSON] : matches) |
| { |
| auto deviceValue = interface.find(matchProp); |
| if (deviceValue != interface.end()) |
| { |
| deviceMatches = deviceMatches && |
| matchProbe(matchJSON, deviceValue->second); |
| } |
| else |
| { |
| // Move on to the next DBus path |
| deviceMatches = false; |
| break; |
| } |
| } |
| if (deviceMatches) |
| { |
| lg2::debug("Found probe match on {PATH} {IFACE}", "PATH", path, |
| "IFACE", interfaceName); |
| devices.emplace_back(interface, path); |
| foundMatch = true; |
| } |
| } |
| return foundMatch; |
| } |
| |
| // default probe entry point, iterates a list looking for specific types to |
| // call specific probe functions |
| bool probe(const std::vector<std::string>& probeCommand, |
| const std::shared_ptr<PerformScan>& scan, FoundDevices& foundDevs) |
| { |
| const static std::regex command(R"(\((.*)\))"); |
| std::smatch match; |
| bool ret = false; |
| bool matchOne = false; |
| bool cur = true; |
| probe_type_codes lastCommand = probe_type_codes::FALSE_T; |
| bool first = true; |
| |
| for (const auto& probe : probeCommand) |
| { |
| FoundProbeTypeT probeType = findProbeType(probe); |
| if (probeType) |
| { |
| switch ((*probeType)->second) |
| { |
| case probe_type_codes::FALSE_T: |
| { |
| cur = false; |
| break; |
| } |
| case probe_type_codes::TRUE_T: |
| { |
| cur = true; |
| break; |
| } |
| case probe_type_codes::MATCH_ONE: |
| { |
| // set current value to last, this probe type shouldn't |
| // affect the outcome |
| cur = ret; |
| matchOne = true; |
| break; |
| } |
| /*case probe_type_codes::AND: |
| break; |
| case probe_type_codes::OR: |
| break; |
| // these are no-ops until the last command switch |
| */ |
| case probe_type_codes::FOUND: |
| { |
| if (!std::regex_search(probe, match, command)) |
| { |
| std::cerr |
| << "found probe syntax error " << probe << "\n"; |
| return false; |
| } |
| std::string commandStr = *(match.begin() + 1); |
| boost::replace_all(commandStr, "'", ""); |
| cur = (std::find(scan->passedProbes.begin(), |
| scan->passedProbes.end(), commandStr) != |
| scan->passedProbes.end()); |
| break; |
| } |
| default: |
| { |
| break; |
| } |
| } |
| } |
| // look on dbus for object |
| else |
| { |
| if (!std::regex_search(probe, match, command)) |
| { |
| std::cerr << "dbus probe syntax error " << probe << "\n"; |
| return false; |
| } |
| std::string commandStr = *(match.begin() + 1); |
| // convert single ticks and single slashes into legal json |
| boost::replace_all(commandStr, "'", "\""); |
| boost::replace_all(commandStr, R"(\)", R"(\\)"); |
| auto json = nlohmann::json::parse(commandStr, nullptr, false, true); |
| if (json.is_discarded()) |
| { |
| std::cerr << "dbus command syntax error " << commandStr << "\n"; |
| return false; |
| } |
| // we can match any (string, variant) property. (string, string) |
| // does a regex |
| std::map<std::string, nlohmann::json> dbusProbeMap = |
| json.get<std::map<std::string, nlohmann::json>>(); |
| auto findStart = probe.find('('); |
| if (findStart == std::string::npos) |
| { |
| return false; |
| } |
| bool foundProbe = !!probeType; |
| std::string probeInterface = probe.substr(0, findStart); |
| cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan, |
| foundProbe); |
| } |
| |
| // some functions like AND and OR only take affect after the |
| // fact |
| if (lastCommand == probe_type_codes::AND) |
| { |
| ret = cur && ret; |
| } |
| else if (lastCommand == probe_type_codes::OR) |
| { |
| ret = cur || ret; |
| } |
| |
| if (first) |
| { |
| ret = cur; |
| first = false; |
| } |
| lastCommand = probeType ? (*probeType)->second |
| : probe_type_codes::FALSE_T; |
| } |
| |
| // probe passed, but empty device |
| if (ret && foundDevs.empty()) |
| { |
| foundDevs.emplace_back( |
| boost::container::flat_map<std::string, DBusValueVariant>{}, |
| std::string{}); |
| } |
| if (matchOne && ret) |
| { |
| // match the last one |
| auto last = foundDevs.back(); |
| foundDevs.clear(); |
| |
| foundDevs.emplace_back(std::move(last)); |
| } |
| return ret; |
| } |
| |
| PerformProbe::PerformProbe(nlohmann::json& recordRef, |
| const std::vector<std::string>& probeCommand, |
| std::string probeName, |
| std::shared_ptr<PerformScan>& scanPtr) : |
| recordRef(recordRef), _probeCommand(probeCommand), |
| probeName(std::move(probeName)), scan(scanPtr) |
| {} |
| PerformProbe::~PerformProbe() |
| { |
| FoundDevices foundDevs; |
| if (probe(_probeCommand, scan, foundDevs)) |
| { |
| scan->updateSystemConfiguration(recordRef, probeName, foundDevs); |
| } |
| } |