blob: e5b57eb0033e2c7a0999f6bfec1bfc49f83f112f [file] [log] [blame]
Alexander Hansen4e1142d2025-07-25 17:07:27 +02001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
3
Christopher Meis26fbbd52025-03-26 14:55:06 +01004#include "perform_probe.hpp"
5
Christopher Meis26fbbd52025-03-26 14:55:06 +01006#include "perform_scan.hpp"
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +10307
Alexander Hansenc3db2c32024-08-20 15:01:38 +02008#include <phosphor-logging/lg2.hpp>
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +10309
10#include <regex>
Andrew Jefferyea4ff022022-04-21 12:31:40 +093011#include <utility>
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103012
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103013// probes dbus interface dictionary for a key with a value that matches a regex
14// When an interface passes a probe, also save its D-Bus path with it.
Andrew Jefferyf5772d22022-04-12 22:05:30 +093015bool probeDbus(const std::string& interfaceName,
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103016 const std::map<std::string, nlohmann::json>& matches,
Christopher Meis26fbbd52025-03-26 14:55:06 +010017 scan::FoundDevices& devices,
18 const std::shared_ptr<scan::PerformScan>& scan, bool& foundProbe)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103019{
20 bool foundMatch = false;
21 foundProbe = false;
22
23 for (const auto& [path, interfaces] : scan->dbusProbeObjects)
24 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +093025 auto it = interfaces.find(interfaceName);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103026 if (it == interfaces.end())
27 {
28 continue;
29 }
30
31 foundProbe = true;
32
33 bool deviceMatches = true;
Andrew Jefferyf5772d22022-04-12 22:05:30 +093034 const DBusInterface& interface = it->second;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103035
36 for (const auto& [matchProp, matchJSON] : matches)
37 {
Andrew Jefferyf5772d22022-04-12 22:05:30 +093038 auto deviceValue = interface.find(matchProp);
39 if (deviceValue != interface.end())
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103040 {
Patrick Williamsdf190612023-05-10 07:51:34 -050041 deviceMatches = deviceMatches &&
42 matchProbe(matchJSON, deviceValue->second);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103043 }
44 else
45 {
46 // Move on to the next DBus path
47 deviceMatches = false;
48 break;
49 }
50 }
51 if (deviceMatches)
52 {
Alexander Hansenc3db2c32024-08-20 15:01:38 +020053 lg2::debug("Found probe match on {PATH} {IFACE}", "PATH", path,
54 "IFACE", interfaceName);
Patrick Williamsd2b78612023-10-20 17:24:54 -050055 devices.emplace_back(interface, path);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103056 foundMatch = true;
57 }
58 }
59 return foundMatch;
60}
61
62// default probe entry point, iterates a list looking for specific types to
63// call specific probe functions
Christopher Meis26fbbd52025-03-26 14:55:06 +010064bool doProbe(const std::vector<std::string>& probeCommand,
65 const std::shared_ptr<scan::PerformScan>& scan,
66 scan::FoundDevices& foundDevs)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103067{
68 const static std::regex command(R"(\((.*)\))");
69 std::smatch match;
70 bool ret = false;
71 bool matchOne = false;
72 bool cur = true;
Christopher Meis26fbbd52025-03-26 14:55:06 +010073 probe::probe_type_codes lastCommand = probe::probe_type_codes::FALSE_T;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103074 bool first = true;
75
Ed Tanous3013fb42022-07-09 08:27:06 -070076 for (const auto& probe : probeCommand)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103077 {
Christopher Meis26fbbd52025-03-26 14:55:06 +010078 probe::FoundProbeTypeT probeType = probe::findProbeType(probe);
Andrew Jeffery666583b2021-12-01 15:50:12 +103079 if (probeType)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103080 {
Amithash Prasad60618a72025-05-13 11:03:02 -070081 switch (*probeType)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103082 {
Christopher Meis26fbbd52025-03-26 14:55:06 +010083 case probe::probe_type_codes::FALSE_T:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103084 {
85 cur = false;
86 break;
87 }
Christopher Meis26fbbd52025-03-26 14:55:06 +010088 case probe::probe_type_codes::TRUE_T:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103089 {
90 cur = true;
91 break;
92 }
Christopher Meis26fbbd52025-03-26 14:55:06 +010093 case probe::probe_type_codes::MATCH_ONE:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +103094 {
95 // set current value to last, this probe type shouldn't
96 // affect the outcome
97 cur = ret;
98 matchOne = true;
99 break;
100 }
Christopher Meis26fbbd52025-03-26 14:55:06 +0100101 /*case probe::probe_type_codes::AND:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030102 break;
Christopher Meis26fbbd52025-03-26 14:55:06 +0100103 case probe::probe_type_codes::OR:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030104 break;
105 // these are no-ops until the last command switch
106 */
Christopher Meis26fbbd52025-03-26 14:55:06 +0100107 case probe::probe_type_codes::FOUND:
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030108 {
109 if (!std::regex_search(probe, match, command))
110 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200111 lg2::error("found probe syntax error {JSON}", "JSON",
112 probe);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030113 return false;
114 }
115 std::string commandStr = *(match.begin() + 1);
George Liu5a61ec82025-08-25 11:16:44 +0800116 replaceAll(commandStr, "'", "");
117
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030118 cur = (std::find(scan->passedProbes.begin(),
Patrick Williamsb7077432024-08-16 15:22:21 -0400119 scan->passedProbes.end(), commandStr) !=
120 scan->passedProbes.end());
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030121 break;
122 }
123 default:
124 {
125 break;
126 }
127 }
128 }
129 // look on dbus for object
130 else
131 {
132 if (!std::regex_search(probe, match, command))
133 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200134 lg2::error("dbus probe syntax error {JSON}", "JSON", probe);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030135 return false;
136 }
137 std::string commandStr = *(match.begin() + 1);
138 // convert single ticks and single slashes into legal json
George Liu5a61ec82025-08-25 11:16:44 +0800139 std::ranges::replace(commandStr, '\'', '"');
140
141 replaceAll(commandStr, R"(\)", R"(\\)");
Potin Lai0f3a4d92023-12-05 00:13:55 +0800142 auto json = nlohmann::json::parse(commandStr, nullptr, false, true);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030143 if (json.is_discarded())
144 {
Alexander Hansen8feb0452025-09-15 14:29:20 +0200145 lg2::error("dbus command syntax error {STR}", "STR",
146 commandStr);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030147 return false;
148 }
149 // we can match any (string, variant) property. (string, string)
150 // does a regex
151 std::map<std::string, nlohmann::json> dbusProbeMap =
152 json.get<std::map<std::string, nlohmann::json>>();
153 auto findStart = probe.find('(');
154 if (findStart == std::string::npos)
155 {
156 return false;
157 }
Andrew Jeffery666583b2021-12-01 15:50:12 +1030158 bool foundProbe = !!probeType;
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030159 std::string probeInterface = probe.substr(0, findStart);
160 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
161 foundProbe);
162 }
163
164 // some functions like AND and OR only take affect after the
165 // fact
Christopher Meis26fbbd52025-03-26 14:55:06 +0100166 if (lastCommand == probe::probe_type_codes::AND)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030167 {
168 ret = cur && ret;
169 }
Christopher Meis26fbbd52025-03-26 14:55:06 +0100170 else if (lastCommand == probe::probe_type_codes::OR)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030171 {
172 ret = cur || ret;
173 }
174
175 if (first)
176 {
177 ret = cur;
178 first = false;
179 }
Amithash Prasad60618a72025-05-13 11:03:02 -0700180 lastCommand = probeType.value_or(probe::probe_type_codes::FALSE_T);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030181 }
182
183 // probe passed, but empty device
Ed Tanous3013fb42022-07-09 08:27:06 -0700184 if (ret && foundDevs.empty())
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030185 {
Patrick Williamsd2b78612023-10-20 17:24:54 -0500186 foundDevs.emplace_back(
187 boost::container::flat_map<std::string, DBusValueVariant>{},
188 std::string{});
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030189 }
190 if (matchOne && ret)
191 {
192 // match the last one
193 auto last = foundDevs.back();
194 foundDevs.clear();
195
196 foundDevs.emplace_back(std::move(last));
197 }
198 return ret;
199}
200
Christopher Meis26fbbd52025-03-26 14:55:06 +0100201namespace probe
202{
203
Andrew Jefferyea4ff022022-04-21 12:31:40 +0930204PerformProbe::PerformProbe(nlohmann::json& recordRef,
205 const std::vector<std::string>& probeCommand,
206 std::string probeName,
Christopher Meis26fbbd52025-03-26 14:55:06 +0100207 std::shared_ptr<scan::PerformScan>& scanPtr) :
Patrick Williamsb7077432024-08-16 15:22:21 -0400208 recordRef(recordRef), _probeCommand(probeCommand),
209 probeName(std::move(probeName)), scan(scanPtr)
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030210{}
Christopher Meis26fbbd52025-03-26 14:55:06 +0100211
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030212PerformProbe::~PerformProbe()
213{
Christopher Meis26fbbd52025-03-26 14:55:06 +0100214 scan::FoundDevices foundDevs;
215 if (doProbe(_probeCommand, scan, foundDevs))
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030216 {
Andrew Jefferyaf9b46b2022-04-21 12:34:43 +0930217 scan->updateSystemConfiguration(recordRef, probeName, foundDevs);
Andrew Jefferyf07c5ed2021-12-01 14:22:04 +1030218 }
219}
Christopher Meis26fbbd52025-03-26 14:55:06 +0100220
221FoundProbeTypeT findProbeType(const std::string& probe)
222{
Peter Yin2ae2b812025-05-08 15:59:39 +0800223 static const boost::container::flat_map<const char*, probe_type_codes,
224 CmpStr>
Christopher Meis26fbbd52025-03-26 14:55:06 +0100225 probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
226 {"TRUE", probe_type_codes::TRUE_T},
227 {"AND", probe_type_codes::AND},
228 {"OR", probe_type_codes::OR},
229 {"FOUND", probe_type_codes::FOUND},
230 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
231
232 boost::container::flat_map<const char*, probe_type_codes,
233 CmpStr>::const_iterator probeType;
234 for (probeType = probeTypes.begin(); probeType != probeTypes.end();
235 ++probeType)
236 {
237 if (probe.find(probeType->first) != std::string::npos)
238 {
Amithash Prasad60618a72025-05-13 11:03:02 -0700239 return probeType->second;
Christopher Meis26fbbd52025-03-26 14:55:06 +0100240 }
241 }
242
243 return std::nullopt;
244}
245
246} // namespace probe