blob: b617e1da8347a996b8e3354212c9d148297aa951 [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
James Feist637b3ef2019-04-15 16:35:30 -070038#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080039#include <fstream>
40#include <iostream>
James Feista465ccc2019-02-08 12:51:01 -080041#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080042#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080043constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
44constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070045constexpr const char* tempConfigDir = "/tmp/configuration/";
46constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
47constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080048constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070049constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080050
James Feistf1b14142019-04-10 15:22:09 -070051constexpr const bool DEBUG = false;
52
James Feist3cb5fec2018-01-23 14:41:51 -080053struct cmp_str
54{
James Feista465ccc2019-02-08 12:51:01 -080055 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080056 {
57 return std::strcmp(a, b) < 0;
58 }
59};
60
61// underscore T for collison with dbus c api
62enum class probe_type_codes
63{
64 FALSE_T,
65 TRUE_T,
66 AND,
67 OR,
James Feist6bd2a022018-03-13 12:30:58 -070068 FOUND,
69 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080070};
James Feista465ccc2019-02-08 12:51:01 -080071const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080072 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
73 {"TRUE", probe_type_codes::TRUE_T},
74 {"AND", probe_type_codes::AND},
75 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070076 {"FOUND", probe_type_codes::FOUND},
77 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080078
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020079static constexpr std::array<const char*, 6> settableInterfaces = {
80 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070081using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080082 std::variant<std::vector<std::string>, std::vector<double>, std::string,
83 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
84 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080085using GetSubTreeType = std::vector<
86 std::pair<std::string,
87 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
88
89using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070090 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080091 boost::container::flat_map<
92 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070093 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080094
James Feistd58879a2019-09-11 11:26:07 -070095// store reference to all interfaces so we can destroy them later
96boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080097 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070098 inventory;
99
James Feist3cb5fec2018-01-23 14:41:51 -0800100// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700101std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700102static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800103
James Feist02d2b932020-02-06 16:28:48 -0800104boost::asio::io_context io;
105
Johnathan Mantey2015f752019-03-26 15:22:31 -0700106const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
107const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800108
James Feist4dc617b2020-05-01 09:54:47 -0700109void registerCallback(nlohmann::json& systemConfiguration,
110 sdbusplus::asio::object_server& objServer,
Matt Spinlere789bf12021-02-24 12:33:01 -0600111 const std::string& path);
James Feist75fdeeb2018-02-20 14:26:16 -0800112
James Feistd58879a2019-09-11 11:26:07 -0700113static std::shared_ptr<sdbusplus::asio::dbus_interface>
114 createInterface(sdbusplus::asio::object_server& objServer,
115 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800116 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700117{
James Feist02d2b932020-02-06 16:28:48 -0800118 // on first add we have no reason to check for null before add, as there
119 // won't be any. For dynamically added interfaces, we check for null so that
120 // a constant delete/add will not create a memory leak
121
122 auto ptr = objServer.add_interface(path, interface);
123 auto& dataVector = inventory[parent];
124 if (checkNull)
125 {
126 auto it = std::find_if(dataVector.begin(), dataVector.end(),
127 [](const auto& p) { return p.expired(); });
128 if (it != dataVector.end())
129 {
130 *it = ptr;
131 return ptr;
132 }
133 }
134 dataVector.emplace_back(ptr);
135 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700136}
137
James Feistb1728ca2020-04-30 15:40:55 -0700138void getInterfaces(
139 const std::tuple<std::string, std::string, std::string>& call,
140 const std::vector<std::shared_ptr<PerformProbe>>& probeVector,
141 std::shared_ptr<PerformScan> scan, size_t retries = 5)
142{
143 if (!retries)
144 {
145 std::cerr << "retries exhausted on " << std::get<0>(call) << " "
146 << std::get<1>(call) << " " << std::get<2>(call) << "\n";
147 return;
148 }
149
150 SYSTEM_BUS->async_method_call(
151 [call, scan, probeVector, retries](
152 boost::system::error_code& errc,
153 const boost::container::flat_map<std::string, BasicVariantType>&
154 resp) {
155 if (errc)
156 {
157 std::cerr << "error calling getall on " << std::get<0>(call)
158 << " " << std::get<1>(call) << " "
159 << std::get<2>(call) << "\n";
160
161 std::shared_ptr<boost::asio::steady_timer> timer =
162 std::make_shared<boost::asio::steady_timer>(io);
163 timer->expires_after(std::chrono::seconds(2));
164
165 timer->async_wait([timer, call, scan, probeVector,
166 retries](const boost::system::error_code&) {
167 getInterfaces(call, probeVector, scan, retries - 1);
168 });
169 return;
170 }
171
Matt Spinlere789bf12021-02-24 12:33:01 -0600172 scan->dbusProbeObjects[std::get<1>(call)][std::get<2>(call)] = resp;
James Feistb1728ca2020-04-30 15:40:55 -0700173 },
174 std::get<0>(call), std::get<1>(call), "org.freedesktop.DBus.Properties",
175 "GetAll", std::get<2>(call));
176
177 if constexpr (DEBUG)
178 {
179 std::cerr << __func__ << " " << __LINE__ << "\n";
180 }
181}
182
Matt Spinlere789bf12021-02-24 12:33:01 -0600183// Populates scan->dbusProbeObjects with all interfaces and properties
184// for the paths that own the interfaces passed in.
James Feistb1728ca2020-04-30 15:40:55 -0700185void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>&& probeVector,
186 boost::container::flat_set<std::string>&& interfaces,
James Feist32d1f0a2020-09-10 14:49:25 -0700187 std::shared_ptr<PerformScan> scan, size_t retries = 5)
James Feist3cb5fec2018-01-23 14:41:51 -0800188{
Matt Spinlere789bf12021-02-24 12:33:01 -0600189 // Filter out interfaces already obtained.
190 for (const auto& [path, probeInterfaces] : scan->dbusProbeObjects)
James Feist733f7652019-11-13 14:32:29 -0800191 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600192 for (const auto& [interface, _] : probeInterfaces)
193 {
194 interfaces.erase(interface);
195 }
James Feist733f7652019-11-13 14:32:29 -0800196 }
197 if (interfaces.empty())
198 {
199 return;
200 }
201
James Feist3cb5fec2018-01-23 14:41:51 -0800202 // find all connections in the mapper that expose a specific type
James Feistb1728ca2020-04-30 15:40:55 -0700203 SYSTEM_BUS->async_method_call(
Matt Spinler4f328892021-02-24 13:19:14 -0600204 [interfaces, probeVector{std::move(probeVector)}, scan,
205 retries](boost::system::error_code& ec,
206 const GetSubTreeType& interfaceSubtree) mutable {
James Feistb1728ca2020-04-30 15:40:55 -0700207 boost::container::flat_set<
208 std::tuple<std::string, std::string, std::string>>
209 interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700210 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700211 {
James Feist0de40152018-07-25 11:56:12 -0700212 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700213 {
James Feist0de40152018-07-25 11:56:12 -0700214 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700215 }
James Feist0de40152018-07-25 11:56:12 -0700216 std::cerr << "Error communicating to mapper.\n";
217
James Feist32d1f0a2020-09-10 14:49:25 -0700218 if (!retries)
219 {
220 // if we can't communicate to the mapper something is very
221 // wrong
222 std::exit(EXIT_FAILURE);
223 }
224 std::shared_ptr<boost::asio::steady_timer> timer =
225 std::make_shared<boost::asio::steady_timer>(io);
226 timer->expires_after(std::chrono::seconds(10));
227
228 timer->async_wait(
229 [timer, interfaces{std::move(interfaces)}, scan,
230 probeVector{std::move(probeVector)},
231 retries](const boost::system::error_code&) mutable {
232 findDbusObjects(std::move(probeVector),
233 std::move(interfaces), scan,
234 retries - 1);
235 });
236 return;
James Feist494155a2018-03-14 16:23:24 -0700237 }
James Feist787c3c32019-11-07 14:42:58 -0800238
James Feistb1728ca2020-04-30 15:40:55 -0700239 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800240 {
James Feistb1728ca2020-04-30 15:40:55 -0700241 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700242 {
James Feistb1728ca2020-04-30 15:40:55 -0700243 for (const std::string& iface : ifaces)
244 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600245 // The 3 default org.freedeskstop interfaces (Peer,
246 // Introspectable, and Properties) are returned by
247 // the mapper but don't have properties, so don't bother
248 // with the GetAll call to save some cycles.
249 if (!boost::algorithm::starts_with(iface,
250 "org.freedesktop"))
James Feistb1728ca2020-04-30 15:40:55 -0700251 {
252 interfaceConnections.emplace(busname, path, iface);
253 }
254 }
James Feist8f2710a2018-05-09 17:18:55 -0700255 }
Matt Spinlere789bf12021-02-24 12:33:01 -0600256
257 // Get a PropertiesChanged callback for all
258 // interfaces on this path.
259 registerCallback(scan->_systemConfiguration, scan->objServer,
260 path);
James Feist3cb5fec2018-01-23 14:41:51 -0800261 }
James Feist787c3c32019-11-07 14:42:58 -0800262
James Feist63845bf2019-01-24 12:19:51 -0800263 if (interfaceConnections.empty())
264 {
James Feist63845bf2019-01-24 12:19:51 -0800265 return;
266 }
James Feist787c3c32019-11-07 14:42:58 -0800267
James Feistb1728ca2020-04-30 15:40:55 -0700268 for (const auto& call : interfaceConnections)
269 {
270 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700271 }
272 },
273 "xyz.openbmc_project.ObjectMapper",
274 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700275 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800276 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700277
278 if constexpr (DEBUG)
279 {
280 std::cerr << __func__ << " " << __LINE__ << "\n";
281 }
James Feist3cb5fec2018-01-23 14:41:51 -0800282}
James Feistb1728ca2020-04-30 15:40:55 -0700283
James Feist8f2710a2018-05-09 17:18:55 -0700284// probes dbus interface dictionary for a key with a value that matches a regex
Matt Spinlere789bf12021-02-24 12:33:01 -0600285// When an interface passes a probe, also save its D-Bus path with it.
James Feist08a5b172019-08-28 14:47:47 -0700286bool probeDbus(const std::string& interface,
287 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800288 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
289 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800290{
James Feist3cb5fec2018-01-23 14:41:51 -0800291 bool foundMatch = false;
Matt Spinlere789bf12021-02-24 12:33:01 -0600292 foundProbe = false;
293
294 for (const auto& [path, interfaces] : scan->dbusProbeObjects)
James Feist3cb5fec2018-01-23 14:41:51 -0800295 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600296 auto it = interfaces.find(interface);
297 if (it == interfaces.end())
James Feist3cb5fec2018-01-23 14:41:51 -0800298 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600299 continue;
300 }
301
302 foundProbe = true;
303
304 bool deviceMatches = true;
305 const boost::container::flat_map<std::string, BasicVariantType>&
306 properties = it->second;
307
308 for (const auto& [matchProp, matchJSON] : matches)
309 {
310 auto deviceValue = properties.find(matchProp);
311 if (deviceValue != properties.end())
James Feist3cb5fec2018-01-23 14:41:51 -0800312 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600313 deviceMatches = matchProbe(matchJSON, deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800314 }
315 else
316 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600317 // Move on to the next DBus path
James Feist3cb5fec2018-01-23 14:41:51 -0800318 deviceMatches = false;
319 break;
320 }
321 }
322 if (deviceMatches)
323 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600324 if constexpr (DEBUG)
325 {
326 std::cerr << "probeDBus: Found probe match on " << path << " "
327 << interface << "\n";
328 }
329 devices.emplace_back(properties, path);
James Feist3cb5fec2018-01-23 14:41:51 -0800330 foundMatch = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800331 }
332 }
333 return foundMatch;
334}
335
336// default probe entry point, iterates a list looking for specific types to
337// call specific probe functions
Matt Spinlere789bf12021-02-24 12:33:01 -0600338bool probe(const std::vector<std::string>& probeCommand,
339 std::shared_ptr<PerformScan> scan, FoundDeviceT& foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800340{
341 const static std::regex command(R"(\((.*)\))");
342 std::smatch match;
343 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700344 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800345 bool cur = true;
346 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700347 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800348
James Feista465ccc2019-02-08 12:51:01 -0800349 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800350 {
351 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800352 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800353 cmp_str>::const_iterator probeType;
354
355 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700356 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800357 {
358 if (probe.find(probeType->first) != std::string::npos)
359 {
360 foundProbe = true;
361 break;
362 }
363 }
364 if (foundProbe)
365 {
366 switch (probeType->second)
367 {
James Feist9eb0b582018-04-27 12:15:46 -0700368 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800369 {
James Feiste31e00a2019-07-24 10:45:43 -0700370 cur = false;
371 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800372 }
James Feist9eb0b582018-04-27 12:15:46 -0700373 case probe_type_codes::TRUE_T:
374 {
James Feiste31e00a2019-07-24 10:45:43 -0700375 cur = true;
376 break;
James Feist9eb0b582018-04-27 12:15:46 -0700377 }
378 case probe_type_codes::MATCH_ONE:
379 {
380 // set current value to last, this probe type shouldn't
381 // affect the outcome
382 cur = ret;
383 matchOne = true;
384 break;
385 }
386 /*case probe_type_codes::AND:
387 break;
388 case probe_type_codes::OR:
389 break;
390 // these are no-ops until the last command switch
391 */
392 case probe_type_codes::FOUND:
393 {
394 if (!std::regex_search(probe, match, command))
395 {
James Feist0eb40352019-04-09 14:44:04 -0700396 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700397 << "\n";
398 return false;
399 }
400 std::string commandStr = *(match.begin() + 1);
401 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800402 cur = (std::find(scan->passedProbes.begin(),
403 scan->passedProbes.end(),
404 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700405 break;
406 }
James Feist0eb40352019-04-09 14:44:04 -0700407 default:
408 {
409 break;
410 }
James Feist3cb5fec2018-01-23 14:41:51 -0800411 }
412 }
413 // look on dbus for object
414 else
415 {
416 if (!std::regex_search(probe, match, command))
417 {
James Feist0eb40352019-04-09 14:44:04 -0700418 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800419 return false;
420 }
421 std::string commandStr = *(match.begin() + 1);
422 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700423 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800424 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800425 auto json = nlohmann::json::parse(commandStr, nullptr, false);
426 if (json.is_discarded())
427 {
James Feist0eb40352019-04-09 14:44:04 -0700428 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800429 return false;
430 }
431 // we can match any (string, variant) property. (string, string)
432 // does a regex
433 std::map<std::string, nlohmann::json> dbusProbeMap =
434 json.get<std::map<std::string, nlohmann::json>>();
435 auto findStart = probe.find("(");
436 if (findStart == std::string::npos)
437 {
438 return false;
439 }
440 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800441 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
442 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800443 }
444
445 // some functions like AND and OR only take affect after the
446 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700447 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800448 {
James Feist54a0dca2019-06-26 10:34:54 -0700449 ret = cur && ret;
450 }
451 else if (lastCommand == probe_type_codes::OR)
452 {
453 ret = cur || ret;
454 }
455
456 if (first)
457 {
458 ret = cur;
459 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800460 }
461 lastCommand = probeType != PROBE_TYPES.end()
462 ? probeType->second
463 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800464 }
465
466 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800467 if (ret && foundDevs.size() == 0)
468 {
James Feist08a5b172019-08-28 14:47:47 -0700469 foundDevs.emplace_back(
Matt Spinlere789bf12021-02-24 12:33:01 -0600470 boost::container::flat_map<std::string, BasicVariantType>{},
471 std::string{});
James Feist3cb5fec2018-01-23 14:41:51 -0800472 }
James Feist0eb40352019-04-09 14:44:04 -0700473 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700474 {
James Feist71f295f2019-06-20 13:35:12 -0700475 // match the last one
476 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700477 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700478
479 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700480 }
James Feist3cb5fec2018-01-23 14:41:51 -0800481 return ret;
482}
483
Matt Spinlere789bf12021-02-24 12:33:01 -0600484PerformProbe::PerformProbe(
485 const std::vector<std::string>& probeCommand,
486 std::shared_ptr<PerformScan>& scanPtr,
487 std::function<void(FoundDeviceT&, const DBusProbeObjectT&)>&& callback) :
James Feist7d807752019-11-13 14:47:57 -0800488 _probeCommand(probeCommand),
489 scan(scanPtr), _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -0700490{}
James Feist7d807752019-11-13 14:47:57 -0800491PerformProbe::~PerformProbe()
492{
493 FoundDeviceT foundDevs;
494 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700495 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600496 _callback(foundDevs, scan->dbusProbeObjects);
James Feist8f2710a2018-05-09 17:18:55 -0700497 }
James Feist7d807752019-11-13 14:47:57 -0800498}
James Feist8f2710a2018-05-09 17:18:55 -0700499
500// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800501bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800502{
James Feist1df06a42019-04-11 14:23:04 -0700503 std::filesystem::create_directory(configurationOutDir);
504 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700505 if (!output.good())
506 {
507 return false;
508 }
James Feist1b2e2242018-01-30 13:45:19 -0800509 output << systemConfiguration.dump(4);
510 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700511 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700512}
James Feist1b2e2242018-01-30 13:45:19 -0800513
James Feist97a63f12018-05-17 13:50:57 -0700514template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800515bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
516 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700517{
518 try
519 {
520 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800521 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700522 ref = value;
523 return true;
524 }
James Feist98132792019-07-09 13:29:09 -0700525 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700526 {
527 return false;
528 }
529}
James Feistbb43d022018-06-12 15:44:33 -0700530
James Feistebcc26b2019-03-22 12:30:43 -0700531// template function to add array as dbus property
532template <typename PropertyType>
533void addArrayToDbus(const std::string& name, const nlohmann::json& array,
534 sdbusplus::asio::dbus_interface* iface,
535 sdbusplus::asio::PropertyPermission permission,
536 nlohmann::json& systemConfiguration,
537 const std::string& jsonPointerString)
538{
539 std::vector<PropertyType> values;
540 for (const auto& property : array)
541 {
542 auto ptr = property.get_ptr<const PropertyType*>();
543 if (ptr != nullptr)
544 {
545 values.emplace_back(*ptr);
546 }
547 }
548
549 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
550 {
551 iface->register_property(name, values);
552 }
553 else
554 {
555 iface->register_property(
556 name, values,
557 [&systemConfiguration,
558 jsonPointerString{std::string(jsonPointerString)}](
559 const std::vector<PropertyType>& newVal,
560 std::vector<PropertyType>& val) {
561 val = newVal;
562 if (!setJsonFromPointer(jsonPointerString, val,
563 systemConfiguration))
564 {
565 std::cerr << "error setting json field\n";
566 return -1;
567 }
568 if (!writeJsonFiles(systemConfiguration))
569 {
570 std::cerr << "error setting json file\n";
571 return -1;
572 }
573 return 1;
574 });
575 }
576}
577
James Feistbb43d022018-06-12 15:44:33 -0700578template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800579void addProperty(const std::string& propertyName, const PropertyType& value,
580 sdbusplus::asio::dbus_interface* iface,
581 nlohmann::json& systemConfiguration,
582 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700583 sdbusplus::asio::PropertyPermission permission)
584{
585 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
586 {
587 iface->register_property(propertyName, value);
588 return;
589 }
James Feist68500ff2018-08-08 15:40:42 -0700590 iface->register_property(
591 propertyName, value,
592 [&systemConfiguration,
593 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800594 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700595 val = newVal;
596 if (!setJsonFromPointer(jsonPointerString, val,
597 systemConfiguration))
598 {
599 std::cerr << "error setting json field\n";
600 return -1;
601 }
James Feistc6248a52018-08-14 10:09:45 -0700602 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700603 {
604 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700605 return -1;
606 }
607 return 1;
608 });
609}
610
611void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800612 const std::string& jsonPointerPath,
613 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
614 sdbusplus::asio::object_server& objServer,
615 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700616{
617 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
618 iface->register_method(
619 "Delete", [&objServer, &systemConfiguration, interface,
620 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700621 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700622 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700623 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700624 {
625 // this technically can't happen as the pointer is pointing to
626 // us
627 throw DBusInternalError();
628 }
629 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700630 systemConfiguration[ptr] = nullptr;
631
James Feist02d2b932020-02-06 16:28:48 -0800632 // todo(james): dig through sdbusplus to find out why we can't
633 // delete it in a method call
634 io.post([&objServer, dbusInterface]() mutable {
635 objServer.remove_interface(dbusInterface);
636 });
637
James Feistc6248a52018-08-14 10:09:45 -0700638 if (!writeJsonFiles(systemConfiguration))
639 {
640 std::cerr << "error setting json file\n";
641 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700642 }
James Feist68500ff2018-08-08 15:40:42 -0700643 });
James Feistbb43d022018-06-12 15:44:33 -0700644}
645
James Feist1b2e2242018-01-30 13:45:19 -0800646// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700647void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800648 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
649 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
650 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700651 sdbusplus::asio::PropertyPermission permission =
652 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800653{
James Feista465ccc2019-02-08 12:51:01 -0800654 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800655 {
James Feist8f2710a2018-05-09 17:18:55 -0700656 auto type = dictPair.value().type();
657 bool array = false;
658 if (dictPair.value().type() == nlohmann::json::value_t::array)
659 {
660 array = true;
661 if (!dictPair.value().size())
662 {
663 continue;
664 }
665 type = dictPair.value()[0].type();
666 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800667 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700668 {
669 if (arrayItem.type() != type)
670 {
671 isLegal = false;
672 break;
673 }
674 }
675 if (!isLegal)
676 {
677 std::cerr << "dbus format error" << dictPair.value() << "\n";
678 continue;
679 }
James Feista218ddb2019-04-11 14:01:31 -0700680 }
681 if (type == nlohmann::json::value_t::object)
682 {
683 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700684 }
James Feist97a63f12018-05-17 13:50:57 -0700685 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700686 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
687 {
688 // all setable numbers are doubles as it is difficult to always
689 // create a configuration file with all whole numbers as decimals
690 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700691 if (array)
692 {
693 if (dictPair.value()[0].is_number())
694 {
695 type = nlohmann::json::value_t::number_float;
696 }
697 }
698 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700699 {
700 type = nlohmann::json::value_t::number_float;
701 }
702 }
703
James Feist8f2710a2018-05-09 17:18:55 -0700704 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800705 {
James Feist9eb0b582018-04-27 12:15:46 -0700706 case (nlohmann::json::value_t::boolean):
707 {
James Feist8f2710a2018-05-09 17:18:55 -0700708 if (array)
709 {
710 // todo: array of bool isn't detected correctly by
711 // sdbusplus, change it to numbers
712 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700713 iface.get(), permission,
714 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700715 }
James Feistbb43d022018-06-12 15:44:33 -0700716
James Feist97a63f12018-05-17 13:50:57 -0700717 else
718 {
James Feistbb43d022018-06-12 15:44:33 -0700719 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700720 iface.get(), systemConfiguration, key,
721 permission);
James Feist97a63f12018-05-17 13:50:57 -0700722 }
James Feist9eb0b582018-04-27 12:15:46 -0700723 break;
724 }
725 case (nlohmann::json::value_t::number_integer):
726 {
James Feist8f2710a2018-05-09 17:18:55 -0700727 if (array)
728 {
729 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700730 iface.get(), permission,
731 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700732 }
733 else
734 {
James Feistbb43d022018-06-12 15:44:33 -0700735 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700736 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700737 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700738 }
James Feist9eb0b582018-04-27 12:15:46 -0700739 break;
740 }
741 case (nlohmann::json::value_t::number_unsigned):
742 {
James Feist8f2710a2018-05-09 17:18:55 -0700743 if (array)
744 {
745 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700746 iface.get(), permission,
747 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700748 }
749 else
750 {
James Feistbb43d022018-06-12 15:44:33 -0700751 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700752 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700753 systemConfiguration, key,
754 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700755 }
James Feist9eb0b582018-04-27 12:15:46 -0700756 break;
757 }
758 case (nlohmann::json::value_t::number_float):
759 {
James Feist8f2710a2018-05-09 17:18:55 -0700760 if (array)
761 {
762 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700763 iface.get(), permission,
764 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700765 }
James Feistbb43d022018-06-12 15:44:33 -0700766
James Feist97a63f12018-05-17 13:50:57 -0700767 else
768 {
James Feistbb43d022018-06-12 15:44:33 -0700769 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700770 iface.get(), systemConfiguration, key,
771 permission);
James Feist97a63f12018-05-17 13:50:57 -0700772 }
James Feist9eb0b582018-04-27 12:15:46 -0700773 break;
774 }
775 case (nlohmann::json::value_t::string):
776 {
James Feist8f2710a2018-05-09 17:18:55 -0700777 if (array)
778 {
James Feistebcc26b2019-03-22 12:30:43 -0700779 addArrayToDbus<std::string>(
780 dictPair.key(), dictPair.value(), iface.get(),
781 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700782 }
783 else
784 {
James Feistc6248a52018-08-14 10:09:45 -0700785 addProperty(
786 dictPair.key(), dictPair.value().get<std::string>(),
787 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700788 }
James Feist9eb0b582018-04-27 12:15:46 -0700789 break;
790 }
James Feist0eb40352019-04-09 14:44:04 -0700791 default:
792 {
James Feista218ddb2019-04-11 14:01:31 -0700793 std::cerr << "Unexpected json type in system configuration "
794 << dictPair.key() << ": "
795 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700796 break;
797 }
James Feist1b2e2242018-01-30 13:45:19 -0800798 }
799 }
James Feistc6248a52018-08-14 10:09:45 -0700800 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
801 {
802 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
803 systemConfiguration);
804 }
James Feist8f2710a2018-05-09 17:18:55 -0700805 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800806}
807
James Feista465ccc2019-02-08 12:51:01 -0800808sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700809{
810 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
811 interface) != settableInterfaces.end()
812 ? sdbusplus::asio::PropertyPermission::readWrite
813 : sdbusplus::asio::PropertyPermission::readOnly;
814}
815
James Feista465ccc2019-02-08 12:51:01 -0800816void createAddObjectMethod(const std::string& jsonPointerPath,
817 const std::string& path,
818 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700819 sdbusplus::asio::object_server& objServer,
820 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700821{
James Feistd58879a2019-09-11 11:26:07 -0700822 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
823 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700824
825 iface->register_method(
826 "AddObject",
827 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700828 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
829 board](const boost::container::flat_map<std::string, JsonVariantType>&
830 data) {
James Feist68500ff2018-08-08 15:40:42 -0700831 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800832 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700833 auto findExposes = base.find("Exposes");
834
835 if (findExposes == base.end())
836 {
837 throw std::invalid_argument("Entity must have children.");
838 }
839
840 // this will throw invalid-argument to sdbusplus if invalid json
841 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800842 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700843 {
James Feista465ccc2019-02-08 12:51:01 -0800844 nlohmann::json& newJson = newData[item.first];
845 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
846 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700847 }
848
849 auto findName = newData.find("Name");
850 auto findType = newData.find("Type");
851 if (findName == newData.end() || findType == newData.end())
852 {
853 throw std::invalid_argument("AddObject missing Name or Type");
854 }
James Feista465ccc2019-02-08 12:51:01 -0800855 const std::string* type = findType->get_ptr<const std::string*>();
856 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700857 if (type == nullptr || name == nullptr)
858 {
859 throw std::invalid_argument("Type and Name must be a string.");
860 }
861
James Feist02d2b932020-02-06 16:28:48 -0800862 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700863 size_t lastIndex = 0;
864 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800865 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700866 {
James Feist02d2b932020-02-06 16:28:48 -0800867 if (expose.is_null())
868 {
869 foundNull = true;
870 continue;
871 }
872
873 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700874 {
875 throw std::invalid_argument(
876 "Field already in JSON, not adding");
877 }
James Feist02d2b932020-02-06 16:28:48 -0800878
879 if (foundNull)
880 {
881 continue;
882 }
883
James Feist68500ff2018-08-08 15:40:42 -0700884 lastIndex++;
885 }
886
887 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
888 *type + ".json");
889 // todo(james) we might want to also make a list of 'can add'
890 // interfaces but for now I think the assumption if there is a
891 // schema avaliable that it is allowed to update is fine
892 if (!schemaFile.good())
893 {
894 throw std::invalid_argument(
895 "No schema avaliable, cannot validate.");
896 }
897 nlohmann::json schema =
898 nlohmann::json::parse(schemaFile, nullptr, false);
899 if (schema.is_discarded())
900 {
901 std::cerr << "Schema not legal" << *type << ".json\n";
902 throw DBusInternalError();
903 }
904 if (!validateJson(schema, newData))
905 {
906 throw std::invalid_argument("Data does not match schema");
907 }
James Feist02d2b932020-02-06 16:28:48 -0800908 if (foundNull)
909 {
910 findExposes->at(lastIndex) = newData;
911 }
912 else
913 {
914 findExposes->push_back(newData);
915 }
James Feist68500ff2018-08-08 15:40:42 -0700916 if (!writeJsonFiles(systemConfiguration))
917 {
918 std::cerr << "Error writing json files\n";
919 throw DBusInternalError();
920 }
921 std::string dbusName = *name;
922
923 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700924 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700925
926 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
927 createInterface(objServer, path + "/" + dbusName,
928 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800929 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700930 // permission is read-write, as since we just created it, must be
931 // runtime modifiable
932 populateInterfaceFromJson(
933 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700934 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700935 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700936 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700937 });
938 iface->initialize();
939}
940
James Feista465ccc2019-02-08 12:51:01 -0800941void postToDbus(const nlohmann::json& newConfiguration,
942 nlohmann::json& systemConfiguration,
943 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800944
James Feist1b2e2242018-01-30 13:45:19 -0800945{
James Feist97a63f12018-05-17 13:50:57 -0700946 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800947 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800948 {
James Feistf1b14142019-04-10 15:22:09 -0700949 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700950 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700951 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700952 // loop through newConfiguration, but use values from system
953 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700954 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700955 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800956 std::string boardType;
957 if (findBoardType != boardValues.end() &&
958 findBoardType->type() == nlohmann::json::value_t::string)
959 {
960 boardType = findBoardType->get<std::string>();
961 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700962 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800963 }
964 else
965 {
966 std::cerr << "Unable to find type for " << boardKey
967 << " reverting to Chassis.\n";
968 boardType = "Chassis";
969 }
James Feist11be6672018-04-06 14:05:32 -0700970 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800971
972 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700973 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700974 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
975 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800976
James Feistd58879a2019-09-11 11:26:07 -0700977 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
978 createInterface(objServer, boardName,
979 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700980
James Feistd58879a2019-09-11 11:26:07 -0700981 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
982 createInterface(objServer, boardName,
983 "xyz.openbmc_project.Inventory.Item." + boardType,
984 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700985
James Feist68500ff2018-08-08 15:40:42 -0700986 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700987 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700988
James Feist97a63f12018-05-17 13:50:57 -0700989 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700990 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700991 jsonPointerPath += "/";
992 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800993 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700994 {
995 if (boardField.value().type() == nlohmann::json::value_t::object)
996 {
James Feistd58879a2019-09-11 11:26:07 -0700997 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
998 createInterface(objServer, boardName, boardField.key(),
999 boardKeyOrig);
1000
James Feistc6248a52018-08-14 10:09:45 -07001001 populateInterfaceFromJson(systemConfiguration,
1002 jsonPointerPath + boardField.key(),
1003 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -07001004 }
1005 }
James Feist97a63f12018-05-17 13:50:57 -07001006
James Feist1e3e6982018-08-03 16:09:28 -07001007 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001008 if (exposes == boardValues.end())
1009 {
1010 continue;
1011 }
James Feist97a63f12018-05-17 13:50:57 -07001012 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001013 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001014
1015 // store the board level pointer so we can modify it on the way down
1016 std::string jsonPointerPathBoard = jsonPointerPath;
1017 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001018 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001019 {
James Feist97a63f12018-05-17 13:50:57 -07001020 exposesIndex++;
1021 jsonPointerPath = jsonPointerPathBoard;
1022 jsonPointerPath += std::to_string(exposesIndex);
1023
James Feistd63d18a2018-07-19 15:23:45 -07001024 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001025 if (findName == item.end())
1026 {
1027 std::cerr << "cannot find name in field " << item << "\n";
1028 continue;
1029 }
James Feist1e3e6982018-08-03 16:09:28 -07001030 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001031 // if status is not found it is assumed to be status = 'okay'
1032 if (findStatus != item.end())
1033 {
1034 if (*findStatus == "disabled")
1035 {
1036 continue;
1037 }
1038 }
James Feistd63d18a2018-07-19 15:23:45 -07001039 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001040 std::string itemType;
1041 if (findType != item.end())
1042 {
1043 itemType = findType->get<std::string>();
1044 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001045 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1046 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001047 }
1048 else
1049 {
1050 itemType = "unknown";
1051 }
1052 std::string itemName = findName->get<std::string>();
1053 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001054 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001055
James Feistd58879a2019-09-11 11:26:07 -07001056 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1057 createInterface(objServer, boardName + "/" + itemName,
1058 "xyz.openbmc_project.Configuration." + itemType,
1059 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001060
James Feist97a63f12018-05-17 13:50:57 -07001061 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001062 itemIface, item, objServer,
1063 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001064
James Feista465ccc2019-02-08 12:51:01 -08001065 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001066 {
James Feist97a63f12018-05-17 13:50:57 -07001067 jsonPointerPath = jsonPointerPathBoard +
1068 std::to_string(exposesIndex) + "/" +
1069 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001070 if (objectPair.value().type() ==
1071 nlohmann::json::value_t::object)
1072 {
James Feistd58879a2019-09-11 11:26:07 -07001073 std::shared_ptr<sdbusplus::asio::dbus_interface>
1074 objectIface = createInterface(
1075 objServer, boardName + "/" + itemName,
1076 "xyz.openbmc_project.Configuration." + itemType +
1077 "." + objectPair.key(),
1078 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001079
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +02001080 populateInterfaceFromJson(systemConfiguration,
1081 jsonPointerPath, objectIface,
1082 objectPair.value(), objServer,
1083 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -08001084 }
1085 else if (objectPair.value().type() ==
1086 nlohmann::json::value_t::array)
1087 {
1088 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001089 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001090 {
James Feist8f2710a2018-05-09 17:18:55 -07001091 continue;
1092 }
1093 bool isLegal = true;
1094 auto type = objectPair.value()[0].type();
1095 if (type != nlohmann::json::value_t::object)
1096 {
1097 continue;
1098 }
1099
1100 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001101 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001102 {
1103 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001104 {
James Feist8f2710a2018-05-09 17:18:55 -07001105 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001106 break;
1107 }
James Feist8f2710a2018-05-09 17:18:55 -07001108 }
1109 if (!isLegal)
1110 {
1111 std::cerr << "dbus format error" << objectPair.value()
1112 << "\n";
1113 break;
1114 }
1115
James Feista465ccc2019-02-08 12:51:01 -08001116 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001117 {
James Feist97a63f12018-05-17 13:50:57 -07001118
James Feistd58879a2019-09-11 11:26:07 -07001119 std::shared_ptr<sdbusplus::asio::dbus_interface>
1120 objectIface = createInterface(
1121 objServer, boardName + "/" + itemName,
1122 "xyz.openbmc_project.Configuration." +
1123 itemType + "." + objectPair.key() +
1124 std::to_string(index),
1125 boardKeyOrig);
1126
James Feistc6248a52018-08-14 10:09:45 -07001127 populateInterfaceFromJson(
1128 systemConfiguration,
1129 jsonPointerPath + "/" + std::to_string(index),
1130 objectIface, arrayItem, objServer,
1131 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001132 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001133 }
1134 }
1135 }
1136 }
1137 }
1138}
1139
James Feist8f2710a2018-05-09 17:18:55 -07001140// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001141bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001142{
1143 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001144 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001145 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1146 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001147 {
1148 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001149 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001150 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001151 }
James Feistb4383f42018-08-06 16:54:10 -07001152
1153 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1154 globalSchema);
1155 if (!schemaStream.good())
1156 {
1157 std::cerr
1158 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1159 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001160 return false;
James Feistb4383f42018-08-06 16:54:10 -07001161 }
1162 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1163 if (schema.is_discarded())
1164 {
1165 std::cerr
1166 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1167 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001168 return false;
James Feistb4383f42018-08-06 16:54:10 -07001169 }
1170
James Feista465ccc2019-02-08 12:51:01 -08001171 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001172 {
1173 std::ifstream jsonStream(jsonPath.c_str());
1174 if (!jsonStream.good())
1175 {
1176 std::cerr << "unable to open " << jsonPath.string() << "\n";
1177 continue;
1178 }
1179 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1180 if (data.is_discarded())
1181 {
1182 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1183 continue;
1184 }
James Feist8da99192019-01-24 08:20:16 -08001185 /*
1186 * todo(james): reenable this once less things are in flight
1187 *
James Feistb4383f42018-08-06 16:54:10 -07001188 if (!validateJson(schema, data))
1189 {
1190 std::cerr << "Error validating " << jsonPath.string() << "\n";
1191 continue;
1192 }
James Feist8da99192019-01-24 08:20:16 -08001193 */
James Feistb4383f42018-08-06 16:54:10 -07001194
James Feist3cb5fec2018-01-23 14:41:51 -08001195 if (data.type() == nlohmann::json::value_t::array)
1196 {
James Feista465ccc2019-02-08 12:51:01 -08001197 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001198 {
1199 configurations.emplace_back(d);
1200 }
1201 }
1202 else
1203 {
1204 configurations.emplace_back(data);
1205 }
1206 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001207 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001208}
James Feist3cb5fec2018-01-23 14:41:51 -08001209
James Feist94219d12020-03-03 11:52:25 -08001210std::string getRecordName(
1211 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1212 const std::string& probeName)
1213{
1214 if (probe.empty())
1215 {
1216 return probeName;
1217 }
1218
1219 // use an array so alphabetical order from the
1220 // flat_map is maintained
1221 auto device = nlohmann::json::array();
1222 for (auto& devPair : probe)
1223 {
1224 device.push_back(devPair.first);
1225 std::visit([&device](auto&& v) { device.push_back(v); },
1226 devPair.second);
1227 }
1228 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1229 // hashes are hard to distinguish, use the
1230 // non-hashed version if we want debug
1231 if constexpr (DEBUG)
1232 {
1233 return probeName + device.dump();
1234 }
1235 else
1236 {
1237 return std::to_string(hash);
1238 }
1239}
1240
James Feist4dc617b2020-05-01 09:54:47 -07001241PerformScan::PerformScan(nlohmann::json& systemConfiguration,
1242 nlohmann::json& missingConfigurations,
1243 std::list<nlohmann::json>& configurations,
1244 sdbusplus::asio::object_server& objServerIn,
1245 std::function<void()>&& callback) :
James Feist733f7652019-11-13 14:32:29 -08001246 _systemConfiguration(systemConfiguration),
1247 _missingConfigurations(missingConfigurations),
James Feist4dc617b2020-05-01 09:54:47 -07001248 _configurations(configurations), objServer(objServerIn),
1249 _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -07001250{}
James Feist733f7652019-11-13 14:32:29 -08001251void PerformScan::run()
1252{
1253 boost::container::flat_set<std::string> dbusProbeInterfaces;
1254 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001255
James Feist733f7652019-11-13 14:32:29 -08001256 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001257 {
James Feist733f7652019-11-13 14:32:29 -08001258 auto findProbe = it->find("Probe");
1259 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001260
James Feist733f7652019-11-13 14:32:29 -08001261 nlohmann::json probeCommand;
1262 // check for poorly formatted fields, probe must be an array
1263 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001264 {
James Feist733f7652019-11-13 14:32:29 -08001265 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1266 it = _configurations.erase(it);
1267 continue;
1268 }
1269 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1270 {
1271 probeCommand = nlohmann::json::array();
1272 probeCommand.push_back(*findProbe);
1273 }
1274 else
1275 {
1276 probeCommand = *findProbe;
1277 }
James Feist3cb5fec2018-01-23 14:41:51 -08001278
James Feist733f7652019-11-13 14:32:29 -08001279 if (findName == it->end())
1280 {
1281 std::cerr << "configuration file missing name:\n " << *it << "\n";
1282 it = _configurations.erase(it);
1283 continue;
1284 }
1285 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001286
James Feist733f7652019-11-13 14:32:29 -08001287 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1288 passedProbes.end())
1289 {
1290 it = _configurations.erase(it);
1291 continue;
1292 }
1293 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001294
James Feist733f7652019-11-13 14:32:29 -08001295 // store reference to this to children to makes sure we don't get
1296 // destroyed too early
1297 auto thisRef = shared_from_this();
1298 auto probePointer = std::make_shared<PerformProbe>(
1299 probeCommand, thisRef,
Matt Spinlere789bf12021-02-24 12:33:01 -06001300 [&, recordPtr, probeName](FoundDeviceT& foundDevices,
1301 const DBusProbeObjectT& allInterfaces) {
James Feist08a5b172019-08-28 14:47:47 -07001302 _passed = true;
James Feist35f5e0e2020-03-16 14:02:27 -07001303 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001304 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001305 std::list<size_t> indexes(foundDevices.size());
1306 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001307
James Feist35f5e0e2020-03-16 14:02:27 -07001308 size_t indexIdx = probeName.find("$");
1309 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001310
1311 // copy over persisted configurations and make sure we remove
1312 // indexes that are already used
1313 for (auto itr = foundDevices.begin();
1314 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001315 {
Matt Spinlere789bf12021-02-24 12:33:01 -06001316 std::string recordName =
1317 getRecordName(std::get<0>(*itr), probeName);
James Feistf1b14142019-04-10 15:22:09 -07001318
James Feist08a5b172019-08-28 14:47:47 -07001319 auto fromLastJson = lastJson.find(recordName);
1320 if (fromLastJson != lastJson.end())
1321 {
James Feist02d2b932020-02-06 16:28:48 -08001322 auto findExposes = fromLastJson->find("Exposes");
1323 // delete nulls from any updates
1324 if (findExposes != fromLastJson->end())
1325 {
1326 auto copy = nlohmann::json::array();
1327 for (auto& expose : *findExposes)
1328 {
1329 if (expose.is_null())
1330 {
1331 continue;
1332 }
1333 copy.emplace_back(expose);
1334 }
1335 *findExposes = copy;
1336 }
1337
James Feist08a5b172019-08-28 14:47:47 -07001338 // keep user changes
1339 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001340 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001341 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001342 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001343 {
1344 auto nameIt = fromLastJson->find("Name");
1345 if (nameIt == fromLastJson->end())
1346 {
1347 std::cerr << "Last JSON Illegal\n";
1348 continue;
1349 }
1350
1351 int index = std::stoi(
1352 nameIt->get<std::string>().substr(indexIdx),
1353 nullptr, 0);
James Feist35f5e0e2020-03-16 14:02:27 -07001354 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001355 auto usedIt = std::find(indexes.begin(),
1356 indexes.end(), index);
1357
1358 if (usedIt == indexes.end())
1359 {
1360 continue; // less items now
1361 }
1362 indexes.erase(usedIt);
1363 }
1364
James Feist08a5b172019-08-28 14:47:47 -07001365 continue;
1366 }
James Feist94219d12020-03-03 11:52:25 -08001367 itr++;
1368 }
James Feist35f5e0e2020-03-16 14:02:27 -07001369
1370 std::optional<std::string> replaceStr;
1371
Matt Spinler4bab9322021-04-08 14:38:43 -05001372 DBusProbeObjectT::mapped_type emptyInterfaces;
1373 boost::container::flat_map<std::string, BasicVariantType>
1374 emptyProps;
1375 emptyInterfaces.emplace(std::string{}, emptyProps);
1376
Matt Spinlere789bf12021-02-24 12:33:01 -06001377 for (auto& foundDeviceAndPath : foundDevices)
James Feist94219d12020-03-03 11:52:25 -08001378 {
Matt Spinlere789bf12021-02-24 12:33:01 -06001379 const boost::container::flat_map<
1380 std::string, BasicVariantType>& foundDevice =
1381 std::get<0>(foundDeviceAndPath);
1382 const std::string& path = std::get<1>(foundDeviceAndPath);
1383
1384 // Need all interfaces on this path so that template
1385 // substitutions can be done with any of the contained
Matt Spinler4bab9322021-04-08 14:38:43 -05001386 // properties. If the probe that passed didn't use an
1387 // interface, such as if it was just TRUE, then
1388 // templateCharReplace will just get passed in an empty
1389 // map.
1390 const DBusProbeObjectT::mapped_type* allInterfacesOnPath =
1391 &emptyInterfaces;
1392
1393 auto ifacesIt = allInterfaces.find(path);
1394 if (ifacesIt != allInterfaces.end())
Matt Spinlere789bf12021-02-24 12:33:01 -06001395 {
Matt Spinler4bab9322021-04-08 14:38:43 -05001396 allInterfacesOnPath = &ifacesIt->second;
Matt Spinlere789bf12021-02-24 12:33:01 -06001397 }
1398
James Feist94219d12020-03-03 11:52:25 -08001399 nlohmann::json record = *recordPtr;
1400 std::string recordName =
1401 getRecordName(foundDevice, probeName);
1402 size_t foundDeviceIdx = indexes.front();
1403 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001404
James Feist35f5e0e2020-03-16 14:02:27 -07001405 // check name first so we have no duplicate names
1406 auto getName = record.find("Name");
1407 if (getName == record.end())
1408 {
1409 std::cerr << "Record Missing Name! " << record.dump();
1410 continue; // this should be impossible at this level
1411 }
1412
1413 nlohmann::json copyForName = {{"Name", getName.value()}};
1414 nlohmann::json::iterator copyIt = copyForName.begin();
Matt Spinlere789bf12021-02-24 12:33:01 -06001415 std::optional<std::string> replaceVal =
Matt Spinler4bab9322021-04-08 14:38:43 -05001416 templateCharReplace(copyIt, *allInterfacesOnPath,
Matt Spinlere789bf12021-02-24 12:33:01 -06001417 foundDeviceIdx, replaceStr);
James Feist35f5e0e2020-03-16 14:02:27 -07001418
1419 if (!replaceStr && replaceVal)
1420 {
1421 if (usedNames.find(copyIt.value()) != usedNames.end())
1422 {
1423 replaceStr = replaceVal;
1424 copyForName = {{"Name", getName.value()}};
1425 copyIt = copyForName.begin();
Matt Spinler4bab9322021-04-08 14:38:43 -05001426 templateCharReplace(copyIt, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001427 foundDeviceIdx, replaceStr);
1428 }
1429 }
1430
1431 if (replaceStr)
1432 {
1433 std::cerr << "Duplicates found, replacing "
1434 << *replaceStr
1435 << " with found device index.\n Consider "
1436 "fixing template to not have duplicates\n";
1437 }
James Feist08a5b172019-08-28 14:47:47 -07001438
1439 for (auto keyPair = record.begin(); keyPair != record.end();
1440 keyPair++)
1441 {
James Feist35f5e0e2020-03-16 14:02:27 -07001442 if (keyPair.key() == "Name")
1443 {
1444 keyPair.value() = copyIt.value();
1445 usedNames.insert(copyIt.value());
1446
1447 continue; // already covered above
1448 }
Matt Spinler4bab9322021-04-08 14:38:43 -05001449 templateCharReplace(keyPair, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001450 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001451 }
1452
James Feist35f5e0e2020-03-16 14:02:27 -07001453 // insert into configuration temporarily to be able to
1454 // reference ourselves
1455
1456 _systemConfiguration[recordName] = record;
1457
James Feist08a5b172019-08-28 14:47:47 -07001458 auto findExpose = record.find("Exposes");
1459 if (findExpose == record.end())
1460 {
James Feistf1b14142019-04-10 15:22:09 -07001461 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001462 continue;
1463 }
James Feistf1b14142019-04-10 15:22:09 -07001464
James Feist08a5b172019-08-28 14:47:47 -07001465 for (auto& expose : *findExpose)
1466 {
1467 for (auto keyPair = expose.begin();
1468 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001469 {
James Feist08a5b172019-08-28 14:47:47 -07001470
Matt Spinler4bab9322021-04-08 14:38:43 -05001471 templateCharReplace(keyPair, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001472 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001473
James Feist668bbb12020-02-05 14:27:26 -08001474 bool isBind =
1475 boost::starts_with(keyPair.key(), "Bind");
1476 bool isDisable = keyPair.key() == "DisableNode";
1477
1478 // special cases
1479 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001480 {
James Feist668bbb12020-02-05 14:27:26 -08001481 continue;
1482 }
1483
1484 if (keyPair.value().type() !=
1485 nlohmann::json::value_t::string &&
1486 keyPair.value().type() !=
1487 nlohmann::json::value_t::array)
1488 {
1489 std::cerr << "Value is invalid type "
1490 << keyPair.key() << "\n";
1491 continue;
1492 }
1493
1494 std::vector<std::string> matches;
1495 if (keyPair.value().type() ==
1496 nlohmann::json::value_t::string)
1497 {
1498 matches.emplace_back(keyPair.value());
1499 }
1500 else
1501 {
1502 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001503 {
James Feist668bbb12020-02-05 14:27:26 -08001504 if (value.type() !=
1505 nlohmann::json::value_t::string)
1506 {
1507 std::cerr << "Value is invalid type "
1508 << value << "\n";
1509 break;
1510 }
1511 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001512 }
James Feist668bbb12020-02-05 14:27:26 -08001513 }
James Feist08a5b172019-08-28 14:47:47 -07001514
James Feist668bbb12020-02-05 14:27:26 -08001515 std::set<std::string> foundMatches;
1516 for (auto& configurationPair :
1517 _systemConfiguration.items())
1518 {
1519 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001520 {
James Feist668bbb12020-02-05 14:27:26 -08001521 // don't disable ourselves
1522 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001523 {
James Feist1b2e2242018-01-30 13:45:19 -08001524 continue;
1525 }
James Feist668bbb12020-02-05 14:27:26 -08001526 }
1527 auto configListFind =
1528 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001529
James Feist668bbb12020-02-05 14:27:26 -08001530 if (configListFind ==
1531 configurationPair.value().end() ||
1532 configListFind->type() !=
1533 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001534 {
James Feist668bbb12020-02-05 14:27:26 -08001535 continue;
James Feist08a5b172019-08-28 14:47:47 -07001536 }
James Feist668bbb12020-02-05 14:27:26 -08001537 for (auto& exposedObject : *configListFind)
1538 {
1539 auto matchIt = std::find_if(
1540 matches.begin(), matches.end(),
1541 [name = (exposedObject)["Name"]
1542 .get<std::string>()](
1543 const std::string& s) {
1544 return s == name;
1545 });
1546 if (matchIt == matches.end())
1547 {
1548 continue;
1549 }
1550 foundMatches.insert(*matchIt);
1551
1552 if (isBind)
1553 {
1554 std::string bind = keyPair.key().substr(
1555 sizeof("Bind") - 1);
1556
1557 exposedObject["Status"] = "okay";
1558 expose[bind] = exposedObject;
1559 }
1560 else if (isDisable)
1561 {
1562 exposedObject["Status"] = "disabled";
1563 }
1564 }
1565 }
1566 if (foundMatches.size() != matches.size())
1567 {
1568 std::cerr << "configuration file "
1569 "dependency error, "
1570 "could not find "
1571 << keyPair.key() << " "
1572 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001573 }
1574 }
1575 }
James Feist08a5b172019-08-28 14:47:47 -07001576 // overwrite ourselves with cleaned up version
1577 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001578 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001579 }
1580 });
James Feist787c3c32019-11-07 14:42:58 -08001581
James Feist733f7652019-11-13 14:32:29 -08001582 // parse out dbus probes by discarding other probe types, store in a
1583 // map
1584 for (const std::string& probe : probeCommand)
1585 {
1586 bool found = false;
1587 boost::container::flat_map<const char*, probe_type_codes,
1588 cmp_str>::const_iterator probeType;
1589 for (probeType = PROBE_TYPES.begin();
1590 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001591 {
James Feist733f7652019-11-13 14:32:29 -08001592 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001593 {
James Feist733f7652019-11-13 14:32:29 -08001594 found = true;
1595 break;
James Feist787c3c32019-11-07 14:42:58 -08001596 }
James Feist787c3c32019-11-07 14:42:58 -08001597 }
James Feist733f7652019-11-13 14:32:29 -08001598 if (found)
1599 {
1600 continue;
1601 }
1602 // syntax requires probe before first open brace
1603 auto findStart = probe.find("(");
1604 std::string interface = probe.substr(0, findStart);
1605 dbusProbeInterfaces.emplace(interface);
1606 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001607 }
James Feist733f7652019-11-13 14:32:29 -08001608 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001609 }
James Feist75fdeeb2018-02-20 14:26:16 -08001610
James Feist733f7652019-11-13 14:32:29 -08001611 // probe vector stores a shared_ptr to each PerformProbe that cares
1612 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001613 findDbusObjects(std::move(dbusProbePointers),
1614 std::move(dbusProbeInterfaces), shared_from_this());
1615 if constexpr (DEBUG)
1616 {
1617 std::cerr << __func__ << " " << __LINE__ << "\n";
1618 }
James Feist733f7652019-11-13 14:32:29 -08001619}
1620
1621PerformScan::~PerformScan()
1622{
1623 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001624 {
James Feist733f7652019-11-13 14:32:29 -08001625 auto nextScan = std::make_shared<PerformScan>(
1626 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001627 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001628 nextScan->passedProbes = std::move(passedProbes);
1629 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1630 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001631
1632 if constexpr (DEBUG)
1633 {
1634 std::cerr << __func__ << " " << __LINE__ << "\n";
1635 }
James Feist8f2710a2018-05-09 17:18:55 -07001636 }
James Feist733f7652019-11-13 14:32:29 -08001637 else
1638 {
James Feist4dc617b2020-05-01 09:54:47 -07001639 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001640
1641 if constexpr (DEBUG)
1642 {
1643 std::cerr << __func__ << " " << __LINE__ << "\n";
1644 }
James Feist733f7652019-11-13 14:32:29 -08001645 }
1646}
James Feistc95cb142018-02-26 10:41:42 -08001647
James Feistb1728ca2020-04-30 15:40:55 -07001648void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001649 nlohmann::json& systemConfiguration)
1650{
1651 static bool scannedPowerOff = false;
1652 static bool scannedPowerOn = false;
1653
James Feistfb00f392019-06-25 14:16:48 -07001654 if (systemConfiguration.empty() || lastJson.empty())
1655 {
1656 return; // not ready yet
1657 }
James Feist1df06a42019-04-11 14:23:04 -07001658 if (scannedPowerOn)
1659 {
1660 return;
1661 }
1662
1663 if (!isPowerOn() && scannedPowerOff)
1664 {
1665 return;
1666 }
1667
James Feistb1728ca2020-04-30 15:40:55 -07001668 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001669 timer.async_wait(
1670 [&systemConfiguration](const boost::system::error_code& ec) {
1671 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001672 {
James Feist1a996582019-05-14 15:10:06 -07001673 // we were cancelled
1674 return;
1675 }
1676
1677 bool powerOff = !isPowerOn();
1678 for (const auto& item : lastJson.items())
1679 {
1680 if (systemConfiguration.find(item.key()) ==
1681 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001682 {
James Feist1a996582019-05-14 15:10:06 -07001683 bool isDetectedPowerOn = false;
1684 auto powerState = item.value().find("PowerState");
1685 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001686 {
James Feist1a996582019-05-14 15:10:06 -07001687 auto ptr = powerState->get_ptr<const std::string*>();
1688 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001689 {
James Feist1a996582019-05-14 15:10:06 -07001690 if (*ptr == "On" || *ptr == "BiosPost")
1691 {
1692 isDetectedPowerOn = true;
1693 }
James Feist1df06a42019-04-11 14:23:04 -07001694 }
1695 }
James Feist1a996582019-05-14 15:10:06 -07001696 if (powerOff && isDetectedPowerOn)
1697 {
1698 // power not on yet, don't know if it's there or not
1699 continue;
1700 }
1701 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1702 {
1703 // already logged it when power was off
1704 continue;
1705 }
James Feist1df06a42019-04-11 14:23:04 -07001706
James Feist1a996582019-05-14 15:10:06 -07001707 logDeviceRemoved(item.value());
1708 }
James Feist1df06a42019-04-11 14:23:04 -07001709 }
James Feist1a996582019-05-14 15:10:06 -07001710 scannedPowerOff = true;
1711 if (!powerOff)
1712 {
1713 scannedPowerOn = true;
1714 }
1715 });
James Feist1df06a42019-04-11 14:23:04 -07001716}
1717
James Feist8f2710a2018-05-09 17:18:55 -07001718// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001719void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1720 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001721{
James Feist2539ccd2020-05-01 16:15:08 -07001722 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001723 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001724 static size_t instance = 0;
1725 instance++;
1726 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001727
James Feistb1728ca2020-04-30 15:40:55 -07001728 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001729
1730 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001731 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001732 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001733 if (ec == boost::asio::error::operation_aborted)
1734 {
1735 // we were cancelled
1736 return;
1737 }
1738 else if (ec)
1739 {
1740 std::cerr << "async wait error " << ec << "\n";
1741 return;
1742 }
1743
James Feist2539ccd2020-05-01 16:15:08 -07001744 if (inProgress)
1745 {
1746 propertiesChangedCallback(systemConfiguration, objServer);
1747 return;
1748 }
1749 inProgress = true;
1750
James Feist8f2710a2018-05-09 17:18:55 -07001751 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001752 auto missingConfigurations = std::make_shared<nlohmann::json>();
1753 *missingConfigurations = systemConfiguration;
1754
James Feist8f2710a2018-05-09 17:18:55 -07001755 std::list<nlohmann::json> configurations;
1756 if (!findJsonFiles(configurations))
1757 {
1758 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001759 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001760 return;
1761 }
1762
1763 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001764 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001765 objServer,
1766 [&systemConfiguration, &objServer, count, oldConfiguration,
1767 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001768 // this is something that since ac has been applied to the bmc
1769 // we saw, and we no longer see it
1770 bool powerOff = !isPowerOn();
1771 for (const auto& item : missingConfigurations->items())
1772 {
1773 bool isDetectedPowerOn = false;
1774 auto powerState = item.value().find("PowerState");
1775 if (powerState != item.value().end())
1776 {
1777 auto ptr = powerState->get_ptr<const std::string*>();
1778 if (ptr)
1779 {
1780 if (*ptr == "On" || *ptr == "BiosPost")
1781 {
1782 isDetectedPowerOn = true;
1783 }
1784 }
1785 }
1786 if (powerOff && isDetectedPowerOn)
1787 {
1788 // power not on yet, don't know if it's there or not
1789 continue;
1790 }
1791 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001792 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001793 ifaces = inventory[name];
1794 for (auto& iface : ifaces)
1795 {
James Feist02d2b932020-02-06 16:28:48 -08001796 auto sharedPtr = iface.lock();
1797 if (!sharedPtr)
1798 {
1799 continue; // was already deleted elsewhere
1800 }
1801 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001802 }
1803 ifaces.clear();
1804 systemConfiguration.erase(item.key());
1805 logDeviceRemoved(item.value());
1806 }
1807
James Feist8f2710a2018-05-09 17:18:55 -07001808 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001809 for (auto it = newConfiguration.begin();
1810 it != newConfiguration.end();)
1811 {
1812 auto findKey = oldConfiguration.find(it.key());
1813 if (findKey != oldConfiguration.end())
1814 {
1815 it = newConfiguration.erase(it);
1816 }
1817 else
1818 {
1819 it++;
1820 }
1821 }
James Feist899e17f2019-09-13 11:46:29 -07001822 for (const auto& item : newConfiguration.items())
1823 {
1824 logDeviceAdded(item.value());
1825 }
1826
James Feist2539ccd2020-05-01 16:15:08 -07001827 inProgress = false;
1828
James Feist8f2710a2018-05-09 17:18:55 -07001829 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001830 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001831
James Feistbb43d022018-06-12 15:44:33 -07001832 io.post([&]() {
1833 if (!writeJsonFiles(systemConfiguration))
1834 {
1835 std::cerr << "Error writing json files\n";
1836 }
1837 });
James Feist8f2710a2018-05-09 17:18:55 -07001838 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001839 postToDbus(newConfiguration, systemConfiguration,
1840 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001841 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001842 {
James Feist899e17f2019-09-13 11:46:29 -07001843 return;
James Feist1df06a42019-04-11 14:23:04 -07001844 }
James Feist899e17f2019-09-13 11:46:29 -07001845 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001846 });
1847 });
1848 });
1849 perfScan->run();
1850 });
James Feist75fdeeb2018-02-20 14:26:16 -08001851}
1852
James Feist4dc617b2020-05-01 09:54:47 -07001853void registerCallback(nlohmann::json& systemConfiguration,
1854 sdbusplus::asio::object_server& objServer,
Matt Spinlere789bf12021-02-24 12:33:01 -06001855 const std::string& path)
James Feist75fdeeb2018-02-20 14:26:16 -08001856{
James Feist4dc617b2020-05-01 09:54:47 -07001857 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1858 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001859
Matt Spinlere789bf12021-02-24 12:33:01 -06001860 auto find = dbusMatches.find(path);
James Feist4dc617b2020-05-01 09:54:47 -07001861 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001862 {
James Feist4dc617b2020-05-01 09:54:47 -07001863 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001864 }
James Feist4dc617b2020-05-01 09:54:47 -07001865 std::function<void(sdbusplus::message::message & message)> eventHandler =
1866
1867 [&](sdbusplus::message::message&) {
1868 propertiesChangedCallback(systemConfiguration, objServer);
1869 };
1870
1871 sdbusplus::bus::match::match match(
1872 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
Matt Spinlere789bf12021-02-24 12:33:01 -06001873 "type='signal',member='PropertiesChanged',path='" + path + "'",
James Feist4dc617b2020-05-01 09:54:47 -07001874 eventHandler);
Matt Spinlere789bf12021-02-24 12:33:01 -06001875 dbusMatches.emplace(path, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001876}
1877
James Feist98132792019-07-09 13:29:09 -07001878int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001879{
1880 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001881 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001882 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001883
James Feist8f2710a2018-05-09 17:18:55 -07001884 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001885
James Feist8f2710a2018-05-09 17:18:55 -07001886 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1887 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1888 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001889
James Feist4131aea2018-03-09 09:47:30 -08001890 // to keep reference to the match / filter objects so they don't get
1891 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001892
1893 nlohmann::json systemConfiguration = nlohmann::json::object();
1894
Brad Bishopc76af0f2020-12-04 13:50:23 -05001895 // We need a poke from DBus for static providers that create all their
1896 // objects prior to claiming a well-known name, and thus don't emit any
1897 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1898 // for any reason, expected or otherwise, we'll need a poke to remove
1899 // entities from DBus.
1900 sdbusplus::bus::match::match nameOwnerChangedMatch(
1901 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1902 sdbusplus::bus::match::rules::nameOwnerChanged(),
1903 [&](sdbusplus::message::message&) {
1904 propertiesChangedCallback(systemConfiguration, objServer);
1905 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001906 // We also need a poke from DBus when new interfaces are created or
1907 // destroyed.
1908 sdbusplus::bus::match::match interfacesAddedMatch(
1909 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1910 sdbusplus::bus::match::rules::interfacesAdded(),
1911 [&](sdbusplus::message::message&) {
1912 propertiesChangedCallback(systemConfiguration, objServer);
1913 });
1914 sdbusplus::bus::match::match interfacesRemovedMatch(
1915 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1916 sdbusplus::bus::match::rules::interfacesRemoved(),
1917 [&](sdbusplus::message::message&) {
1918 propertiesChangedCallback(systemConfiguration, objServer);
1919 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001920
James Feist4dc617b2020-05-01 09:54:47 -07001921 io.post(
1922 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001923
James Feistfd1264a2018-05-03 12:10:00 -07001924 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001925 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001926 });
James Feist8f2710a2018-05-09 17:18:55 -07001927 entityIface->initialize();
1928
James Feist1df06a42019-04-11 14:23:04 -07001929 if (fwVersionIsSame())
1930 {
1931 if (std::filesystem::is_regular_file(currentConfiguration))
1932 {
1933 // this file could just be deleted, but it's nice for debug
1934 std::filesystem::create_directory(tempConfigDir);
1935 std::filesystem::remove(lastConfiguration);
1936 std::filesystem::copy(currentConfiguration, lastConfiguration);
1937 std::filesystem::remove(currentConfiguration);
1938
1939 std::ifstream jsonStream(lastConfiguration);
1940 if (jsonStream.good())
1941 {
1942 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1943 if (data.is_discarded())
1944 {
1945 std::cerr << "syntax error in " << lastConfiguration
1946 << "\n";
1947 }
1948 else
1949 {
1950 lastJson = std::move(data);
1951 }
1952 }
1953 else
1954 {
1955 std::cerr << "unable to open " << lastConfiguration << "\n";
1956 }
1957 }
1958 }
1959 else
1960 {
1961 // not an error, just logging at this level to make it in the journal
1962 std::cerr << "Clearing previous configuration\n";
1963 std::filesystem::remove(currentConfiguration);
1964 }
1965
1966 // some boards only show up after power is on, we want to not say they are
1967 // removed until the same state happens
1968 setupPowerMatch(SYSTEM_BUS);
1969
James Feist1b2e2242018-01-30 13:45:19 -08001970 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001971
1972 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001973}