EntityManager: Split out PerformProbe
Continue breaking down the code so regular people can comprehend it.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: Ic4458054df7426d579cb99dd21f3578d1265711d
diff --git a/src/PerformProbe.cpp b/src/PerformProbe.cpp
new file mode 100644
index 0000000..7024e47
--- /dev/null
+++ b/src/PerformProbe.cpp
@@ -0,0 +1,248 @@
+/*
+// 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 PerformProbe.cpp
+#include "EntityManager.hpp"
+
+#include <boost/algorithm/string/replace.hpp>
+
+#include <regex>
+
+constexpr const bool debug = false;
+
+/* Keep this in sync with EntityManager.cpp */
+static const boost::container::flat_map<const char*, probe_type_codes, CmpStr>
+ probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
+ {"TRUE", probe_type_codes::TRUE_T},
+ {"AND", probe_type_codes::AND},
+ {"OR", probe_type_codes::OR},
+ {"FOUND", probe_type_codes::FOUND},
+ {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
+
+// 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& interface,
+ const std::map<std::string, nlohmann::json>& matches,
+ FoundDeviceT& 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(interface);
+ if (it == interfaces.end())
+ {
+ continue;
+ }
+
+ foundProbe = true;
+
+ bool deviceMatches = true;
+ const boost::container::flat_map<std::string, BasicVariantType>&
+ properties = it->second;
+
+ for (const auto& [matchProp, matchJSON] : matches)
+ {
+ auto deviceValue = properties.find(matchProp);
+ if (deviceValue != properties.end())
+ {
+ deviceMatches =
+ deviceMatches && matchProbe(matchJSON, deviceValue->second);
+ }
+ else
+ {
+ // Move on to the next DBus path
+ deviceMatches = false;
+ break;
+ }
+ }
+ if (deviceMatches)
+ {
+ if constexpr (debug)
+ {
+ std::cerr << "probeDBus: Found probe match on " << path << " "
+ << interface << "\n";
+ }
+ devices.emplace_back(properties, 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, FoundDeviceT& 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 (auto& probe : probeCommand)
+ {
+ bool foundProbe = false;
+ boost::container::flat_map<const char*, probe_type_codes,
+ CmpStr>::const_iterator probeType;
+
+ for (probeType = probeTypes.begin(); probeType != probeTypes.end();
+ ++probeType)
+ {
+ if (probe.find(probeType->first) != std::string::npos)
+ {
+ foundProbe = true;
+ break;
+ }
+ }
+ if (foundProbe)
+ {
+ 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);
+ 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;
+ }
+ 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 != probeTypes.end() ? probeType->second
+ : probe_type_codes::FALSE_T;
+ }
+
+ // probe passed, but empty device
+ if (ret && foundDevs.size() == 0)
+ {
+ foundDevs.emplace_back(
+ boost::container::flat_map<std::string, BasicVariantType>{},
+ 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(
+ const std::vector<std::string>& probeCommand,
+ std::shared_ptr<PerformScan>& scanPtr,
+ std::function<void(FoundDeviceT&, const DBusProbeObjectT&)>&& callback) :
+ _probeCommand(probeCommand),
+ scan(scanPtr), _callback(std::move(callback))
+{}
+PerformProbe::~PerformProbe()
+{
+ FoundDeviceT foundDevs;
+ if (probe(_probeCommand, scan, foundDevs))
+ {
+ _callback(foundDevs, scan->dbusProbeObjects);
+ }
+}