xref: /openbmc/entity-manager/src/entity_manager/perform_probe.cpp (revision 60618a73e852a6671fce0e879e7eac532313ccdb)
1fc9e7fdaSChristopher Meis /*
2fc9e7fdaSChristopher Meis // Copyright (c) 2018 Intel Corporation
3fc9e7fdaSChristopher Meis //
4fc9e7fdaSChristopher Meis // Licensed under the Apache License, Version 2.0 (the "License");
5fc9e7fdaSChristopher Meis // you may not use this file except in compliance with the License.
6fc9e7fdaSChristopher Meis // You may obtain a copy of the License at
7fc9e7fdaSChristopher Meis //
8fc9e7fdaSChristopher Meis //      http://www.apache.org/licenses/LICENSE-2.0
9fc9e7fdaSChristopher Meis //
10fc9e7fdaSChristopher Meis // Unless required by applicable law or agreed to in writing, software
11fc9e7fdaSChristopher Meis // distributed under the License is distributed on an "AS IS" BASIS,
12fc9e7fdaSChristopher Meis // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fc9e7fdaSChristopher Meis // See the License for the specific language governing permissions and
14fc9e7fdaSChristopher Meis // limitations under the License.
15fc9e7fdaSChristopher Meis */
16fc9e7fdaSChristopher Meis /// \file perform_probe.cpp
17fc9e7fdaSChristopher Meis #include "perform_probe.hpp"
18fc9e7fdaSChristopher Meis 
19fc9e7fdaSChristopher Meis #include "perform_scan.hpp"
20fc9e7fdaSChristopher Meis 
21fc9e7fdaSChristopher Meis #include <boost/algorithm/string/replace.hpp>
22fc9e7fdaSChristopher Meis #include <phosphor-logging/lg2.hpp>
23fc9e7fdaSChristopher Meis 
2459ef1e72SChristopher Meis #include <iostream>
25fc9e7fdaSChristopher Meis #include <regex>
26fc9e7fdaSChristopher Meis #include <utility>
27fc9e7fdaSChristopher Meis 
28fc9e7fdaSChristopher Meis // probes dbus interface dictionary for a key with a value that matches a regex
29fc9e7fdaSChristopher Meis // When an interface passes a probe, also save its D-Bus path with it.
probeDbus(const std::string & interfaceName,const std::map<std::string,nlohmann::json> & matches,scan::FoundDevices & devices,const std::shared_ptr<scan::PerformScan> & scan,bool & foundProbe)30fc9e7fdaSChristopher Meis bool probeDbus(const std::string& interfaceName,
31fc9e7fdaSChristopher Meis                const std::map<std::string, nlohmann::json>& matches,
32fc9e7fdaSChristopher Meis                scan::FoundDevices& devices,
33fc9e7fdaSChristopher Meis                const std::shared_ptr<scan::PerformScan>& scan, bool& foundProbe)
34fc9e7fdaSChristopher Meis {
35fc9e7fdaSChristopher Meis     bool foundMatch = false;
36fc9e7fdaSChristopher Meis     foundProbe = false;
37fc9e7fdaSChristopher Meis 
38fc9e7fdaSChristopher Meis     for (const auto& [path, interfaces] : scan->dbusProbeObjects)
39fc9e7fdaSChristopher Meis     {
40fc9e7fdaSChristopher Meis         auto it = interfaces.find(interfaceName);
41fc9e7fdaSChristopher Meis         if (it == interfaces.end())
42fc9e7fdaSChristopher Meis         {
43fc9e7fdaSChristopher Meis             continue;
44fc9e7fdaSChristopher Meis         }
45fc9e7fdaSChristopher Meis 
46fc9e7fdaSChristopher Meis         foundProbe = true;
47fc9e7fdaSChristopher Meis 
48fc9e7fdaSChristopher Meis         bool deviceMatches = true;
49fc9e7fdaSChristopher Meis         const DBusInterface& interface = it->second;
50fc9e7fdaSChristopher Meis 
51fc9e7fdaSChristopher Meis         for (const auto& [matchProp, matchJSON] : matches)
52fc9e7fdaSChristopher Meis         {
53fc9e7fdaSChristopher Meis             auto deviceValue = interface.find(matchProp);
54fc9e7fdaSChristopher Meis             if (deviceValue != interface.end())
55fc9e7fdaSChristopher Meis             {
56fc9e7fdaSChristopher Meis                 deviceMatches = deviceMatches &&
57fc9e7fdaSChristopher Meis                                 matchProbe(matchJSON, deviceValue->second);
58fc9e7fdaSChristopher Meis             }
59fc9e7fdaSChristopher Meis             else
60fc9e7fdaSChristopher Meis             {
61fc9e7fdaSChristopher Meis                 // Move on to the next DBus path
62fc9e7fdaSChristopher Meis                 deviceMatches = false;
63fc9e7fdaSChristopher Meis                 break;
64fc9e7fdaSChristopher Meis             }
65fc9e7fdaSChristopher Meis         }
66fc9e7fdaSChristopher Meis         if (deviceMatches)
67fc9e7fdaSChristopher Meis         {
68fc9e7fdaSChristopher Meis             lg2::debug("Found probe match on {PATH} {IFACE}", "PATH", path,
69fc9e7fdaSChristopher Meis                        "IFACE", interfaceName);
70fc9e7fdaSChristopher Meis             devices.emplace_back(interface, path);
71fc9e7fdaSChristopher Meis             foundMatch = true;
72fc9e7fdaSChristopher Meis         }
73fc9e7fdaSChristopher Meis     }
74fc9e7fdaSChristopher Meis     return foundMatch;
75fc9e7fdaSChristopher Meis }
76fc9e7fdaSChristopher Meis 
77fc9e7fdaSChristopher Meis // default probe entry point, iterates a list looking for specific types to
78fc9e7fdaSChristopher Meis // call specific probe functions
doProbe(const std::vector<std::string> & probeCommand,const std::shared_ptr<scan::PerformScan> & scan,scan::FoundDevices & foundDevs)79fc9e7fdaSChristopher Meis bool doProbe(const std::vector<std::string>& probeCommand,
80fc9e7fdaSChristopher Meis              const std::shared_ptr<scan::PerformScan>& scan,
81fc9e7fdaSChristopher Meis              scan::FoundDevices& foundDevs)
82fc9e7fdaSChristopher Meis {
83fc9e7fdaSChristopher Meis     const static std::regex command(R"(\((.*)\))");
84fc9e7fdaSChristopher Meis     std::smatch match;
85fc9e7fdaSChristopher Meis     bool ret = false;
86fc9e7fdaSChristopher Meis     bool matchOne = false;
87fc9e7fdaSChristopher Meis     bool cur = true;
88fc9e7fdaSChristopher Meis     probe::probe_type_codes lastCommand = probe::probe_type_codes::FALSE_T;
89fc9e7fdaSChristopher Meis     bool first = true;
90fc9e7fdaSChristopher Meis 
91fc9e7fdaSChristopher Meis     for (const auto& probe : probeCommand)
92fc9e7fdaSChristopher Meis     {
93fc9e7fdaSChristopher Meis         probe::FoundProbeTypeT probeType = probe::findProbeType(probe);
94fc9e7fdaSChristopher Meis         if (probeType)
95fc9e7fdaSChristopher Meis         {
96*60618a73SAmithash Prasad             switch (*probeType)
97fc9e7fdaSChristopher Meis             {
98fc9e7fdaSChristopher Meis                 case probe::probe_type_codes::FALSE_T:
99fc9e7fdaSChristopher Meis                 {
100fc9e7fdaSChristopher Meis                     cur = false;
101fc9e7fdaSChristopher Meis                     break;
102fc9e7fdaSChristopher Meis                 }
103fc9e7fdaSChristopher Meis                 case probe::probe_type_codes::TRUE_T:
104fc9e7fdaSChristopher Meis                 {
105fc9e7fdaSChristopher Meis                     cur = true;
106fc9e7fdaSChristopher Meis                     break;
107fc9e7fdaSChristopher Meis                 }
108fc9e7fdaSChristopher Meis                 case probe::probe_type_codes::MATCH_ONE:
109fc9e7fdaSChristopher Meis                 {
110fc9e7fdaSChristopher Meis                     // set current value to last, this probe type shouldn't
111fc9e7fdaSChristopher Meis                     // affect the outcome
112fc9e7fdaSChristopher Meis                     cur = ret;
113fc9e7fdaSChristopher Meis                     matchOne = true;
114fc9e7fdaSChristopher Meis                     break;
115fc9e7fdaSChristopher Meis                 }
116fc9e7fdaSChristopher Meis                 /*case probe::probe_type_codes::AND:
117fc9e7fdaSChristopher Meis                   break;
118fc9e7fdaSChristopher Meis                 case probe::probe_type_codes::OR:
119fc9e7fdaSChristopher Meis                   break;
120fc9e7fdaSChristopher Meis                   // these are no-ops until the last command switch
121fc9e7fdaSChristopher Meis                   */
122fc9e7fdaSChristopher Meis                 case probe::probe_type_codes::FOUND:
123fc9e7fdaSChristopher Meis                 {
124fc9e7fdaSChristopher Meis                     if (!std::regex_search(probe, match, command))
125fc9e7fdaSChristopher Meis                     {
126fc9e7fdaSChristopher Meis                         std::cerr
127fc9e7fdaSChristopher Meis                             << "found probe syntax error " << probe << "\n";
128fc9e7fdaSChristopher Meis                         return false;
129fc9e7fdaSChristopher Meis                     }
130fc9e7fdaSChristopher Meis                     std::string commandStr = *(match.begin() + 1);
131fc9e7fdaSChristopher Meis                     boost::replace_all(commandStr, "'", "");
132fc9e7fdaSChristopher Meis                     cur = (std::find(scan->passedProbes.begin(),
133fc9e7fdaSChristopher Meis                                      scan->passedProbes.end(), commandStr) !=
134fc9e7fdaSChristopher Meis                            scan->passedProbes.end());
135fc9e7fdaSChristopher Meis                     break;
136fc9e7fdaSChristopher Meis                 }
137fc9e7fdaSChristopher Meis                 default:
138fc9e7fdaSChristopher Meis                 {
139fc9e7fdaSChristopher Meis                     break;
140fc9e7fdaSChristopher Meis                 }
141fc9e7fdaSChristopher Meis             }
142fc9e7fdaSChristopher Meis         }
143fc9e7fdaSChristopher Meis         // look on dbus for object
144fc9e7fdaSChristopher Meis         else
145fc9e7fdaSChristopher Meis         {
146fc9e7fdaSChristopher Meis             if (!std::regex_search(probe, match, command))
147fc9e7fdaSChristopher Meis             {
148fc9e7fdaSChristopher Meis                 std::cerr << "dbus probe syntax error " << probe << "\n";
149fc9e7fdaSChristopher Meis                 return false;
150fc9e7fdaSChristopher Meis             }
151fc9e7fdaSChristopher Meis             std::string commandStr = *(match.begin() + 1);
152fc9e7fdaSChristopher Meis             // convert single ticks and single slashes into legal json
153fc9e7fdaSChristopher Meis             boost::replace_all(commandStr, "'", "\"");
154fc9e7fdaSChristopher Meis             boost::replace_all(commandStr, R"(\)", R"(\\)");
155fc9e7fdaSChristopher Meis             auto json = nlohmann::json::parse(commandStr, nullptr, false, true);
156fc9e7fdaSChristopher Meis             if (json.is_discarded())
157fc9e7fdaSChristopher Meis             {
158fc9e7fdaSChristopher Meis                 std::cerr << "dbus command syntax error " << commandStr << "\n";
159fc9e7fdaSChristopher Meis                 return false;
160fc9e7fdaSChristopher Meis             }
161fc9e7fdaSChristopher Meis             // we can match any (string, variant) property. (string, string)
162fc9e7fdaSChristopher Meis             // does a regex
163fc9e7fdaSChristopher Meis             std::map<std::string, nlohmann::json> dbusProbeMap =
164fc9e7fdaSChristopher Meis                 json.get<std::map<std::string, nlohmann::json>>();
165fc9e7fdaSChristopher Meis             auto findStart = probe.find('(');
166fc9e7fdaSChristopher Meis             if (findStart == std::string::npos)
167fc9e7fdaSChristopher Meis             {
168fc9e7fdaSChristopher Meis                 return false;
169fc9e7fdaSChristopher Meis             }
170fc9e7fdaSChristopher Meis             bool foundProbe = !!probeType;
171fc9e7fdaSChristopher Meis             std::string probeInterface = probe.substr(0, findStart);
172fc9e7fdaSChristopher Meis             cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
173fc9e7fdaSChristopher Meis                             foundProbe);
174fc9e7fdaSChristopher Meis         }
175fc9e7fdaSChristopher Meis 
176fc9e7fdaSChristopher Meis         // some functions like AND and OR only take affect after the
177fc9e7fdaSChristopher Meis         // fact
178fc9e7fdaSChristopher Meis         if (lastCommand == probe::probe_type_codes::AND)
179fc9e7fdaSChristopher Meis         {
180fc9e7fdaSChristopher Meis             ret = cur && ret;
181fc9e7fdaSChristopher Meis         }
182fc9e7fdaSChristopher Meis         else if (lastCommand == probe::probe_type_codes::OR)
183fc9e7fdaSChristopher Meis         {
184fc9e7fdaSChristopher Meis             ret = cur || ret;
185fc9e7fdaSChristopher Meis         }
186fc9e7fdaSChristopher Meis 
187fc9e7fdaSChristopher Meis         if (first)
188fc9e7fdaSChristopher Meis         {
189fc9e7fdaSChristopher Meis             ret = cur;
190fc9e7fdaSChristopher Meis             first = false;
191fc9e7fdaSChristopher Meis         }
192*60618a73SAmithash Prasad         lastCommand = probeType.value_or(probe::probe_type_codes::FALSE_T);
193fc9e7fdaSChristopher Meis     }
194fc9e7fdaSChristopher Meis 
195fc9e7fdaSChristopher Meis     // probe passed, but empty device
196fc9e7fdaSChristopher Meis     if (ret && foundDevs.empty())
197fc9e7fdaSChristopher Meis     {
198fc9e7fdaSChristopher Meis         foundDevs.emplace_back(
199fc9e7fdaSChristopher Meis             boost::container::flat_map<std::string, DBusValueVariant>{},
200fc9e7fdaSChristopher Meis             std::string{});
201fc9e7fdaSChristopher Meis     }
202fc9e7fdaSChristopher Meis     if (matchOne && ret)
203fc9e7fdaSChristopher Meis     {
204fc9e7fdaSChristopher Meis         // match the last one
205fc9e7fdaSChristopher Meis         auto last = foundDevs.back();
206fc9e7fdaSChristopher Meis         foundDevs.clear();
207fc9e7fdaSChristopher Meis 
208fc9e7fdaSChristopher Meis         foundDevs.emplace_back(std::move(last));
209fc9e7fdaSChristopher Meis     }
210fc9e7fdaSChristopher Meis     return ret;
211fc9e7fdaSChristopher Meis }
212fc9e7fdaSChristopher Meis 
213fc9e7fdaSChristopher Meis namespace probe
214fc9e7fdaSChristopher Meis {
215fc9e7fdaSChristopher Meis 
PerformProbe(nlohmann::json & recordRef,const std::vector<std::string> & probeCommand,std::string probeName,std::shared_ptr<scan::PerformScan> & scanPtr)216fc9e7fdaSChristopher Meis PerformProbe::PerformProbe(nlohmann::json& recordRef,
217fc9e7fdaSChristopher Meis                            const std::vector<std::string>& probeCommand,
218fc9e7fdaSChristopher Meis                            std::string probeName,
219fc9e7fdaSChristopher Meis                            std::shared_ptr<scan::PerformScan>& scanPtr) :
220fc9e7fdaSChristopher Meis     recordRef(recordRef), _probeCommand(probeCommand),
221fc9e7fdaSChristopher Meis     probeName(std::move(probeName)), scan(scanPtr)
222fc9e7fdaSChristopher Meis {}
223fc9e7fdaSChristopher Meis 
~PerformProbe()224fc9e7fdaSChristopher Meis PerformProbe::~PerformProbe()
225fc9e7fdaSChristopher Meis {
226fc9e7fdaSChristopher Meis     scan::FoundDevices foundDevs;
227fc9e7fdaSChristopher Meis     if (doProbe(_probeCommand, scan, foundDevs))
228fc9e7fdaSChristopher Meis     {
229fc9e7fdaSChristopher Meis         scan->updateSystemConfiguration(recordRef, probeName, foundDevs);
230fc9e7fdaSChristopher Meis     }
231fc9e7fdaSChristopher Meis }
232fc9e7fdaSChristopher Meis 
findProbeType(const std::string & probe)233fc9e7fdaSChristopher Meis FoundProbeTypeT findProbeType(const std::string& probe)
234fc9e7fdaSChristopher Meis {
2352ae2b818SPeter Yin     static const boost::container::flat_map<const char*, probe_type_codes,
2362ae2b818SPeter Yin                                             CmpStr>
237fc9e7fdaSChristopher Meis         probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
238fc9e7fdaSChristopher Meis                     {"TRUE", probe_type_codes::TRUE_T},
239fc9e7fdaSChristopher Meis                     {"AND", probe_type_codes::AND},
240fc9e7fdaSChristopher Meis                     {"OR", probe_type_codes::OR},
241fc9e7fdaSChristopher Meis                     {"FOUND", probe_type_codes::FOUND},
242fc9e7fdaSChristopher Meis                     {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
243fc9e7fdaSChristopher Meis 
244fc9e7fdaSChristopher Meis     boost::container::flat_map<const char*, probe_type_codes,
245fc9e7fdaSChristopher Meis                                CmpStr>::const_iterator probeType;
246fc9e7fdaSChristopher Meis     for (probeType = probeTypes.begin(); probeType != probeTypes.end();
247fc9e7fdaSChristopher Meis          ++probeType)
248fc9e7fdaSChristopher Meis     {
249fc9e7fdaSChristopher Meis         if (probe.find(probeType->first) != std::string::npos)
250fc9e7fdaSChristopher Meis         {
251*60618a73SAmithash Prasad             return probeType->second;
252fc9e7fdaSChristopher Meis         }
253fc9e7fdaSChristopher Meis     }
254fc9e7fdaSChristopher Meis 
255fc9e7fdaSChristopher Meis     return std::nullopt;
256fc9e7fdaSChristopher Meis }
257fc9e7fdaSChristopher Meis 
258fc9e7fdaSChristopher Meis } // namespace probe
259