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_scan.cpp
17fc9e7fdaSChristopher Meis #include "perform_scan.hpp"
18fc9e7fdaSChristopher Meis
19fc9e7fdaSChristopher Meis #include "perform_probe.hpp"
2059ef1e72SChristopher Meis #include "utils.hpp"
21fc9e7fdaSChristopher Meis
22fc9e7fdaSChristopher Meis #include <boost/algorithm/string/predicate.hpp>
23fc9e7fdaSChristopher Meis #include <boost/asio/steady_timer.hpp>
24fc9e7fdaSChristopher Meis #include <boost/container/flat_map.hpp>
25fc9e7fdaSChristopher Meis #include <boost/container/flat_set.hpp>
26fc9e7fdaSChristopher Meis #include <phosphor-logging/lg2.hpp>
27fc9e7fdaSChristopher Meis
28fc9e7fdaSChristopher Meis #include <charconv>
2959ef1e72SChristopher Meis #include <iostream>
30fc9e7fdaSChristopher Meis
31fc9e7fdaSChristopher Meis using GetSubTreeType = std::vector<
32fc9e7fdaSChristopher Meis std::pair<std::string,
33fc9e7fdaSChristopher Meis std::vector<std::pair<std::string, std::vector<std::string>>>>>;
34fc9e7fdaSChristopher Meis
35fc9e7fdaSChristopher Meis constexpr const int32_t maxMapperDepth = 0;
36fc9e7fdaSChristopher Meis
37fc9e7fdaSChristopher Meis struct DBusInterfaceInstance
38fc9e7fdaSChristopher Meis {
39fc9e7fdaSChristopher Meis std::string busName;
40fc9e7fdaSChristopher Meis std::string path;
41fc9e7fdaSChristopher Meis std::string interface;
42fc9e7fdaSChristopher Meis };
43fc9e7fdaSChristopher Meis
44fc9e7fdaSChristopher Meis void getInterfaces(
45fc9e7fdaSChristopher Meis const DBusInterfaceInstance& instance,
46fc9e7fdaSChristopher Meis const std::vector<std::shared_ptr<probe::PerformProbe>>& probeVector,
47fc9e7fdaSChristopher Meis const std::shared_ptr<scan::PerformScan>& scan, size_t retries = 5)
48fc9e7fdaSChristopher Meis {
49fc9e7fdaSChristopher Meis if (retries == 0U)
50fc9e7fdaSChristopher Meis {
51fc9e7fdaSChristopher Meis std::cerr << "retries exhausted on " << instance.busName << " "
52fc9e7fdaSChristopher Meis << instance.path << " " << instance.interface << "\n";
getInterfaces(const DBusInterfaceInstance & instance,const std::vector<std::shared_ptr<probe::PerformProbe>> & probeVector,const std::shared_ptr<scan::PerformScan> & scan,size_t retries=5)53fc9e7fdaSChristopher Meis return;
54fc9e7fdaSChristopher Meis }
55fc9e7fdaSChristopher Meis
56*cf6a75bdSChristopher Meis scan->_em.systemBus->async_method_call(
57fc9e7fdaSChristopher Meis [instance, scan, probeVector,
58fc9e7fdaSChristopher Meis retries](boost::system::error_code& errc, const DBusInterface& resp) {
59fc9e7fdaSChristopher Meis if (errc)
60fc9e7fdaSChristopher Meis {
61fc9e7fdaSChristopher Meis std::cerr << "error calling getall on " << instance.busName
62fc9e7fdaSChristopher Meis << " " << instance.path << " "
63fc9e7fdaSChristopher Meis << instance.interface << "\n";
64fc9e7fdaSChristopher Meis
65fc9e7fdaSChristopher Meis auto timer = std::make_shared<boost::asio::steady_timer>(io);
66fc9e7fdaSChristopher Meis timer->expires_after(std::chrono::seconds(2));
__anonb841ee130102(boost::system::error_code& errc, const DBusInterface& resp) 67fc9e7fdaSChristopher Meis
68fc9e7fdaSChristopher Meis timer->async_wait([timer, instance, scan, probeVector,
69fc9e7fdaSChristopher Meis retries](const boost::system::error_code&) {
70fc9e7fdaSChristopher Meis getInterfaces(instance, probeVector, scan, retries - 1);
71fc9e7fdaSChristopher Meis });
72fc9e7fdaSChristopher Meis return;
73fc9e7fdaSChristopher Meis }
74fc9e7fdaSChristopher Meis
75fc9e7fdaSChristopher Meis scan->dbusProbeObjects[instance.path][instance.interface] = resp;
76fc9e7fdaSChristopher Meis },
77fc9e7fdaSChristopher Meis instance.busName, instance.path, "org.freedesktop.DBus.Properties",
__anonb841ee130202(const boost::system::error_code&) 78fc9e7fdaSChristopher Meis "GetAll", instance.interface);
79fc9e7fdaSChristopher Meis }
80fc9e7fdaSChristopher Meis
81fc9e7fdaSChristopher Meis static void processDbusObjects(
82fc9e7fdaSChristopher Meis std::vector<std::shared_ptr<probe::PerformProbe>>& probeVector,
83fc9e7fdaSChristopher Meis const std::shared_ptr<scan::PerformScan>& scan,
84fc9e7fdaSChristopher Meis const GetSubTreeType& interfaceSubtree)
85fc9e7fdaSChristopher Meis {
86fc9e7fdaSChristopher Meis for (const auto& [path, object] : interfaceSubtree)
87fc9e7fdaSChristopher Meis {
88fc9e7fdaSChristopher Meis // Get a PropertiesChanged callback for all interfaces on this path.
89*cf6a75bdSChristopher Meis scan->_em.registerCallback(path);
registerCallback(nlohmann::json & systemConfiguration,sdbusplus::asio::object_server & objServer,const std::string & path)90fc9e7fdaSChristopher Meis
91fc9e7fdaSChristopher Meis for (const auto& [busname, ifaces] : object)
92fc9e7fdaSChristopher Meis {
93fc9e7fdaSChristopher Meis for (const std::string& iface : ifaces)
94fc9e7fdaSChristopher Meis {
95fc9e7fdaSChristopher Meis // The 3 default org.freedeskstop interfaces (Peer,
96fc9e7fdaSChristopher Meis // Introspectable, and Properties) are returned by
97fc9e7fdaSChristopher Meis // the mapper but don't have properties, so don't bother
98fc9e7fdaSChristopher Meis // with the GetAll call to save some cycles.
99fc9e7fdaSChristopher Meis if (!boost::algorithm::starts_with(iface, "org.freedesktop"))
100fc9e7fdaSChristopher Meis {
101fc9e7fdaSChristopher Meis getInterfaces({busname, path, iface}, probeVector, scan);
102fc9e7fdaSChristopher Meis }
103fc9e7fdaSChristopher Meis }
104fc9e7fdaSChristopher Meis }
105fc9e7fdaSChristopher Meis }
106fc9e7fdaSChristopher Meis }
107fc9e7fdaSChristopher Meis
108fc9e7fdaSChristopher Meis // Populates scan->dbusProbeObjects with all interfaces and properties
109fc9e7fdaSChristopher Meis // for the paths that own the interfaces passed in.
110fc9e7fdaSChristopher Meis void findDbusObjects(
111fc9e7fdaSChristopher Meis std::vector<std::shared_ptr<probe::PerformProbe>>&& probeVector,
112fc9e7fdaSChristopher Meis boost::container::flat_set<std::string>&& interfaces,
113fc9e7fdaSChristopher Meis const std::shared_ptr<scan::PerformScan>& scan, size_t retries = 5)
114fc9e7fdaSChristopher Meis {
processDbusObjects(std::vector<std::shared_ptr<probe::PerformProbe>> & probeVector,const std::shared_ptr<scan::PerformScan> & scan,const GetSubTreeType & interfaceSubtree)115fc9e7fdaSChristopher Meis // Filter out interfaces already obtained.
116fc9e7fdaSChristopher Meis for (const auto& [path, probeInterfaces] : scan->dbusProbeObjects)
117fc9e7fdaSChristopher Meis {
118fc9e7fdaSChristopher Meis for (const auto& [interface, _] : probeInterfaces)
119fc9e7fdaSChristopher Meis {
120fc9e7fdaSChristopher Meis interfaces.erase(interface);
121fc9e7fdaSChristopher Meis }
122fc9e7fdaSChristopher Meis }
123fc9e7fdaSChristopher Meis if (interfaces.empty())
124fc9e7fdaSChristopher Meis {
125fc9e7fdaSChristopher Meis return;
126fc9e7fdaSChristopher Meis }
127fc9e7fdaSChristopher Meis
128fc9e7fdaSChristopher Meis // find all connections in the mapper that expose a specific type
129*cf6a75bdSChristopher Meis scan->_em.systemBus->async_method_call(
130fc9e7fdaSChristopher Meis [interfaces, probeVector{std::move(probeVector)}, scan,
131fc9e7fdaSChristopher Meis retries](boost::system::error_code& ec,
132fc9e7fdaSChristopher Meis const GetSubTreeType& interfaceSubtree) mutable {
133fc9e7fdaSChristopher Meis if (ec)
134fc9e7fdaSChristopher Meis {
135fc9e7fdaSChristopher Meis if (ec.value() == ENOENT)
136fc9e7fdaSChristopher Meis {
137fc9e7fdaSChristopher Meis return; // wasn't found by mapper
138fc9e7fdaSChristopher Meis }
139fc9e7fdaSChristopher Meis std::cerr << "Error communicating to mapper.\n";
140fc9e7fdaSChristopher Meis
141fc9e7fdaSChristopher Meis if (retries == 0U)
142fc9e7fdaSChristopher Meis {
143fc9e7fdaSChristopher Meis // if we can't communicate to the mapper something is very
findDbusObjects(std::vector<std::shared_ptr<probe::PerformProbe>> && probeVector,boost::container::flat_set<std::string> && interfaces,const std::shared_ptr<scan::PerformScan> & scan,size_t retries=5)144fc9e7fdaSChristopher Meis // wrong
145fc9e7fdaSChristopher Meis std::exit(EXIT_FAILURE);
146fc9e7fdaSChristopher Meis }
147fc9e7fdaSChristopher Meis
148fc9e7fdaSChristopher Meis auto timer = std::make_shared<boost::asio::steady_timer>(io);
149fc9e7fdaSChristopher Meis timer->expires_after(std::chrono::seconds(10));
150fc9e7fdaSChristopher Meis
151fc9e7fdaSChristopher Meis timer->async_wait(
152fc9e7fdaSChristopher Meis [timer, interfaces{std::move(interfaces)}, scan,
153fc9e7fdaSChristopher Meis probeVector{std::move(probeVector)},
154fc9e7fdaSChristopher Meis retries](const boost::system::error_code&) mutable {
155fc9e7fdaSChristopher Meis findDbusObjects(std::move(probeVector),
156fc9e7fdaSChristopher Meis std::move(interfaces), scan,
157fc9e7fdaSChristopher Meis retries - 1);
158fc9e7fdaSChristopher Meis });
159fc9e7fdaSChristopher Meis return;
160fc9e7fdaSChristopher Meis }
161fc9e7fdaSChristopher Meis
162fc9e7fdaSChristopher Meis processDbusObjects(probeVector, scan, interfaceSubtree);
163fc9e7fdaSChristopher Meis },
164fc9e7fdaSChristopher Meis "xyz.openbmc_project.ObjectMapper",
165fc9e7fdaSChristopher Meis "/xyz/openbmc_project/object_mapper",
__anonb841ee130402(boost::system::error_code& ec, const GetSubTreeType& interfaceSubtree) 166fc9e7fdaSChristopher Meis "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", maxMapperDepth,
167fc9e7fdaSChristopher Meis interfaces);
168fc9e7fdaSChristopher Meis }
169fc9e7fdaSChristopher Meis
170fc9e7fdaSChristopher Meis static std::string getRecordName(const DBusInterface& probe,
171fc9e7fdaSChristopher Meis const std::string& probeName)
172fc9e7fdaSChristopher Meis {
173fc9e7fdaSChristopher Meis if (probe.empty())
174fc9e7fdaSChristopher Meis {
175fc9e7fdaSChristopher Meis return probeName;
176fc9e7fdaSChristopher Meis }
177fc9e7fdaSChristopher Meis
178fc9e7fdaSChristopher Meis // use an array so alphabetical order from the flat_map is maintained
179fc9e7fdaSChristopher Meis auto device = nlohmann::json::array();
180fc9e7fdaSChristopher Meis for (const auto& devPair : probe)
181fc9e7fdaSChristopher Meis {
182fc9e7fdaSChristopher Meis device.push_back(devPair.first);
183fc9e7fdaSChristopher Meis std::visit([&device](auto&& v) { device.push_back(v); },
184fc9e7fdaSChristopher Meis devPair.second);
185fc9e7fdaSChristopher Meis }
186fc9e7fdaSChristopher Meis
187fc9e7fdaSChristopher Meis // hashes are hard to distinguish, use the non-hashed version if we want
__anonb841ee130502(const boost::system::error_code&) 188fc9e7fdaSChristopher Meis // debug
189fc9e7fdaSChristopher Meis // return probeName + device.dump();
190fc9e7fdaSChristopher Meis
191fc9e7fdaSChristopher Meis return std::to_string(std::hash<std::string>{}(probeName + device.dump()));
192fc9e7fdaSChristopher Meis }
193fc9e7fdaSChristopher Meis
194*cf6a75bdSChristopher Meis scan::PerformScan::PerformScan(EntityManager& em,
195fc9e7fdaSChristopher Meis nlohmann::json& missingConfigurations,
196fc9e7fdaSChristopher Meis std::list<nlohmann::json>& configurations,
197fc9e7fdaSChristopher Meis std::function<void()>&& callback) :
198*cf6a75bdSChristopher Meis _em(em), _missingConfigurations(missingConfigurations),
199*cf6a75bdSChristopher Meis _configurations(configurations), _callback(std::move(callback))
200fc9e7fdaSChristopher Meis {}
201fc9e7fdaSChristopher Meis
202fc9e7fdaSChristopher Meis static void pruneRecordExposes(nlohmann::json& record)
203fc9e7fdaSChristopher Meis {
getRecordName(const DBusInterface & probe,const std::string & probeName)204fc9e7fdaSChristopher Meis auto findExposes = record.find("Exposes");
205fc9e7fdaSChristopher Meis if (findExposes == record.end())
206fc9e7fdaSChristopher Meis {
207fc9e7fdaSChristopher Meis return;
208fc9e7fdaSChristopher Meis }
209fc9e7fdaSChristopher Meis
210fc9e7fdaSChristopher Meis auto copy = nlohmann::json::array();
211fc9e7fdaSChristopher Meis for (auto& expose : *findExposes)
212fc9e7fdaSChristopher Meis {
213fc9e7fdaSChristopher Meis if (!expose.is_null())
214fc9e7fdaSChristopher Meis {
215fc9e7fdaSChristopher Meis copy.emplace_back(expose);
216fc9e7fdaSChristopher Meis }
__anonb841ee130602(auto&& v) 217fc9e7fdaSChristopher Meis }
218fc9e7fdaSChristopher Meis *findExposes = copy;
219fc9e7fdaSChristopher Meis }
220fc9e7fdaSChristopher Meis
221fc9e7fdaSChristopher Meis static void recordDiscoveredIdentifiers(
222fc9e7fdaSChristopher Meis std::set<nlohmann::json>& usedNames, std::list<size_t>& indexes,
223fc9e7fdaSChristopher Meis const std::string& probeName, const nlohmann::json& record)
224fc9e7fdaSChristopher Meis {
225fc9e7fdaSChristopher Meis size_t indexIdx = probeName.find('$');
226fc9e7fdaSChristopher Meis if (indexIdx == std::string::npos)
227fc9e7fdaSChristopher Meis {
PerformScan(nlohmann::json & systemConfiguration,nlohmann::json & missingConfigurations,std::list<nlohmann::json> & configurations,sdbusplus::asio::object_server & objServerIn,std::function<void ()> && callback)228fc9e7fdaSChristopher Meis return;
229fc9e7fdaSChristopher Meis }
230fc9e7fdaSChristopher Meis
231fc9e7fdaSChristopher Meis auto nameIt = record.find("Name");
232fc9e7fdaSChristopher Meis if (nameIt == record.end())
233fc9e7fdaSChristopher Meis {
234fc9e7fdaSChristopher Meis std::cerr << "Last JSON Illegal\n";
235fc9e7fdaSChristopher Meis return;
236fc9e7fdaSChristopher Meis }
237fc9e7fdaSChristopher Meis
238fc9e7fdaSChristopher Meis int index = 0;
pruneRecordExposes(nlohmann::json & record)239fc9e7fdaSChristopher Meis auto str = nameIt->get<std::string>().substr(indexIdx);
240fc9e7fdaSChristopher Meis // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
241fc9e7fdaSChristopher Meis const char* endPtr = str.data() + str.size();
242fc9e7fdaSChristopher Meis auto [p, ec] = std::from_chars(str.data(), endPtr, index);
243fc9e7fdaSChristopher Meis if (ec != std::errc())
244fc9e7fdaSChristopher Meis {
245fc9e7fdaSChristopher Meis return; // non-numeric replacement
246fc9e7fdaSChristopher Meis }
247fc9e7fdaSChristopher Meis
248fc9e7fdaSChristopher Meis usedNames.insert(nameIt.value());
249fc9e7fdaSChristopher Meis
250fc9e7fdaSChristopher Meis auto usedIt = std::find(indexes.begin(), indexes.end(), index);
251fc9e7fdaSChristopher Meis if (usedIt != indexes.end())
252fc9e7fdaSChristopher Meis {
253fc9e7fdaSChristopher Meis indexes.erase(usedIt);
254fc9e7fdaSChristopher Meis }
255fc9e7fdaSChristopher Meis }
256fc9e7fdaSChristopher Meis
257fc9e7fdaSChristopher Meis static bool extractExposeActionRecordNames(std::vector<std::string>& matches,
recordDiscoveredIdentifiers(std::set<nlohmann::json> & usedNames,std::list<size_t> & indexes,const std::string & probeName,const nlohmann::json & record)258fc9e7fdaSChristopher Meis nlohmann::json::iterator& keyPair)
259fc9e7fdaSChristopher Meis {
260fc9e7fdaSChristopher Meis if (keyPair.value().is_string())
261fc9e7fdaSChristopher Meis {
262fc9e7fdaSChristopher Meis matches.emplace_back(keyPair.value());
263fc9e7fdaSChristopher Meis return true;
264fc9e7fdaSChristopher Meis }
265fc9e7fdaSChristopher Meis
266fc9e7fdaSChristopher Meis if (keyPair.value().is_array())
267fc9e7fdaSChristopher Meis {
268fc9e7fdaSChristopher Meis for (const auto& value : keyPair.value())
269fc9e7fdaSChristopher Meis {
270fc9e7fdaSChristopher Meis if (!value.is_string())
271fc9e7fdaSChristopher Meis {
272fc9e7fdaSChristopher Meis std::cerr << "Value is invalid type " << value << "\n";
273fc9e7fdaSChristopher Meis break;
274fc9e7fdaSChristopher Meis }
275fc9e7fdaSChristopher Meis matches.emplace_back(value);
276fc9e7fdaSChristopher Meis }
277fc9e7fdaSChristopher Meis
278fc9e7fdaSChristopher Meis return true;
279fc9e7fdaSChristopher Meis }
280fc9e7fdaSChristopher Meis
281fc9e7fdaSChristopher Meis std::cerr << "Value is invalid type " << keyPair.key() << "\n";
282fc9e7fdaSChristopher Meis
283fc9e7fdaSChristopher Meis return false;
284fc9e7fdaSChristopher Meis }
285fc9e7fdaSChristopher Meis
286fc9e7fdaSChristopher Meis static std::optional<std::vector<std::string>::iterator> findExposeActionRecord(
287fc9e7fdaSChristopher Meis std::vector<std::string>& matches, const nlohmann::json& record)
288fc9e7fdaSChristopher Meis {
289fc9e7fdaSChristopher Meis const auto& name = (record)["Name"].get_ref<const std::string&>();
290fc9e7fdaSChristopher Meis auto compare = [&name](const std::string& s) { return s == name; };
291fc9e7fdaSChristopher Meis auto matchIt = std::find_if(matches.begin(), matches.end(), compare);
292fc9e7fdaSChristopher Meis
293fc9e7fdaSChristopher Meis if (matchIt == matches.end())
extractExposeActionRecordNames(std::vector<std::string> & matches,nlohmann::json::iterator & keyPair)294fc9e7fdaSChristopher Meis {
295fc9e7fdaSChristopher Meis return std::nullopt;
296fc9e7fdaSChristopher Meis }
297fc9e7fdaSChristopher Meis
298fc9e7fdaSChristopher Meis return matchIt;
299fc9e7fdaSChristopher Meis }
300fc9e7fdaSChristopher Meis
301fc9e7fdaSChristopher Meis static void applyBindExposeAction(nlohmann::json& exposedObject,
302fc9e7fdaSChristopher Meis nlohmann::json& expose,
303fc9e7fdaSChristopher Meis const std::string& propertyName)
304fc9e7fdaSChristopher Meis {
305fc9e7fdaSChristopher Meis if (boost::starts_with(propertyName, "Bind"))
306fc9e7fdaSChristopher Meis {
307fc9e7fdaSChristopher Meis std::string bind = propertyName.substr(sizeof("Bind") - 1);
308fc9e7fdaSChristopher Meis exposedObject["Status"] = "okay";
309fc9e7fdaSChristopher Meis expose[bind] = exposedObject;
310fc9e7fdaSChristopher Meis }
311fc9e7fdaSChristopher Meis }
312fc9e7fdaSChristopher Meis
313fc9e7fdaSChristopher Meis static void applyDisableExposeAction(nlohmann::json& exposedObject,
314fc9e7fdaSChristopher Meis const std::string& propertyName)
315fc9e7fdaSChristopher Meis {
316fc9e7fdaSChristopher Meis if (propertyName == "DisableNode")
317fc9e7fdaSChristopher Meis {
318fc9e7fdaSChristopher Meis exposedObject["Status"] = "disabled";
319fc9e7fdaSChristopher Meis }
320fc9e7fdaSChristopher Meis }
321fc9e7fdaSChristopher Meis
322fc9e7fdaSChristopher Meis static void applyConfigExposeActions(
findExposeActionRecord(std::vector<std::string> & matches,const nlohmann::json & record)323fc9e7fdaSChristopher Meis std::vector<std::string>& matches, nlohmann::json& expose,
324fc9e7fdaSChristopher Meis const std::string& propertyName, nlohmann::json& configExposes)
325fc9e7fdaSChristopher Meis {
326fc9e7fdaSChristopher Meis for (auto& exposedObject : configExposes)
327fc9e7fdaSChristopher Meis {
328fc9e7fdaSChristopher Meis auto match = findExposeActionRecord(matches, exposedObject);
329fc9e7fdaSChristopher Meis if (match)
330fc9e7fdaSChristopher Meis {
331fc9e7fdaSChristopher Meis matches.erase(*match);
332fc9e7fdaSChristopher Meis applyBindExposeAction(exposedObject, expose, propertyName);
333fc9e7fdaSChristopher Meis applyDisableExposeAction(exposedObject, propertyName);
334fc9e7fdaSChristopher Meis }
335fc9e7fdaSChristopher Meis }
336fc9e7fdaSChristopher Meis }
337fc9e7fdaSChristopher Meis
applyBindExposeAction(nlohmann::json & exposedObject,nlohmann::json & expose,const std::string & propertyName)338fc9e7fdaSChristopher Meis static void applyExposeActions(
339fc9e7fdaSChristopher Meis nlohmann::json& systemConfiguration, const std::string& recordName,
340fc9e7fdaSChristopher Meis nlohmann::json& expose, nlohmann::json::iterator& keyPair)
341fc9e7fdaSChristopher Meis {
342fc9e7fdaSChristopher Meis bool isBind = boost::starts_with(keyPair.key(), "Bind");
343fc9e7fdaSChristopher Meis bool isDisable = keyPair.key() == "DisableNode";
344fc9e7fdaSChristopher Meis bool isExposeAction = isBind || isDisable;
345fc9e7fdaSChristopher Meis
346fc9e7fdaSChristopher Meis if (!isExposeAction)
347fc9e7fdaSChristopher Meis {
348fc9e7fdaSChristopher Meis return;
349fc9e7fdaSChristopher Meis }
350fc9e7fdaSChristopher Meis
351fc9e7fdaSChristopher Meis std::vector<std::string> matches;
352fc9e7fdaSChristopher Meis
353fc9e7fdaSChristopher Meis if (!extractExposeActionRecordNames(matches, keyPair))
354fc9e7fdaSChristopher Meis {
355fc9e7fdaSChristopher Meis return;
356fc9e7fdaSChristopher Meis }
357fc9e7fdaSChristopher Meis
358fc9e7fdaSChristopher Meis for (const auto& [configId, config] : systemConfiguration.items())
359fc9e7fdaSChristopher Meis {
360fc9e7fdaSChristopher Meis // don't disable ourselves
361fc9e7fdaSChristopher Meis if (isDisable && configId == recordName)
362fc9e7fdaSChristopher Meis {
363fc9e7fdaSChristopher Meis continue;
364fc9e7fdaSChristopher Meis }
365fc9e7fdaSChristopher Meis
366fc9e7fdaSChristopher Meis auto configListFind = config.find("Exposes");
367fc9e7fdaSChristopher Meis if (configListFind == config.end())
368fc9e7fdaSChristopher Meis {
369fc9e7fdaSChristopher Meis continue;
370fc9e7fdaSChristopher Meis }
371fc9e7fdaSChristopher Meis
372fc9e7fdaSChristopher Meis if (!configListFind->is_array())
373fc9e7fdaSChristopher Meis {
374fc9e7fdaSChristopher Meis continue;
375fc9e7fdaSChristopher Meis }
376fc9e7fdaSChristopher Meis
377fc9e7fdaSChristopher Meis applyConfigExposeActions(matches, expose, keyPair.key(),
378fc9e7fdaSChristopher Meis *configListFind);
379fc9e7fdaSChristopher Meis }
380fc9e7fdaSChristopher Meis
381fc9e7fdaSChristopher Meis if (!matches.empty())
382fc9e7fdaSChristopher Meis {
383fc9e7fdaSChristopher Meis std::cerr << "configuration file dependency error, could not find "
384fc9e7fdaSChristopher Meis << keyPair.key() << " " << keyPair.value() << "\n";
385fc9e7fdaSChristopher Meis }
386fc9e7fdaSChristopher Meis }
387fc9e7fdaSChristopher Meis
388fc9e7fdaSChristopher Meis static std::string generateDeviceName(
389fc9e7fdaSChristopher Meis const std::set<nlohmann::json>& usedNames, const DBusObject& dbusObject,
390fc9e7fdaSChristopher Meis size_t foundDeviceIdx, const std::string& nameTemplate,
391fc9e7fdaSChristopher Meis std::optional<std::string>& replaceStr)
392fc9e7fdaSChristopher Meis {
393fc9e7fdaSChristopher Meis nlohmann::json copyForName = {{"Name", nameTemplate}};
394fc9e7fdaSChristopher Meis nlohmann::json::iterator copyIt = copyForName.begin();
39559ef1e72SChristopher Meis std::optional<std::string> replaceVal = em_utils::templateCharReplace(
39659ef1e72SChristopher Meis copyIt, dbusObject, foundDeviceIdx, replaceStr);
397fc9e7fdaSChristopher Meis
398fc9e7fdaSChristopher Meis if (!replaceStr && replaceVal)
399fc9e7fdaSChristopher Meis {
4006459993cSPatrick Williams if (usedNames.contains(copyIt.value()))
401fc9e7fdaSChristopher Meis {
402fc9e7fdaSChristopher Meis replaceStr = replaceVal;
403fc9e7fdaSChristopher Meis copyForName = {{"Name", nameTemplate}};
404fc9e7fdaSChristopher Meis copyIt = copyForName.begin();
40559ef1e72SChristopher Meis em_utils::templateCharReplace(copyIt, dbusObject, foundDeviceIdx,
40659ef1e72SChristopher Meis replaceStr);
407fc9e7fdaSChristopher Meis }
408fc9e7fdaSChristopher Meis }
409fc9e7fdaSChristopher Meis
410fc9e7fdaSChristopher Meis if (replaceStr)
411fc9e7fdaSChristopher Meis {
412fc9e7fdaSChristopher Meis std::cerr << "Duplicates found, replacing " << *replaceStr
413fc9e7fdaSChristopher Meis << " with found device index.\n Consider "
414fc9e7fdaSChristopher Meis "fixing template to not have duplicates\n";
415fc9e7fdaSChristopher Meis }
416fc9e7fdaSChristopher Meis
417fc9e7fdaSChristopher Meis return copyIt.value();
418fc9e7fdaSChristopher Meis }
419fc9e7fdaSChristopher Meis
420fc9e7fdaSChristopher Meis void scan::PerformScan::updateSystemConfiguration(
421fc9e7fdaSChristopher Meis const nlohmann::json& recordRef, const std::string& probeName,
422fc9e7fdaSChristopher Meis FoundDevices& foundDevices)
423fc9e7fdaSChristopher Meis {
424fc9e7fdaSChristopher Meis _passed = true;
generateDeviceName(const std::set<nlohmann::json> & usedNames,const DBusObject & dbusObject,size_t foundDeviceIdx,const std::string & nameTemplate,std::optional<std::string> & replaceStr)425fc9e7fdaSChristopher Meis passedProbes.push_back(probeName);
426fc9e7fdaSChristopher Meis
427fc9e7fdaSChristopher Meis std::set<nlohmann::json> usedNames;
428fc9e7fdaSChristopher Meis std::list<size_t> indexes(foundDevices.size());
429fc9e7fdaSChristopher Meis std::iota(indexes.begin(), indexes.end(), 1);
430fc9e7fdaSChristopher Meis
431fc9e7fdaSChristopher Meis // copy over persisted configurations and make sure we remove
432fc9e7fdaSChristopher Meis // indexes that are already used
433fc9e7fdaSChristopher Meis for (auto itr = foundDevices.begin(); itr != foundDevices.end();)
434fc9e7fdaSChristopher Meis {
435fc9e7fdaSChristopher Meis std::string recordName = getRecordName(itr->interface, probeName);
436fc9e7fdaSChristopher Meis
437*cf6a75bdSChristopher Meis auto record = _em.systemConfiguration.find(recordName);
438*cf6a75bdSChristopher Meis if (record == _em.systemConfiguration.end())
439fc9e7fdaSChristopher Meis {
440*cf6a75bdSChristopher Meis record = _em.lastJson.find(recordName);
441*cf6a75bdSChristopher Meis if (record == _em.lastJson.end())
442fc9e7fdaSChristopher Meis {
443fc9e7fdaSChristopher Meis itr++;
444fc9e7fdaSChristopher Meis continue;
445fc9e7fdaSChristopher Meis }
446fc9e7fdaSChristopher Meis
447fc9e7fdaSChristopher Meis pruneRecordExposes(*record);
448fc9e7fdaSChristopher Meis
449*cf6a75bdSChristopher Meis _em.systemConfiguration[recordName] = *record;
450fc9e7fdaSChristopher Meis }
451fc9e7fdaSChristopher Meis _missingConfigurations.erase(recordName);
452fc9e7fdaSChristopher Meis
453fc9e7fdaSChristopher Meis // We've processed the device, remove it and advance the
454fc9e7fdaSChristopher Meis // iterator
455fc9e7fdaSChristopher Meis itr = foundDevices.erase(itr);
456fc9e7fdaSChristopher Meis recordDiscoveredIdentifiers(usedNames, indexes, probeName, *record);
updateSystemConfiguration(const nlohmann::json & recordRef,const std::string & probeName,FoundDevices & foundDevices)457fc9e7fdaSChristopher Meis }
458fc9e7fdaSChristopher Meis
459fc9e7fdaSChristopher Meis std::optional<std::string> replaceStr;
460fc9e7fdaSChristopher Meis
461fc9e7fdaSChristopher Meis DBusObject emptyObject;
462fc9e7fdaSChristopher Meis DBusInterface emptyInterface;
463fc9e7fdaSChristopher Meis emptyObject.emplace(std::string{}, emptyInterface);
464fc9e7fdaSChristopher Meis
465fc9e7fdaSChristopher Meis for (const auto& [foundDevice, path] : foundDevices)
466fc9e7fdaSChristopher Meis {
467fc9e7fdaSChristopher Meis // Need all interfaces on this path so that template
468fc9e7fdaSChristopher Meis // substitutions can be done with any of the contained
469fc9e7fdaSChristopher Meis // properties. If the probe that passed didn't use an
470fc9e7fdaSChristopher Meis // interface, such as if it was just TRUE, then
471fc9e7fdaSChristopher Meis // templateCharReplace will just get passed in an empty
472fc9e7fdaSChristopher Meis // map.
473fc9e7fdaSChristopher Meis auto objectIt = dbusProbeObjects.find(path);
474fc9e7fdaSChristopher Meis const DBusObject& dbusObject = (objectIt == dbusProbeObjects.end())
475fc9e7fdaSChristopher Meis ? emptyObject
476fc9e7fdaSChristopher Meis : objectIt->second;
477fc9e7fdaSChristopher Meis
478fc9e7fdaSChristopher Meis nlohmann::json record = recordRef;
479fc9e7fdaSChristopher Meis std::string recordName = getRecordName(foundDevice, probeName);
480fc9e7fdaSChristopher Meis size_t foundDeviceIdx = indexes.front();
481fc9e7fdaSChristopher Meis indexes.pop_front();
482fc9e7fdaSChristopher Meis
483fc9e7fdaSChristopher Meis // check name first so we have no duplicate names
484fc9e7fdaSChristopher Meis auto getName = record.find("Name");
485fc9e7fdaSChristopher Meis if (getName == record.end())
486fc9e7fdaSChristopher Meis {
487fc9e7fdaSChristopher Meis std::cerr << "Record Missing Name! " << record.dump();
488fc9e7fdaSChristopher Meis continue; // this should be impossible at this level
489fc9e7fdaSChristopher Meis }
490fc9e7fdaSChristopher Meis
491fc9e7fdaSChristopher Meis std::string deviceName = generateDeviceName(
492fc9e7fdaSChristopher Meis usedNames, dbusObject, foundDeviceIdx, getName.value(), replaceStr);
493fc9e7fdaSChristopher Meis getName.value() = deviceName;
494fc9e7fdaSChristopher Meis usedNames.insert(deviceName);
495fc9e7fdaSChristopher Meis
496fc9e7fdaSChristopher Meis for (auto keyPair = record.begin(); keyPair != record.end(); keyPair++)
497fc9e7fdaSChristopher Meis {
498fc9e7fdaSChristopher Meis if (keyPair.key() != "Name")
499fc9e7fdaSChristopher Meis {
50059ef1e72SChristopher Meis em_utils::templateCharReplace(keyPair, dbusObject,
50159ef1e72SChristopher Meis foundDeviceIdx, replaceStr);
502fc9e7fdaSChristopher Meis }
503fc9e7fdaSChristopher Meis }
504fc9e7fdaSChristopher Meis
505fc9e7fdaSChristopher Meis // insert into configuration temporarily to be able to
506fc9e7fdaSChristopher Meis // reference ourselves
507fc9e7fdaSChristopher Meis
508*cf6a75bdSChristopher Meis _em.systemConfiguration[recordName] = record;
509fc9e7fdaSChristopher Meis
510fc9e7fdaSChristopher Meis auto findExpose = record.find("Exposes");
511fc9e7fdaSChristopher Meis if (findExpose == record.end())
512fc9e7fdaSChristopher Meis {
513fc9e7fdaSChristopher Meis continue;
514fc9e7fdaSChristopher Meis }
515fc9e7fdaSChristopher Meis
516fc9e7fdaSChristopher Meis for (auto& expose : *findExpose)
517fc9e7fdaSChristopher Meis {
518fc9e7fdaSChristopher Meis for (auto keyPair = expose.begin(); keyPair != expose.end();
519fc9e7fdaSChristopher Meis keyPair++)
520fc9e7fdaSChristopher Meis {
52159ef1e72SChristopher Meis em_utils::templateCharReplace(keyPair, dbusObject,
52259ef1e72SChristopher Meis foundDeviceIdx, replaceStr);
523fc9e7fdaSChristopher Meis
524*cf6a75bdSChristopher Meis applyExposeActions(_em.systemConfiguration, recordName, expose,
525fc9e7fdaSChristopher Meis keyPair);
526fc9e7fdaSChristopher Meis }
527fc9e7fdaSChristopher Meis }
528fc9e7fdaSChristopher Meis
529fc9e7fdaSChristopher Meis // overwrite ourselves with cleaned up version
530*cf6a75bdSChristopher Meis _em.systemConfiguration[recordName] = record;
531fc9e7fdaSChristopher Meis _missingConfigurations.erase(recordName);
532fc9e7fdaSChristopher Meis }
533fc9e7fdaSChristopher Meis }
534fc9e7fdaSChristopher Meis
535fc9e7fdaSChristopher Meis void scan::PerformScan::run()
536fc9e7fdaSChristopher Meis {
537fc9e7fdaSChristopher Meis boost::container::flat_set<std::string> dbusProbeInterfaces;
538fc9e7fdaSChristopher Meis std::vector<std::shared_ptr<probe::PerformProbe>> dbusProbePointers;
539fc9e7fdaSChristopher Meis
540fc9e7fdaSChristopher Meis for (auto it = _configurations.begin(); it != _configurations.end();)
541fc9e7fdaSChristopher Meis {
542fc9e7fdaSChristopher Meis // check for poorly formatted fields, probe must be an array
543fc9e7fdaSChristopher Meis auto findProbe = it->find("Probe");
544fc9e7fdaSChristopher Meis if (findProbe == it->end())
545fc9e7fdaSChristopher Meis {
546fc9e7fdaSChristopher Meis std::cerr << "configuration file missing probe:\n " << *it << "\n";
547fc9e7fdaSChristopher Meis it = _configurations.erase(it);
548fc9e7fdaSChristopher Meis continue;
549fc9e7fdaSChristopher Meis }
550fc9e7fdaSChristopher Meis
551fc9e7fdaSChristopher Meis auto findName = it->find("Name");
552fc9e7fdaSChristopher Meis if (findName == it->end())
553fc9e7fdaSChristopher Meis {
554fc9e7fdaSChristopher Meis std::cerr << "configuration file missing name:\n " << *it << "\n";
555fc9e7fdaSChristopher Meis it = _configurations.erase(it);
556fc9e7fdaSChristopher Meis continue;
557fc9e7fdaSChristopher Meis }
558fc9e7fdaSChristopher Meis std::string probeName = *findName;
559fc9e7fdaSChristopher Meis
560fc9e7fdaSChristopher Meis if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
561fc9e7fdaSChristopher Meis passedProbes.end())
562fc9e7fdaSChristopher Meis {
563fc9e7fdaSChristopher Meis it = _configurations.erase(it);
564fc9e7fdaSChristopher Meis continue;
565fc9e7fdaSChristopher Meis }
566fc9e7fdaSChristopher Meis
567fc9e7fdaSChristopher Meis nlohmann::json& recordRef = *it;
568fc9e7fdaSChristopher Meis nlohmann::json probeCommand;
569fc9e7fdaSChristopher Meis if ((*findProbe).type() != nlohmann::json::value_t::array)
570fc9e7fdaSChristopher Meis {
571fc9e7fdaSChristopher Meis probeCommand = nlohmann::json::array();
run()572fc9e7fdaSChristopher Meis probeCommand.push_back(*findProbe);
573fc9e7fdaSChristopher Meis }
574fc9e7fdaSChristopher Meis else
575fc9e7fdaSChristopher Meis {
576fc9e7fdaSChristopher Meis probeCommand = *findProbe;
577fc9e7fdaSChristopher Meis }
578fc9e7fdaSChristopher Meis
579fc9e7fdaSChristopher Meis // store reference to this to children to makes sure we don't get
580fc9e7fdaSChristopher Meis // destroyed too early
581fc9e7fdaSChristopher Meis auto thisRef = shared_from_this();
582fc9e7fdaSChristopher Meis auto probePointer = std::make_shared<probe::PerformProbe>(
583fc9e7fdaSChristopher Meis recordRef, probeCommand, probeName, thisRef);
584fc9e7fdaSChristopher Meis
585fc9e7fdaSChristopher Meis // parse out dbus probes by discarding other probe types, store in a
586fc9e7fdaSChristopher Meis // map
587fc9e7fdaSChristopher Meis for (const nlohmann::json& probeJson : probeCommand)
588fc9e7fdaSChristopher Meis {
589fc9e7fdaSChristopher Meis const std::string* probe = probeJson.get_ptr<const std::string*>();
590fc9e7fdaSChristopher Meis if (probe == nullptr)
591fc9e7fdaSChristopher Meis {
592fc9e7fdaSChristopher Meis std::cerr << "Probe statement wasn't a string, can't parse";
593fc9e7fdaSChristopher Meis continue;
594fc9e7fdaSChristopher Meis }
595fc9e7fdaSChristopher Meis if (probe::findProbeType(*probe))
596fc9e7fdaSChristopher Meis {
597fc9e7fdaSChristopher Meis continue;
598fc9e7fdaSChristopher Meis }
599fc9e7fdaSChristopher Meis // syntax requires probe before first open brace
600fc9e7fdaSChristopher Meis auto findStart = probe->find('(');
601fc9e7fdaSChristopher Meis std::string interface = probe->substr(0, findStart);
602fc9e7fdaSChristopher Meis dbusProbeInterfaces.emplace(interface);
603fc9e7fdaSChristopher Meis dbusProbePointers.emplace_back(probePointer);
604fc9e7fdaSChristopher Meis }
605fc9e7fdaSChristopher Meis it++;
606fc9e7fdaSChristopher Meis }
607fc9e7fdaSChristopher Meis
608fc9e7fdaSChristopher Meis // probe vector stores a shared_ptr to each PerformProbe that cares
609fc9e7fdaSChristopher Meis // about a dbus interface
610fc9e7fdaSChristopher Meis findDbusObjects(std::move(dbusProbePointers),
611fc9e7fdaSChristopher Meis std::move(dbusProbeInterfaces), shared_from_this());
612fc9e7fdaSChristopher Meis }
613fc9e7fdaSChristopher Meis
614fc9e7fdaSChristopher Meis scan::PerformScan::~PerformScan()
615fc9e7fdaSChristopher Meis {
616fc9e7fdaSChristopher Meis if (_passed)
617fc9e7fdaSChristopher Meis {
618fc9e7fdaSChristopher Meis auto nextScan = std::make_shared<PerformScan>(
619*cf6a75bdSChristopher Meis _em, _missingConfigurations, _configurations, std::move(_callback));
620fc9e7fdaSChristopher Meis nextScan->passedProbes = std::move(passedProbes);
621fc9e7fdaSChristopher Meis nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
622fc9e7fdaSChristopher Meis nextScan->run();
623fc9e7fdaSChristopher Meis }
624fc9e7fdaSChristopher Meis else
625fc9e7fdaSChristopher Meis {
626fc9e7fdaSChristopher Meis _callback();
627fc9e7fdaSChristopher Meis }
628fc9e7fdaSChristopher Meis }
629