blob: d7dcd8a83f13900999b52d9fb67497da083cc115 [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*/
Brad Bishope45d8c72022-05-25 15:12:53 -040016/// \file perform_probe.cpp
Christopher Meis26fbbd52025-03-26 14:55:06 +010017#include "perform_probe.hpp"
18
Brad Bishope45d8c72022-05-25 15:12:53 -040019#include "entity_manager.hpp"
Christopher Meis26fbbd52025-03-26 14:55:06 +010020#include "perform_scan.hpp"
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103021
22#include <boost/algorithm/string/replace.hpp>
Alexander Hansenc3db2c32024-08-20 15:01:38 +020023#include <phosphor-logging/lg2.hpp>
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103024
25#include <regex>
Andrew Jefferyea4ff022022-04-21 12:31:40 +093026#include <utility>
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103027
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103028// probes dbus interface dictionary for a key with a value that matches a regex
29// When an interface passes a probe, also save its D-Bus path with it.
Andrew Jefferyf5772d22022-04-12 22:05:30 +093030bool probeDbus(const std::string& interfaceName,
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103031 const std::map<std::string, nlohmann::json>& matches,
Christopher Meis26fbbd52025-03-26 14:55:06 +010032 scan::FoundDevices& devices,
33 const std::shared_ptr<scan::PerformScan>& scan, bool& foundProbe)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103034{
35 bool foundMatch = false;
36 foundProbe = false;
37
38 for (const auto& [path, interfaces] : scan->dbusProbeObjects)
39 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +093040 auto it = interfaces.find(interfaceName);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103041 if (it == interfaces.end())
42 {
43 continue;
44 }
45
46 foundProbe = true;
47
48 bool deviceMatches = true;
Andrew Jefferyf5772d22022-04-12 22:05:30 +093049 const DBusInterface& interface = it->second;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103050
51 for (const auto& [matchProp, matchJSON] : matches)
52 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +093053 auto deviceValue = interface.find(matchProp);
54 if (deviceValue != interface.end())
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103055 {
Patrick Williamsdf190612023-05-10 07:51:34 -050056 deviceMatches = deviceMatches &&
57 matchProbe(matchJSON, deviceValue->second);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103058 }
59 else
60 {
61 // Move on to the next DBus path
62 deviceMatches = false;
63 break;
64 }
65 }
66 if (deviceMatches)
67 {
Alexander Hansenc3db2c32024-08-20 15:01:38 +020068 lg2::debug("Found probe match on {PATH} {IFACE}", "PATH", path,
69 "IFACE", interfaceName);
Patrick Williamsd2b78612023-10-20 17:24:54 -050070 devices.emplace_back(interface, path);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103071 foundMatch = true;
72 }
73 }
74 return foundMatch;
75}
76
77// default probe entry point, iterates a list looking for specific types to
78// call specific probe functions
Christopher Meis26fbbd52025-03-26 14:55:06 +010079bool doProbe(const std::vector<std::string>& probeCommand,
80 const std::shared_ptr<scan::PerformScan>& scan,
81 scan::FoundDevices& foundDevs)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103082{
83 const static std::regex command(R"(\((.*)\))");
84 std::smatch match;
85 bool ret = false;
86 bool matchOne = false;
87 bool cur = true;
Christopher Meis26fbbd52025-03-26 14:55:06 +010088 probe::probe_type_codes lastCommand = probe::probe_type_codes::FALSE_T;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103089 bool first = true;
90
Ed Tanous3013fb42022-07-09 08:27:06 -070091 for (const auto& probe : probeCommand)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103092 {
Christopher Meis26fbbd52025-03-26 14:55:06 +010093 probe::FoundProbeTypeT probeType = probe::findProbeType(probe);
Andrew Jeffery666583b2021-12-01 15:50:12 +103094 if (probeType)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103095 {
Andrew Jeffery666583b2021-12-01 15:50:12 +103096 switch ((*probeType)->second)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103097 {
Christopher Meis26fbbd52025-03-26 14:55:06 +010098 case probe::probe_type_codes::FALSE_T:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103099 {
100 cur = false;
101 break;
102 }
Christopher Meis26fbbd52025-03-26 14:55:06 +0100103 case probe::probe_type_codes::TRUE_T:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030104 {
105 cur = true;
106 break;
107 }
Christopher Meis26fbbd52025-03-26 14:55:06 +0100108 case probe::probe_type_codes::MATCH_ONE:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030109 {
110 // set current value to last, this probe type shouldn't
111 // affect the outcome
112 cur = ret;
113 matchOne = true;
114 break;
115 }
Christopher Meis26fbbd52025-03-26 14:55:06 +0100116 /*case probe::probe_type_codes::AND:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030117 break;
Christopher Meis26fbbd52025-03-26 14:55:06 +0100118 case probe::probe_type_codes::OR:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030119 break;
120 // these are no-ops until the last command switch
121 */
Christopher Meis26fbbd52025-03-26 14:55:06 +0100122 case probe::probe_type_codes::FOUND:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030123 {
124 if (!std::regex_search(probe, match, command))
125 {
Patrick Williamsb7077432024-08-16 15:22:21 -0400126 std::cerr
127 << "found probe syntax error " << probe << "\n";
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030128 return false;
129 }
130 std::string commandStr = *(match.begin() + 1);
131 boost::replace_all(commandStr, "'", "");
132 cur = (std::find(scan->passedProbes.begin(),
Patrick Williamsb7077432024-08-16 15:22:21 -0400133 scan->passedProbes.end(), commandStr) !=
134 scan->passedProbes.end());
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030135 break;
136 }
137 default:
138 {
139 break;
140 }
141 }
142 }
143 // look on dbus for object
144 else
145 {
146 if (!std::regex_search(probe, match, command))
147 {
148 std::cerr << "dbus probe syntax error " << probe << "\n";
149 return false;
150 }
151 std::string commandStr = *(match.begin() + 1);
152 // convert single ticks and single slashes into legal json
153 boost::replace_all(commandStr, "'", "\"");
154 boost::replace_all(commandStr, R"(\)", R"(\\)");
Potin Lai0f3a4d92023-12-05 00:13:55 +0800155 auto json = nlohmann::json::parse(commandStr, nullptr, false, true);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030156 if (json.is_discarded())
157 {
158 std::cerr << "dbus command syntax error " << commandStr << "\n";
159 return false;
160 }
161 // we can match any (string, variant) property. (string, string)
162 // does a regex
163 std::map<std::string, nlohmann::json> dbusProbeMap =
164 json.get<std::map<std::string, nlohmann::json>>();
165 auto findStart = probe.find('(');
166 if (findStart == std::string::npos)
167 {
168 return false;
169 }
Andrew Jeffery666583b2021-12-01 15:50:12 +1030170 bool foundProbe = !!probeType;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030171 std::string probeInterface = probe.substr(0, findStart);
172 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
173 foundProbe);
174 }
175
176 // some functions like AND and OR only take affect after the
177 // fact
Christopher Meis26fbbd52025-03-26 14:55:06 +0100178 if (lastCommand == probe::probe_type_codes::AND)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030179 {
180 ret = cur && ret;
181 }
Christopher Meis26fbbd52025-03-26 14:55:06 +0100182 else if (lastCommand == probe::probe_type_codes::OR)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030183 {
184 ret = cur || ret;
185 }
186
187 if (first)
188 {
189 ret = cur;
190 first = false;
191 }
Patrick Williamsdf190612023-05-10 07:51:34 -0500192 lastCommand = probeType ? (*probeType)->second
Christopher Meis26fbbd52025-03-26 14:55:06 +0100193 : probe::probe_type_codes::FALSE_T;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030194 }
195
196 // probe passed, but empty device
Ed Tanous3013fb42022-07-09 08:27:06 -0700197 if (ret && foundDevs.empty())
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030198 {
Patrick Williamsd2b78612023-10-20 17:24:54 -0500199 foundDevs.emplace_back(
200 boost::container::flat_map<std::string, DBusValueVariant>{},
201 std::string{});
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030202 }
203 if (matchOne && ret)
204 {
205 // match the last one
206 auto last = foundDevs.back();
207 foundDevs.clear();
208
209 foundDevs.emplace_back(std::move(last));
210 }
211 return ret;
212}
213
Christopher Meis26fbbd52025-03-26 14:55:06 +0100214namespace probe
215{
216
Andrew Jefferyea4ff022022-04-21 12:31:40 +0930217PerformProbe::PerformProbe(nlohmann::json& recordRef,
218 const std::vector<std::string>& probeCommand,
219 std::string probeName,
Christopher Meis26fbbd52025-03-26 14:55:06 +0100220 std::shared_ptr<scan::PerformScan>& scanPtr) :
Patrick Williamsb7077432024-08-16 15:22:21 -0400221 recordRef(recordRef), _probeCommand(probeCommand),
222 probeName(std::move(probeName)), scan(scanPtr)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030223{}
Christopher Meis26fbbd52025-03-26 14:55:06 +0100224
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030225PerformProbe::~PerformProbe()
226{
Christopher Meis26fbbd52025-03-26 14:55:06 +0100227 scan::FoundDevices foundDevs;
228 if (doProbe(_probeCommand, scan, foundDevs))
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030229 {
Andrew Jefferyaf9b46b2022-04-21 12:34:43 +0930230 scan->updateSystemConfiguration(recordRef, probeName, foundDevs);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030231 }
232}
Christopher Meis26fbbd52025-03-26 14:55:06 +0100233
234FoundProbeTypeT findProbeType(const std::string& probe)
235{
236 const boost::container::flat_map<const char*, probe_type_codes, CmpStr>
237 probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
238 {"TRUE", probe_type_codes::TRUE_T},
239 {"AND", probe_type_codes::AND},
240 {"OR", probe_type_codes::OR},
241 {"FOUND", probe_type_codes::FOUND},
242 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
243
244 boost::container::flat_map<const char*, probe_type_codes,
245 CmpStr>::const_iterator probeType;
246 for (probeType = probeTypes.begin(); probeType != probeTypes.end();
247 ++probeType)
248 {
249 if (probe.find(probeType->first) != std::string::npos)
250 {
251 return probeType;
252 }
253 }
254
255 return std::nullopt;
256}
257
258} // namespace probe