blob: 7c5a2b211d5abe10890303b47e5ce804aa6d54a5 [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>
Andrew Jefferyea4ff022022-04-21 12:31:40 +093022#include <utility>
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103023
24constexpr const bool debug = false;
25
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103026// probes dbus interface dictionary for a key with a value that matches a regex
27// When an interface passes a probe, also save its D-Bus path with it.
Andrew Jefferyf5772d22022-04-12 22:05:30 +093028bool probeDbus(const std::string& interfaceName,
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103029 const std::map<std::string, nlohmann::json>& matches,
Andrew Jefferyf5772d22022-04-12 22:05:30 +093030 FoundDevices& devices, const std::shared_ptr<PerformScan>& scan,
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103031 bool& foundProbe)
32{
33 bool foundMatch = false;
34 foundProbe = false;
35
36 for (const auto& [path, interfaces] : scan->dbusProbeObjects)
37 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +093038 auto it = interfaces.find(interfaceName);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103039 if (it == interfaces.end())
40 {
41 continue;
42 }
43
44 foundProbe = true;
45
46 bool deviceMatches = true;
Andrew Jefferyf5772d22022-04-12 22:05:30 +093047 const DBusInterface& interface = it->second;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103048
49 for (const auto& [matchProp, matchJSON] : matches)
50 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +093051 auto deviceValue = interface.find(matchProp);
52 if (deviceValue != interface.end())
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103053 {
54 deviceMatches =
55 deviceMatches && matchProbe(matchJSON, deviceValue->second);
56 }
57 else
58 {
59 // Move on to the next DBus path
60 deviceMatches = false;
61 break;
62 }
63 }
64 if (deviceMatches)
65 {
66 if constexpr (debug)
67 {
68 std::cerr << "probeDBus: Found probe match on " << path << " "
Andrew Jefferyf5772d22022-04-12 22:05:30 +093069 << interfaceName << "\n";
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103070 }
Andrew Jefferyf5772d22022-04-12 22:05:30 +093071 // Use emplace back when clang implements
72 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0960r3.html
73 //
74 // https://en.cppreference.com/w/cpp/compiler_support/20
75 devices.push_back({interface, path});
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103076 foundMatch = true;
77 }
78 }
79 return foundMatch;
80}
81
82// default probe entry point, iterates a list looking for specific types to
83// call specific probe functions
84bool probe(const std::vector<std::string>& probeCommand,
Andrew Jefferyf5772d22022-04-12 22:05:30 +093085 const std::shared_ptr<PerformScan>& scan, FoundDevices& foundDevs)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103086{
87 const static std::regex command(R"(\((.*)\))");
88 std::smatch match;
89 bool ret = false;
90 bool matchOne = false;
91 bool cur = true;
92 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
93 bool first = true;
94
95 for (auto& probe : probeCommand)
96 {
Andrew Jeffery666583b2021-12-01 15:50:12 +103097 FoundProbeTypeT probeType = findProbeType(probe);
98 if (probeType)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103099 {
Andrew Jeffery666583b2021-12-01 15:50:12 +1030100 switch ((*probeType)->second)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030101 {
102 case probe_type_codes::FALSE_T:
103 {
104 cur = false;
105 break;
106 }
107 case probe_type_codes::TRUE_T:
108 {
109 cur = true;
110 break;
111 }
112 case probe_type_codes::MATCH_ONE:
113 {
114 // set current value to last, this probe type shouldn't
115 // affect the outcome
116 cur = ret;
117 matchOne = true;
118 break;
119 }
120 /*case probe_type_codes::AND:
121 break;
122 case probe_type_codes::OR:
123 break;
124 // these are no-ops until the last command switch
125 */
126 case probe_type_codes::FOUND:
127 {
128 if (!std::regex_search(probe, match, command))
129 {
130 std::cerr << "found probe syntax error " << probe
131 << "\n";
132 return false;
133 }
134 std::string commandStr = *(match.begin() + 1);
135 boost::replace_all(commandStr, "'", "");
136 cur = (std::find(scan->passedProbes.begin(),
137 scan->passedProbes.end(),
138 commandStr) != scan->passedProbes.end());
139 break;
140 }
141 default:
142 {
143 break;
144 }
145 }
146 }
147 // look on dbus for object
148 else
149 {
150 if (!std::regex_search(probe, match, command))
151 {
152 std::cerr << "dbus probe syntax error " << probe << "\n";
153 return false;
154 }
155 std::string commandStr = *(match.begin() + 1);
156 // convert single ticks and single slashes into legal json
157 boost::replace_all(commandStr, "'", "\"");
158 boost::replace_all(commandStr, R"(\)", R"(\\)");
159 auto json = nlohmann::json::parse(commandStr, nullptr, false);
160 if (json.is_discarded())
161 {
162 std::cerr << "dbus command syntax error " << commandStr << "\n";
163 return false;
164 }
165 // we can match any (string, variant) property. (string, string)
166 // does a regex
167 std::map<std::string, nlohmann::json> dbusProbeMap =
168 json.get<std::map<std::string, nlohmann::json>>();
169 auto findStart = probe.find('(');
170 if (findStart == std::string::npos)
171 {
172 return false;
173 }
Andrew Jeffery666583b2021-12-01 15:50:12 +1030174 bool foundProbe = !!probeType;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030175 std::string probeInterface = probe.substr(0, findStart);
176 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
177 foundProbe);
178 }
179
180 // some functions like AND and OR only take affect after the
181 // fact
182 if (lastCommand == probe_type_codes::AND)
183 {
184 ret = cur && ret;
185 }
186 else if (lastCommand == probe_type_codes::OR)
187 {
188 ret = cur || ret;
189 }
190
191 if (first)
192 {
193 ret = cur;
194 first = false;
195 }
Andrew Jeffery666583b2021-12-01 15:50:12 +1030196 lastCommand =
197 probeType ? (*probeType)->second : probe_type_codes::FALSE_T;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030198 }
199
200 // probe passed, but empty device
201 if (ret && foundDevs.size() == 0)
202 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +0930203 // Use emplace back when clang implements
204 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0960r3.html
205 //
206 // https://en.cppreference.com/w/cpp/compiler_support/20
207 foundDevs.push_back(
208 {boost::container::flat_map<std::string, DBusValueVariant>{},
209 std::string{}});
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030210 }
211 if (matchOne && ret)
212 {
213 // match the last one
214 auto last = foundDevs.back();
215 foundDevs.clear();
216
217 foundDevs.emplace_back(std::move(last));
218 }
219 return ret;
220}
221
Andrew Jefferyea4ff022022-04-21 12:31:40 +0930222PerformProbe::PerformProbe(nlohmann::json& recordRef,
223 const std::vector<std::string>& probeCommand,
224 std::string probeName,
225 std::shared_ptr<PerformScan>& scanPtr) :
226 recordRef(recordRef),
227 _probeCommand(probeCommand), probeName(std::move(probeName)), scan(scanPtr)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030228{}
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 {
Andrew Jefferyea4ff022022-04-21 12:31:40 +0930234 scan->updateSystemConfiguration(recordRef, probeName, foundDevs,
235 scan->dbusProbeObjects);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030236 }
237}