blob: 932b6f969d4aa4b26b2b20030d410071e4aaff71 [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 Feist3cb5fec2018-01-23 14:41:51 -08001303
James Feist35f5e0e2020-03-16 14:02:27 -07001304 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001305 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001306 std::list<size_t> indexes(foundDevices.size());
1307 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001308
James Feist35f5e0e2020-03-16 14:02:27 -07001309 size_t indexIdx = probeName.find("$");
1310 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001311
1312 // copy over persisted configurations and make sure we remove
1313 // indexes that are already used
1314 for (auto itr = foundDevices.begin();
1315 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001316 {
Matt Spinlere789bf12021-02-24 12:33:01 -06001317 std::string recordName =
1318 getRecordName(std::get<0>(*itr), probeName);
James Feistf1b14142019-04-10 15:22:09 -07001319
James Feist08a5b172019-08-28 14:47:47 -07001320 auto fromLastJson = lastJson.find(recordName);
1321 if (fromLastJson != lastJson.end())
1322 {
James Feist02d2b932020-02-06 16:28:48 -08001323 auto findExposes = fromLastJson->find("Exposes");
1324 // delete nulls from any updates
1325 if (findExposes != fromLastJson->end())
1326 {
1327 auto copy = nlohmann::json::array();
1328 for (auto& expose : *findExposes)
1329 {
1330 if (expose.is_null())
1331 {
1332 continue;
1333 }
1334 copy.emplace_back(expose);
1335 }
1336 *findExposes = copy;
1337 }
1338
James Feist08a5b172019-08-28 14:47:47 -07001339 // keep user changes
1340 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001341 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001342 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001343 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001344 {
1345 auto nameIt = fromLastJson->find("Name");
1346 if (nameIt == fromLastJson->end())
1347 {
1348 std::cerr << "Last JSON Illegal\n";
1349 continue;
1350 }
1351
1352 int index = std::stoi(
1353 nameIt->get<std::string>().substr(indexIdx),
1354 nullptr, 0);
James Feist35f5e0e2020-03-16 14:02:27 -07001355 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001356 auto usedIt = std::find(indexes.begin(),
1357 indexes.end(), index);
1358
1359 if (usedIt == indexes.end())
1360 {
1361 continue; // less items now
1362 }
1363 indexes.erase(usedIt);
1364 }
1365
James Feist08a5b172019-08-28 14:47:47 -07001366 continue;
1367 }
James Feist94219d12020-03-03 11:52:25 -08001368 itr++;
1369 }
James Feist35f5e0e2020-03-16 14:02:27 -07001370
1371 std::optional<std::string> replaceStr;
1372
Matt Spinlere789bf12021-02-24 12:33:01 -06001373 for (auto& foundDeviceAndPath : foundDevices)
James Feist94219d12020-03-03 11:52:25 -08001374 {
Matt Spinlere789bf12021-02-24 12:33:01 -06001375 const boost::container::flat_map<
1376 std::string, BasicVariantType>& foundDevice =
1377 std::get<0>(foundDeviceAndPath);
1378 const std::string& path = std::get<1>(foundDeviceAndPath);
1379
1380 // Need all interfaces on this path so that template
1381 // substitutions can be done with any of the contained
1382 // properties.
1383 auto allInterfacesOnPath = allInterfaces.find(path);
1384 if (allInterfacesOnPath == allInterfaces.end())
1385 {
1386 // Should be impossible at this point.
1387 std::cerr << "Unrecognized path " << path << "\n";
1388 continue;
1389 }
1390
James Feist94219d12020-03-03 11:52:25 -08001391 nlohmann::json record = *recordPtr;
1392 std::string recordName =
1393 getRecordName(foundDevice, probeName);
1394 size_t foundDeviceIdx = indexes.front();
1395 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001396
James Feist35f5e0e2020-03-16 14:02:27 -07001397 // check name first so we have no duplicate names
1398 auto getName = record.find("Name");
1399 if (getName == record.end())
1400 {
1401 std::cerr << "Record Missing Name! " << record.dump();
1402 continue; // this should be impossible at this level
1403 }
1404
1405 nlohmann::json copyForName = {{"Name", getName.value()}};
1406 nlohmann::json::iterator copyIt = copyForName.begin();
Matt Spinlere789bf12021-02-24 12:33:01 -06001407 std::optional<std::string> replaceVal =
1408 templateCharReplace(copyIt, allInterfacesOnPath->second,
1409 foundDeviceIdx, replaceStr);
James Feist35f5e0e2020-03-16 14:02:27 -07001410
1411 if (!replaceStr && replaceVal)
1412 {
1413 if (usedNames.find(copyIt.value()) != usedNames.end())
1414 {
1415 replaceStr = replaceVal;
1416 copyForName = {{"Name", getName.value()}};
1417 copyIt = copyForName.begin();
Matt Spinlere789bf12021-02-24 12:33:01 -06001418 templateCharReplace(copyIt,
1419 allInterfacesOnPath->second,
James Feist35f5e0e2020-03-16 14:02:27 -07001420 foundDeviceIdx, replaceStr);
1421 }
1422 }
1423
1424 if (replaceStr)
1425 {
1426 std::cerr << "Duplicates found, replacing "
1427 << *replaceStr
1428 << " with found device index.\n Consider "
1429 "fixing template to not have duplicates\n";
1430 }
James Feist08a5b172019-08-28 14:47:47 -07001431
1432 for (auto keyPair = record.begin(); keyPair != record.end();
1433 keyPair++)
1434 {
James Feist35f5e0e2020-03-16 14:02:27 -07001435 if (keyPair.key() == "Name")
1436 {
1437 keyPair.value() = copyIt.value();
1438 usedNames.insert(copyIt.value());
1439
1440 continue; // already covered above
1441 }
Matt Spinlere789bf12021-02-24 12:33:01 -06001442 templateCharReplace(keyPair,
1443 allInterfacesOnPath->second,
James Feist35f5e0e2020-03-16 14:02:27 -07001444 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001445 }
1446
James Feist35f5e0e2020-03-16 14:02:27 -07001447 // insert into configuration temporarily to be able to
1448 // reference ourselves
1449
1450 _systemConfiguration[recordName] = record;
1451
James Feist08a5b172019-08-28 14:47:47 -07001452 auto findExpose = record.find("Exposes");
1453 if (findExpose == record.end())
1454 {
James Feistf1b14142019-04-10 15:22:09 -07001455 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001456 continue;
1457 }
James Feistf1b14142019-04-10 15:22:09 -07001458
James Feist08a5b172019-08-28 14:47:47 -07001459 for (auto& expose : *findExpose)
1460 {
1461 for (auto keyPair = expose.begin();
1462 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001463 {
James Feist08a5b172019-08-28 14:47:47 -07001464
Matt Spinlere789bf12021-02-24 12:33:01 -06001465 templateCharReplace(keyPair,
1466 allInterfacesOnPath->second,
James Feist35f5e0e2020-03-16 14:02:27 -07001467 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001468
James Feist668bbb12020-02-05 14:27:26 -08001469 bool isBind =
1470 boost::starts_with(keyPair.key(), "Bind");
1471 bool isDisable = keyPair.key() == "DisableNode";
1472
1473 // special cases
1474 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001475 {
James Feist668bbb12020-02-05 14:27:26 -08001476 continue;
1477 }
1478
1479 if (keyPair.value().type() !=
1480 nlohmann::json::value_t::string &&
1481 keyPair.value().type() !=
1482 nlohmann::json::value_t::array)
1483 {
1484 std::cerr << "Value is invalid type "
1485 << keyPair.key() << "\n";
1486 continue;
1487 }
1488
1489 std::vector<std::string> matches;
1490 if (keyPair.value().type() ==
1491 nlohmann::json::value_t::string)
1492 {
1493 matches.emplace_back(keyPair.value());
1494 }
1495 else
1496 {
1497 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001498 {
James Feist668bbb12020-02-05 14:27:26 -08001499 if (value.type() !=
1500 nlohmann::json::value_t::string)
1501 {
1502 std::cerr << "Value is invalid type "
1503 << value << "\n";
1504 break;
1505 }
1506 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001507 }
James Feist668bbb12020-02-05 14:27:26 -08001508 }
James Feist08a5b172019-08-28 14:47:47 -07001509
James Feist668bbb12020-02-05 14:27:26 -08001510 std::set<std::string> foundMatches;
1511 for (auto& configurationPair :
1512 _systemConfiguration.items())
1513 {
1514 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001515 {
James Feist668bbb12020-02-05 14:27:26 -08001516 // don't disable ourselves
1517 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001518 {
James Feist1b2e2242018-01-30 13:45:19 -08001519 continue;
1520 }
James Feist668bbb12020-02-05 14:27:26 -08001521 }
1522 auto configListFind =
1523 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001524
James Feist668bbb12020-02-05 14:27:26 -08001525 if (configListFind ==
1526 configurationPair.value().end() ||
1527 configListFind->type() !=
1528 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001529 {
James Feist668bbb12020-02-05 14:27:26 -08001530 continue;
James Feist08a5b172019-08-28 14:47:47 -07001531 }
James Feist668bbb12020-02-05 14:27:26 -08001532 for (auto& exposedObject : *configListFind)
1533 {
1534 auto matchIt = std::find_if(
1535 matches.begin(), matches.end(),
1536 [name = (exposedObject)["Name"]
1537 .get<std::string>()](
1538 const std::string& s) {
1539 return s == name;
1540 });
1541 if (matchIt == matches.end())
1542 {
1543 continue;
1544 }
1545 foundMatches.insert(*matchIt);
1546
1547 if (isBind)
1548 {
1549 std::string bind = keyPair.key().substr(
1550 sizeof("Bind") - 1);
1551
1552 exposedObject["Status"] = "okay";
1553 expose[bind] = exposedObject;
1554 }
1555 else if (isDisable)
1556 {
1557 exposedObject["Status"] = "disabled";
1558 }
1559 }
1560 }
1561 if (foundMatches.size() != matches.size())
1562 {
1563 std::cerr << "configuration file "
1564 "dependency error, "
1565 "could not find "
1566 << keyPair.key() << " "
1567 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001568 }
1569 }
1570 }
James Feist08a5b172019-08-28 14:47:47 -07001571 // overwrite ourselves with cleaned up version
1572 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001573 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001574 }
1575 });
James Feist787c3c32019-11-07 14:42:58 -08001576
James Feist733f7652019-11-13 14:32:29 -08001577 // parse out dbus probes by discarding other probe types, store in a
1578 // map
1579 for (const std::string& probe : probeCommand)
1580 {
1581 bool found = false;
1582 boost::container::flat_map<const char*, probe_type_codes,
1583 cmp_str>::const_iterator probeType;
1584 for (probeType = PROBE_TYPES.begin();
1585 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001586 {
James Feist733f7652019-11-13 14:32:29 -08001587 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001588 {
James Feist733f7652019-11-13 14:32:29 -08001589 found = true;
1590 break;
James Feist787c3c32019-11-07 14:42:58 -08001591 }
James Feist787c3c32019-11-07 14:42:58 -08001592 }
James Feist733f7652019-11-13 14:32:29 -08001593 if (found)
1594 {
1595 continue;
1596 }
1597 // syntax requires probe before first open brace
1598 auto findStart = probe.find("(");
1599 std::string interface = probe.substr(0, findStart);
1600 dbusProbeInterfaces.emplace(interface);
1601 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001602 }
James Feist733f7652019-11-13 14:32:29 -08001603 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001604 }
James Feist75fdeeb2018-02-20 14:26:16 -08001605
James Feist733f7652019-11-13 14:32:29 -08001606 // probe vector stores a shared_ptr to each PerformProbe that cares
1607 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001608 findDbusObjects(std::move(dbusProbePointers),
1609 std::move(dbusProbeInterfaces), shared_from_this());
1610 if constexpr (DEBUG)
1611 {
1612 std::cerr << __func__ << " " << __LINE__ << "\n";
1613 }
James Feist733f7652019-11-13 14:32:29 -08001614}
1615
1616PerformScan::~PerformScan()
1617{
1618 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001619 {
James Feist733f7652019-11-13 14:32:29 -08001620 auto nextScan = std::make_shared<PerformScan>(
1621 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001622 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001623 nextScan->passedProbes = std::move(passedProbes);
1624 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1625 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001626
1627 if constexpr (DEBUG)
1628 {
1629 std::cerr << __func__ << " " << __LINE__ << "\n";
1630 }
James Feist8f2710a2018-05-09 17:18:55 -07001631 }
James Feist733f7652019-11-13 14:32:29 -08001632 else
1633 {
James Feist4dc617b2020-05-01 09:54:47 -07001634 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001635
1636 if constexpr (DEBUG)
1637 {
1638 std::cerr << __func__ << " " << __LINE__ << "\n";
1639 }
James Feist733f7652019-11-13 14:32:29 -08001640 }
1641}
James Feistc95cb142018-02-26 10:41:42 -08001642
James Feistb1728ca2020-04-30 15:40:55 -07001643void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001644 nlohmann::json& systemConfiguration)
1645{
1646 static bool scannedPowerOff = false;
1647 static bool scannedPowerOn = false;
1648
James Feistfb00f392019-06-25 14:16:48 -07001649 if (systemConfiguration.empty() || lastJson.empty())
1650 {
1651 return; // not ready yet
1652 }
James Feist1df06a42019-04-11 14:23:04 -07001653 if (scannedPowerOn)
1654 {
1655 return;
1656 }
1657
1658 if (!isPowerOn() && scannedPowerOff)
1659 {
1660 return;
1661 }
1662
James Feistb1728ca2020-04-30 15:40:55 -07001663 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001664 timer.async_wait(
1665 [&systemConfiguration](const boost::system::error_code& ec) {
1666 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001667 {
James Feist1a996582019-05-14 15:10:06 -07001668 // we were cancelled
1669 return;
1670 }
1671
1672 bool powerOff = !isPowerOn();
1673 for (const auto& item : lastJson.items())
1674 {
1675 if (systemConfiguration.find(item.key()) ==
1676 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001677 {
James Feist1a996582019-05-14 15:10:06 -07001678 bool isDetectedPowerOn = false;
1679 auto powerState = item.value().find("PowerState");
1680 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001681 {
James Feist1a996582019-05-14 15:10:06 -07001682 auto ptr = powerState->get_ptr<const std::string*>();
1683 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001684 {
James Feist1a996582019-05-14 15:10:06 -07001685 if (*ptr == "On" || *ptr == "BiosPost")
1686 {
1687 isDetectedPowerOn = true;
1688 }
James Feist1df06a42019-04-11 14:23:04 -07001689 }
1690 }
James Feist1a996582019-05-14 15:10:06 -07001691 if (powerOff && isDetectedPowerOn)
1692 {
1693 // power not on yet, don't know if it's there or not
1694 continue;
1695 }
1696 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1697 {
1698 // already logged it when power was off
1699 continue;
1700 }
James Feist1df06a42019-04-11 14:23:04 -07001701
James Feist1a996582019-05-14 15:10:06 -07001702 logDeviceRemoved(item.value());
1703 }
James Feist1df06a42019-04-11 14:23:04 -07001704 }
James Feist1a996582019-05-14 15:10:06 -07001705 scannedPowerOff = true;
1706 if (!powerOff)
1707 {
1708 scannedPowerOn = true;
1709 }
1710 });
James Feist1df06a42019-04-11 14:23:04 -07001711}
1712
James Feist8f2710a2018-05-09 17:18:55 -07001713// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001714void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1715 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001716{
James Feist2539ccd2020-05-01 16:15:08 -07001717 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001718 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001719 static size_t instance = 0;
1720 instance++;
1721 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001722
James Feistb1728ca2020-04-30 15:40:55 -07001723 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001724
1725 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001726 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001727 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001728 if (ec == boost::asio::error::operation_aborted)
1729 {
1730 // we were cancelled
1731 return;
1732 }
1733 else if (ec)
1734 {
1735 std::cerr << "async wait error " << ec << "\n";
1736 return;
1737 }
1738
James Feist2539ccd2020-05-01 16:15:08 -07001739 if (inProgress)
1740 {
1741 propertiesChangedCallback(systemConfiguration, objServer);
1742 return;
1743 }
1744 inProgress = true;
1745
James Feist8f2710a2018-05-09 17:18:55 -07001746 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001747 auto missingConfigurations = std::make_shared<nlohmann::json>();
1748 *missingConfigurations = systemConfiguration;
1749
James Feist8f2710a2018-05-09 17:18:55 -07001750 std::list<nlohmann::json> configurations;
1751 if (!findJsonFiles(configurations))
1752 {
1753 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001754 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001755 return;
1756 }
1757
1758 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001759 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001760 objServer,
1761 [&systemConfiguration, &objServer, count, oldConfiguration,
1762 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001763 // this is something that since ac has been applied to the bmc
1764 // we saw, and we no longer see it
1765 bool powerOff = !isPowerOn();
1766 for (const auto& item : missingConfigurations->items())
1767 {
1768 bool isDetectedPowerOn = false;
1769 auto powerState = item.value().find("PowerState");
1770 if (powerState != item.value().end())
1771 {
1772 auto ptr = powerState->get_ptr<const std::string*>();
1773 if (ptr)
1774 {
1775 if (*ptr == "On" || *ptr == "BiosPost")
1776 {
1777 isDetectedPowerOn = true;
1778 }
1779 }
1780 }
1781 if (powerOff && isDetectedPowerOn)
1782 {
1783 // power not on yet, don't know if it's there or not
1784 continue;
1785 }
1786 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001787 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001788 ifaces = inventory[name];
1789 for (auto& iface : ifaces)
1790 {
James Feist02d2b932020-02-06 16:28:48 -08001791 auto sharedPtr = iface.lock();
1792 if (!sharedPtr)
1793 {
1794 continue; // was already deleted elsewhere
1795 }
1796 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001797 }
1798 ifaces.clear();
1799 systemConfiguration.erase(item.key());
1800 logDeviceRemoved(item.value());
1801 }
1802
James Feist8f2710a2018-05-09 17:18:55 -07001803 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001804 for (auto it = newConfiguration.begin();
1805 it != newConfiguration.end();)
1806 {
1807 auto findKey = oldConfiguration.find(it.key());
1808 if (findKey != oldConfiguration.end())
1809 {
1810 it = newConfiguration.erase(it);
1811 }
1812 else
1813 {
1814 it++;
1815 }
1816 }
James Feist899e17f2019-09-13 11:46:29 -07001817 for (const auto& item : newConfiguration.items())
1818 {
1819 logDeviceAdded(item.value());
1820 }
1821
James Feist2539ccd2020-05-01 16:15:08 -07001822 inProgress = false;
1823
James Feist8f2710a2018-05-09 17:18:55 -07001824 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001825 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001826
James Feistbb43d022018-06-12 15:44:33 -07001827 io.post([&]() {
1828 if (!writeJsonFiles(systemConfiguration))
1829 {
1830 std::cerr << "Error writing json files\n";
1831 }
1832 });
James Feist8f2710a2018-05-09 17:18:55 -07001833 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001834 postToDbus(newConfiguration, systemConfiguration,
1835 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001836 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001837 {
James Feist899e17f2019-09-13 11:46:29 -07001838 return;
James Feist1df06a42019-04-11 14:23:04 -07001839 }
James Feist899e17f2019-09-13 11:46:29 -07001840 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001841 });
1842 });
1843 });
1844 perfScan->run();
1845 });
James Feist75fdeeb2018-02-20 14:26:16 -08001846}
1847
James Feist4dc617b2020-05-01 09:54:47 -07001848void registerCallback(nlohmann::json& systemConfiguration,
1849 sdbusplus::asio::object_server& objServer,
Matt Spinlere789bf12021-02-24 12:33:01 -06001850 const std::string& path)
James Feist75fdeeb2018-02-20 14:26:16 -08001851{
James Feist4dc617b2020-05-01 09:54:47 -07001852 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1853 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001854
Matt Spinlere789bf12021-02-24 12:33:01 -06001855 auto find = dbusMatches.find(path);
James Feist4dc617b2020-05-01 09:54:47 -07001856 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001857 {
James Feist4dc617b2020-05-01 09:54:47 -07001858 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001859 }
James Feist4dc617b2020-05-01 09:54:47 -07001860 std::function<void(sdbusplus::message::message & message)> eventHandler =
1861
1862 [&](sdbusplus::message::message&) {
1863 propertiesChangedCallback(systemConfiguration, objServer);
1864 };
1865
1866 sdbusplus::bus::match::match match(
1867 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
Matt Spinlere789bf12021-02-24 12:33:01 -06001868 "type='signal',member='PropertiesChanged',path='" + path + "'",
James Feist4dc617b2020-05-01 09:54:47 -07001869 eventHandler);
Matt Spinlere789bf12021-02-24 12:33:01 -06001870 dbusMatches.emplace(path, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001871}
1872
James Feist98132792019-07-09 13:29:09 -07001873int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001874{
1875 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001876 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001877 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001878
James Feist8f2710a2018-05-09 17:18:55 -07001879 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001880
James Feist8f2710a2018-05-09 17:18:55 -07001881 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1882 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1883 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001884
James Feist4131aea2018-03-09 09:47:30 -08001885 // to keep reference to the match / filter objects so they don't get
1886 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001887
1888 nlohmann::json systemConfiguration = nlohmann::json::object();
1889
Brad Bishopc76af0f2020-12-04 13:50:23 -05001890 // We need a poke from DBus for static providers that create all their
1891 // objects prior to claiming a well-known name, and thus don't emit any
1892 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1893 // for any reason, expected or otherwise, we'll need a poke to remove
1894 // entities from DBus.
1895 sdbusplus::bus::match::match nameOwnerChangedMatch(
1896 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1897 sdbusplus::bus::match::rules::nameOwnerChanged(),
1898 [&](sdbusplus::message::message&) {
1899 propertiesChangedCallback(systemConfiguration, objServer);
1900 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001901 // We also need a poke from DBus when new interfaces are created or
1902 // destroyed.
1903 sdbusplus::bus::match::match interfacesAddedMatch(
1904 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1905 sdbusplus::bus::match::rules::interfacesAdded(),
1906 [&](sdbusplus::message::message&) {
1907 propertiesChangedCallback(systemConfiguration, objServer);
1908 });
1909 sdbusplus::bus::match::match interfacesRemovedMatch(
1910 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1911 sdbusplus::bus::match::rules::interfacesRemoved(),
1912 [&](sdbusplus::message::message&) {
1913 propertiesChangedCallback(systemConfiguration, objServer);
1914 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001915
James Feist4dc617b2020-05-01 09:54:47 -07001916 io.post(
1917 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001918
James Feistfd1264a2018-05-03 12:10:00 -07001919 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001920 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001921 });
James Feist8f2710a2018-05-09 17:18:55 -07001922 entityIface->initialize();
1923
James Feist1df06a42019-04-11 14:23:04 -07001924 if (fwVersionIsSame())
1925 {
1926 if (std::filesystem::is_regular_file(currentConfiguration))
1927 {
1928 // this file could just be deleted, but it's nice for debug
1929 std::filesystem::create_directory(tempConfigDir);
1930 std::filesystem::remove(lastConfiguration);
1931 std::filesystem::copy(currentConfiguration, lastConfiguration);
1932 std::filesystem::remove(currentConfiguration);
1933
1934 std::ifstream jsonStream(lastConfiguration);
1935 if (jsonStream.good())
1936 {
1937 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1938 if (data.is_discarded())
1939 {
1940 std::cerr << "syntax error in " << lastConfiguration
1941 << "\n";
1942 }
1943 else
1944 {
1945 lastJson = std::move(data);
1946 }
1947 }
1948 else
1949 {
1950 std::cerr << "unable to open " << lastConfiguration << "\n";
1951 }
1952 }
1953 }
1954 else
1955 {
1956 // not an error, just logging at this level to make it in the journal
1957 std::cerr << "Clearing previous configuration\n";
1958 std::filesystem::remove(currentConfiguration);
1959 }
1960
1961 // some boards only show up after power is on, we want to not say they are
1962 // removed until the same state happens
1963 setupPowerMatch(SYSTEM_BUS);
1964
James Feist1b2e2242018-01-30 13:45:19 -08001965 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001966
1967 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001968}