blob: d120b3cdffe8b7f18fc830c895e9e51da2d97114 [file] [log] [blame]
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +10301/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16/// \file PerformProbe.cpp
17#include "EntityManager.hpp"
18
19#include <boost/algorithm/string/replace.hpp>
20
21#include <regex>
22
23constexpr const bool debug = false;
24
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103025// probes dbus interface dictionary for a key with a value that matches a regex
26// When an interface passes a probe, also save its D-Bus path with it.
Andrew Jefferyf5772d22022-04-12 22:05:30 +093027bool probeDbus(const std::string& interfaceName,
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103028 const std::map<std::string, nlohmann::json>& matches,
Andrew Jefferyf5772d22022-04-12 22:05:30 +093029 FoundDevices& devices, const std::shared_ptr<PerformScan>& scan,
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103030 bool& foundProbe)
31{
32 bool foundMatch = false;
33 foundProbe = false;
34
35 for (const auto& [path, interfaces] : scan->dbusProbeObjects)
36 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +093037 auto it = interfaces.find(interfaceName);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103038 if (it == interfaces.end())
39 {
40 continue;
41 }
42
43 foundProbe = true;
44
45 bool deviceMatches = true;
Andrew Jefferyf5772d22022-04-12 22:05:30 +093046 const DBusInterface& interface = it->second;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103047
48 for (const auto& [matchProp, matchJSON] : matches)
49 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +093050 auto deviceValue = interface.find(matchProp);
51 if (deviceValue != interface.end())
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103052 {
53 deviceMatches =
54 deviceMatches && matchProbe(matchJSON, deviceValue->second);
55 }
56 else
57 {
58 // Move on to the next DBus path
59 deviceMatches = false;
60 break;
61 }
62 }
63 if (deviceMatches)
64 {
65 if constexpr (debug)
66 {
67 std::cerr << "probeDBus: Found probe match on " << path << " "
Andrew Jefferyf5772d22022-04-12 22:05:30 +093068 << interfaceName << "\n";
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103069 }
Andrew Jefferyf5772d22022-04-12 22:05:30 +093070 // Use emplace back when clang implements
71 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0960r3.html
72 //
73 // https://en.cppreference.com/w/cpp/compiler_support/20
74 devices.push_back({interface, path});
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103075 foundMatch = true;
76 }
77 }
78 return foundMatch;
79}
80
81// default probe entry point, iterates a list looking for specific types to
82// call specific probe functions
83bool probe(const std::vector<std::string>& probeCommand,
Andrew Jefferyf5772d22022-04-12 22:05:30 +093084 const std::shared_ptr<PerformScan>& scan, FoundDevices& foundDevs)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103085{
86 const static std::regex command(R"(\((.*)\))");
87 std::smatch match;
88 bool ret = false;
89 bool matchOne = false;
90 bool cur = true;
91 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
92 bool first = true;
93
94 for (auto& probe : probeCommand)
95 {
Andrew Jeffery666583b2021-12-01 15:50:12 +103096 FoundProbeTypeT probeType = findProbeType(probe);
97 if (probeType)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103098 {
Andrew Jeffery666583b2021-12-01 15:50:12 +103099 switch ((*probeType)->second)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030100 {
101 case probe_type_codes::FALSE_T:
102 {
103 cur = false;
104 break;
105 }
106 case probe_type_codes::TRUE_T:
107 {
108 cur = true;
109 break;
110 }
111 case probe_type_codes::MATCH_ONE:
112 {
113 // set current value to last, this probe type shouldn't
114 // affect the outcome
115 cur = ret;
116 matchOne = true;
117 break;
118 }
119 /*case probe_type_codes::AND:
120 break;
121 case probe_type_codes::OR:
122 break;
123 // these are no-ops until the last command switch
124 */
125 case probe_type_codes::FOUND:
126 {
127 if (!std::regex_search(probe, match, command))
128 {
129 std::cerr << "found probe syntax error " << probe
130 << "\n";
131 return false;
132 }
133 std::string commandStr = *(match.begin() + 1);
134 boost::replace_all(commandStr, "'", "");
135 cur = (std::find(scan->passedProbes.begin(),
136 scan->passedProbes.end(),
137 commandStr) != scan->passedProbes.end());
138 break;
139 }
140 default:
141 {
142 break;
143 }
144 }
145 }
146 // look on dbus for object
147 else
148 {
149 if (!std::regex_search(probe, match, command))
150 {
151 std::cerr << "dbus probe syntax error " << probe << "\n";
152 return false;
153 }
154 std::string commandStr = *(match.begin() + 1);
155 // convert single ticks and single slashes into legal json
156 boost::replace_all(commandStr, "'", "\"");
157 boost::replace_all(commandStr, R"(\)", R"(\\)");
158 auto json = nlohmann::json::parse(commandStr, nullptr, false);
159 if (json.is_discarded())
160 {
161 std::cerr << "dbus command syntax error " << commandStr << "\n";
162 return false;
163 }
164 // we can match any (string, variant) property. (string, string)
165 // does a regex
166 std::map<std::string, nlohmann::json> dbusProbeMap =
167 json.get<std::map<std::string, nlohmann::json>>();
168 auto findStart = probe.find('(');
169 if (findStart == std::string::npos)
170 {
171 return false;
172 }
Andrew Jeffery666583b2021-12-01 15:50:12 +1030173 bool foundProbe = !!probeType;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030174 std::string probeInterface = probe.substr(0, findStart);
175 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
176 foundProbe);
177 }
178
179 // some functions like AND and OR only take affect after the
180 // fact
181 if (lastCommand == probe_type_codes::AND)
182 {
183 ret = cur && ret;
184 }
185 else if (lastCommand == probe_type_codes::OR)
186 {
187 ret = cur || ret;
188 }
189
190 if (first)
191 {
192 ret = cur;
193 first = false;
194 }
Andrew Jeffery666583b2021-12-01 15:50:12 +1030195 lastCommand =
196 probeType ? (*probeType)->second : probe_type_codes::FALSE_T;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030197 }
198
199 // probe passed, but empty device
200 if (ret && foundDevs.size() == 0)
201 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +0930202 // Use emplace back when clang implements
203 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0960r3.html
204 //
205 // https://en.cppreference.com/w/cpp/compiler_support/20
206 foundDevs.push_back(
207 {boost::container::flat_map<std::string, DBusValueVariant>{},
208 std::string{}});
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030209 }
210 if (matchOne && ret)
211 {
212 // match the last one
213 auto last = foundDevs.back();
214 foundDevs.clear();
215
216 foundDevs.emplace_back(std::move(last));
217 }
218 return ret;
219}
220
221PerformProbe::PerformProbe(
222 const std::vector<std::string>& probeCommand,
223 std::shared_ptr<PerformScan>& scanPtr,
Andrew Jefferyf5772d22022-04-12 22:05:30 +0930224 std::function<void(FoundDevices&, const MapperGetSubTreeResponse&)>&&
Andrew Jefferyac20bd92022-04-05 11:11:40 +0930225 callback) :
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030226 _probeCommand(probeCommand),
227 scan(scanPtr), _callback(std::move(callback))
228{}
229PerformProbe::~PerformProbe()
230{
Andrew Jefferyf5772d22022-04-12 22:05:30 +0930231 FoundDevices foundDevs;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030232 if (probe(_probeCommand, scan, foundDevs))
233 {
234 _callback(foundDevs, scan->dbusProbeObjects);
235 }
236}