blob: 490c0f5c3476278e634ae6c347c54798fc215efd [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
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 Bishop1fb9f3f2020-08-28 08:15:13 -040016/// \file EntityManager.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
James Feist1df06a42019-04-11 14:23:04 -070018#include "EntityManager.hpp"
19
Patrick Venturea49dc332019-10-26 08:32:02 -070020#include "Overlay.hpp"
21#include "Utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070022#include "VariantVisitors.hpp"
23
James Feist11be6672018-04-06 14:05:32 -070024#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070025#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070028#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080029#include <boost/asio/io_context.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070030#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080031#include <boost/container/flat_map.hpp>
32#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070033#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070034#include <nlohmann/json.hpp>
35#include <sdbusplus/asio/connection.hpp>
36#include <sdbusplus/asio/object_server.hpp>
37
Igor Kononenko9fd87e52020-10-06 01:18:17 +030038#include <charconv>
James Feist637b3ef2019-04-15 16:35:30 -070039#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080040#include <fstream>
41#include <iostream>
James Feista465ccc2019-02-08 12:51:01 -080042#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080043#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080044constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
45constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070046constexpr const char* tempConfigDir = "/tmp/configuration/";
47constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
48constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080049constexpr const char* globalSchema = "global.json";
Ed Tanous07d467b2021-02-23 14:48:37 -080050constexpr const int32_t maxMapperDepth = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080051
Ed Tanous07d467b2021-02-23 14:48:37 -080052constexpr const bool debug = false;
James Feistf1b14142019-04-10 15:22:09 -070053
Ed Tanous07d467b2021-02-23 14:48:37 -080054struct CmpStr
James Feist3cb5fec2018-01-23 14:41:51 -080055{
James Feista465ccc2019-02-08 12:51:01 -080056 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080057 {
58 return std::strcmp(a, b) < 0;
59 }
60};
61
62// underscore T for collison with dbus c api
63enum class probe_type_codes
64{
65 FALSE_T,
66 TRUE_T,
67 AND,
68 OR,
James Feist6bd2a022018-03-13 12:30:58 -070069 FOUND,
70 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080071};
Ed Tanous07d467b2021-02-23 14:48:37 -080072const static boost::container::flat_map<const char*, probe_type_codes, CmpStr>
73 probeTypes{{{"FALSE", probe_type_codes::FALSE_T},
74 {"TRUE", probe_type_codes::TRUE_T},
75 {"AND", probe_type_codes::AND},
76 {"OR", probe_type_codes::OR},
77 {"FOUND", probe_type_codes::FOUND},
78 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080079
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020080static constexpr std::array<const char*, 6> settableInterfaces = {
81 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070082using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080083 std::variant<std::vector<std::string>, std::vector<double>, std::string,
84 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
85 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080086using GetSubTreeType = std::vector<
87 std::pair<std::string,
88 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
89
90using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070091 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080092 boost::container::flat_map<
93 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070094 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080095
James Feistd58879a2019-09-11 11:26:07 -070096// store reference to all interfaces so we can destroy them later
97boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080098 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070099 inventory;
100
James Feist3cb5fec2018-01-23 14:41:51 -0800101// todo: pass this through nicer
Ed Tanous07d467b2021-02-23 14:48:37 -0800102std::shared_ptr<sdbusplus::asio::connection> systemBus;
James Feist1df06a42019-04-11 14:23:04 -0700103static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800104
James Feist02d2b932020-02-06 16:28:48 -0800105boost::asio::io_context io;
106
Ed Tanous07d467b2021-02-23 14:48:37 -0800107const std::regex illegalDbusPathRegex("[^A-Za-z0-9_.]");
108const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800109
James Feist4dc617b2020-05-01 09:54:47 -0700110void registerCallback(nlohmann::json& systemConfiguration,
111 sdbusplus::asio::object_server& objServer,
Matt Spinlere789bf12021-02-24 12:33:01 -0600112 const std::string& path);
James Feist75fdeeb2018-02-20 14:26:16 -0800113
James Feistd58879a2019-09-11 11:26:07 -0700114static std::shared_ptr<sdbusplus::asio::dbus_interface>
115 createInterface(sdbusplus::asio::object_server& objServer,
116 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800117 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700118{
James Feist02d2b932020-02-06 16:28:48 -0800119 // on first add we have no reason to check for null before add, as there
120 // won't be any. For dynamically added interfaces, we check for null so that
121 // a constant delete/add will not create a memory leak
122
123 auto ptr = objServer.add_interface(path, interface);
124 auto& dataVector = inventory[parent];
125 if (checkNull)
126 {
127 auto it = std::find_if(dataVector.begin(), dataVector.end(),
128 [](const auto& p) { return p.expired(); });
129 if (it != dataVector.end())
130 {
131 *it = ptr;
132 return ptr;
133 }
134 }
135 dataVector.emplace_back(ptr);
136 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700137}
138
James Feistb1728ca2020-04-30 15:40:55 -0700139void getInterfaces(
140 const std::tuple<std::string, std::string, std::string>& call,
141 const std::vector<std::shared_ptr<PerformProbe>>& probeVector,
Ed Tanous07d467b2021-02-23 14:48:37 -0800142 const std::shared_ptr<PerformScan>& scan, size_t retries = 5)
James Feistb1728ca2020-04-30 15:40:55 -0700143{
144 if (!retries)
145 {
146 std::cerr << "retries exhausted on " << std::get<0>(call) << " "
147 << std::get<1>(call) << " " << std::get<2>(call) << "\n";
148 return;
149 }
150
Ed Tanous07d467b2021-02-23 14:48:37 -0800151 systemBus->async_method_call(
James Feistb1728ca2020-04-30 15:40:55 -0700152 [call, scan, probeVector, retries](
153 boost::system::error_code& errc,
154 const boost::container::flat_map<std::string, BasicVariantType>&
155 resp) {
156 if (errc)
157 {
158 std::cerr << "error calling getall on " << std::get<0>(call)
159 << " " << std::get<1>(call) << " "
160 << std::get<2>(call) << "\n";
161
162 std::shared_ptr<boost::asio::steady_timer> timer =
163 std::make_shared<boost::asio::steady_timer>(io);
164 timer->expires_after(std::chrono::seconds(2));
165
166 timer->async_wait([timer, call, scan, probeVector,
167 retries](const boost::system::error_code&) {
168 getInterfaces(call, probeVector, scan, retries - 1);
169 });
170 return;
171 }
172
Matt Spinlere789bf12021-02-24 12:33:01 -0600173 scan->dbusProbeObjects[std::get<1>(call)][std::get<2>(call)] = resp;
James Feistb1728ca2020-04-30 15:40:55 -0700174 },
175 std::get<0>(call), std::get<1>(call), "org.freedesktop.DBus.Properties",
176 "GetAll", std::get<2>(call));
177
Ed Tanous07d467b2021-02-23 14:48:37 -0800178 if constexpr (debug)
James Feistb1728ca2020-04-30 15:40:55 -0700179 {
180 std::cerr << __func__ << " " << __LINE__ << "\n";
181 }
182}
183
Matt Spinlere789bf12021-02-24 12:33:01 -0600184// Populates scan->dbusProbeObjects with all interfaces and properties
185// for the paths that own the interfaces passed in.
James Feistb1728ca2020-04-30 15:40:55 -0700186void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>&& probeVector,
187 boost::container::flat_set<std::string>&& interfaces,
Ed Tanous07d467b2021-02-23 14:48:37 -0800188 const std::shared_ptr<PerformScan>& scan,
189 size_t retries = 5)
James Feist3cb5fec2018-01-23 14:41:51 -0800190{
Matt Spinlere789bf12021-02-24 12:33:01 -0600191 // Filter out interfaces already obtained.
192 for (const auto& [path, probeInterfaces] : scan->dbusProbeObjects)
James Feist733f7652019-11-13 14:32:29 -0800193 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600194 for (const auto& [interface, _] : probeInterfaces)
195 {
196 interfaces.erase(interface);
197 }
James Feist733f7652019-11-13 14:32:29 -0800198 }
199 if (interfaces.empty())
200 {
201 return;
202 }
203
James Feist3cb5fec2018-01-23 14:41:51 -0800204 // find all connections in the mapper that expose a specific type
Ed Tanous07d467b2021-02-23 14:48:37 -0800205 systemBus->async_method_call(
Matt Spinler4f328892021-02-24 13:19:14 -0600206 [interfaces, probeVector{std::move(probeVector)}, scan,
207 retries](boost::system::error_code& ec,
208 const GetSubTreeType& interfaceSubtree) mutable {
James Feistb1728ca2020-04-30 15:40:55 -0700209 boost::container::flat_set<
210 std::tuple<std::string, std::string, std::string>>
211 interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700212 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700213 {
James Feist0de40152018-07-25 11:56:12 -0700214 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700215 {
James Feist0de40152018-07-25 11:56:12 -0700216 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700217 }
James Feist0de40152018-07-25 11:56:12 -0700218 std::cerr << "Error communicating to mapper.\n";
219
James Feist32d1f0a2020-09-10 14:49:25 -0700220 if (!retries)
221 {
222 // if we can't communicate to the mapper something is very
223 // wrong
224 std::exit(EXIT_FAILURE);
225 }
226 std::shared_ptr<boost::asio::steady_timer> timer =
227 std::make_shared<boost::asio::steady_timer>(io);
228 timer->expires_after(std::chrono::seconds(10));
229
230 timer->async_wait(
231 [timer, interfaces{std::move(interfaces)}, scan,
232 probeVector{std::move(probeVector)},
233 retries](const boost::system::error_code&) mutable {
234 findDbusObjects(std::move(probeVector),
235 std::move(interfaces), scan,
236 retries - 1);
237 });
238 return;
James Feist494155a2018-03-14 16:23:24 -0700239 }
James Feist787c3c32019-11-07 14:42:58 -0800240
James Feistb1728ca2020-04-30 15:40:55 -0700241 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800242 {
James Feistb1728ca2020-04-30 15:40:55 -0700243 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700244 {
James Feistb1728ca2020-04-30 15:40:55 -0700245 for (const std::string& iface : ifaces)
246 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600247 // The 3 default org.freedeskstop interfaces (Peer,
248 // Introspectable, and Properties) are returned by
249 // the mapper but don't have properties, so don't bother
250 // with the GetAll call to save some cycles.
251 if (!boost::algorithm::starts_with(iface,
252 "org.freedesktop"))
James Feistb1728ca2020-04-30 15:40:55 -0700253 {
254 interfaceConnections.emplace(busname, path, iface);
255 }
256 }
James Feist8f2710a2018-05-09 17:18:55 -0700257 }
Matt Spinlere789bf12021-02-24 12:33:01 -0600258
259 // Get a PropertiesChanged callback for all
260 // interfaces on this path.
261 registerCallback(scan->_systemConfiguration, scan->objServer,
262 path);
James Feist3cb5fec2018-01-23 14:41:51 -0800263 }
James Feist787c3c32019-11-07 14:42:58 -0800264
James Feist63845bf2019-01-24 12:19:51 -0800265 if (interfaceConnections.empty())
266 {
James Feist63845bf2019-01-24 12:19:51 -0800267 return;
268 }
James Feist787c3c32019-11-07 14:42:58 -0800269
James Feistb1728ca2020-04-30 15:40:55 -0700270 for (const auto& call : interfaceConnections)
271 {
272 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700273 }
274 },
275 "xyz.openbmc_project.ObjectMapper",
276 "/xyz/openbmc_project/object_mapper",
Ed Tanous07d467b2021-02-23 14:48:37 -0800277 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", maxMapperDepth,
James Feist787c3c32019-11-07 14:42:58 -0800278 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700279
Ed Tanous07d467b2021-02-23 14:48:37 -0800280 if constexpr (debug)
James Feistb1728ca2020-04-30 15:40:55 -0700281 {
282 std::cerr << __func__ << " " << __LINE__ << "\n";
283 }
James Feist3cb5fec2018-01-23 14:41:51 -0800284}
James Feistb1728ca2020-04-30 15:40:55 -0700285
James Feist8f2710a2018-05-09 17:18:55 -0700286// probes dbus interface dictionary for a key with a value that matches a regex
Matt Spinlere789bf12021-02-24 12:33:01 -0600287// When an interface passes a probe, also save its D-Bus path with it.
James Feist08a5b172019-08-28 14:47:47 -0700288bool probeDbus(const std::string& interface,
289 const std::map<std::string, nlohmann::json>& matches,
Ed Tanous07d467b2021-02-23 14:48:37 -0800290 FoundDeviceT& devices, const std::shared_ptr<PerformScan>& scan,
James Feist733f7652019-11-13 14:32:29 -0800291 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800292{
James Feist3cb5fec2018-01-23 14:41:51 -0800293 bool foundMatch = false;
Matt Spinlere789bf12021-02-24 12:33:01 -0600294 foundProbe = false;
295
296 for (const auto& [path, interfaces] : scan->dbusProbeObjects)
James Feist3cb5fec2018-01-23 14:41:51 -0800297 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600298 auto it = interfaces.find(interface);
299 if (it == interfaces.end())
James Feist3cb5fec2018-01-23 14:41:51 -0800300 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600301 continue;
302 }
303
304 foundProbe = true;
305
306 bool deviceMatches = true;
307 const boost::container::flat_map<std::string, BasicVariantType>&
308 properties = it->second;
309
310 for (const auto& [matchProp, matchJSON] : matches)
311 {
312 auto deviceValue = properties.find(matchProp);
313 if (deviceValue != properties.end())
James Feist3cb5fec2018-01-23 14:41:51 -0800314 {
Jonathan Doman395c6d42021-05-04 19:25:10 -0700315 deviceMatches =
316 deviceMatches && matchProbe(matchJSON, deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800317 }
318 else
319 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600320 // Move on to the next DBus path
James Feist3cb5fec2018-01-23 14:41:51 -0800321 deviceMatches = false;
322 break;
323 }
324 }
325 if (deviceMatches)
326 {
Ed Tanous07d467b2021-02-23 14:48:37 -0800327 if constexpr (debug)
Matt Spinlere789bf12021-02-24 12:33:01 -0600328 {
329 std::cerr << "probeDBus: Found probe match on " << path << " "
330 << interface << "\n";
331 }
332 devices.emplace_back(properties, path);
James Feist3cb5fec2018-01-23 14:41:51 -0800333 foundMatch = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800334 }
335 }
336 return foundMatch;
337}
338
339// default probe entry point, iterates a list looking for specific types to
340// call specific probe functions
Matt Spinlere789bf12021-02-24 12:33:01 -0600341bool probe(const std::vector<std::string>& probeCommand,
Ed Tanous07d467b2021-02-23 14:48:37 -0800342 const std::shared_ptr<PerformScan>& scan, FoundDeviceT& foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800343{
344 const static std::regex command(R"(\((.*)\))");
345 std::smatch match;
346 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700347 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800348 bool cur = true;
349 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700350 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800351
James Feista465ccc2019-02-08 12:51:01 -0800352 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800353 {
354 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800355 boost::container::flat_map<const char*, probe_type_codes,
Ed Tanous07d467b2021-02-23 14:48:37 -0800356 CmpStr>::const_iterator probeType;
James Feist3cb5fec2018-01-23 14:41:51 -0800357
Ed Tanous07d467b2021-02-23 14:48:37 -0800358 for (probeType = probeTypes.begin(); probeType != probeTypes.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700359 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800360 {
361 if (probe.find(probeType->first) != std::string::npos)
362 {
363 foundProbe = true;
364 break;
365 }
366 }
367 if (foundProbe)
368 {
369 switch (probeType->second)
370 {
James Feist9eb0b582018-04-27 12:15:46 -0700371 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800372 {
James Feiste31e00a2019-07-24 10:45:43 -0700373 cur = false;
374 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800375 }
James Feist9eb0b582018-04-27 12:15:46 -0700376 case probe_type_codes::TRUE_T:
377 {
James Feiste31e00a2019-07-24 10:45:43 -0700378 cur = true;
379 break;
James Feist9eb0b582018-04-27 12:15:46 -0700380 }
381 case probe_type_codes::MATCH_ONE:
382 {
383 // set current value to last, this probe type shouldn't
384 // affect the outcome
385 cur = ret;
386 matchOne = true;
387 break;
388 }
389 /*case probe_type_codes::AND:
390 break;
391 case probe_type_codes::OR:
392 break;
393 // these are no-ops until the last command switch
394 */
395 case probe_type_codes::FOUND:
396 {
397 if (!std::regex_search(probe, match, command))
398 {
James Feist0eb40352019-04-09 14:44:04 -0700399 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700400 << "\n";
401 return false;
402 }
403 std::string commandStr = *(match.begin() + 1);
404 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800405 cur = (std::find(scan->passedProbes.begin(),
406 scan->passedProbes.end(),
407 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700408 break;
409 }
James Feist0eb40352019-04-09 14:44:04 -0700410 default:
411 {
412 break;
413 }
James Feist3cb5fec2018-01-23 14:41:51 -0800414 }
415 }
416 // look on dbus for object
417 else
418 {
419 if (!std::regex_search(probe, match, command))
420 {
James Feist0eb40352019-04-09 14:44:04 -0700421 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800422 return false;
423 }
424 std::string commandStr = *(match.begin() + 1);
425 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700426 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800427 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800428 auto json = nlohmann::json::parse(commandStr, nullptr, false);
429 if (json.is_discarded())
430 {
James Feist0eb40352019-04-09 14:44:04 -0700431 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800432 return false;
433 }
434 // we can match any (string, variant) property. (string, string)
435 // does a regex
436 std::map<std::string, nlohmann::json> dbusProbeMap =
437 json.get<std::map<std::string, nlohmann::json>>();
Ed Tanous07d467b2021-02-23 14:48:37 -0800438 auto findStart = probe.find('(');
James Feist3cb5fec2018-01-23 14:41:51 -0800439 if (findStart == std::string::npos)
440 {
441 return false;
442 }
443 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800444 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
445 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800446 }
447
448 // some functions like AND and OR only take affect after the
449 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700450 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800451 {
James Feist54a0dca2019-06-26 10:34:54 -0700452 ret = cur && ret;
453 }
454 else if (lastCommand == probe_type_codes::OR)
455 {
456 ret = cur || ret;
457 }
458
459 if (first)
460 {
461 ret = cur;
462 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800463 }
Ed Tanous07d467b2021-02-23 14:48:37 -0800464 lastCommand = probeType != probeTypes.end() ? probeType->second
465 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800466 }
467
468 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800469 if (ret && foundDevs.size() == 0)
470 {
James Feist08a5b172019-08-28 14:47:47 -0700471 foundDevs.emplace_back(
Matt Spinlere789bf12021-02-24 12:33:01 -0600472 boost::container::flat_map<std::string, BasicVariantType>{},
473 std::string{});
James Feist3cb5fec2018-01-23 14:41:51 -0800474 }
James Feist0eb40352019-04-09 14:44:04 -0700475 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700476 {
James Feist71f295f2019-06-20 13:35:12 -0700477 // match the last one
478 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700479 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700480
481 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700482 }
James Feist3cb5fec2018-01-23 14:41:51 -0800483 return ret;
484}
485
Matt Spinlere789bf12021-02-24 12:33:01 -0600486PerformProbe::PerformProbe(
487 const std::vector<std::string>& probeCommand,
488 std::shared_ptr<PerformScan>& scanPtr,
489 std::function<void(FoundDeviceT&, const DBusProbeObjectT&)>&& callback) :
James Feist7d807752019-11-13 14:47:57 -0800490 _probeCommand(probeCommand),
491 scan(scanPtr), _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -0700492{}
James Feist7d807752019-11-13 14:47:57 -0800493PerformProbe::~PerformProbe()
494{
495 FoundDeviceT foundDevs;
496 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700497 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600498 _callback(foundDevs, scan->dbusProbeObjects);
James Feist8f2710a2018-05-09 17:18:55 -0700499 }
James Feist7d807752019-11-13 14:47:57 -0800500}
James Feist8f2710a2018-05-09 17:18:55 -0700501
502// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800503bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800504{
James Feist1df06a42019-04-11 14:23:04 -0700505 std::filesystem::create_directory(configurationOutDir);
506 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700507 if (!output.good())
508 {
509 return false;
510 }
James Feist1b2e2242018-01-30 13:45:19 -0800511 output << systemConfiguration.dump(4);
512 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700513 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700514}
James Feist1b2e2242018-01-30 13:45:19 -0800515
James Feist97a63f12018-05-17 13:50:57 -0700516template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800517bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
518 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700519{
520 try
521 {
522 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800523 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700524 ref = value;
525 return true;
526 }
James Feist98132792019-07-09 13:29:09 -0700527 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700528 {
529 return false;
530 }
531}
James Feistbb43d022018-06-12 15:44:33 -0700532
James Feistebcc26b2019-03-22 12:30:43 -0700533// template function to add array as dbus property
534template <typename PropertyType>
535void addArrayToDbus(const std::string& name, const nlohmann::json& array,
536 sdbusplus::asio::dbus_interface* iface,
537 sdbusplus::asio::PropertyPermission permission,
538 nlohmann::json& systemConfiguration,
539 const std::string& jsonPointerString)
540{
541 std::vector<PropertyType> values;
542 for (const auto& property : array)
543 {
544 auto ptr = property.get_ptr<const PropertyType*>();
545 if (ptr != nullptr)
546 {
547 values.emplace_back(*ptr);
548 }
549 }
550
551 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
552 {
553 iface->register_property(name, values);
554 }
555 else
556 {
557 iface->register_property(
558 name, values,
559 [&systemConfiguration,
560 jsonPointerString{std::string(jsonPointerString)}](
561 const std::vector<PropertyType>& newVal,
562 std::vector<PropertyType>& val) {
563 val = newVal;
564 if (!setJsonFromPointer(jsonPointerString, val,
565 systemConfiguration))
566 {
567 std::cerr << "error setting json field\n";
568 return -1;
569 }
570 if (!writeJsonFiles(systemConfiguration))
571 {
572 std::cerr << "error setting json file\n";
573 return -1;
574 }
575 return 1;
576 });
577 }
578}
579
James Feistbb43d022018-06-12 15:44:33 -0700580template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800581void addProperty(const std::string& propertyName, const PropertyType& value,
582 sdbusplus::asio::dbus_interface* iface,
583 nlohmann::json& systemConfiguration,
584 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700585 sdbusplus::asio::PropertyPermission permission)
586{
587 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
588 {
589 iface->register_property(propertyName, value);
590 return;
591 }
James Feist68500ff2018-08-08 15:40:42 -0700592 iface->register_property(
593 propertyName, value,
594 [&systemConfiguration,
595 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800596 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700597 val = newVal;
598 if (!setJsonFromPointer(jsonPointerString, val,
599 systemConfiguration))
600 {
601 std::cerr << "error setting json field\n";
602 return -1;
603 }
James Feistc6248a52018-08-14 10:09:45 -0700604 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700605 {
606 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700607 return -1;
608 }
609 return 1;
610 });
611}
612
613void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800614 const std::string& jsonPointerPath,
615 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
616 sdbusplus::asio::object_server& objServer,
617 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700618{
619 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
620 iface->register_method(
621 "Delete", [&objServer, &systemConfiguration, interface,
622 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700623 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700624 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700625 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700626 {
627 // this technically can't happen as the pointer is pointing to
628 // us
629 throw DBusInternalError();
630 }
631 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700632 systemConfiguration[ptr] = nullptr;
633
James Feist02d2b932020-02-06 16:28:48 -0800634 // todo(james): dig through sdbusplus to find out why we can't
635 // delete it in a method call
636 io.post([&objServer, dbusInterface]() mutable {
637 objServer.remove_interface(dbusInterface);
638 });
639
James Feistc6248a52018-08-14 10:09:45 -0700640 if (!writeJsonFiles(systemConfiguration))
641 {
642 std::cerr << "error setting json file\n";
643 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700644 }
James Feist68500ff2018-08-08 15:40:42 -0700645 });
James Feistbb43d022018-06-12 15:44:33 -0700646}
647
James Feist1b2e2242018-01-30 13:45:19 -0800648// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700649void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800650 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
651 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
652 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700653 sdbusplus::asio::PropertyPermission permission =
654 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800655{
James Feista465ccc2019-02-08 12:51:01 -0800656 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800657 {
James Feist8f2710a2018-05-09 17:18:55 -0700658 auto type = dictPair.value().type();
659 bool array = false;
660 if (dictPair.value().type() == nlohmann::json::value_t::array)
661 {
662 array = true;
663 if (!dictPair.value().size())
664 {
665 continue;
666 }
667 type = dictPair.value()[0].type();
668 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800669 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700670 {
671 if (arrayItem.type() != type)
672 {
673 isLegal = false;
674 break;
675 }
676 }
677 if (!isLegal)
678 {
679 std::cerr << "dbus format error" << dictPair.value() << "\n";
680 continue;
681 }
James Feista218ddb2019-04-11 14:01:31 -0700682 }
683 if (type == nlohmann::json::value_t::object)
684 {
685 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700686 }
James Feist97a63f12018-05-17 13:50:57 -0700687 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700688 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
689 {
690 // all setable numbers are doubles as it is difficult to always
691 // create a configuration file with all whole numbers as decimals
692 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700693 if (array)
694 {
695 if (dictPair.value()[0].is_number())
696 {
697 type = nlohmann::json::value_t::number_float;
698 }
699 }
700 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700701 {
702 type = nlohmann::json::value_t::number_float;
703 }
704 }
705
James Feist8f2710a2018-05-09 17:18:55 -0700706 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800707 {
James Feist9eb0b582018-04-27 12:15:46 -0700708 case (nlohmann::json::value_t::boolean):
709 {
James Feist8f2710a2018-05-09 17:18:55 -0700710 if (array)
711 {
712 // todo: array of bool isn't detected correctly by
713 // sdbusplus, change it to numbers
714 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700715 iface.get(), permission,
716 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700717 }
James Feistbb43d022018-06-12 15:44:33 -0700718
James Feist97a63f12018-05-17 13:50:57 -0700719 else
720 {
James Feistbb43d022018-06-12 15:44:33 -0700721 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700722 iface.get(), systemConfiguration, key,
723 permission);
James Feist97a63f12018-05-17 13:50:57 -0700724 }
James Feist9eb0b582018-04-27 12:15:46 -0700725 break;
726 }
727 case (nlohmann::json::value_t::number_integer):
728 {
James Feist8f2710a2018-05-09 17:18:55 -0700729 if (array)
730 {
731 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700732 iface.get(), permission,
733 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700734 }
735 else
736 {
James Feistbb43d022018-06-12 15:44:33 -0700737 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700738 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700739 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700740 }
James Feist9eb0b582018-04-27 12:15:46 -0700741 break;
742 }
743 case (nlohmann::json::value_t::number_unsigned):
744 {
James Feist8f2710a2018-05-09 17:18:55 -0700745 if (array)
746 {
747 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700748 iface.get(), permission,
749 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700750 }
751 else
752 {
James Feistbb43d022018-06-12 15:44:33 -0700753 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700754 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700755 systemConfiguration, key,
756 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700757 }
James Feist9eb0b582018-04-27 12:15:46 -0700758 break;
759 }
760 case (nlohmann::json::value_t::number_float):
761 {
James Feist8f2710a2018-05-09 17:18:55 -0700762 if (array)
763 {
764 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700765 iface.get(), permission,
766 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700767 }
James Feistbb43d022018-06-12 15:44:33 -0700768
James Feist97a63f12018-05-17 13:50:57 -0700769 else
770 {
James Feistbb43d022018-06-12 15:44:33 -0700771 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700772 iface.get(), systemConfiguration, key,
773 permission);
James Feist97a63f12018-05-17 13:50:57 -0700774 }
James Feist9eb0b582018-04-27 12:15:46 -0700775 break;
776 }
777 case (nlohmann::json::value_t::string):
778 {
James Feist8f2710a2018-05-09 17:18:55 -0700779 if (array)
780 {
James Feistebcc26b2019-03-22 12:30:43 -0700781 addArrayToDbus<std::string>(
782 dictPair.key(), dictPair.value(), iface.get(),
783 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700784 }
785 else
786 {
James Feistc6248a52018-08-14 10:09:45 -0700787 addProperty(
788 dictPair.key(), dictPair.value().get<std::string>(),
789 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700790 }
James Feist9eb0b582018-04-27 12:15:46 -0700791 break;
792 }
James Feist0eb40352019-04-09 14:44:04 -0700793 default:
794 {
James Feista218ddb2019-04-11 14:01:31 -0700795 std::cerr << "Unexpected json type in system configuration "
796 << dictPair.key() << ": "
797 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700798 break;
799 }
James Feist1b2e2242018-01-30 13:45:19 -0800800 }
801 }
James Feistc6248a52018-08-14 10:09:45 -0700802 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
803 {
804 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
805 systemConfiguration);
806 }
James Feist8f2710a2018-05-09 17:18:55 -0700807 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800808}
809
James Feista465ccc2019-02-08 12:51:01 -0800810sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700811{
812 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
813 interface) != settableInterfaces.end()
814 ? sdbusplus::asio::PropertyPermission::readWrite
815 : sdbusplus::asio::PropertyPermission::readOnly;
816}
817
James Feista465ccc2019-02-08 12:51:01 -0800818void createAddObjectMethod(const std::string& jsonPointerPath,
819 const std::string& path,
820 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700821 sdbusplus::asio::object_server& objServer,
822 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700823{
James Feistd58879a2019-09-11 11:26:07 -0700824 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
825 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700826
827 iface->register_method(
828 "AddObject",
829 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700830 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
831 board](const boost::container::flat_map<std::string, JsonVariantType>&
832 data) {
James Feist68500ff2018-08-08 15:40:42 -0700833 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800834 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700835 auto findExposes = base.find("Exposes");
836
837 if (findExposes == base.end())
838 {
839 throw std::invalid_argument("Entity must have children.");
840 }
841
842 // this will throw invalid-argument to sdbusplus if invalid json
843 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800844 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700845 {
James Feista465ccc2019-02-08 12:51:01 -0800846 nlohmann::json& newJson = newData[item.first];
847 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
848 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700849 }
850
851 auto findName = newData.find("Name");
852 auto findType = newData.find("Type");
853 if (findName == newData.end() || findType == newData.end())
854 {
855 throw std::invalid_argument("AddObject missing Name or Type");
856 }
James Feista465ccc2019-02-08 12:51:01 -0800857 const std::string* type = findType->get_ptr<const std::string*>();
858 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700859 if (type == nullptr || name == nullptr)
860 {
861 throw std::invalid_argument("Type and Name must be a string.");
862 }
863
James Feist02d2b932020-02-06 16:28:48 -0800864 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700865 size_t lastIndex = 0;
866 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800867 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700868 {
James Feist02d2b932020-02-06 16:28:48 -0800869 if (expose.is_null())
870 {
871 foundNull = true;
872 continue;
873 }
874
875 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700876 {
877 throw std::invalid_argument(
878 "Field already in JSON, not adding");
879 }
James Feist02d2b932020-02-06 16:28:48 -0800880
881 if (foundNull)
882 {
883 continue;
884 }
885
James Feist68500ff2018-08-08 15:40:42 -0700886 lastIndex++;
887 }
888
889 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
890 *type + ".json");
891 // todo(james) we might want to also make a list of 'can add'
892 // interfaces but for now I think the assumption if there is a
893 // schema avaliable that it is allowed to update is fine
894 if (!schemaFile.good())
895 {
896 throw std::invalid_argument(
897 "No schema avaliable, cannot validate.");
898 }
899 nlohmann::json schema =
900 nlohmann::json::parse(schemaFile, nullptr, false);
901 if (schema.is_discarded())
902 {
903 std::cerr << "Schema not legal" << *type << ".json\n";
904 throw DBusInternalError();
905 }
906 if (!validateJson(schema, newData))
907 {
908 throw std::invalid_argument("Data does not match schema");
909 }
James Feist02d2b932020-02-06 16:28:48 -0800910 if (foundNull)
911 {
912 findExposes->at(lastIndex) = newData;
913 }
914 else
915 {
916 findExposes->push_back(newData);
917 }
James Feist68500ff2018-08-08 15:40:42 -0700918 if (!writeJsonFiles(systemConfiguration))
919 {
920 std::cerr << "Error writing json files\n";
921 throw DBusInternalError();
922 }
923 std::string dbusName = *name;
924
925 std::regex_replace(dbusName.begin(), dbusName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800926 dbusName.end(), illegalDbusMemberRegex, "_");
James Feistd58879a2019-09-11 11:26:07 -0700927
928 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
929 createInterface(objServer, path + "/" + dbusName,
930 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800931 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700932 // permission is read-write, as since we just created it, must be
933 // runtime modifiable
934 populateInterfaceFromJson(
935 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700936 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700937 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700938 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700939 });
940 iface->initialize();
941}
942
James Feista465ccc2019-02-08 12:51:01 -0800943void postToDbus(const nlohmann::json& newConfiguration,
944 nlohmann::json& systemConfiguration,
945 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800946
James Feist1b2e2242018-01-30 13:45:19 -0800947{
James Feist97a63f12018-05-17 13:50:57 -0700948 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800949 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800950 {
James Feistf1b14142019-04-10 15:22:09 -0700951 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700952 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700953 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700954 // loop through newConfiguration, but use values from system
955 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700956 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700957 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800958 std::string boardType;
959 if (findBoardType != boardValues.end() &&
960 findBoardType->type() == nlohmann::json::value_t::string)
961 {
962 boardType = findBoardType->get<std::string>();
963 std::regex_replace(boardType.begin(), boardType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800964 boardType.end(), illegalDbusMemberRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800965 }
966 else
967 {
968 std::cerr << "Unable to find type for " << boardKey
969 << " reverting to Chassis.\n";
970 boardType = "Chassis";
971 }
James Feist11be6672018-04-06 14:05:32 -0700972 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800973
974 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Ed Tanous07d467b2021-02-23 14:48:37 -0800975 illegalDbusMemberRegex, "_");
976 std::string boardName = "/xyz/openbmc_project/inventory/system/";
977 boardName += boardtypeLower;
978 boardName += "/";
979 boardName += boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800980
James Feistd58879a2019-09-11 11:26:07 -0700981 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
982 createInterface(objServer, boardName,
983 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700984
James Feistd58879a2019-09-11 11:26:07 -0700985 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
986 createInterface(objServer, boardName,
987 "xyz.openbmc_project.Inventory.Item." + boardType,
988 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700989
James Feist68500ff2018-08-08 15:40:42 -0700990 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700991 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700992
James Feist97a63f12018-05-17 13:50:57 -0700993 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700994 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700995 jsonPointerPath += "/";
996 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800997 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700998 {
999 if (boardField.value().type() == nlohmann::json::value_t::object)
1000 {
James Feistd58879a2019-09-11 11:26:07 -07001001 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
1002 createInterface(objServer, boardName, boardField.key(),
1003 boardKeyOrig);
1004
James Feistc6248a52018-08-14 10:09:45 -07001005 populateInterfaceFromJson(systemConfiguration,
1006 jsonPointerPath + boardField.key(),
1007 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -07001008 }
1009 }
James Feist97a63f12018-05-17 13:50:57 -07001010
James Feist1e3e6982018-08-03 16:09:28 -07001011 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001012 if (exposes == boardValues.end())
1013 {
1014 continue;
1015 }
James Feist97a63f12018-05-17 13:50:57 -07001016 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001017 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001018
1019 // store the board level pointer so we can modify it on the way down
1020 std::string jsonPointerPathBoard = jsonPointerPath;
1021 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001022 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001023 {
James Feist97a63f12018-05-17 13:50:57 -07001024 exposesIndex++;
1025 jsonPointerPath = jsonPointerPathBoard;
1026 jsonPointerPath += std::to_string(exposesIndex);
1027
James Feistd63d18a2018-07-19 15:23:45 -07001028 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001029 if (findName == item.end())
1030 {
1031 std::cerr << "cannot find name in field " << item << "\n";
1032 continue;
1033 }
James Feist1e3e6982018-08-03 16:09:28 -07001034 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001035 // if status is not found it is assumed to be status = 'okay'
1036 if (findStatus != item.end())
1037 {
1038 if (*findStatus == "disabled")
1039 {
1040 continue;
1041 }
1042 }
James Feistd63d18a2018-07-19 15:23:45 -07001043 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001044 std::string itemType;
1045 if (findType != item.end())
1046 {
1047 itemType = findType->get<std::string>();
1048 std::regex_replace(itemType.begin(), itemType.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -08001049 itemType.end(), illegalDbusPathRegex, "_");
James Feist1b2e2242018-01-30 13:45:19 -08001050 }
1051 else
1052 {
1053 itemType = "unknown";
1054 }
1055 std::string itemName = findName->get<std::string>();
1056 std::regex_replace(itemName.begin(), itemName.begin(),
Ed Tanous07d467b2021-02-23 14:48:37 -08001057 itemName.end(), illegalDbusMemberRegex, "_");
1058 std::string ifacePath = boardName;
1059 ifacePath += "/";
1060 ifacePath += itemName;
James Feistc6248a52018-08-14 10:09:45 -07001061
James Feistd58879a2019-09-11 11:26:07 -07001062 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
Ed Tanous07d467b2021-02-23 14:48:37 -08001063 createInterface(objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -07001064 "xyz.openbmc_project.Configuration." + itemType,
1065 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001066
James Feist97a63f12018-05-17 13:50:57 -07001067 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001068 itemIface, item, objServer,
1069 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001070
James Feista465ccc2019-02-08 12:51:01 -08001071 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001072 {
James Feist97a63f12018-05-17 13:50:57 -07001073 jsonPointerPath = jsonPointerPathBoard +
1074 std::to_string(exposesIndex) + "/" +
1075 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001076 if (objectPair.value().type() ==
1077 nlohmann::json::value_t::object)
1078 {
James Feistd58879a2019-09-11 11:26:07 -07001079 std::shared_ptr<sdbusplus::asio::dbus_interface>
1080 objectIface = createInterface(
Ed Tanous07d467b2021-02-23 14:48:37 -08001081 objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -07001082 "xyz.openbmc_project.Configuration." + itemType +
1083 "." + objectPair.key(),
1084 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001085
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +02001086 populateInterfaceFromJson(systemConfiguration,
1087 jsonPointerPath, objectIface,
1088 objectPair.value(), objServer,
1089 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -08001090 }
1091 else if (objectPair.value().type() ==
1092 nlohmann::json::value_t::array)
1093 {
1094 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001095 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001096 {
James Feist8f2710a2018-05-09 17:18:55 -07001097 continue;
1098 }
1099 bool isLegal = true;
1100 auto type = objectPair.value()[0].type();
1101 if (type != nlohmann::json::value_t::object)
1102 {
1103 continue;
1104 }
1105
1106 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001107 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001108 {
1109 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001110 {
James Feist8f2710a2018-05-09 17:18:55 -07001111 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001112 break;
1113 }
James Feist8f2710a2018-05-09 17:18:55 -07001114 }
1115 if (!isLegal)
1116 {
1117 std::cerr << "dbus format error" << objectPair.value()
1118 << "\n";
1119 break;
1120 }
1121
James Feista465ccc2019-02-08 12:51:01 -08001122 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001123 {
James Feist97a63f12018-05-17 13:50:57 -07001124
James Feistd58879a2019-09-11 11:26:07 -07001125 std::shared_ptr<sdbusplus::asio::dbus_interface>
1126 objectIface = createInterface(
Ed Tanous07d467b2021-02-23 14:48:37 -08001127 objServer, ifacePath,
James Feistd58879a2019-09-11 11:26:07 -07001128 "xyz.openbmc_project.Configuration." +
1129 itemType + "." + objectPair.key() +
1130 std::to_string(index),
1131 boardKeyOrig);
1132
James Feistc6248a52018-08-14 10:09:45 -07001133 populateInterfaceFromJson(
1134 systemConfiguration,
1135 jsonPointerPath + "/" + std::to_string(index),
1136 objectIface, arrayItem, objServer,
1137 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001138 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001139 }
1140 }
1141 }
1142 }
1143 }
1144}
1145
James Feist8f2710a2018-05-09 17:18:55 -07001146// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001147bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001148{
1149 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001150 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001151 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1152 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001153 {
1154 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001155 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001156 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001157 }
James Feistb4383f42018-08-06 16:54:10 -07001158
1159 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1160 globalSchema);
1161 if (!schemaStream.good())
1162 {
1163 std::cerr
1164 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1165 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001166 return false;
James Feistb4383f42018-08-06 16:54:10 -07001167 }
1168 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1169 if (schema.is_discarded())
1170 {
1171 std::cerr
1172 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1173 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001174 return false;
James Feistb4383f42018-08-06 16:54:10 -07001175 }
1176
James Feista465ccc2019-02-08 12:51:01 -08001177 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001178 {
1179 std::ifstream jsonStream(jsonPath.c_str());
1180 if (!jsonStream.good())
1181 {
1182 std::cerr << "unable to open " << jsonPath.string() << "\n";
1183 continue;
1184 }
1185 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1186 if (data.is_discarded())
1187 {
1188 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1189 continue;
1190 }
James Feist8da99192019-01-24 08:20:16 -08001191 /*
1192 * todo(james): reenable this once less things are in flight
1193 *
James Feistb4383f42018-08-06 16:54:10 -07001194 if (!validateJson(schema, data))
1195 {
1196 std::cerr << "Error validating " << jsonPath.string() << "\n";
1197 continue;
1198 }
James Feist8da99192019-01-24 08:20:16 -08001199 */
James Feistb4383f42018-08-06 16:54:10 -07001200
James Feist3cb5fec2018-01-23 14:41:51 -08001201 if (data.type() == nlohmann::json::value_t::array)
1202 {
James Feista465ccc2019-02-08 12:51:01 -08001203 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001204 {
1205 configurations.emplace_back(d);
1206 }
1207 }
1208 else
1209 {
1210 configurations.emplace_back(data);
1211 }
1212 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001213 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001214}
James Feist3cb5fec2018-01-23 14:41:51 -08001215
James Feist94219d12020-03-03 11:52:25 -08001216std::string getRecordName(
1217 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1218 const std::string& probeName)
1219{
1220 if (probe.empty())
1221 {
1222 return probeName;
1223 }
1224
1225 // use an array so alphabetical order from the
1226 // flat_map is maintained
1227 auto device = nlohmann::json::array();
1228 for (auto& devPair : probe)
1229 {
1230 device.push_back(devPair.first);
1231 std::visit([&device](auto&& v) { device.push_back(v); },
1232 devPair.second);
1233 }
1234 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1235 // hashes are hard to distinguish, use the
1236 // non-hashed version if we want debug
Ed Tanous07d467b2021-02-23 14:48:37 -08001237 if constexpr (debug)
James Feist94219d12020-03-03 11:52:25 -08001238 {
1239 return probeName + device.dump();
1240 }
1241 else
1242 {
1243 return std::to_string(hash);
1244 }
1245}
1246
James Feist4dc617b2020-05-01 09:54:47 -07001247PerformScan::PerformScan(nlohmann::json& systemConfiguration,
1248 nlohmann::json& missingConfigurations,
1249 std::list<nlohmann::json>& configurations,
1250 sdbusplus::asio::object_server& objServerIn,
1251 std::function<void()>&& callback) :
James Feist733f7652019-11-13 14:32:29 -08001252 _systemConfiguration(systemConfiguration),
1253 _missingConfigurations(missingConfigurations),
James Feist4dc617b2020-05-01 09:54:47 -07001254 _configurations(configurations), objServer(objServerIn),
1255 _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -07001256{}
James Feist733f7652019-11-13 14:32:29 -08001257void PerformScan::run()
1258{
1259 boost::container::flat_set<std::string> dbusProbeInterfaces;
1260 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001261
James Feist733f7652019-11-13 14:32:29 -08001262 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001263 {
James Feist733f7652019-11-13 14:32:29 -08001264 auto findProbe = it->find("Probe");
1265 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001266
James Feist733f7652019-11-13 14:32:29 -08001267 nlohmann::json probeCommand;
1268 // check for poorly formatted fields, probe must be an array
1269 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001270 {
James Feist733f7652019-11-13 14:32:29 -08001271 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1272 it = _configurations.erase(it);
1273 continue;
1274 }
Ed Tanous07d467b2021-02-23 14:48:37 -08001275 if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist733f7652019-11-13 14:32:29 -08001276 {
1277 probeCommand = nlohmann::json::array();
1278 probeCommand.push_back(*findProbe);
1279 }
1280 else
1281 {
1282 probeCommand = *findProbe;
1283 }
James Feist3cb5fec2018-01-23 14:41:51 -08001284
James Feist733f7652019-11-13 14:32:29 -08001285 if (findName == it->end())
1286 {
1287 std::cerr << "configuration file missing name:\n " << *it << "\n";
1288 it = _configurations.erase(it);
1289 continue;
1290 }
1291 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001292
James Feist733f7652019-11-13 14:32:29 -08001293 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1294 passedProbes.end())
1295 {
1296 it = _configurations.erase(it);
1297 continue;
1298 }
1299 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001300
James Feist733f7652019-11-13 14:32:29 -08001301 // store reference to this to children to makes sure we don't get
1302 // destroyed too early
1303 auto thisRef = shared_from_this();
1304 auto probePointer = std::make_shared<PerformProbe>(
1305 probeCommand, thisRef,
Matt Spinlere789bf12021-02-24 12:33:01 -06001306 [&, recordPtr, probeName](FoundDeviceT& foundDevices,
1307 const DBusProbeObjectT& allInterfaces) {
James Feist08a5b172019-08-28 14:47:47 -07001308 _passed = true;
James Feist35f5e0e2020-03-16 14:02:27 -07001309 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001310 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001311 std::list<size_t> indexes(foundDevices.size());
1312 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001313
Ed Tanous07d467b2021-02-23 14:48:37 -08001314 size_t indexIdx = probeName.find('$');
James Feist35f5e0e2020-03-16 14:02:27 -07001315 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001316
1317 // copy over persisted configurations and make sure we remove
1318 // indexes that are already used
1319 for (auto itr = foundDevices.begin();
1320 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001321 {
Matt Spinlere789bf12021-02-24 12:33:01 -06001322 std::string recordName =
1323 getRecordName(std::get<0>(*itr), probeName);
James Feistf1b14142019-04-10 15:22:09 -07001324
James Feist08a5b172019-08-28 14:47:47 -07001325 auto fromLastJson = lastJson.find(recordName);
1326 if (fromLastJson != lastJson.end())
1327 {
James Feist02d2b932020-02-06 16:28:48 -08001328 auto findExposes = fromLastJson->find("Exposes");
1329 // delete nulls from any updates
1330 if (findExposes != fromLastJson->end())
1331 {
1332 auto copy = nlohmann::json::array();
1333 for (auto& expose : *findExposes)
1334 {
1335 if (expose.is_null())
1336 {
1337 continue;
1338 }
1339 copy.emplace_back(expose);
1340 }
1341 *findExposes = copy;
1342 }
1343
James Feist08a5b172019-08-28 14:47:47 -07001344 // keep user changes
1345 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001346 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001347 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001348 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001349 {
1350 auto nameIt = fromLastJson->find("Name");
1351 if (nameIt == fromLastJson->end())
1352 {
1353 std::cerr << "Last JSON Illegal\n";
1354 continue;
1355 }
Igor Kononenko9fd87e52020-10-06 01:18:17 +03001356 int index = 0;
1357 auto str =
1358 nameIt->get<std::string>().substr(indexIdx);
1359 auto [p, ec] = std::from_chars(
1360 str.data(), str.data() + str.size(), index);
1361 if (ec != std::errc())
1362 {
1363 continue; // non-numeric replacement
1364 }
James Feist35f5e0e2020-03-16 14:02:27 -07001365 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001366 auto usedIt = std::find(indexes.begin(),
1367 indexes.end(), index);
1368
1369 if (usedIt == indexes.end())
1370 {
1371 continue; // less items now
1372 }
1373 indexes.erase(usedIt);
1374 }
1375
James Feist08a5b172019-08-28 14:47:47 -07001376 continue;
1377 }
James Feist94219d12020-03-03 11:52:25 -08001378 itr++;
1379 }
James Feist35f5e0e2020-03-16 14:02:27 -07001380
1381 std::optional<std::string> replaceStr;
1382
Matt Spinler4bab9322021-04-08 14:38:43 -05001383 DBusProbeObjectT::mapped_type emptyInterfaces;
1384 boost::container::flat_map<std::string, BasicVariantType>
1385 emptyProps;
1386 emptyInterfaces.emplace(std::string{}, emptyProps);
1387
Matt Spinlere789bf12021-02-24 12:33:01 -06001388 for (auto& foundDeviceAndPath : foundDevices)
James Feist94219d12020-03-03 11:52:25 -08001389 {
Matt Spinlere789bf12021-02-24 12:33:01 -06001390 const boost::container::flat_map<
1391 std::string, BasicVariantType>& foundDevice =
1392 std::get<0>(foundDeviceAndPath);
1393 const std::string& path = std::get<1>(foundDeviceAndPath);
1394
1395 // Need all interfaces on this path so that template
1396 // substitutions can be done with any of the contained
Matt Spinler4bab9322021-04-08 14:38:43 -05001397 // properties. If the probe that passed didn't use an
1398 // interface, such as if it was just TRUE, then
1399 // templateCharReplace will just get passed in an empty
1400 // map.
1401 const DBusProbeObjectT::mapped_type* allInterfacesOnPath =
1402 &emptyInterfaces;
1403
1404 auto ifacesIt = allInterfaces.find(path);
1405 if (ifacesIt != allInterfaces.end())
Matt Spinlere789bf12021-02-24 12:33:01 -06001406 {
Matt Spinler4bab9322021-04-08 14:38:43 -05001407 allInterfacesOnPath = &ifacesIt->second;
Matt Spinlere789bf12021-02-24 12:33:01 -06001408 }
1409
James Feist94219d12020-03-03 11:52:25 -08001410 nlohmann::json record = *recordPtr;
1411 std::string recordName =
1412 getRecordName(foundDevice, probeName);
1413 size_t foundDeviceIdx = indexes.front();
1414 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001415
James Feist35f5e0e2020-03-16 14:02:27 -07001416 // check name first so we have no duplicate names
1417 auto getName = record.find("Name");
1418 if (getName == record.end())
1419 {
1420 std::cerr << "Record Missing Name! " << record.dump();
1421 continue; // this should be impossible at this level
1422 }
1423
1424 nlohmann::json copyForName = {{"Name", getName.value()}};
1425 nlohmann::json::iterator copyIt = copyForName.begin();
Matt Spinlere789bf12021-02-24 12:33:01 -06001426 std::optional<std::string> replaceVal =
Matt Spinler4bab9322021-04-08 14:38:43 -05001427 templateCharReplace(copyIt, *allInterfacesOnPath,
Matt Spinlere789bf12021-02-24 12:33:01 -06001428 foundDeviceIdx, replaceStr);
James Feist35f5e0e2020-03-16 14:02:27 -07001429
1430 if (!replaceStr && replaceVal)
1431 {
1432 if (usedNames.find(copyIt.value()) != usedNames.end())
1433 {
1434 replaceStr = replaceVal;
1435 copyForName = {{"Name", getName.value()}};
1436 copyIt = copyForName.begin();
Matt Spinler4bab9322021-04-08 14:38:43 -05001437 templateCharReplace(copyIt, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001438 foundDeviceIdx, replaceStr);
1439 }
1440 }
1441
1442 if (replaceStr)
1443 {
1444 std::cerr << "Duplicates found, replacing "
1445 << *replaceStr
1446 << " with found device index.\n Consider "
1447 "fixing template to not have duplicates\n";
1448 }
James Feist08a5b172019-08-28 14:47:47 -07001449
1450 for (auto keyPair = record.begin(); keyPair != record.end();
1451 keyPair++)
1452 {
James Feist35f5e0e2020-03-16 14:02:27 -07001453 if (keyPair.key() == "Name")
1454 {
1455 keyPair.value() = copyIt.value();
1456 usedNames.insert(copyIt.value());
1457
1458 continue; // already covered above
1459 }
Matt Spinler4bab9322021-04-08 14:38:43 -05001460 templateCharReplace(keyPair, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001461 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001462 }
1463
James Feist35f5e0e2020-03-16 14:02:27 -07001464 // insert into configuration temporarily to be able to
1465 // reference ourselves
1466
1467 _systemConfiguration[recordName] = record;
1468
James Feist08a5b172019-08-28 14:47:47 -07001469 auto findExpose = record.find("Exposes");
1470 if (findExpose == record.end())
1471 {
James Feistf1b14142019-04-10 15:22:09 -07001472 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001473 continue;
1474 }
James Feistf1b14142019-04-10 15:22:09 -07001475
James Feist08a5b172019-08-28 14:47:47 -07001476 for (auto& expose : *findExpose)
1477 {
1478 for (auto keyPair = expose.begin();
1479 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001480 {
James Feist08a5b172019-08-28 14:47:47 -07001481
Matt Spinler4bab9322021-04-08 14:38:43 -05001482 templateCharReplace(keyPair, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001483 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001484
James Feist668bbb12020-02-05 14:27:26 -08001485 bool isBind =
1486 boost::starts_with(keyPair.key(), "Bind");
1487 bool isDisable = keyPair.key() == "DisableNode";
1488
1489 // special cases
1490 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001491 {
James Feist668bbb12020-02-05 14:27:26 -08001492 continue;
1493 }
1494
1495 if (keyPair.value().type() !=
1496 nlohmann::json::value_t::string &&
1497 keyPair.value().type() !=
1498 nlohmann::json::value_t::array)
1499 {
1500 std::cerr << "Value is invalid type "
1501 << keyPair.key() << "\n";
1502 continue;
1503 }
1504
1505 std::vector<std::string> matches;
1506 if (keyPair.value().type() ==
1507 nlohmann::json::value_t::string)
1508 {
1509 matches.emplace_back(keyPair.value());
1510 }
1511 else
1512 {
1513 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001514 {
James Feist668bbb12020-02-05 14:27:26 -08001515 if (value.type() !=
1516 nlohmann::json::value_t::string)
1517 {
1518 std::cerr << "Value is invalid type "
1519 << value << "\n";
1520 break;
1521 }
1522 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001523 }
James Feist668bbb12020-02-05 14:27:26 -08001524 }
James Feist08a5b172019-08-28 14:47:47 -07001525
James Feist668bbb12020-02-05 14:27:26 -08001526 std::set<std::string> foundMatches;
1527 for (auto& configurationPair :
1528 _systemConfiguration.items())
1529 {
1530 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001531 {
James Feist668bbb12020-02-05 14:27:26 -08001532 // don't disable ourselves
1533 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001534 {
James Feist1b2e2242018-01-30 13:45:19 -08001535 continue;
1536 }
James Feist668bbb12020-02-05 14:27:26 -08001537 }
1538 auto configListFind =
1539 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001540
James Feist668bbb12020-02-05 14:27:26 -08001541 if (configListFind ==
1542 configurationPair.value().end() ||
1543 configListFind->type() !=
1544 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001545 {
James Feist668bbb12020-02-05 14:27:26 -08001546 continue;
James Feist08a5b172019-08-28 14:47:47 -07001547 }
James Feist668bbb12020-02-05 14:27:26 -08001548 for (auto& exposedObject : *configListFind)
1549 {
1550 auto matchIt = std::find_if(
1551 matches.begin(), matches.end(),
1552 [name = (exposedObject)["Name"]
1553 .get<std::string>()](
1554 const std::string& s) {
1555 return s == name;
1556 });
1557 if (matchIt == matches.end())
1558 {
1559 continue;
1560 }
1561 foundMatches.insert(*matchIt);
1562
1563 if (isBind)
1564 {
1565 std::string bind = keyPair.key().substr(
1566 sizeof("Bind") - 1);
1567
1568 exposedObject["Status"] = "okay";
1569 expose[bind] = exposedObject;
1570 }
1571 else if (isDisable)
1572 {
1573 exposedObject["Status"] = "disabled";
1574 }
1575 }
1576 }
1577 if (foundMatches.size() != matches.size())
1578 {
1579 std::cerr << "configuration file "
1580 "dependency error, "
1581 "could not find "
1582 << keyPair.key() << " "
1583 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001584 }
1585 }
1586 }
James Feist08a5b172019-08-28 14:47:47 -07001587 // overwrite ourselves with cleaned up version
1588 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001589 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001590 }
1591 });
James Feist787c3c32019-11-07 14:42:58 -08001592
James Feist733f7652019-11-13 14:32:29 -08001593 // parse out dbus probes by discarding other probe types, store in a
1594 // map
Ed Tanous07d467b2021-02-23 14:48:37 -08001595 for (const nlohmann::json& probeJson : probeCommand)
James Feist733f7652019-11-13 14:32:29 -08001596 {
Ed Tanous07d467b2021-02-23 14:48:37 -08001597 const std::string* probe = probeJson.get_ptr<const std::string*>();
1598 if (probe == nullptr)
1599 {
1600 std::cerr << "Probe statement wasn't a string, can't parse";
1601 continue;
1602 }
James Feist733f7652019-11-13 14:32:29 -08001603 bool found = false;
1604 boost::container::flat_map<const char*, probe_type_codes,
Ed Tanous07d467b2021-02-23 14:48:37 -08001605 CmpStr>::const_iterator probeType;
1606 for (probeType = probeTypes.begin(); probeType != probeTypes.end();
1607 ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001608 {
Ed Tanous07d467b2021-02-23 14:48:37 -08001609 if (probe->find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001610 {
James Feist733f7652019-11-13 14:32:29 -08001611 found = true;
1612 break;
James Feist787c3c32019-11-07 14:42:58 -08001613 }
James Feist787c3c32019-11-07 14:42:58 -08001614 }
James Feist733f7652019-11-13 14:32:29 -08001615 if (found)
1616 {
1617 continue;
1618 }
1619 // syntax requires probe before first open brace
Ed Tanous07d467b2021-02-23 14:48:37 -08001620 auto findStart = probe->find('(');
1621 std::string interface = probe->substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -08001622 dbusProbeInterfaces.emplace(interface);
1623 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001624 }
James Feist733f7652019-11-13 14:32:29 -08001625 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001626 }
James Feist75fdeeb2018-02-20 14:26:16 -08001627
James Feist733f7652019-11-13 14:32:29 -08001628 // probe vector stores a shared_ptr to each PerformProbe that cares
1629 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001630 findDbusObjects(std::move(dbusProbePointers),
1631 std::move(dbusProbeInterfaces), shared_from_this());
Ed Tanous07d467b2021-02-23 14:48:37 -08001632 if constexpr (debug)
James Feistb1728ca2020-04-30 15:40:55 -07001633 {
1634 std::cerr << __func__ << " " << __LINE__ << "\n";
1635 }
James Feist733f7652019-11-13 14:32:29 -08001636}
1637
1638PerformScan::~PerformScan()
1639{
1640 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001641 {
James Feist733f7652019-11-13 14:32:29 -08001642 auto nextScan = std::make_shared<PerformScan>(
1643 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001644 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001645 nextScan->passedProbes = std::move(passedProbes);
1646 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1647 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001648
Ed Tanous07d467b2021-02-23 14:48:37 -08001649 if constexpr (debug)
James Feistb1728ca2020-04-30 15:40:55 -07001650 {
1651 std::cerr << __func__ << " " << __LINE__ << "\n";
1652 }
James Feist8f2710a2018-05-09 17:18:55 -07001653 }
James Feist733f7652019-11-13 14:32:29 -08001654 else
1655 {
James Feist4dc617b2020-05-01 09:54:47 -07001656 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001657
Ed Tanous07d467b2021-02-23 14:48:37 -08001658 if constexpr (debug)
James Feistb1728ca2020-04-30 15:40:55 -07001659 {
1660 std::cerr << __func__ << " " << __LINE__ << "\n";
1661 }
James Feist733f7652019-11-13 14:32:29 -08001662 }
1663}
James Feistc95cb142018-02-26 10:41:42 -08001664
James Feistb1728ca2020-04-30 15:40:55 -07001665void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001666 nlohmann::json& systemConfiguration)
1667{
1668 static bool scannedPowerOff = false;
1669 static bool scannedPowerOn = false;
1670
James Feistfb00f392019-06-25 14:16:48 -07001671 if (systemConfiguration.empty() || lastJson.empty())
1672 {
1673 return; // not ready yet
1674 }
James Feist1df06a42019-04-11 14:23:04 -07001675 if (scannedPowerOn)
1676 {
1677 return;
1678 }
1679
1680 if (!isPowerOn() && scannedPowerOff)
1681 {
1682 return;
1683 }
1684
James Feistb1728ca2020-04-30 15:40:55 -07001685 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001686 timer.async_wait(
1687 [&systemConfiguration](const boost::system::error_code& ec) {
1688 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001689 {
James Feist1a996582019-05-14 15:10:06 -07001690 // we were cancelled
1691 return;
1692 }
1693
1694 bool powerOff = !isPowerOn();
1695 for (const auto& item : lastJson.items())
1696 {
1697 if (systemConfiguration.find(item.key()) ==
1698 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001699 {
James Feist1a996582019-05-14 15:10:06 -07001700 bool isDetectedPowerOn = false;
1701 auto powerState = item.value().find("PowerState");
1702 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001703 {
James Feist1a996582019-05-14 15:10:06 -07001704 auto ptr = powerState->get_ptr<const std::string*>();
1705 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001706 {
James Feist1a996582019-05-14 15:10:06 -07001707 if (*ptr == "On" || *ptr == "BiosPost")
1708 {
1709 isDetectedPowerOn = true;
1710 }
James Feist1df06a42019-04-11 14:23:04 -07001711 }
1712 }
James Feist1a996582019-05-14 15:10:06 -07001713 if (powerOff && isDetectedPowerOn)
1714 {
1715 // power not on yet, don't know if it's there or not
1716 continue;
1717 }
1718 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1719 {
1720 // already logged it when power was off
1721 continue;
1722 }
James Feist1df06a42019-04-11 14:23:04 -07001723
James Feist1a996582019-05-14 15:10:06 -07001724 logDeviceRemoved(item.value());
1725 }
James Feist1df06a42019-04-11 14:23:04 -07001726 }
James Feist1a996582019-05-14 15:10:06 -07001727 scannedPowerOff = true;
1728 if (!powerOff)
1729 {
1730 scannedPowerOn = true;
1731 }
1732 });
James Feist1df06a42019-04-11 14:23:04 -07001733}
1734
James Feist8f2710a2018-05-09 17:18:55 -07001735// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001736void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1737 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001738{
James Feist2539ccd2020-05-01 16:15:08 -07001739 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001740 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001741 static size_t instance = 0;
1742 instance++;
1743 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001744
James Feistb1728ca2020-04-30 15:40:55 -07001745 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001746
1747 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001748 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001749 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001750 if (ec == boost::asio::error::operation_aborted)
1751 {
1752 // we were cancelled
1753 return;
1754 }
Ed Tanous07d467b2021-02-23 14:48:37 -08001755 if (ec)
James Feist8f2710a2018-05-09 17:18:55 -07001756 {
1757 std::cerr << "async wait error " << ec << "\n";
1758 return;
1759 }
1760
James Feist2539ccd2020-05-01 16:15:08 -07001761 if (inProgress)
1762 {
1763 propertiesChangedCallback(systemConfiguration, objServer);
1764 return;
1765 }
1766 inProgress = true;
1767
James Feist8f2710a2018-05-09 17:18:55 -07001768 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001769 auto missingConfigurations = std::make_shared<nlohmann::json>();
1770 *missingConfigurations = systemConfiguration;
1771
James Feist8f2710a2018-05-09 17:18:55 -07001772 std::list<nlohmann::json> configurations;
1773 if (!findJsonFiles(configurations))
1774 {
1775 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001776 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001777 return;
1778 }
1779
1780 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001781 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001782 objServer,
1783 [&systemConfiguration, &objServer, count, oldConfiguration,
1784 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001785 // this is something that since ac has been applied to the bmc
1786 // we saw, and we no longer see it
1787 bool powerOff = !isPowerOn();
1788 for (const auto& item : missingConfigurations->items())
1789 {
1790 bool isDetectedPowerOn = false;
1791 auto powerState = item.value().find("PowerState");
1792 if (powerState != item.value().end())
1793 {
1794 auto ptr = powerState->get_ptr<const std::string*>();
1795 if (ptr)
1796 {
1797 if (*ptr == "On" || *ptr == "BiosPost")
1798 {
1799 isDetectedPowerOn = true;
1800 }
1801 }
1802 }
1803 if (powerOff && isDetectedPowerOn)
1804 {
1805 // power not on yet, don't know if it's there or not
1806 continue;
1807 }
1808 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001809 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001810 ifaces = inventory[name];
1811 for (auto& iface : ifaces)
1812 {
James Feist02d2b932020-02-06 16:28:48 -08001813 auto sharedPtr = iface.lock();
1814 if (!sharedPtr)
1815 {
1816 continue; // was already deleted elsewhere
1817 }
1818 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001819 }
1820 ifaces.clear();
1821 systemConfiguration.erase(item.key());
1822 logDeviceRemoved(item.value());
1823 }
1824
James Feist8f2710a2018-05-09 17:18:55 -07001825 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001826 for (auto it = newConfiguration.begin();
1827 it != newConfiguration.end();)
1828 {
1829 auto findKey = oldConfiguration.find(it.key());
1830 if (findKey != oldConfiguration.end())
1831 {
1832 it = newConfiguration.erase(it);
1833 }
1834 else
1835 {
1836 it++;
1837 }
1838 }
James Feist899e17f2019-09-13 11:46:29 -07001839 for (const auto& item : newConfiguration.items())
1840 {
1841 logDeviceAdded(item.value());
1842 }
1843
James Feist2539ccd2020-05-01 16:15:08 -07001844 inProgress = false;
1845
Jonathan Doman6d649822021-05-05 16:53:04 -07001846 io.post([count, newConfiguration, &systemConfiguration,
1847 &objServer]() {
James Feist8f2710a2018-05-09 17:18:55 -07001848 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001849
Jonathan Doman6d649822021-05-05 16:53:04 -07001850 io.post([&systemConfiguration]() {
James Feistbb43d022018-06-12 15:44:33 -07001851 if (!writeJsonFiles(systemConfiguration))
1852 {
1853 std::cerr << "Error writing json files\n";
1854 }
1855 });
Jonathan Doman6d649822021-05-05 16:53:04 -07001856 io.post([count, newConfiguration, &systemConfiguration,
1857 &objServer]() {
James Feist97a63f12018-05-17 13:50:57 -07001858 postToDbus(newConfiguration, systemConfiguration,
1859 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001860 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001861 {
James Feist899e17f2019-09-13 11:46:29 -07001862 return;
James Feist1df06a42019-04-11 14:23:04 -07001863 }
James Feist899e17f2019-09-13 11:46:29 -07001864 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001865 });
1866 });
1867 });
1868 perfScan->run();
1869 });
James Feist75fdeeb2018-02-20 14:26:16 -08001870}
1871
James Feist4dc617b2020-05-01 09:54:47 -07001872void registerCallback(nlohmann::json& systemConfiguration,
1873 sdbusplus::asio::object_server& objServer,
Matt Spinlere789bf12021-02-24 12:33:01 -06001874 const std::string& path)
James Feist75fdeeb2018-02-20 14:26:16 -08001875{
James Feist4dc617b2020-05-01 09:54:47 -07001876 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1877 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001878
Matt Spinlere789bf12021-02-24 12:33:01 -06001879 auto find = dbusMatches.find(path);
James Feist4dc617b2020-05-01 09:54:47 -07001880 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001881 {
James Feist4dc617b2020-05-01 09:54:47 -07001882 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001883 }
James Feist4dc617b2020-05-01 09:54:47 -07001884 std::function<void(sdbusplus::message::message & message)> eventHandler =
1885
1886 [&](sdbusplus::message::message&) {
1887 propertiesChangedCallback(systemConfiguration, objServer);
1888 };
1889
1890 sdbusplus::bus::match::match match(
Ed Tanous07d467b2021-02-23 14:48:37 -08001891 static_cast<sdbusplus::bus::bus&>(*systemBus),
Matt Spinlere789bf12021-02-24 12:33:01 -06001892 "type='signal',member='PropertiesChanged',path='" + path + "'",
James Feist4dc617b2020-05-01 09:54:47 -07001893 eventHandler);
Matt Spinlere789bf12021-02-24 12:33:01 -06001894 dbusMatches.emplace(path, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001895}
1896
James Feist98132792019-07-09 13:29:09 -07001897int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001898{
1899 // setup connection to dbus
Ed Tanous07d467b2021-02-23 14:48:37 -08001900 systemBus = std::make_shared<sdbusplus::asio::connection>(io);
1901 systemBus->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001902
Ed Tanous07d467b2021-02-23 14:48:37 -08001903 sdbusplus::asio::object_server objServer(systemBus);
James Feistfd1264a2018-05-03 12:10:00 -07001904
James Feist8f2710a2018-05-09 17:18:55 -07001905 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1906 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1907 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001908
James Feist4131aea2018-03-09 09:47:30 -08001909 // to keep reference to the match / filter objects so they don't get
1910 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001911
1912 nlohmann::json systemConfiguration = nlohmann::json::object();
1913
Brad Bishopc76af0f2020-12-04 13:50:23 -05001914 // We need a poke from DBus for static providers that create all their
1915 // objects prior to claiming a well-known name, and thus don't emit any
1916 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1917 // for any reason, expected or otherwise, we'll need a poke to remove
1918 // entities from DBus.
1919 sdbusplus::bus::match::match nameOwnerChangedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001920 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishopc76af0f2020-12-04 13:50:23 -05001921 sdbusplus::bus::match::rules::nameOwnerChanged(),
1922 [&](sdbusplus::message::message&) {
1923 propertiesChangedCallback(systemConfiguration, objServer);
1924 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001925 // We also need a poke from DBus when new interfaces are created or
1926 // destroyed.
1927 sdbusplus::bus::match::match interfacesAddedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001928 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001929 sdbusplus::bus::match::rules::interfacesAdded(),
1930 [&](sdbusplus::message::message&) {
1931 propertiesChangedCallback(systemConfiguration, objServer);
1932 });
1933 sdbusplus::bus::match::match interfacesRemovedMatch(
Ed Tanous07d467b2021-02-23 14:48:37 -08001934 static_cast<sdbusplus::bus::bus&>(*systemBus),
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001935 sdbusplus::bus::match::rules::interfacesRemoved(),
1936 [&](sdbusplus::message::message&) {
1937 propertiesChangedCallback(systemConfiguration, objServer);
1938 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001939
James Feist4dc617b2020-05-01 09:54:47 -07001940 io.post(
1941 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001942
James Feistfd1264a2018-05-03 12:10:00 -07001943 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001944 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001945 });
James Feist8f2710a2018-05-09 17:18:55 -07001946 entityIface->initialize();
1947
James Feist1df06a42019-04-11 14:23:04 -07001948 if (fwVersionIsSame())
1949 {
1950 if (std::filesystem::is_regular_file(currentConfiguration))
1951 {
1952 // this file could just be deleted, but it's nice for debug
1953 std::filesystem::create_directory(tempConfigDir);
1954 std::filesystem::remove(lastConfiguration);
1955 std::filesystem::copy(currentConfiguration, lastConfiguration);
1956 std::filesystem::remove(currentConfiguration);
1957
1958 std::ifstream jsonStream(lastConfiguration);
1959 if (jsonStream.good())
1960 {
1961 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1962 if (data.is_discarded())
1963 {
1964 std::cerr << "syntax error in " << lastConfiguration
1965 << "\n";
1966 }
1967 else
1968 {
1969 lastJson = std::move(data);
1970 }
1971 }
1972 else
1973 {
1974 std::cerr << "unable to open " << lastConfiguration << "\n";
1975 }
1976 }
1977 }
1978 else
1979 {
1980 // not an error, just logging at this level to make it in the journal
1981 std::cerr << "Clearing previous configuration\n";
1982 std::filesystem::remove(currentConfiguration);
1983 }
1984
1985 // some boards only show up after power is on, we want to not say they are
1986 // removed until the same state happens
Ed Tanous07d467b2021-02-23 14:48:37 -08001987 setupPowerMatch(systemBus);
James Feist1df06a42019-04-11 14:23:04 -07001988
James Feist1b2e2242018-01-30 13:45:19 -08001989 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001990
1991 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001992}