blob: 744bcf8064d0d557b2e3d14643b868f6b03a7145 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
Brad Bishop1fb9f3f2020-08-28 08:15:13 -040016/// \file EntityManager.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
James Feist1df06a42019-04-11 14:23:04 -070018#include "EntityManager.hpp"
19
Patrick Venturea49dc332019-10-26 08:32:02 -070020#include "Overlay.hpp"
21#include "Utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070022#include "VariantVisitors.hpp"
23
James Feist11be6672018-04-06 14:05:32 -070024#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070025#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070028#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080029#include <boost/asio/io_context.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070030#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080031#include <boost/container/flat_map.hpp>
32#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070033#include <boost/range/iterator_range.hpp>
James Feist8c505da2020-05-28 10:06:33 -070034#include <nlohmann/json.hpp>
35#include <sdbusplus/asio/connection.hpp>
36#include <sdbusplus/asio/object_server.hpp>
37
Igor Kononenko9fd87e52020-10-06 01:18:17 +030038#include <charconv>
James Feist637b3ef2019-04-15 16:35:30 -070039#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080040#include <fstream>
41#include <iostream>
James Feista465ccc2019-02-08 12:51:01 -080042#include <regex>
James Feista465ccc2019-02-08 12:51:01 -080043#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080044constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
45constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070046constexpr const char* tempConfigDir = "/tmp/configuration/";
47constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
48constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080049constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070050constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080051
James Feistf1b14142019-04-10 15:22:09 -070052constexpr const bool DEBUG = false;
53
James Feist3cb5fec2018-01-23 14:41:51 -080054struct cmp_str
55{
James Feista465ccc2019-02-08 12:51:01 -080056 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080057 {
58 return std::strcmp(a, b) < 0;
59 }
60};
61
62// underscore T for collison with dbus c api
63enum class probe_type_codes
64{
65 FALSE_T,
66 TRUE_T,
67 AND,
68 OR,
James Feist6bd2a022018-03-13 12:30:58 -070069 FOUND,
70 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080071};
James Feista465ccc2019-02-08 12:51:01 -080072const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080073 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
74 {"TRUE", probe_type_codes::TRUE_T},
75 {"AND", probe_type_codes::AND},
76 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070077 {"FOUND", probe_type_codes::FOUND},
78 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080079
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +020080static constexpr std::array<const char*, 6> settableInterfaces = {
81 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
James Feist68500ff2018-08-08 15:40:42 -070082using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080083 std::variant<std::vector<std::string>, std::vector<double>, std::string,
84 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
85 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080086using GetSubTreeType = std::vector<
87 std::pair<std::string,
88 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
89
90using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070091 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080092 boost::container::flat_map<
93 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070094 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080095
James Feistd58879a2019-09-11 11:26:07 -070096// store reference to all interfaces so we can destroy them later
97boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080098 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070099 inventory;
100
James Feist3cb5fec2018-01-23 14:41:51 -0800101// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700102std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700103static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800104
James Feist02d2b932020-02-06 16:28:48 -0800105boost::asio::io_context io;
106
Johnathan Mantey2015f752019-03-26 15:22:31 -0700107const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
108const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800109
James Feist4dc617b2020-05-01 09:54:47 -0700110void registerCallback(nlohmann::json& systemConfiguration,
111 sdbusplus::asio::object_server& objServer,
Matt Spinlere789bf12021-02-24 12:33:01 -0600112 const std::string& path);
James Feist75fdeeb2018-02-20 14:26:16 -0800113
James Feistd58879a2019-09-11 11:26:07 -0700114static std::shared_ptr<sdbusplus::asio::dbus_interface>
115 createInterface(sdbusplus::asio::object_server& objServer,
116 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800117 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700118{
James Feist02d2b932020-02-06 16:28:48 -0800119 // on first add we have no reason to check for null before add, as there
120 // won't be any. For dynamically added interfaces, we check for null so that
121 // a constant delete/add will not create a memory leak
122
123 auto ptr = objServer.add_interface(path, interface);
124 auto& dataVector = inventory[parent];
125 if (checkNull)
126 {
127 auto it = std::find_if(dataVector.begin(), dataVector.end(),
128 [](const auto& p) { return p.expired(); });
129 if (it != dataVector.end())
130 {
131 *it = ptr;
132 return ptr;
133 }
134 }
135 dataVector.emplace_back(ptr);
136 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700137}
138
James Feistb1728ca2020-04-30 15:40:55 -0700139void getInterfaces(
140 const std::tuple<std::string, std::string, std::string>& call,
141 const std::vector<std::shared_ptr<PerformProbe>>& probeVector,
142 std::shared_ptr<PerformScan> scan, size_t retries = 5)
143{
144 if (!retries)
145 {
146 std::cerr << "retries exhausted on " << std::get<0>(call) << " "
147 << std::get<1>(call) << " " << std::get<2>(call) << "\n";
148 return;
149 }
150
151 SYSTEM_BUS->async_method_call(
152 [call, scan, probeVector, retries](
153 boost::system::error_code& errc,
154 const boost::container::flat_map<std::string, BasicVariantType>&
155 resp) {
156 if (errc)
157 {
158 std::cerr << "error calling getall on " << std::get<0>(call)
159 << " " << std::get<1>(call) << " "
160 << std::get<2>(call) << "\n";
161
162 std::shared_ptr<boost::asio::steady_timer> timer =
163 std::make_shared<boost::asio::steady_timer>(io);
164 timer->expires_after(std::chrono::seconds(2));
165
166 timer->async_wait([timer, call, scan, probeVector,
167 retries](const boost::system::error_code&) {
168 getInterfaces(call, probeVector, scan, retries - 1);
169 });
170 return;
171 }
172
Matt Spinlere789bf12021-02-24 12:33:01 -0600173 scan->dbusProbeObjects[std::get<1>(call)][std::get<2>(call)] = resp;
James Feistb1728ca2020-04-30 15:40:55 -0700174 },
175 std::get<0>(call), std::get<1>(call), "org.freedesktop.DBus.Properties",
176 "GetAll", std::get<2>(call));
177
178 if constexpr (DEBUG)
179 {
180 std::cerr << __func__ << " " << __LINE__ << "\n";
181 }
182}
183
Matt Spinlere789bf12021-02-24 12:33:01 -0600184// Populates scan->dbusProbeObjects with all interfaces and properties
185// for the paths that own the interfaces passed in.
James Feistb1728ca2020-04-30 15:40:55 -0700186void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>&& probeVector,
187 boost::container::flat_set<std::string>&& interfaces,
James Feist32d1f0a2020-09-10 14:49:25 -0700188 std::shared_ptr<PerformScan> scan, size_t retries = 5)
James Feist3cb5fec2018-01-23 14:41:51 -0800189{
Matt Spinlere789bf12021-02-24 12:33:01 -0600190 // Filter out interfaces already obtained.
191 for (const auto& [path, probeInterfaces] : scan->dbusProbeObjects)
James Feist733f7652019-11-13 14:32:29 -0800192 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600193 for (const auto& [interface, _] : probeInterfaces)
194 {
195 interfaces.erase(interface);
196 }
James Feist733f7652019-11-13 14:32:29 -0800197 }
198 if (interfaces.empty())
199 {
200 return;
201 }
202
James Feist3cb5fec2018-01-23 14:41:51 -0800203 // find all connections in the mapper that expose a specific type
James Feistb1728ca2020-04-30 15:40:55 -0700204 SYSTEM_BUS->async_method_call(
Matt Spinler4f328892021-02-24 13:19:14 -0600205 [interfaces, probeVector{std::move(probeVector)}, scan,
206 retries](boost::system::error_code& ec,
207 const GetSubTreeType& interfaceSubtree) mutable {
James Feistb1728ca2020-04-30 15:40:55 -0700208 boost::container::flat_set<
209 std::tuple<std::string, std::string, std::string>>
210 interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700211 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700212 {
James Feist0de40152018-07-25 11:56:12 -0700213 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700214 {
James Feist0de40152018-07-25 11:56:12 -0700215 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700216 }
James Feist0de40152018-07-25 11:56:12 -0700217 std::cerr << "Error communicating to mapper.\n";
218
James Feist32d1f0a2020-09-10 14:49:25 -0700219 if (!retries)
220 {
221 // if we can't communicate to the mapper something is very
222 // wrong
223 std::exit(EXIT_FAILURE);
224 }
225 std::shared_ptr<boost::asio::steady_timer> timer =
226 std::make_shared<boost::asio::steady_timer>(io);
227 timer->expires_after(std::chrono::seconds(10));
228
229 timer->async_wait(
230 [timer, interfaces{std::move(interfaces)}, scan,
231 probeVector{std::move(probeVector)},
232 retries](const boost::system::error_code&) mutable {
233 findDbusObjects(std::move(probeVector),
234 std::move(interfaces), scan,
235 retries - 1);
236 });
237 return;
James Feist494155a2018-03-14 16:23:24 -0700238 }
James Feist787c3c32019-11-07 14:42:58 -0800239
James Feistb1728ca2020-04-30 15:40:55 -0700240 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800241 {
James Feistb1728ca2020-04-30 15:40:55 -0700242 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700243 {
James Feistb1728ca2020-04-30 15:40:55 -0700244 for (const std::string& iface : ifaces)
245 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600246 // The 3 default org.freedeskstop interfaces (Peer,
247 // Introspectable, and Properties) are returned by
248 // the mapper but don't have properties, so don't bother
249 // with the GetAll call to save some cycles.
250 if (!boost::algorithm::starts_with(iface,
251 "org.freedesktop"))
James Feistb1728ca2020-04-30 15:40:55 -0700252 {
253 interfaceConnections.emplace(busname, path, iface);
254 }
255 }
James Feist8f2710a2018-05-09 17:18:55 -0700256 }
Matt Spinlere789bf12021-02-24 12:33:01 -0600257
258 // Get a PropertiesChanged callback for all
259 // interfaces on this path.
260 registerCallback(scan->_systemConfiguration, scan->objServer,
261 path);
James Feist3cb5fec2018-01-23 14:41:51 -0800262 }
James Feist787c3c32019-11-07 14:42:58 -0800263
James Feist63845bf2019-01-24 12:19:51 -0800264 if (interfaceConnections.empty())
265 {
James Feist63845bf2019-01-24 12:19:51 -0800266 return;
267 }
James Feist787c3c32019-11-07 14:42:58 -0800268
James Feistb1728ca2020-04-30 15:40:55 -0700269 for (const auto& call : interfaceConnections)
270 {
271 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700272 }
273 },
274 "xyz.openbmc_project.ObjectMapper",
275 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700276 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800277 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700278
279 if constexpr (DEBUG)
280 {
281 std::cerr << __func__ << " " << __LINE__ << "\n";
282 }
James Feist3cb5fec2018-01-23 14:41:51 -0800283}
James Feistb1728ca2020-04-30 15:40:55 -0700284
James Feist8f2710a2018-05-09 17:18:55 -0700285// probes dbus interface dictionary for a key with a value that matches a regex
Matt Spinlere789bf12021-02-24 12:33:01 -0600286// When an interface passes a probe, also save its D-Bus path with it.
James Feist08a5b172019-08-28 14:47:47 -0700287bool probeDbus(const std::string& interface,
288 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800289 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
290 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800291{
James Feist3cb5fec2018-01-23 14:41:51 -0800292 bool foundMatch = false;
Matt Spinlere789bf12021-02-24 12:33:01 -0600293 foundProbe = false;
294
295 for (const auto& [path, interfaces] : scan->dbusProbeObjects)
James Feist3cb5fec2018-01-23 14:41:51 -0800296 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600297 auto it = interfaces.find(interface);
298 if (it == interfaces.end())
James Feist3cb5fec2018-01-23 14:41:51 -0800299 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600300 continue;
301 }
302
303 foundProbe = true;
304
305 bool deviceMatches = true;
306 const boost::container::flat_map<std::string, BasicVariantType>&
307 properties = it->second;
308
309 for (const auto& [matchProp, matchJSON] : matches)
310 {
311 auto deviceValue = properties.find(matchProp);
312 if (deviceValue != properties.end())
James Feist3cb5fec2018-01-23 14:41:51 -0800313 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600314 deviceMatches = matchProbe(matchJSON, deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800315 }
316 else
317 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600318 // Move on to the next DBus path
James Feist3cb5fec2018-01-23 14:41:51 -0800319 deviceMatches = false;
320 break;
321 }
322 }
323 if (deviceMatches)
324 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600325 if constexpr (DEBUG)
326 {
327 std::cerr << "probeDBus: Found probe match on " << path << " "
328 << interface << "\n";
329 }
330 devices.emplace_back(properties, path);
James Feist3cb5fec2018-01-23 14:41:51 -0800331 foundMatch = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800332 }
333 }
334 return foundMatch;
335}
336
337// default probe entry point, iterates a list looking for specific types to
338// call specific probe functions
Matt Spinlere789bf12021-02-24 12:33:01 -0600339bool probe(const std::vector<std::string>& probeCommand,
340 std::shared_ptr<PerformScan> scan, FoundDeviceT& foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800341{
342 const static std::regex command(R"(\((.*)\))");
343 std::smatch match;
344 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700345 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800346 bool cur = true;
347 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700348 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800349
James Feista465ccc2019-02-08 12:51:01 -0800350 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800351 {
352 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800353 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800354 cmp_str>::const_iterator probeType;
355
356 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700357 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800358 {
359 if (probe.find(probeType->first) != std::string::npos)
360 {
361 foundProbe = true;
362 break;
363 }
364 }
365 if (foundProbe)
366 {
367 switch (probeType->second)
368 {
James Feist9eb0b582018-04-27 12:15:46 -0700369 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800370 {
James Feiste31e00a2019-07-24 10:45:43 -0700371 cur = false;
372 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800373 }
James Feist9eb0b582018-04-27 12:15:46 -0700374 case probe_type_codes::TRUE_T:
375 {
James Feiste31e00a2019-07-24 10:45:43 -0700376 cur = true;
377 break;
James Feist9eb0b582018-04-27 12:15:46 -0700378 }
379 case probe_type_codes::MATCH_ONE:
380 {
381 // set current value to last, this probe type shouldn't
382 // affect the outcome
383 cur = ret;
384 matchOne = true;
385 break;
386 }
387 /*case probe_type_codes::AND:
388 break;
389 case probe_type_codes::OR:
390 break;
391 // these are no-ops until the last command switch
392 */
393 case probe_type_codes::FOUND:
394 {
395 if (!std::regex_search(probe, match, command))
396 {
James Feist0eb40352019-04-09 14:44:04 -0700397 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700398 << "\n";
399 return false;
400 }
401 std::string commandStr = *(match.begin() + 1);
402 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800403 cur = (std::find(scan->passedProbes.begin(),
404 scan->passedProbes.end(),
405 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700406 break;
407 }
James Feist0eb40352019-04-09 14:44:04 -0700408 default:
409 {
410 break;
411 }
James Feist3cb5fec2018-01-23 14:41:51 -0800412 }
413 }
414 // look on dbus for object
415 else
416 {
417 if (!std::regex_search(probe, match, command))
418 {
James Feist0eb40352019-04-09 14:44:04 -0700419 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800420 return false;
421 }
422 std::string commandStr = *(match.begin() + 1);
423 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700424 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800425 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800426 auto json = nlohmann::json::parse(commandStr, nullptr, false);
427 if (json.is_discarded())
428 {
James Feist0eb40352019-04-09 14:44:04 -0700429 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800430 return false;
431 }
432 // we can match any (string, variant) property. (string, string)
433 // does a regex
434 std::map<std::string, nlohmann::json> dbusProbeMap =
435 json.get<std::map<std::string, nlohmann::json>>();
436 auto findStart = probe.find("(");
437 if (findStart == std::string::npos)
438 {
439 return false;
440 }
441 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800442 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
443 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800444 }
445
446 // some functions like AND and OR only take affect after the
447 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700448 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800449 {
James Feist54a0dca2019-06-26 10:34:54 -0700450 ret = cur && ret;
451 }
452 else if (lastCommand == probe_type_codes::OR)
453 {
454 ret = cur || ret;
455 }
456
457 if (first)
458 {
459 ret = cur;
460 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800461 }
462 lastCommand = probeType != PROBE_TYPES.end()
463 ? probeType->second
464 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800465 }
466
467 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800468 if (ret && foundDevs.size() == 0)
469 {
James Feist08a5b172019-08-28 14:47:47 -0700470 foundDevs.emplace_back(
Matt Spinlere789bf12021-02-24 12:33:01 -0600471 boost::container::flat_map<std::string, BasicVariantType>{},
472 std::string{});
James Feist3cb5fec2018-01-23 14:41:51 -0800473 }
James Feist0eb40352019-04-09 14:44:04 -0700474 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700475 {
James Feist71f295f2019-06-20 13:35:12 -0700476 // match the last one
477 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700478 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700479
480 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700481 }
James Feist3cb5fec2018-01-23 14:41:51 -0800482 return ret;
483}
484
Matt Spinlere789bf12021-02-24 12:33:01 -0600485PerformProbe::PerformProbe(
486 const std::vector<std::string>& probeCommand,
487 std::shared_ptr<PerformScan>& scanPtr,
488 std::function<void(FoundDeviceT&, const DBusProbeObjectT&)>&& callback) :
James Feist7d807752019-11-13 14:47:57 -0800489 _probeCommand(probeCommand),
490 scan(scanPtr), _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -0700491{}
James Feist7d807752019-11-13 14:47:57 -0800492PerformProbe::~PerformProbe()
493{
494 FoundDeviceT foundDevs;
495 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700496 {
Matt Spinlere789bf12021-02-24 12:33:01 -0600497 _callback(foundDevs, scan->dbusProbeObjects);
James Feist8f2710a2018-05-09 17:18:55 -0700498 }
James Feist7d807752019-11-13 14:47:57 -0800499}
James Feist8f2710a2018-05-09 17:18:55 -0700500
501// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800502bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800503{
James Feist1df06a42019-04-11 14:23:04 -0700504 std::filesystem::create_directory(configurationOutDir);
505 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700506 if (!output.good())
507 {
508 return false;
509 }
James Feist1b2e2242018-01-30 13:45:19 -0800510 output << systemConfiguration.dump(4);
511 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700512 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700513}
James Feist1b2e2242018-01-30 13:45:19 -0800514
James Feist97a63f12018-05-17 13:50:57 -0700515template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800516bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
517 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700518{
519 try
520 {
521 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800522 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700523 ref = value;
524 return true;
525 }
James Feist98132792019-07-09 13:29:09 -0700526 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700527 {
528 return false;
529 }
530}
James Feistbb43d022018-06-12 15:44:33 -0700531
James Feistebcc26b2019-03-22 12:30:43 -0700532// template function to add array as dbus property
533template <typename PropertyType>
534void addArrayToDbus(const std::string& name, const nlohmann::json& array,
535 sdbusplus::asio::dbus_interface* iface,
536 sdbusplus::asio::PropertyPermission permission,
537 nlohmann::json& systemConfiguration,
538 const std::string& jsonPointerString)
539{
540 std::vector<PropertyType> values;
541 for (const auto& property : array)
542 {
543 auto ptr = property.get_ptr<const PropertyType*>();
544 if (ptr != nullptr)
545 {
546 values.emplace_back(*ptr);
547 }
548 }
549
550 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
551 {
552 iface->register_property(name, values);
553 }
554 else
555 {
556 iface->register_property(
557 name, values,
558 [&systemConfiguration,
559 jsonPointerString{std::string(jsonPointerString)}](
560 const std::vector<PropertyType>& newVal,
561 std::vector<PropertyType>& val) {
562 val = newVal;
563 if (!setJsonFromPointer(jsonPointerString, val,
564 systemConfiguration))
565 {
566 std::cerr << "error setting json field\n";
567 return -1;
568 }
569 if (!writeJsonFiles(systemConfiguration))
570 {
571 std::cerr << "error setting json file\n";
572 return -1;
573 }
574 return 1;
575 });
576 }
577}
578
James Feistbb43d022018-06-12 15:44:33 -0700579template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800580void addProperty(const std::string& propertyName, const PropertyType& value,
581 sdbusplus::asio::dbus_interface* iface,
582 nlohmann::json& systemConfiguration,
583 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700584 sdbusplus::asio::PropertyPermission permission)
585{
586 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
587 {
588 iface->register_property(propertyName, value);
589 return;
590 }
James Feist68500ff2018-08-08 15:40:42 -0700591 iface->register_property(
592 propertyName, value,
593 [&systemConfiguration,
594 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800595 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700596 val = newVal;
597 if (!setJsonFromPointer(jsonPointerString, val,
598 systemConfiguration))
599 {
600 std::cerr << "error setting json field\n";
601 return -1;
602 }
James Feistc6248a52018-08-14 10:09:45 -0700603 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700604 {
605 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700606 return -1;
607 }
608 return 1;
609 });
610}
611
612void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800613 const std::string& jsonPointerPath,
614 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
615 sdbusplus::asio::object_server& objServer,
616 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700617{
618 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
619 iface->register_method(
620 "Delete", [&objServer, &systemConfiguration, interface,
621 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700622 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700623 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700624 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700625 {
626 // this technically can't happen as the pointer is pointing to
627 // us
628 throw DBusInternalError();
629 }
630 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700631 systemConfiguration[ptr] = nullptr;
632
James Feist02d2b932020-02-06 16:28:48 -0800633 // todo(james): dig through sdbusplus to find out why we can't
634 // delete it in a method call
635 io.post([&objServer, dbusInterface]() mutable {
636 objServer.remove_interface(dbusInterface);
637 });
638
James Feistc6248a52018-08-14 10:09:45 -0700639 if (!writeJsonFiles(systemConfiguration))
640 {
641 std::cerr << "error setting json file\n";
642 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700643 }
James Feist68500ff2018-08-08 15:40:42 -0700644 });
James Feistbb43d022018-06-12 15:44:33 -0700645}
646
James Feist1b2e2242018-01-30 13:45:19 -0800647// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700648void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800649 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
650 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
651 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700652 sdbusplus::asio::PropertyPermission permission =
653 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800654{
James Feista465ccc2019-02-08 12:51:01 -0800655 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800656 {
James Feist8f2710a2018-05-09 17:18:55 -0700657 auto type = dictPair.value().type();
658 bool array = false;
659 if (dictPair.value().type() == nlohmann::json::value_t::array)
660 {
661 array = true;
662 if (!dictPair.value().size())
663 {
664 continue;
665 }
666 type = dictPair.value()[0].type();
667 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800668 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700669 {
670 if (arrayItem.type() != type)
671 {
672 isLegal = false;
673 break;
674 }
675 }
676 if (!isLegal)
677 {
678 std::cerr << "dbus format error" << dictPair.value() << "\n";
679 continue;
680 }
James Feista218ddb2019-04-11 14:01:31 -0700681 }
682 if (type == nlohmann::json::value_t::object)
683 {
684 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700685 }
James Feist97a63f12018-05-17 13:50:57 -0700686 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700687 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
688 {
689 // all setable numbers are doubles as it is difficult to always
690 // create a configuration file with all whole numbers as decimals
691 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700692 if (array)
693 {
694 if (dictPair.value()[0].is_number())
695 {
696 type = nlohmann::json::value_t::number_float;
697 }
698 }
699 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700700 {
701 type = nlohmann::json::value_t::number_float;
702 }
703 }
704
James Feist8f2710a2018-05-09 17:18:55 -0700705 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800706 {
James Feist9eb0b582018-04-27 12:15:46 -0700707 case (nlohmann::json::value_t::boolean):
708 {
James Feist8f2710a2018-05-09 17:18:55 -0700709 if (array)
710 {
711 // todo: array of bool isn't detected correctly by
712 // sdbusplus, change it to numbers
713 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700714 iface.get(), permission,
715 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700716 }
James Feistbb43d022018-06-12 15:44:33 -0700717
James Feist97a63f12018-05-17 13:50:57 -0700718 else
719 {
James Feistbb43d022018-06-12 15:44:33 -0700720 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700721 iface.get(), systemConfiguration, key,
722 permission);
James Feist97a63f12018-05-17 13:50:57 -0700723 }
James Feist9eb0b582018-04-27 12:15:46 -0700724 break;
725 }
726 case (nlohmann::json::value_t::number_integer):
727 {
James Feist8f2710a2018-05-09 17:18:55 -0700728 if (array)
729 {
730 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700731 iface.get(), permission,
732 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700733 }
734 else
735 {
James Feistbb43d022018-06-12 15:44:33 -0700736 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700737 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700738 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700739 }
James Feist9eb0b582018-04-27 12:15:46 -0700740 break;
741 }
742 case (nlohmann::json::value_t::number_unsigned):
743 {
James Feist8f2710a2018-05-09 17:18:55 -0700744 if (array)
745 {
746 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700747 iface.get(), permission,
748 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700749 }
750 else
751 {
James Feistbb43d022018-06-12 15:44:33 -0700752 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700753 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700754 systemConfiguration, key,
755 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700756 }
James Feist9eb0b582018-04-27 12:15:46 -0700757 break;
758 }
759 case (nlohmann::json::value_t::number_float):
760 {
James Feist8f2710a2018-05-09 17:18:55 -0700761 if (array)
762 {
763 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700764 iface.get(), permission,
765 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700766 }
James Feistbb43d022018-06-12 15:44:33 -0700767
James Feist97a63f12018-05-17 13:50:57 -0700768 else
769 {
James Feistbb43d022018-06-12 15:44:33 -0700770 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700771 iface.get(), systemConfiguration, key,
772 permission);
James Feist97a63f12018-05-17 13:50:57 -0700773 }
James Feist9eb0b582018-04-27 12:15:46 -0700774 break;
775 }
776 case (nlohmann::json::value_t::string):
777 {
James Feist8f2710a2018-05-09 17:18:55 -0700778 if (array)
779 {
James Feistebcc26b2019-03-22 12:30:43 -0700780 addArrayToDbus<std::string>(
781 dictPair.key(), dictPair.value(), iface.get(),
782 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700783 }
784 else
785 {
James Feistc6248a52018-08-14 10:09:45 -0700786 addProperty(
787 dictPair.key(), dictPair.value().get<std::string>(),
788 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700789 }
James Feist9eb0b582018-04-27 12:15:46 -0700790 break;
791 }
James Feist0eb40352019-04-09 14:44:04 -0700792 default:
793 {
James Feista218ddb2019-04-11 14:01:31 -0700794 std::cerr << "Unexpected json type in system configuration "
795 << dictPair.key() << ": "
796 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700797 break;
798 }
James Feist1b2e2242018-01-30 13:45:19 -0800799 }
800 }
James Feistc6248a52018-08-14 10:09:45 -0700801 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
802 {
803 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
804 systemConfiguration);
805 }
James Feist8f2710a2018-05-09 17:18:55 -0700806 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800807}
808
James Feista465ccc2019-02-08 12:51:01 -0800809sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700810{
811 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
812 interface) != settableInterfaces.end()
813 ? sdbusplus::asio::PropertyPermission::readWrite
814 : sdbusplus::asio::PropertyPermission::readOnly;
815}
816
James Feista465ccc2019-02-08 12:51:01 -0800817void createAddObjectMethod(const std::string& jsonPointerPath,
818 const std::string& path,
819 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700820 sdbusplus::asio::object_server& objServer,
821 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700822{
James Feistd58879a2019-09-11 11:26:07 -0700823 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
824 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700825
826 iface->register_method(
827 "AddObject",
828 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700829 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
830 board](const boost::container::flat_map<std::string, JsonVariantType>&
831 data) {
James Feist68500ff2018-08-08 15:40:42 -0700832 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800833 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700834 auto findExposes = base.find("Exposes");
835
836 if (findExposes == base.end())
837 {
838 throw std::invalid_argument("Entity must have children.");
839 }
840
841 // this will throw invalid-argument to sdbusplus if invalid json
842 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800843 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700844 {
James Feista465ccc2019-02-08 12:51:01 -0800845 nlohmann::json& newJson = newData[item.first];
846 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
847 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700848 }
849
850 auto findName = newData.find("Name");
851 auto findType = newData.find("Type");
852 if (findName == newData.end() || findType == newData.end())
853 {
854 throw std::invalid_argument("AddObject missing Name or Type");
855 }
James Feista465ccc2019-02-08 12:51:01 -0800856 const std::string* type = findType->get_ptr<const std::string*>();
857 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700858 if (type == nullptr || name == nullptr)
859 {
860 throw std::invalid_argument("Type and Name must be a string.");
861 }
862
James Feist02d2b932020-02-06 16:28:48 -0800863 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700864 size_t lastIndex = 0;
865 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800866 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700867 {
James Feist02d2b932020-02-06 16:28:48 -0800868 if (expose.is_null())
869 {
870 foundNull = true;
871 continue;
872 }
873
874 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700875 {
876 throw std::invalid_argument(
877 "Field already in JSON, not adding");
878 }
James Feist02d2b932020-02-06 16:28:48 -0800879
880 if (foundNull)
881 {
882 continue;
883 }
884
James Feist68500ff2018-08-08 15:40:42 -0700885 lastIndex++;
886 }
887
888 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
889 *type + ".json");
890 // todo(james) we might want to also make a list of 'can add'
891 // interfaces but for now I think the assumption if there is a
892 // schema avaliable that it is allowed to update is fine
893 if (!schemaFile.good())
894 {
895 throw std::invalid_argument(
896 "No schema avaliable, cannot validate.");
897 }
898 nlohmann::json schema =
899 nlohmann::json::parse(schemaFile, nullptr, false);
900 if (schema.is_discarded())
901 {
902 std::cerr << "Schema not legal" << *type << ".json\n";
903 throw DBusInternalError();
904 }
905 if (!validateJson(schema, newData))
906 {
907 throw std::invalid_argument("Data does not match schema");
908 }
James Feist02d2b932020-02-06 16:28:48 -0800909 if (foundNull)
910 {
911 findExposes->at(lastIndex) = newData;
912 }
913 else
914 {
915 findExposes->push_back(newData);
916 }
James Feist68500ff2018-08-08 15:40:42 -0700917 if (!writeJsonFiles(systemConfiguration))
918 {
919 std::cerr << "Error writing json files\n";
920 throw DBusInternalError();
921 }
922 std::string dbusName = *name;
923
924 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700925 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700926
927 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
928 createInterface(objServer, path + "/" + dbusName,
929 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800930 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700931 // permission is read-write, as since we just created it, must be
932 // runtime modifiable
933 populateInterfaceFromJson(
934 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700935 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700936 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700937 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700938 });
939 iface->initialize();
940}
941
James Feista465ccc2019-02-08 12:51:01 -0800942void postToDbus(const nlohmann::json& newConfiguration,
943 nlohmann::json& systemConfiguration,
944 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800945
James Feist1b2e2242018-01-30 13:45:19 -0800946{
James Feist97a63f12018-05-17 13:50:57 -0700947 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800948 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800949 {
James Feistf1b14142019-04-10 15:22:09 -0700950 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700951 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700952 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700953 // loop through newConfiguration, but use values from system
954 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700955 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700956 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800957 std::string boardType;
958 if (findBoardType != boardValues.end() &&
959 findBoardType->type() == nlohmann::json::value_t::string)
960 {
961 boardType = findBoardType->get<std::string>();
962 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700963 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800964 }
965 else
966 {
967 std::cerr << "Unable to find type for " << boardKey
968 << " reverting to Chassis.\n";
969 boardType = "Chassis";
970 }
James Feist11be6672018-04-06 14:05:32 -0700971 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800972
973 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700974 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700975 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
976 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800977
James Feistd58879a2019-09-11 11:26:07 -0700978 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
979 createInterface(objServer, boardName,
980 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700981
James Feistd58879a2019-09-11 11:26:07 -0700982 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
983 createInterface(objServer, boardName,
984 "xyz.openbmc_project.Inventory.Item." + boardType,
985 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700986
James Feist68500ff2018-08-08 15:40:42 -0700987 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700988 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700989
James Feist97a63f12018-05-17 13:50:57 -0700990 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700991 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700992 jsonPointerPath += "/";
993 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800994 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700995 {
996 if (boardField.value().type() == nlohmann::json::value_t::object)
997 {
James Feistd58879a2019-09-11 11:26:07 -0700998 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
999 createInterface(objServer, boardName, boardField.key(),
1000 boardKeyOrig);
1001
James Feistc6248a52018-08-14 10:09:45 -07001002 populateInterfaceFromJson(systemConfiguration,
1003 jsonPointerPath + boardField.key(),
1004 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -07001005 }
1006 }
James Feist97a63f12018-05-17 13:50:57 -07001007
James Feist1e3e6982018-08-03 16:09:28 -07001008 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001009 if (exposes == boardValues.end())
1010 {
1011 continue;
1012 }
James Feist97a63f12018-05-17 13:50:57 -07001013 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001014 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001015
1016 // store the board level pointer so we can modify it on the way down
1017 std::string jsonPointerPathBoard = jsonPointerPath;
1018 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001019 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001020 {
James Feist97a63f12018-05-17 13:50:57 -07001021 exposesIndex++;
1022 jsonPointerPath = jsonPointerPathBoard;
1023 jsonPointerPath += std::to_string(exposesIndex);
1024
James Feistd63d18a2018-07-19 15:23:45 -07001025 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001026 if (findName == item.end())
1027 {
1028 std::cerr << "cannot find name in field " << item << "\n";
1029 continue;
1030 }
James Feist1e3e6982018-08-03 16:09:28 -07001031 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001032 // if status is not found it is assumed to be status = 'okay'
1033 if (findStatus != item.end())
1034 {
1035 if (*findStatus == "disabled")
1036 {
1037 continue;
1038 }
1039 }
James Feistd63d18a2018-07-19 15:23:45 -07001040 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001041 std::string itemType;
1042 if (findType != item.end())
1043 {
1044 itemType = findType->get<std::string>();
1045 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001046 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1047 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001048 }
1049 else
1050 {
1051 itemType = "unknown";
1052 }
1053 std::string itemName = findName->get<std::string>();
1054 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001055 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001056
James Feistd58879a2019-09-11 11:26:07 -07001057 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1058 createInterface(objServer, boardName + "/" + itemName,
1059 "xyz.openbmc_project.Configuration." + itemType,
1060 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001061
James Feist97a63f12018-05-17 13:50:57 -07001062 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001063 itemIface, item, objServer,
1064 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001065
James Feista465ccc2019-02-08 12:51:01 -08001066 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001067 {
James Feist97a63f12018-05-17 13:50:57 -07001068 jsonPointerPath = jsonPointerPathBoard +
1069 std::to_string(exposesIndex) + "/" +
1070 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001071 if (objectPair.value().type() ==
1072 nlohmann::json::value_t::object)
1073 {
James Feistd58879a2019-09-11 11:26:07 -07001074 std::shared_ptr<sdbusplus::asio::dbus_interface>
1075 objectIface = createInterface(
1076 objServer, boardName + "/" + itemName,
1077 "xyz.openbmc_project.Configuration." + itemType +
1078 "." + objectPair.key(),
1079 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001080
Adrian Ambrożewiczc789fca2020-05-14 15:50:05 +02001081 populateInterfaceFromJson(systemConfiguration,
1082 jsonPointerPath, objectIface,
1083 objectPair.value(), objServer,
1084 getPermission(objectPair.key()));
James Feist1b2e2242018-01-30 13:45:19 -08001085 }
1086 else if (objectPair.value().type() ==
1087 nlohmann::json::value_t::array)
1088 {
1089 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001090 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001091 {
James Feist8f2710a2018-05-09 17:18:55 -07001092 continue;
1093 }
1094 bool isLegal = true;
1095 auto type = objectPair.value()[0].type();
1096 if (type != nlohmann::json::value_t::object)
1097 {
1098 continue;
1099 }
1100
1101 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001102 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001103 {
1104 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001105 {
James Feist8f2710a2018-05-09 17:18:55 -07001106 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001107 break;
1108 }
James Feist8f2710a2018-05-09 17:18:55 -07001109 }
1110 if (!isLegal)
1111 {
1112 std::cerr << "dbus format error" << objectPair.value()
1113 << "\n";
1114 break;
1115 }
1116
James Feista465ccc2019-02-08 12:51:01 -08001117 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001118 {
James Feist97a63f12018-05-17 13:50:57 -07001119
James Feistd58879a2019-09-11 11:26:07 -07001120 std::shared_ptr<sdbusplus::asio::dbus_interface>
1121 objectIface = createInterface(
1122 objServer, boardName + "/" + itemName,
1123 "xyz.openbmc_project.Configuration." +
1124 itemType + "." + objectPair.key() +
1125 std::to_string(index),
1126 boardKeyOrig);
1127
James Feistc6248a52018-08-14 10:09:45 -07001128 populateInterfaceFromJson(
1129 systemConfiguration,
1130 jsonPointerPath + "/" + std::to_string(index),
1131 objectIface, arrayItem, objServer,
1132 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001133 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001134 }
1135 }
1136 }
1137 }
1138 }
1139}
1140
James Feist8f2710a2018-05-09 17:18:55 -07001141// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001142bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001143{
1144 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001145 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001146 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1147 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001148 {
1149 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001150 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001151 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001152 }
James Feistb4383f42018-08-06 16:54:10 -07001153
1154 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1155 globalSchema);
1156 if (!schemaStream.good())
1157 {
1158 std::cerr
1159 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1160 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001161 return false;
James Feistb4383f42018-08-06 16:54:10 -07001162 }
1163 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1164 if (schema.is_discarded())
1165 {
1166 std::cerr
1167 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1168 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001169 return false;
James Feistb4383f42018-08-06 16:54:10 -07001170 }
1171
James Feista465ccc2019-02-08 12:51:01 -08001172 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001173 {
1174 std::ifstream jsonStream(jsonPath.c_str());
1175 if (!jsonStream.good())
1176 {
1177 std::cerr << "unable to open " << jsonPath.string() << "\n";
1178 continue;
1179 }
1180 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1181 if (data.is_discarded())
1182 {
1183 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1184 continue;
1185 }
James Feist8da99192019-01-24 08:20:16 -08001186 /*
1187 * todo(james): reenable this once less things are in flight
1188 *
James Feistb4383f42018-08-06 16:54:10 -07001189 if (!validateJson(schema, data))
1190 {
1191 std::cerr << "Error validating " << jsonPath.string() << "\n";
1192 continue;
1193 }
James Feist8da99192019-01-24 08:20:16 -08001194 */
James Feistb4383f42018-08-06 16:54:10 -07001195
James Feist3cb5fec2018-01-23 14:41:51 -08001196 if (data.type() == nlohmann::json::value_t::array)
1197 {
James Feista465ccc2019-02-08 12:51:01 -08001198 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001199 {
1200 configurations.emplace_back(d);
1201 }
1202 }
1203 else
1204 {
1205 configurations.emplace_back(data);
1206 }
1207 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001208 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001209}
James Feist3cb5fec2018-01-23 14:41:51 -08001210
James Feist94219d12020-03-03 11:52:25 -08001211std::string getRecordName(
1212 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1213 const std::string& probeName)
1214{
1215 if (probe.empty())
1216 {
1217 return probeName;
1218 }
1219
1220 // use an array so alphabetical order from the
1221 // flat_map is maintained
1222 auto device = nlohmann::json::array();
1223 for (auto& devPair : probe)
1224 {
1225 device.push_back(devPair.first);
1226 std::visit([&device](auto&& v) { device.push_back(v); },
1227 devPair.second);
1228 }
1229 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1230 // hashes are hard to distinguish, use the
1231 // non-hashed version if we want debug
1232 if constexpr (DEBUG)
1233 {
1234 return probeName + device.dump();
1235 }
1236 else
1237 {
1238 return std::to_string(hash);
1239 }
1240}
1241
James Feist4dc617b2020-05-01 09:54:47 -07001242PerformScan::PerformScan(nlohmann::json& systemConfiguration,
1243 nlohmann::json& missingConfigurations,
1244 std::list<nlohmann::json>& configurations,
1245 sdbusplus::asio::object_server& objServerIn,
1246 std::function<void()>&& callback) :
James Feist733f7652019-11-13 14:32:29 -08001247 _systemConfiguration(systemConfiguration),
1248 _missingConfigurations(missingConfigurations),
James Feist4dc617b2020-05-01 09:54:47 -07001249 _configurations(configurations), objServer(objServerIn),
1250 _callback(std::move(callback))
James Feist8c505da2020-05-28 10:06:33 -07001251{}
James Feist733f7652019-11-13 14:32:29 -08001252void PerformScan::run()
1253{
1254 boost::container::flat_set<std::string> dbusProbeInterfaces;
1255 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001256
James Feist733f7652019-11-13 14:32:29 -08001257 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001258 {
James Feist733f7652019-11-13 14:32:29 -08001259 auto findProbe = it->find("Probe");
1260 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001261
James Feist733f7652019-11-13 14:32:29 -08001262 nlohmann::json probeCommand;
1263 // check for poorly formatted fields, probe must be an array
1264 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001265 {
James Feist733f7652019-11-13 14:32:29 -08001266 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1267 it = _configurations.erase(it);
1268 continue;
1269 }
1270 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1271 {
1272 probeCommand = nlohmann::json::array();
1273 probeCommand.push_back(*findProbe);
1274 }
1275 else
1276 {
1277 probeCommand = *findProbe;
1278 }
James Feist3cb5fec2018-01-23 14:41:51 -08001279
James Feist733f7652019-11-13 14:32:29 -08001280 if (findName == it->end())
1281 {
1282 std::cerr << "configuration file missing name:\n " << *it << "\n";
1283 it = _configurations.erase(it);
1284 continue;
1285 }
1286 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001287
James Feist733f7652019-11-13 14:32:29 -08001288 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1289 passedProbes.end())
1290 {
1291 it = _configurations.erase(it);
1292 continue;
1293 }
1294 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001295
James Feist733f7652019-11-13 14:32:29 -08001296 // store reference to this to children to makes sure we don't get
1297 // destroyed too early
1298 auto thisRef = shared_from_this();
1299 auto probePointer = std::make_shared<PerformProbe>(
1300 probeCommand, thisRef,
Matt Spinlere789bf12021-02-24 12:33:01 -06001301 [&, recordPtr, probeName](FoundDeviceT& foundDevices,
1302 const DBusProbeObjectT& allInterfaces) {
James Feist08a5b172019-08-28 14:47:47 -07001303 _passed = true;
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 }
Igor Kononenko9fd87e52020-10-06 01:18:17 +03001351 int index = 0;
1352 auto str =
1353 nameIt->get<std::string>().substr(indexIdx);
1354 auto [p, ec] = std::from_chars(
1355 str.data(), str.data() + str.size(), index);
1356 if (ec != std::errc())
1357 {
1358 continue; // non-numeric replacement
1359 }
James Feist35f5e0e2020-03-16 14:02:27 -07001360 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001361 auto usedIt = std::find(indexes.begin(),
1362 indexes.end(), index);
1363
1364 if (usedIt == indexes.end())
1365 {
1366 continue; // less items now
1367 }
1368 indexes.erase(usedIt);
1369 }
1370
James Feist08a5b172019-08-28 14:47:47 -07001371 continue;
1372 }
James Feist94219d12020-03-03 11:52:25 -08001373 itr++;
1374 }
James Feist35f5e0e2020-03-16 14:02:27 -07001375
1376 std::optional<std::string> replaceStr;
1377
Matt Spinler4bab9322021-04-08 14:38:43 -05001378 DBusProbeObjectT::mapped_type emptyInterfaces;
1379 boost::container::flat_map<std::string, BasicVariantType>
1380 emptyProps;
1381 emptyInterfaces.emplace(std::string{}, emptyProps);
1382
Matt Spinlere789bf12021-02-24 12:33:01 -06001383 for (auto& foundDeviceAndPath : foundDevices)
James Feist94219d12020-03-03 11:52:25 -08001384 {
Matt Spinlere789bf12021-02-24 12:33:01 -06001385 const boost::container::flat_map<
1386 std::string, BasicVariantType>& foundDevice =
1387 std::get<0>(foundDeviceAndPath);
1388 const std::string& path = std::get<1>(foundDeviceAndPath);
1389
1390 // Need all interfaces on this path so that template
1391 // substitutions can be done with any of the contained
Matt Spinler4bab9322021-04-08 14:38:43 -05001392 // properties. If the probe that passed didn't use an
1393 // interface, such as if it was just TRUE, then
1394 // templateCharReplace will just get passed in an empty
1395 // map.
1396 const DBusProbeObjectT::mapped_type* allInterfacesOnPath =
1397 &emptyInterfaces;
1398
1399 auto ifacesIt = allInterfaces.find(path);
1400 if (ifacesIt != allInterfaces.end())
Matt Spinlere789bf12021-02-24 12:33:01 -06001401 {
Matt Spinler4bab9322021-04-08 14:38:43 -05001402 allInterfacesOnPath = &ifacesIt->second;
Matt Spinlere789bf12021-02-24 12:33:01 -06001403 }
1404
James Feist94219d12020-03-03 11:52:25 -08001405 nlohmann::json record = *recordPtr;
1406 std::string recordName =
1407 getRecordName(foundDevice, probeName);
1408 size_t foundDeviceIdx = indexes.front();
1409 indexes.pop_front();
James Feistf1b14142019-04-10 15:22:09 -07001410
James Feist35f5e0e2020-03-16 14:02:27 -07001411 // check name first so we have no duplicate names
1412 auto getName = record.find("Name");
1413 if (getName == record.end())
1414 {
1415 std::cerr << "Record Missing Name! " << record.dump();
1416 continue; // this should be impossible at this level
1417 }
1418
1419 nlohmann::json copyForName = {{"Name", getName.value()}};
1420 nlohmann::json::iterator copyIt = copyForName.begin();
Matt Spinlere789bf12021-02-24 12:33:01 -06001421 std::optional<std::string> replaceVal =
Matt Spinler4bab9322021-04-08 14:38:43 -05001422 templateCharReplace(copyIt, *allInterfacesOnPath,
Matt Spinlere789bf12021-02-24 12:33:01 -06001423 foundDeviceIdx, replaceStr);
James Feist35f5e0e2020-03-16 14:02:27 -07001424
1425 if (!replaceStr && replaceVal)
1426 {
1427 if (usedNames.find(copyIt.value()) != usedNames.end())
1428 {
1429 replaceStr = replaceVal;
1430 copyForName = {{"Name", getName.value()}};
1431 copyIt = copyForName.begin();
Matt Spinler4bab9322021-04-08 14:38:43 -05001432 templateCharReplace(copyIt, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001433 foundDeviceIdx, replaceStr);
1434 }
1435 }
1436
1437 if (replaceStr)
1438 {
1439 std::cerr << "Duplicates found, replacing "
1440 << *replaceStr
1441 << " with found device index.\n Consider "
1442 "fixing template to not have duplicates\n";
1443 }
James Feist08a5b172019-08-28 14:47:47 -07001444
1445 for (auto keyPair = record.begin(); keyPair != record.end();
1446 keyPair++)
1447 {
James Feist35f5e0e2020-03-16 14:02:27 -07001448 if (keyPair.key() == "Name")
1449 {
1450 keyPair.value() = copyIt.value();
1451 usedNames.insert(copyIt.value());
1452
1453 continue; // already covered above
1454 }
Matt Spinler4bab9322021-04-08 14:38:43 -05001455 templateCharReplace(keyPair, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001456 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001457 }
1458
James Feist35f5e0e2020-03-16 14:02:27 -07001459 // insert into configuration temporarily to be able to
1460 // reference ourselves
1461
1462 _systemConfiguration[recordName] = record;
1463
James Feist08a5b172019-08-28 14:47:47 -07001464 auto findExpose = record.find("Exposes");
1465 if (findExpose == record.end())
1466 {
James Feistf1b14142019-04-10 15:22:09 -07001467 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001468 continue;
1469 }
James Feistf1b14142019-04-10 15:22:09 -07001470
James Feist08a5b172019-08-28 14:47:47 -07001471 for (auto& expose : *findExpose)
1472 {
1473 for (auto keyPair = expose.begin();
1474 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001475 {
James Feist08a5b172019-08-28 14:47:47 -07001476
Matt Spinler4bab9322021-04-08 14:38:43 -05001477 templateCharReplace(keyPair, *allInterfacesOnPath,
James Feist35f5e0e2020-03-16 14:02:27 -07001478 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001479
James Feist668bbb12020-02-05 14:27:26 -08001480 bool isBind =
1481 boost::starts_with(keyPair.key(), "Bind");
1482 bool isDisable = keyPair.key() == "DisableNode";
1483
1484 // special cases
1485 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001486 {
James Feist668bbb12020-02-05 14:27:26 -08001487 continue;
1488 }
1489
1490 if (keyPair.value().type() !=
1491 nlohmann::json::value_t::string &&
1492 keyPair.value().type() !=
1493 nlohmann::json::value_t::array)
1494 {
1495 std::cerr << "Value is invalid type "
1496 << keyPair.key() << "\n";
1497 continue;
1498 }
1499
1500 std::vector<std::string> matches;
1501 if (keyPair.value().type() ==
1502 nlohmann::json::value_t::string)
1503 {
1504 matches.emplace_back(keyPair.value());
1505 }
1506 else
1507 {
1508 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001509 {
James Feist668bbb12020-02-05 14:27:26 -08001510 if (value.type() !=
1511 nlohmann::json::value_t::string)
1512 {
1513 std::cerr << "Value is invalid type "
1514 << value << "\n";
1515 break;
1516 }
1517 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001518 }
James Feist668bbb12020-02-05 14:27:26 -08001519 }
James Feist08a5b172019-08-28 14:47:47 -07001520
James Feist668bbb12020-02-05 14:27:26 -08001521 std::set<std::string> foundMatches;
1522 for (auto& configurationPair :
1523 _systemConfiguration.items())
1524 {
1525 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001526 {
James Feist668bbb12020-02-05 14:27:26 -08001527 // don't disable ourselves
1528 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001529 {
James Feist1b2e2242018-01-30 13:45:19 -08001530 continue;
1531 }
James Feist668bbb12020-02-05 14:27:26 -08001532 }
1533 auto configListFind =
1534 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001535
James Feist668bbb12020-02-05 14:27:26 -08001536 if (configListFind ==
1537 configurationPair.value().end() ||
1538 configListFind->type() !=
1539 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001540 {
James Feist668bbb12020-02-05 14:27:26 -08001541 continue;
James Feist08a5b172019-08-28 14:47:47 -07001542 }
James Feist668bbb12020-02-05 14:27:26 -08001543 for (auto& exposedObject : *configListFind)
1544 {
1545 auto matchIt = std::find_if(
1546 matches.begin(), matches.end(),
1547 [name = (exposedObject)["Name"]
1548 .get<std::string>()](
1549 const std::string& s) {
1550 return s == name;
1551 });
1552 if (matchIt == matches.end())
1553 {
1554 continue;
1555 }
1556 foundMatches.insert(*matchIt);
1557
1558 if (isBind)
1559 {
1560 std::string bind = keyPair.key().substr(
1561 sizeof("Bind") - 1);
1562
1563 exposedObject["Status"] = "okay";
1564 expose[bind] = exposedObject;
1565 }
1566 else if (isDisable)
1567 {
1568 exposedObject["Status"] = "disabled";
1569 }
1570 }
1571 }
1572 if (foundMatches.size() != matches.size())
1573 {
1574 std::cerr << "configuration file "
1575 "dependency error, "
1576 "could not find "
1577 << keyPair.key() << " "
1578 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001579 }
1580 }
1581 }
James Feist08a5b172019-08-28 14:47:47 -07001582 // overwrite ourselves with cleaned up version
1583 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001584 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001585 }
1586 });
James Feist787c3c32019-11-07 14:42:58 -08001587
James Feist733f7652019-11-13 14:32:29 -08001588 // parse out dbus probes by discarding other probe types, store in a
1589 // map
Ed Tanouscda14732021-05-11 16:02:46 -07001590 for (const std::string probe : probeCommand)
James Feist733f7652019-11-13 14:32:29 -08001591 {
1592 bool found = false;
1593 boost::container::flat_map<const char*, probe_type_codes,
1594 cmp_str>::const_iterator probeType;
1595 for (probeType = PROBE_TYPES.begin();
1596 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001597 {
James Feist733f7652019-11-13 14:32:29 -08001598 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001599 {
James Feist733f7652019-11-13 14:32:29 -08001600 found = true;
1601 break;
James Feist787c3c32019-11-07 14:42:58 -08001602 }
James Feist787c3c32019-11-07 14:42:58 -08001603 }
James Feist733f7652019-11-13 14:32:29 -08001604 if (found)
1605 {
1606 continue;
1607 }
1608 // syntax requires probe before first open brace
1609 auto findStart = probe.find("(");
1610 std::string interface = probe.substr(0, findStart);
1611 dbusProbeInterfaces.emplace(interface);
1612 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001613 }
James Feist733f7652019-11-13 14:32:29 -08001614 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001615 }
James Feist75fdeeb2018-02-20 14:26:16 -08001616
James Feist733f7652019-11-13 14:32:29 -08001617 // probe vector stores a shared_ptr to each PerformProbe that cares
1618 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001619 findDbusObjects(std::move(dbusProbePointers),
1620 std::move(dbusProbeInterfaces), shared_from_this());
1621 if constexpr (DEBUG)
1622 {
1623 std::cerr << __func__ << " " << __LINE__ << "\n";
1624 }
James Feist733f7652019-11-13 14:32:29 -08001625}
1626
1627PerformScan::~PerformScan()
1628{
1629 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001630 {
James Feist733f7652019-11-13 14:32:29 -08001631 auto nextScan = std::make_shared<PerformScan>(
1632 _systemConfiguration, _missingConfigurations, _configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001633 objServer, std::move(_callback));
James Feist733f7652019-11-13 14:32:29 -08001634 nextScan->passedProbes = std::move(passedProbes);
1635 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1636 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001637
1638 if constexpr (DEBUG)
1639 {
1640 std::cerr << __func__ << " " << __LINE__ << "\n";
1641 }
James Feist8f2710a2018-05-09 17:18:55 -07001642 }
James Feist733f7652019-11-13 14:32:29 -08001643 else
1644 {
James Feist4dc617b2020-05-01 09:54:47 -07001645 _callback();
James Feistb1728ca2020-04-30 15:40:55 -07001646
1647 if constexpr (DEBUG)
1648 {
1649 std::cerr << __func__ << " " << __LINE__ << "\n";
1650 }
James Feist733f7652019-11-13 14:32:29 -08001651 }
1652}
James Feistc95cb142018-02-26 10:41:42 -08001653
James Feistb1728ca2020-04-30 15:40:55 -07001654void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001655 nlohmann::json& systemConfiguration)
1656{
1657 static bool scannedPowerOff = false;
1658 static bool scannedPowerOn = false;
1659
James Feistfb00f392019-06-25 14:16:48 -07001660 if (systemConfiguration.empty() || lastJson.empty())
1661 {
1662 return; // not ready yet
1663 }
James Feist1df06a42019-04-11 14:23:04 -07001664 if (scannedPowerOn)
1665 {
1666 return;
1667 }
1668
1669 if (!isPowerOn() && scannedPowerOff)
1670 {
1671 return;
1672 }
1673
James Feistb1728ca2020-04-30 15:40:55 -07001674 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001675 timer.async_wait(
1676 [&systemConfiguration](const boost::system::error_code& ec) {
1677 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001678 {
James Feist1a996582019-05-14 15:10:06 -07001679 // we were cancelled
1680 return;
1681 }
1682
1683 bool powerOff = !isPowerOn();
1684 for (const auto& item : lastJson.items())
1685 {
1686 if (systemConfiguration.find(item.key()) ==
1687 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001688 {
James Feist1a996582019-05-14 15:10:06 -07001689 bool isDetectedPowerOn = false;
1690 auto powerState = item.value().find("PowerState");
1691 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001692 {
James Feist1a996582019-05-14 15:10:06 -07001693 auto ptr = powerState->get_ptr<const std::string*>();
1694 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001695 {
James Feist1a996582019-05-14 15:10:06 -07001696 if (*ptr == "On" || *ptr == "BiosPost")
1697 {
1698 isDetectedPowerOn = true;
1699 }
James Feist1df06a42019-04-11 14:23:04 -07001700 }
1701 }
James Feist1a996582019-05-14 15:10:06 -07001702 if (powerOff && isDetectedPowerOn)
1703 {
1704 // power not on yet, don't know if it's there or not
1705 continue;
1706 }
1707 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1708 {
1709 // already logged it when power was off
1710 continue;
1711 }
James Feist1df06a42019-04-11 14:23:04 -07001712
James Feist1a996582019-05-14 15:10:06 -07001713 logDeviceRemoved(item.value());
1714 }
James Feist1df06a42019-04-11 14:23:04 -07001715 }
James Feist1a996582019-05-14 15:10:06 -07001716 scannedPowerOff = true;
1717 if (!powerOff)
1718 {
1719 scannedPowerOn = true;
1720 }
1721 });
James Feist1df06a42019-04-11 14:23:04 -07001722}
1723
James Feist8f2710a2018-05-09 17:18:55 -07001724// main properties changed entry
James Feist4dc617b2020-05-01 09:54:47 -07001725void propertiesChangedCallback(nlohmann::json& systemConfiguration,
1726 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001727{
James Feist2539ccd2020-05-01 16:15:08 -07001728 static bool inProgress = false;
James Feistb1728ca2020-04-30 15:40:55 -07001729 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001730 static size_t instance = 0;
1731 instance++;
1732 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001733
James Feistb1728ca2020-04-30 15:40:55 -07001734 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001735
1736 // setup an async wait as we normally get flooded with new requests
James Feist4dc617b2020-05-01 09:54:47 -07001737 timer.async_wait([&systemConfiguration, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001738 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001739 if (ec == boost::asio::error::operation_aborted)
1740 {
1741 // we were cancelled
1742 return;
1743 }
1744 else if (ec)
1745 {
1746 std::cerr << "async wait error " << ec << "\n";
1747 return;
1748 }
1749
James Feist2539ccd2020-05-01 16:15:08 -07001750 if (inProgress)
1751 {
1752 propertiesChangedCallback(systemConfiguration, objServer);
1753 return;
1754 }
1755 inProgress = true;
1756
James Feist8f2710a2018-05-09 17:18:55 -07001757 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001758 auto missingConfigurations = std::make_shared<nlohmann::json>();
1759 *missingConfigurations = systemConfiguration;
1760
James Feist8f2710a2018-05-09 17:18:55 -07001761 std::list<nlohmann::json> configurations;
1762 if (!findJsonFiles(configurations))
1763 {
1764 std::cerr << "cannot find json files\n";
James Feist2539ccd2020-05-01 16:15:08 -07001765 inProgress = false;
James Feist8f2710a2018-05-09 17:18:55 -07001766 return;
1767 }
1768
1769 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001770 systemConfiguration, *missingConfigurations, configurations,
James Feist4dc617b2020-05-01 09:54:47 -07001771 objServer,
1772 [&systemConfiguration, &objServer, count, oldConfiguration,
1773 missingConfigurations]() {
James Feist899e17f2019-09-13 11:46:29 -07001774 // this is something that since ac has been applied to the bmc
1775 // we saw, and we no longer see it
1776 bool powerOff = !isPowerOn();
1777 for (const auto& item : missingConfigurations->items())
1778 {
1779 bool isDetectedPowerOn = false;
1780 auto powerState = item.value().find("PowerState");
1781 if (powerState != item.value().end())
1782 {
1783 auto ptr = powerState->get_ptr<const std::string*>();
1784 if (ptr)
1785 {
1786 if (*ptr == "On" || *ptr == "BiosPost")
1787 {
1788 isDetectedPowerOn = true;
1789 }
1790 }
1791 }
1792 if (powerOff && isDetectedPowerOn)
1793 {
1794 // power not on yet, don't know if it's there or not
1795 continue;
1796 }
1797 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001798 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001799 ifaces = inventory[name];
1800 for (auto& iface : ifaces)
1801 {
James Feist02d2b932020-02-06 16:28:48 -08001802 auto sharedPtr = iface.lock();
1803 if (!sharedPtr)
1804 {
1805 continue; // was already deleted elsewhere
1806 }
1807 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001808 }
1809 ifaces.clear();
1810 systemConfiguration.erase(item.key());
1811 logDeviceRemoved(item.value());
1812 }
1813
James Feist8f2710a2018-05-09 17:18:55 -07001814 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001815 for (auto it = newConfiguration.begin();
1816 it != newConfiguration.end();)
1817 {
1818 auto findKey = oldConfiguration.find(it.key());
1819 if (findKey != oldConfiguration.end())
1820 {
1821 it = newConfiguration.erase(it);
1822 }
1823 else
1824 {
1825 it++;
1826 }
1827 }
James Feist899e17f2019-09-13 11:46:29 -07001828 for (const auto& item : newConfiguration.items())
1829 {
1830 logDeviceAdded(item.value());
1831 }
1832
James Feist2539ccd2020-05-01 16:15:08 -07001833 inProgress = false;
1834
Jonathan Doman6d649822021-05-05 16:53:04 -07001835 io.post([count, newConfiguration, &systemConfiguration,
1836 &objServer]() {
James Feist8f2710a2018-05-09 17:18:55 -07001837 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001838
Jonathan Doman6d649822021-05-05 16:53:04 -07001839 io.post([&systemConfiguration]() {
James Feistbb43d022018-06-12 15:44:33 -07001840 if (!writeJsonFiles(systemConfiguration))
1841 {
1842 std::cerr << "Error writing json files\n";
1843 }
1844 });
Jonathan Doman6d649822021-05-05 16:53:04 -07001845 io.post([count, newConfiguration, &systemConfiguration,
1846 &objServer]() {
James Feist97a63f12018-05-17 13:50:57 -07001847 postToDbus(newConfiguration, systemConfiguration,
1848 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001849 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001850 {
James Feist899e17f2019-09-13 11:46:29 -07001851 return;
James Feist1df06a42019-04-11 14:23:04 -07001852 }
James Feist899e17f2019-09-13 11:46:29 -07001853 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001854 });
1855 });
1856 });
1857 perfScan->run();
1858 });
James Feist75fdeeb2018-02-20 14:26:16 -08001859}
1860
James Feist4dc617b2020-05-01 09:54:47 -07001861void registerCallback(nlohmann::json& systemConfiguration,
1862 sdbusplus::asio::object_server& objServer,
Matt Spinlere789bf12021-02-24 12:33:01 -06001863 const std::string& path)
James Feist75fdeeb2018-02-20 14:26:16 -08001864{
James Feist4dc617b2020-05-01 09:54:47 -07001865 static boost::container::flat_map<std::string, sdbusplus::bus::match::match>
1866 dbusMatches;
James Feist75fdeeb2018-02-20 14:26:16 -08001867
Matt Spinlere789bf12021-02-24 12:33:01 -06001868 auto find = dbusMatches.find(path);
James Feist4dc617b2020-05-01 09:54:47 -07001869 if (find != dbusMatches.end())
James Feist75fdeeb2018-02-20 14:26:16 -08001870 {
James Feist4dc617b2020-05-01 09:54:47 -07001871 return;
James Feist75fdeeb2018-02-20 14:26:16 -08001872 }
James Feist4dc617b2020-05-01 09:54:47 -07001873 std::function<void(sdbusplus::message::message & message)> eventHandler =
1874
1875 [&](sdbusplus::message::message&) {
1876 propertiesChangedCallback(systemConfiguration, objServer);
1877 };
1878
1879 sdbusplus::bus::match::match match(
1880 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
Matt Spinlere789bf12021-02-24 12:33:01 -06001881 "type='signal',member='PropertiesChanged',path='" + path + "'",
James Feist4dc617b2020-05-01 09:54:47 -07001882 eventHandler);
Matt Spinlere789bf12021-02-24 12:33:01 -06001883 dbusMatches.emplace(path, std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001884}
1885
James Feist98132792019-07-09 13:29:09 -07001886int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001887{
1888 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001889 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001890 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001891
James Feist8f2710a2018-05-09 17:18:55 -07001892 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001893
James Feist8f2710a2018-05-09 17:18:55 -07001894 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1895 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1896 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001897
James Feist4131aea2018-03-09 09:47:30 -08001898 // to keep reference to the match / filter objects so they don't get
1899 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001900
1901 nlohmann::json systemConfiguration = nlohmann::json::object();
1902
Brad Bishopc76af0f2020-12-04 13:50:23 -05001903 // We need a poke from DBus for static providers that create all their
1904 // objects prior to claiming a well-known name, and thus don't emit any
1905 // org.freedesktop.DBus.Properties signals. Similarly if a process exits
1906 // for any reason, expected or otherwise, we'll need a poke to remove
1907 // entities from DBus.
1908 sdbusplus::bus::match::match nameOwnerChangedMatch(
1909 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1910 sdbusplus::bus::match::rules::nameOwnerChanged(),
1911 [&](sdbusplus::message::message&) {
1912 propertiesChangedCallback(systemConfiguration, objServer);
1913 });
Brad Bishop10a8c5f2020-12-07 21:40:07 -05001914 // We also need a poke from DBus when new interfaces are created or
1915 // destroyed.
1916 sdbusplus::bus::match::match interfacesAddedMatch(
1917 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1918 sdbusplus::bus::match::rules::interfacesAdded(),
1919 [&](sdbusplus::message::message&) {
1920 propertiesChangedCallback(systemConfiguration, objServer);
1921 });
1922 sdbusplus::bus::match::match interfacesRemovedMatch(
1923 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
1924 sdbusplus::bus::match::rules::interfacesRemoved(),
1925 [&](sdbusplus::message::message&) {
1926 propertiesChangedCallback(systemConfiguration, objServer);
1927 });
Brad Bishopc76af0f2020-12-04 13:50:23 -05001928
James Feist4dc617b2020-05-01 09:54:47 -07001929 io.post(
1930 [&]() { propertiesChangedCallback(systemConfiguration, objServer); });
James Feist4131aea2018-03-09 09:47:30 -08001931
James Feistfd1264a2018-05-03 12:10:00 -07001932 entityIface->register_method("ReScan", [&]() {
James Feist4dc617b2020-05-01 09:54:47 -07001933 propertiesChangedCallback(systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001934 });
James Feist8f2710a2018-05-09 17:18:55 -07001935 entityIface->initialize();
1936
James Feist1df06a42019-04-11 14:23:04 -07001937 if (fwVersionIsSame())
1938 {
1939 if (std::filesystem::is_regular_file(currentConfiguration))
1940 {
1941 // this file could just be deleted, but it's nice for debug
1942 std::filesystem::create_directory(tempConfigDir);
1943 std::filesystem::remove(lastConfiguration);
1944 std::filesystem::copy(currentConfiguration, lastConfiguration);
1945 std::filesystem::remove(currentConfiguration);
1946
1947 std::ifstream jsonStream(lastConfiguration);
1948 if (jsonStream.good())
1949 {
1950 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1951 if (data.is_discarded())
1952 {
1953 std::cerr << "syntax error in " << lastConfiguration
1954 << "\n";
1955 }
1956 else
1957 {
1958 lastJson = std::move(data);
1959 }
1960 }
1961 else
1962 {
1963 std::cerr << "unable to open " << lastConfiguration << "\n";
1964 }
1965 }
1966 }
1967 else
1968 {
1969 // not an error, just logging at this level to make it in the journal
1970 std::cerr << "Clearing previous configuration\n";
1971 std::filesystem::remove(currentConfiguration);
1972 }
1973
1974 // some boards only show up after power is on, we want to not say they are
1975 // removed until the same state happens
1976 setupPowerMatch(SYSTEM_BUS);
1977
James Feist1b2e2242018-01-30 13:45:19 -08001978 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001979
1980 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001981}