blob: e001bea9bd53a920a729648b8d24af4ad6807ea1 [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*/
16
James Feist1df06a42019-04-11 14:23:04 -070017#include "EntityManager.hpp"
18
Patrick Venturea49dc332019-10-26 08:32:02 -070019#include "Overlay.hpp"
20#include "Utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070021#include "VariantVisitors.hpp"
22
James Feist11be6672018-04-06 14:05:32 -070023#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070024#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070027#include <boost/algorithm/string/split.hpp>
James Feist02d2b932020-02-06 16:28:48 -080028#include <boost/asio/io_context.hpp>
James Feistb1728ca2020-04-30 15:40:55 -070029#include <boost/asio/steady_timer.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080030#include <boost/container/flat_map.hpp>
31#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070032#include <boost/range/iterator_range.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070033#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080034#include <fstream>
35#include <iostream>
36#include <nlohmann/json.hpp>
37#include <regex>
38#include <sdbusplus/asio/connection.hpp>
39#include <sdbusplus/asio/object_server.hpp>
40#include <variant>
James Feista465ccc2019-02-08 12:51:01 -080041constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
42constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070043constexpr const char* tempConfigDir = "/tmp/configuration/";
44constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
45constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080046constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070047constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080048
James Feistf1b14142019-04-10 15:22:09 -070049constexpr const bool DEBUG = false;
50
James Feist3cb5fec2018-01-23 14:41:51 -080051struct cmp_str
52{
James Feista465ccc2019-02-08 12:51:01 -080053 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080054 {
55 return std::strcmp(a, b) < 0;
56 }
57};
58
59// underscore T for collison with dbus c api
60enum class probe_type_codes
61{
62 FALSE_T,
63 TRUE_T,
64 AND,
65 OR,
James Feist6bd2a022018-03-13 12:30:58 -070066 FOUND,
67 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080068};
James Feista465ccc2019-02-08 12:51:01 -080069const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080070 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
71 {"TRUE", probe_type_codes::TRUE_T},
72 {"AND", probe_type_codes::AND},
73 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070074 {"FOUND", probe_type_codes::FOUND},
75 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080076
James Feist41334262019-03-25 13:30:20 -070077static constexpr std::array<const char*, 5> settableInterfaces = {
78 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070079using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080080 std::variant<std::vector<std::string>, std::vector<double>, std::string,
81 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
82 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080083using GetSubTreeType = std::vector<
84 std::pair<std::string,
85 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
86
87using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070088 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080089 boost::container::flat_map<
90 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070091 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080092
James Feistd58879a2019-09-11 11:26:07 -070093// store reference to all interfaces so we can destroy them later
94boost::container::flat_map<
James Feist02d2b932020-02-06 16:28:48 -080095 std::string, std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>>
James Feistd58879a2019-09-11 11:26:07 -070096 inventory;
97
James Feist3cb5fec2018-01-23 14:41:51 -080098// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070099std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700100static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800101
James Feist02d2b932020-02-06 16:28:48 -0800102boost::asio::io_context io;
103
Johnathan Mantey2015f752019-03-26 15:22:31 -0700104const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
105const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800106
James Feist02d2b932020-02-06 16:28:48 -0800107void registerCallbacks(std::vector<sdbusplus::bus::match::match>& dbusMatches,
James Feista465ccc2019-02-08 12:51:01 -0800108 nlohmann::json& systemConfiguration,
James Feist733f7652019-11-13 14:32:29 -0800109 sdbusplus::asio::object_server& objServer,
110 const DBusProbeObjectT& dbusProbeObjects);
James Feist75fdeeb2018-02-20 14:26:16 -0800111
James Feistd58879a2019-09-11 11:26:07 -0700112static std::shared_ptr<sdbusplus::asio::dbus_interface>
113 createInterface(sdbusplus::asio::object_server& objServer,
114 const std::string& path, const std::string& interface,
James Feist02d2b932020-02-06 16:28:48 -0800115 const std::string& parent, bool checkNull = false)
James Feistd58879a2019-09-11 11:26:07 -0700116{
James Feist02d2b932020-02-06 16:28:48 -0800117 // on first add we have no reason to check for null before add, as there
118 // won't be any. For dynamically added interfaces, we check for null so that
119 // a constant delete/add will not create a memory leak
120
121 auto ptr = objServer.add_interface(path, interface);
122 auto& dataVector = inventory[parent];
123 if (checkNull)
124 {
125 auto it = std::find_if(dataVector.begin(), dataVector.end(),
126 [](const auto& p) { return p.expired(); });
127 if (it != dataVector.end())
128 {
129 *it = ptr;
130 return ptr;
131 }
132 }
133 dataVector.emplace_back(ptr);
134 return ptr;
James Feistd58879a2019-09-11 11:26:07 -0700135}
136
James Feistb1728ca2020-04-30 15:40:55 -0700137void getInterfaces(
138 const std::tuple<std::string, std::string, std::string>& call,
139 const std::vector<std::shared_ptr<PerformProbe>>& probeVector,
140 std::shared_ptr<PerformScan> scan, size_t retries = 5)
141{
142 if (!retries)
143 {
144 std::cerr << "retries exhausted on " << std::get<0>(call) << " "
145 << std::get<1>(call) << " " << std::get<2>(call) << "\n";
146 return;
147 }
148
149 SYSTEM_BUS->async_method_call(
150 [call, scan, probeVector, retries](
151 boost::system::error_code& errc,
152 const boost::container::flat_map<std::string, BasicVariantType>&
153 resp) {
154 if (errc)
155 {
156 std::cerr << "error calling getall on " << std::get<0>(call)
157 << " " << std::get<1>(call) << " "
158 << std::get<2>(call) << "\n";
159
160 std::shared_ptr<boost::asio::steady_timer> timer =
161 std::make_shared<boost::asio::steady_timer>(io);
162 timer->expires_after(std::chrono::seconds(2));
163
164 timer->async_wait([timer, call, scan, probeVector,
165 retries](const boost::system::error_code&) {
166 getInterfaces(call, probeVector, scan, retries - 1);
167 });
168 return;
169 }
170
171 scan->dbusProbeObjects[std::get<2>(call)].emplace_back(resp);
172 },
173 std::get<0>(call), std::get<1>(call), "org.freedesktop.DBus.Properties",
174 "GetAll", std::get<2>(call));
175
176 if constexpr (DEBUG)
177 {
178 std::cerr << __func__ << " " << __LINE__ << "\n";
179 }
180}
181
James Feist3cb5fec2018-01-23 14:41:51 -0800182// calls the mapper to find all exposed objects of an interface type
183// and creates a vector<flat_map> that contains all the key value pairs
184// getManagedObjects
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 Feist733f7652019-11-13 14:32:29 -0800187 std::shared_ptr<PerformScan> scan)
James Feist3cb5fec2018-01-23 14:41:51 -0800188{
James Feist8f2710a2018-05-09 17:18:55 -0700189
James Feist733f7652019-11-13 14:32:29 -0800190 for (const auto& [interface, _] : scan->dbusProbeObjects)
191 {
192 interfaces.erase(interface);
193 }
194 if (interfaces.empty())
195 {
196 return;
197 }
198
James Feist3cb5fec2018-01-23 14:41:51 -0800199 // find all connections in the mapper that expose a specific type
James Feistb1728ca2020-04-30 15:40:55 -0700200 SYSTEM_BUS->async_method_call(
201 [interfaces{std::move(interfaces)}, probeVector{std::move(probeVector)},
James Feist733f7652019-11-13 14:32:29 -0800202 scan](boost::system::error_code& ec,
203 const GetSubTreeType& interfaceSubtree) {
James Feistb1728ca2020-04-30 15:40:55 -0700204 boost::container::flat_set<
205 std::tuple<std::string, std::string, std::string>>
206 interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700207 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700208 {
James Feist0de40152018-07-25 11:56:12 -0700209 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700210 {
James Feist0de40152018-07-25 11:56:12 -0700211 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700212 }
James Feist0de40152018-07-25 11:56:12 -0700213 std::cerr << "Error communicating to mapper.\n";
214
215 // if we can't communicate to the mapper something is very wrong
216 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700217 }
James Feist787c3c32019-11-07 14:42:58 -0800218
James Feistb1728ca2020-04-30 15:40:55 -0700219 for (const auto& [path, object] : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800220 {
James Feistb1728ca2020-04-30 15:40:55 -0700221 for (const auto& [busname, ifaces] : object)
James Feist8f2710a2018-05-09 17:18:55 -0700222 {
James Feistb1728ca2020-04-30 15:40:55 -0700223 for (const std::string& iface : ifaces)
224 {
225 auto ifaceObjFind = interfaces.find(iface);
226
227 if (ifaceObjFind != interfaces.end())
228 {
229 interfaceConnections.emplace(busname, path, iface);
230 }
231 }
James Feist8f2710a2018-05-09 17:18:55 -0700232 }
James Feist3cb5fec2018-01-23 14:41:51 -0800233 }
James Feist787c3c32019-11-07 14:42:58 -0800234
James Feist63845bf2019-01-24 12:19:51 -0800235 if (interfaceConnections.empty())
236 {
James Feist63845bf2019-01-24 12:19:51 -0800237 return;
238 }
James Feist787c3c32019-11-07 14:42:58 -0800239
James Feistb1728ca2020-04-30 15:40:55 -0700240 for (const auto& call : interfaceConnections)
241 {
242 getInterfaces(call, probeVector, scan);
James Feist8f2710a2018-05-09 17:18:55 -0700243 }
244 },
245 "xyz.openbmc_project.ObjectMapper",
246 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700247 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800248 interfaces);
James Feistb1728ca2020-04-30 15:40:55 -0700249
250 if constexpr (DEBUG)
251 {
252 std::cerr << __func__ << " " << __LINE__ << "\n";
253 }
James Feist3cb5fec2018-01-23 14:41:51 -0800254}
James Feistb1728ca2020-04-30 15:40:55 -0700255
James Feist8f2710a2018-05-09 17:18:55 -0700256// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700257bool probeDbus(const std::string& interface,
258 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800259 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
260 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800261{
James Feista465ccc2019-02-08 12:51:01 -0800262 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
James Feist733f7652019-11-13 14:32:29 -0800263 dbusObject = scan->dbusProbeObjects[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800264 if (dbusObject.empty())
265 {
James Feist8f2710a2018-05-09 17:18:55 -0700266 foundProbe = false;
267 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800268 }
269 foundProbe = true;
270
271 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800272 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800273 {
274 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800275 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800276 {
277 auto deviceValue = device.find(match.first);
278 if (deviceValue != device.end())
279 {
280 switch (match.second.type())
281 {
James Feist9eb0b582018-04-27 12:15:46 -0700282 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800283 {
James Feist9eb0b582018-04-27 12:15:46 -0700284 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700285 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700286
287 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800288 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700289 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700290 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700291 {
292 deviceMatches = false;
293 break;
294 }
James Feist3cb5fec2018-01-23 14:41:51 -0800295 break;
296 }
James Feist9eb0b582018-04-27 12:15:46 -0700297 case nlohmann::json::value_t::boolean:
298 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800299 {
James Feista465ccc2019-02-08 12:51:01 -0800300 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700301 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800302
James Feist9eb0b582018-04-27 12:15:46 -0700303 if (probeValue != match.second.get<unsigned int>())
304 {
305 deviceMatches = false;
306 }
307 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800308 }
James Feist9eb0b582018-04-27 12:15:46 -0700309 case nlohmann::json::value_t::number_integer:
310 {
James Feista465ccc2019-02-08 12:51:01 -0800311 int probeValue = std::visit(VariantToIntVisitor(),
312 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800313
James Feist9eb0b582018-04-27 12:15:46 -0700314 if (probeValue != match.second.get<int>())
315 {
316 deviceMatches = false;
317 }
318 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800319 }
James Feist9eb0b582018-04-27 12:15:46 -0700320 case nlohmann::json::value_t::number_float:
321 {
James Feista465ccc2019-02-08 12:51:01 -0800322 float probeValue = std::visit(VariantToFloatVisitor(),
323 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700324
325 if (probeValue != match.second.get<float>())
326 {
327 deviceMatches = false;
328 }
329 break;
330 }
James Feist0eb40352019-04-09 14:44:04 -0700331 default:
332 {
333 std::cerr << "unexpected dbus probe type "
334 << match.second.type_name() << "\n";
335 }
James Feist3cb5fec2018-01-23 14:41:51 -0800336 }
337 }
338 else
339 {
340 deviceMatches = false;
341 break;
342 }
343 }
344 if (deviceMatches)
345 {
James Feistf5125b02019-06-06 11:27:43 -0700346 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800347 foundMatch = true;
348 deviceMatches = false; // for next iteration
349 }
350 }
351 return foundMatch;
352}
353
354// default probe entry point, iterates a list looking for specific types to
355// call specific probe functions
356bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800357 const std::vector<std::string>& probeCommand,
James Feist733f7652019-11-13 14:32:29 -0800358 std::shared_ptr<PerformScan> scan,
James Feist08a5b172019-08-28 14:47:47 -0700359 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
360 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800361{
362 const static std::regex command(R"(\((.*)\))");
363 std::smatch match;
364 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700365 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800366 bool cur = true;
367 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700368 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800369
James Feista465ccc2019-02-08 12:51:01 -0800370 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800371 {
372 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800373 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800374 cmp_str>::const_iterator probeType;
375
376 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700377 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800378 {
379 if (probe.find(probeType->first) != std::string::npos)
380 {
381 foundProbe = true;
382 break;
383 }
384 }
385 if (foundProbe)
386 {
387 switch (probeType->second)
388 {
James Feist9eb0b582018-04-27 12:15:46 -0700389 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800390 {
James Feiste31e00a2019-07-24 10:45:43 -0700391 cur = false;
392 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800393 }
James Feist9eb0b582018-04-27 12:15:46 -0700394 case probe_type_codes::TRUE_T:
395 {
James Feiste31e00a2019-07-24 10:45:43 -0700396 cur = true;
397 break;
James Feist9eb0b582018-04-27 12:15:46 -0700398 }
399 case probe_type_codes::MATCH_ONE:
400 {
401 // set current value to last, this probe type shouldn't
402 // affect the outcome
403 cur = ret;
404 matchOne = true;
405 break;
406 }
407 /*case probe_type_codes::AND:
408 break;
409 case probe_type_codes::OR:
410 break;
411 // these are no-ops until the last command switch
412 */
413 case probe_type_codes::FOUND:
414 {
415 if (!std::regex_search(probe, match, command))
416 {
James Feist0eb40352019-04-09 14:44:04 -0700417 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700418 << "\n";
419 return false;
420 }
421 std::string commandStr = *(match.begin() + 1);
422 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800423 cur = (std::find(scan->passedProbes.begin(),
424 scan->passedProbes.end(),
425 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700426 break;
427 }
James Feist0eb40352019-04-09 14:44:04 -0700428 default:
429 {
430 break;
431 }
James Feist3cb5fec2018-01-23 14:41:51 -0800432 }
433 }
434 // look on dbus for object
435 else
436 {
437 if (!std::regex_search(probe, match, command))
438 {
James Feist0eb40352019-04-09 14:44:04 -0700439 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800440 return false;
441 }
442 std::string commandStr = *(match.begin() + 1);
443 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700444 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800445 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800446 auto json = nlohmann::json::parse(commandStr, nullptr, false);
447 if (json.is_discarded())
448 {
James Feist0eb40352019-04-09 14:44:04 -0700449 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800450 return false;
451 }
452 // we can match any (string, variant) property. (string, string)
453 // does a regex
454 std::map<std::string, nlohmann::json> dbusProbeMap =
455 json.get<std::map<std::string, nlohmann::json>>();
456 auto findStart = probe.find("(");
457 if (findStart == std::string::npos)
458 {
459 return false;
460 }
461 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800462 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
463 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800464 }
465
466 // some functions like AND and OR only take affect after the
467 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700468 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800469 {
James Feist54a0dca2019-06-26 10:34:54 -0700470 ret = cur && ret;
471 }
472 else if (lastCommand == probe_type_codes::OR)
473 {
474 ret = cur || ret;
475 }
476
477 if (first)
478 {
479 ret = cur;
480 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800481 }
482 lastCommand = probeType != PROBE_TYPES.end()
483 ? probeType->second
484 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800485 }
486
487 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800488 if (ret && foundDevs.size() == 0)
489 {
James Feist08a5b172019-08-28 14:47:47 -0700490 foundDevs.emplace_back(
491 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800492 }
James Feist0eb40352019-04-09 14:44:04 -0700493 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700494 {
James Feist71f295f2019-06-20 13:35:12 -0700495 // match the last one
496 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700497 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700498
499 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700500 }
James Feist3cb5fec2018-01-23 14:41:51 -0800501 return ret;
502}
503
James Feist7d807752019-11-13 14:47:57 -0800504PerformProbe::PerformProbe(const std::vector<std::string>& probeCommand,
505 std::shared_ptr<PerformScan>& scanPtr,
506 std::function<void(FoundDeviceT&)>&& callback) :
507 _probeCommand(probeCommand),
508 scan(scanPtr), _callback(std::move(callback))
509{
510}
511PerformProbe::~PerformProbe()
512{
513 FoundDeviceT foundDevs;
514 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700515 {
James Feist7d807752019-11-13 14:47:57 -0800516 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700517 }
James Feist7d807752019-11-13 14:47:57 -0800518}
James Feist8f2710a2018-05-09 17:18:55 -0700519
520// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800521bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800522{
James Feist1df06a42019-04-11 14:23:04 -0700523 std::filesystem::create_directory(configurationOutDir);
524 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700525 if (!output.good())
526 {
527 return false;
528 }
James Feist1b2e2242018-01-30 13:45:19 -0800529 output << systemConfiguration.dump(4);
530 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700531 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700532}
James Feist1b2e2242018-01-30 13:45:19 -0800533
James Feist97a63f12018-05-17 13:50:57 -0700534template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800535bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
536 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700537{
538 try
539 {
540 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800541 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700542 ref = value;
543 return true;
544 }
James Feist98132792019-07-09 13:29:09 -0700545 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700546 {
547 return false;
548 }
549}
James Feistbb43d022018-06-12 15:44:33 -0700550
James Feistebcc26b2019-03-22 12:30:43 -0700551// template function to add array as dbus property
552template <typename PropertyType>
553void addArrayToDbus(const std::string& name, const nlohmann::json& array,
554 sdbusplus::asio::dbus_interface* iface,
555 sdbusplus::asio::PropertyPermission permission,
556 nlohmann::json& systemConfiguration,
557 const std::string& jsonPointerString)
558{
559 std::vector<PropertyType> values;
560 for (const auto& property : array)
561 {
562 auto ptr = property.get_ptr<const PropertyType*>();
563 if (ptr != nullptr)
564 {
565 values.emplace_back(*ptr);
566 }
567 }
568
569 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
570 {
571 iface->register_property(name, values);
572 }
573 else
574 {
575 iface->register_property(
576 name, values,
577 [&systemConfiguration,
578 jsonPointerString{std::string(jsonPointerString)}](
579 const std::vector<PropertyType>& newVal,
580 std::vector<PropertyType>& val) {
581 val = newVal;
582 if (!setJsonFromPointer(jsonPointerString, val,
583 systemConfiguration))
584 {
585 std::cerr << "error setting json field\n";
586 return -1;
587 }
588 if (!writeJsonFiles(systemConfiguration))
589 {
590 std::cerr << "error setting json file\n";
591 return -1;
592 }
593 return 1;
594 });
595 }
596}
597
James Feistbb43d022018-06-12 15:44:33 -0700598template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800599void addProperty(const std::string& propertyName, const PropertyType& value,
600 sdbusplus::asio::dbus_interface* iface,
601 nlohmann::json& systemConfiguration,
602 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700603 sdbusplus::asio::PropertyPermission permission)
604{
605 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
606 {
607 iface->register_property(propertyName, value);
608 return;
609 }
James Feist68500ff2018-08-08 15:40:42 -0700610 iface->register_property(
611 propertyName, value,
612 [&systemConfiguration,
613 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800614 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700615 val = newVal;
616 if (!setJsonFromPointer(jsonPointerString, val,
617 systemConfiguration))
618 {
619 std::cerr << "error setting json field\n";
620 return -1;
621 }
James Feistc6248a52018-08-14 10:09:45 -0700622 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700623 {
624 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700625 return -1;
626 }
627 return 1;
628 });
629}
630
631void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800632 const std::string& jsonPointerPath,
633 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
634 sdbusplus::asio::object_server& objServer,
635 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700636{
637 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
638 iface->register_method(
639 "Delete", [&objServer, &systemConfiguration, interface,
640 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700641 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700642 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700643 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700644 {
645 // this technically can't happen as the pointer is pointing to
646 // us
647 throw DBusInternalError();
648 }
649 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700650 systemConfiguration[ptr] = nullptr;
651
James Feist02d2b932020-02-06 16:28:48 -0800652 // todo(james): dig through sdbusplus to find out why we can't
653 // delete it in a method call
654 io.post([&objServer, dbusInterface]() mutable {
655 objServer.remove_interface(dbusInterface);
656 });
657
James Feistc6248a52018-08-14 10:09:45 -0700658 if (!writeJsonFiles(systemConfiguration))
659 {
660 std::cerr << "error setting json file\n";
661 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700662 }
James Feist68500ff2018-08-08 15:40:42 -0700663 });
James Feistbb43d022018-06-12 15:44:33 -0700664}
665
James Feist1b2e2242018-01-30 13:45:19 -0800666// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700667void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800668 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
669 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
670 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700671 sdbusplus::asio::PropertyPermission permission =
672 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800673{
James Feista465ccc2019-02-08 12:51:01 -0800674 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800675 {
James Feist8f2710a2018-05-09 17:18:55 -0700676 auto type = dictPair.value().type();
677 bool array = false;
678 if (dictPair.value().type() == nlohmann::json::value_t::array)
679 {
680 array = true;
681 if (!dictPair.value().size())
682 {
683 continue;
684 }
685 type = dictPair.value()[0].type();
686 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800687 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700688 {
689 if (arrayItem.type() != type)
690 {
691 isLegal = false;
692 break;
693 }
694 }
695 if (!isLegal)
696 {
697 std::cerr << "dbus format error" << dictPair.value() << "\n";
698 continue;
699 }
James Feista218ddb2019-04-11 14:01:31 -0700700 }
701 if (type == nlohmann::json::value_t::object)
702 {
703 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700704 }
James Feist97a63f12018-05-17 13:50:57 -0700705 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700706 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
707 {
708 // all setable numbers are doubles as it is difficult to always
709 // create a configuration file with all whole numbers as decimals
710 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700711 if (array)
712 {
713 if (dictPair.value()[0].is_number())
714 {
715 type = nlohmann::json::value_t::number_float;
716 }
717 }
718 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700719 {
720 type = nlohmann::json::value_t::number_float;
721 }
722 }
723
James Feist8f2710a2018-05-09 17:18:55 -0700724 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800725 {
James Feist9eb0b582018-04-27 12:15:46 -0700726 case (nlohmann::json::value_t::boolean):
727 {
James Feist8f2710a2018-05-09 17:18:55 -0700728 if (array)
729 {
730 // todo: array of bool isn't detected correctly by
731 // sdbusplus, change it to numbers
732 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700733 iface.get(), permission,
734 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700735 }
James Feistbb43d022018-06-12 15:44:33 -0700736
James Feist97a63f12018-05-17 13:50:57 -0700737 else
738 {
James Feistbb43d022018-06-12 15:44:33 -0700739 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700740 iface.get(), systemConfiguration, key,
741 permission);
James Feist97a63f12018-05-17 13:50:57 -0700742 }
James Feist9eb0b582018-04-27 12:15:46 -0700743 break;
744 }
745 case (nlohmann::json::value_t::number_integer):
746 {
James Feist8f2710a2018-05-09 17:18:55 -0700747 if (array)
748 {
749 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700750 iface.get(), permission,
751 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700752 }
753 else
754 {
James Feistbb43d022018-06-12 15:44:33 -0700755 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700756 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700757 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700758 }
James Feist9eb0b582018-04-27 12:15:46 -0700759 break;
760 }
761 case (nlohmann::json::value_t::number_unsigned):
762 {
James Feist8f2710a2018-05-09 17:18:55 -0700763 if (array)
764 {
765 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700766 iface.get(), permission,
767 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700768 }
769 else
770 {
James Feistbb43d022018-06-12 15:44:33 -0700771 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700772 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700773 systemConfiguration, key,
774 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700775 }
James Feist9eb0b582018-04-27 12:15:46 -0700776 break;
777 }
778 case (nlohmann::json::value_t::number_float):
779 {
James Feist8f2710a2018-05-09 17:18:55 -0700780 if (array)
781 {
782 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700783 iface.get(), permission,
784 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700785 }
James Feistbb43d022018-06-12 15:44:33 -0700786
James Feist97a63f12018-05-17 13:50:57 -0700787 else
788 {
James Feistbb43d022018-06-12 15:44:33 -0700789 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700790 iface.get(), systemConfiguration, key,
791 permission);
James Feist97a63f12018-05-17 13:50:57 -0700792 }
James Feist9eb0b582018-04-27 12:15:46 -0700793 break;
794 }
795 case (nlohmann::json::value_t::string):
796 {
James Feist8f2710a2018-05-09 17:18:55 -0700797 if (array)
798 {
James Feistebcc26b2019-03-22 12:30:43 -0700799 addArrayToDbus<std::string>(
800 dictPair.key(), dictPair.value(), iface.get(),
801 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700802 }
803 else
804 {
James Feistc6248a52018-08-14 10:09:45 -0700805 addProperty(
806 dictPair.key(), dictPair.value().get<std::string>(),
807 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700808 }
James Feist9eb0b582018-04-27 12:15:46 -0700809 break;
810 }
James Feist0eb40352019-04-09 14:44:04 -0700811 default:
812 {
James Feista218ddb2019-04-11 14:01:31 -0700813 std::cerr << "Unexpected json type in system configuration "
814 << dictPair.key() << ": "
815 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700816 break;
817 }
James Feist1b2e2242018-01-30 13:45:19 -0800818 }
819 }
James Feistc6248a52018-08-14 10:09:45 -0700820 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
821 {
822 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
823 systemConfiguration);
824 }
James Feist8f2710a2018-05-09 17:18:55 -0700825 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800826}
827
James Feista465ccc2019-02-08 12:51:01 -0800828sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700829{
830 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
831 interface) != settableInterfaces.end()
832 ? sdbusplus::asio::PropertyPermission::readWrite
833 : sdbusplus::asio::PropertyPermission::readOnly;
834}
835
James Feista465ccc2019-02-08 12:51:01 -0800836void createAddObjectMethod(const std::string& jsonPointerPath,
837 const std::string& path,
838 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700839 sdbusplus::asio::object_server& objServer,
840 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700841{
James Feistd58879a2019-09-11 11:26:07 -0700842 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
843 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700844
845 iface->register_method(
846 "AddObject",
847 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700848 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
849 board](const boost::container::flat_map<std::string, JsonVariantType>&
850 data) {
James Feist68500ff2018-08-08 15:40:42 -0700851 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800852 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700853 auto findExposes = base.find("Exposes");
854
855 if (findExposes == base.end())
856 {
857 throw std::invalid_argument("Entity must have children.");
858 }
859
860 // this will throw invalid-argument to sdbusplus if invalid json
861 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800862 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700863 {
James Feista465ccc2019-02-08 12:51:01 -0800864 nlohmann::json& newJson = newData[item.first];
865 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
866 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700867 }
868
869 auto findName = newData.find("Name");
870 auto findType = newData.find("Type");
871 if (findName == newData.end() || findType == newData.end())
872 {
873 throw std::invalid_argument("AddObject missing Name or Type");
874 }
James Feista465ccc2019-02-08 12:51:01 -0800875 const std::string* type = findType->get_ptr<const std::string*>();
876 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700877 if (type == nullptr || name == nullptr)
878 {
879 throw std::invalid_argument("Type and Name must be a string.");
880 }
881
James Feist02d2b932020-02-06 16:28:48 -0800882 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700883 size_t lastIndex = 0;
884 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800885 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700886 {
James Feist02d2b932020-02-06 16:28:48 -0800887 if (expose.is_null())
888 {
889 foundNull = true;
890 continue;
891 }
892
893 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700894 {
895 throw std::invalid_argument(
896 "Field already in JSON, not adding");
897 }
James Feist02d2b932020-02-06 16:28:48 -0800898
899 if (foundNull)
900 {
901 continue;
902 }
903
James Feist68500ff2018-08-08 15:40:42 -0700904 lastIndex++;
905 }
906
907 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
908 *type + ".json");
909 // todo(james) we might want to also make a list of 'can add'
910 // interfaces but for now I think the assumption if there is a
911 // schema avaliable that it is allowed to update is fine
912 if (!schemaFile.good())
913 {
914 throw std::invalid_argument(
915 "No schema avaliable, cannot validate.");
916 }
917 nlohmann::json schema =
918 nlohmann::json::parse(schemaFile, nullptr, false);
919 if (schema.is_discarded())
920 {
921 std::cerr << "Schema not legal" << *type << ".json\n";
922 throw DBusInternalError();
923 }
924 if (!validateJson(schema, newData))
925 {
926 throw std::invalid_argument("Data does not match schema");
927 }
James Feist02d2b932020-02-06 16:28:48 -0800928 if (foundNull)
929 {
930 findExposes->at(lastIndex) = newData;
931 }
932 else
933 {
934 findExposes->push_back(newData);
935 }
James Feist68500ff2018-08-08 15:40:42 -0700936 if (!writeJsonFiles(systemConfiguration))
937 {
938 std::cerr << "Error writing json files\n";
939 throw DBusInternalError();
940 }
941 std::string dbusName = *name;
942
943 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700944 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700945
946 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
947 createInterface(objServer, path + "/" + dbusName,
948 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800949 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700950 // permission is read-write, as since we just created it, must be
951 // runtime modifiable
952 populateInterfaceFromJson(
953 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700954 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700955 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700956 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700957 });
958 iface->initialize();
959}
960
James Feista465ccc2019-02-08 12:51:01 -0800961void postToDbus(const nlohmann::json& newConfiguration,
962 nlohmann::json& systemConfiguration,
963 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800964
James Feist1b2e2242018-01-30 13:45:19 -0800965{
James Feist97a63f12018-05-17 13:50:57 -0700966 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800967 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800968 {
James Feistf1b14142019-04-10 15:22:09 -0700969 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700970 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700971 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700972 // loop through newConfiguration, but use values from system
973 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700974 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700975 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800976 std::string boardType;
977 if (findBoardType != boardValues.end() &&
978 findBoardType->type() == nlohmann::json::value_t::string)
979 {
980 boardType = findBoardType->get<std::string>();
981 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700982 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800983 }
984 else
985 {
986 std::cerr << "Unable to find type for " << boardKey
987 << " reverting to Chassis.\n";
988 boardType = "Chassis";
989 }
James Feist11be6672018-04-06 14:05:32 -0700990 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800991
992 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700993 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700994 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
995 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800996
James Feistd58879a2019-09-11 11:26:07 -0700997 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
998 createInterface(objServer, boardName,
999 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -07001000
James Feistd58879a2019-09-11 11:26:07 -07001001 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
1002 createInterface(objServer, boardName,
1003 "xyz.openbmc_project.Inventory.Item." + boardType,
1004 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -07001005
James Feist68500ff2018-08-08 15:40:42 -07001006 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -07001007 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -07001008
James Feist97a63f12018-05-17 13:50:57 -07001009 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001010 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -07001011 jsonPointerPath += "/";
1012 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -08001013 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -07001014 {
1015 if (boardField.value().type() == nlohmann::json::value_t::object)
1016 {
James Feistd58879a2019-09-11 11:26:07 -07001017 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
1018 createInterface(objServer, boardName, boardField.key(),
1019 boardKeyOrig);
1020
James Feistc6248a52018-08-14 10:09:45 -07001021 populateInterfaceFromJson(systemConfiguration,
1022 jsonPointerPath + boardField.key(),
1023 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -07001024 }
1025 }
James Feist97a63f12018-05-17 13:50:57 -07001026
James Feist1e3e6982018-08-03 16:09:28 -07001027 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001028 if (exposes == boardValues.end())
1029 {
1030 continue;
1031 }
James Feist97a63f12018-05-17 13:50:57 -07001032 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001033 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001034
1035 // store the board level pointer so we can modify it on the way down
1036 std::string jsonPointerPathBoard = jsonPointerPath;
1037 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001038 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001039 {
James Feist97a63f12018-05-17 13:50:57 -07001040 exposesIndex++;
1041 jsonPointerPath = jsonPointerPathBoard;
1042 jsonPointerPath += std::to_string(exposesIndex);
1043
James Feistd63d18a2018-07-19 15:23:45 -07001044 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001045 if (findName == item.end())
1046 {
1047 std::cerr << "cannot find name in field " << item << "\n";
1048 continue;
1049 }
James Feist1e3e6982018-08-03 16:09:28 -07001050 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001051 // if status is not found it is assumed to be status = 'okay'
1052 if (findStatus != item.end())
1053 {
1054 if (*findStatus == "disabled")
1055 {
1056 continue;
1057 }
1058 }
James Feistd63d18a2018-07-19 15:23:45 -07001059 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001060 std::string itemType;
1061 if (findType != item.end())
1062 {
1063 itemType = findType->get<std::string>();
1064 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001065 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1066 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001067 }
1068 else
1069 {
1070 itemType = "unknown";
1071 }
1072 std::string itemName = findName->get<std::string>();
1073 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001074 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001075
James Feistd58879a2019-09-11 11:26:07 -07001076 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1077 createInterface(objServer, boardName + "/" + itemName,
1078 "xyz.openbmc_project.Configuration." + itemType,
1079 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001080
James Feist97a63f12018-05-17 13:50:57 -07001081 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001082 itemIface, item, objServer,
1083 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001084
James Feista465ccc2019-02-08 12:51:01 -08001085 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001086 {
James Feist97a63f12018-05-17 13:50:57 -07001087 jsonPointerPath = jsonPointerPathBoard +
1088 std::to_string(exposesIndex) + "/" +
1089 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001090 if (objectPair.value().type() ==
1091 nlohmann::json::value_t::object)
1092 {
James Feistd58879a2019-09-11 11:26:07 -07001093 std::shared_ptr<sdbusplus::asio::dbus_interface>
1094 objectIface = createInterface(
1095 objServer, boardName + "/" + itemName,
1096 "xyz.openbmc_project.Configuration." + itemType +
1097 "." + objectPair.key(),
1098 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001099
1100 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001101 systemConfiguration, jsonPointerPath, objectIface,
1102 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001103 }
1104 else if (objectPair.value().type() ==
1105 nlohmann::json::value_t::array)
1106 {
1107 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001108 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001109 {
James Feist8f2710a2018-05-09 17:18:55 -07001110 continue;
1111 }
1112 bool isLegal = true;
1113 auto type = objectPair.value()[0].type();
1114 if (type != nlohmann::json::value_t::object)
1115 {
1116 continue;
1117 }
1118
1119 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001120 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001121 {
1122 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001123 {
James Feist8f2710a2018-05-09 17:18:55 -07001124 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001125 break;
1126 }
James Feist8f2710a2018-05-09 17:18:55 -07001127 }
1128 if (!isLegal)
1129 {
1130 std::cerr << "dbus format error" << objectPair.value()
1131 << "\n";
1132 break;
1133 }
1134
James Feista465ccc2019-02-08 12:51:01 -08001135 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001136 {
James Feist97a63f12018-05-17 13:50:57 -07001137
James Feistd58879a2019-09-11 11:26:07 -07001138 std::shared_ptr<sdbusplus::asio::dbus_interface>
1139 objectIface = createInterface(
1140 objServer, boardName + "/" + itemName,
1141 "xyz.openbmc_project.Configuration." +
1142 itemType + "." + objectPair.key() +
1143 std::to_string(index),
1144 boardKeyOrig);
1145
James Feistc6248a52018-08-14 10:09:45 -07001146 populateInterfaceFromJson(
1147 systemConfiguration,
1148 jsonPointerPath + "/" + std::to_string(index),
1149 objectIface, arrayItem, objServer,
1150 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001151 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001152 }
1153 }
1154 }
1155 }
1156 }
1157}
1158
James Feist8f2710a2018-05-09 17:18:55 -07001159// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001160bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001161{
1162 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001163 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001164 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1165 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001166 {
1167 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001168 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001169 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001170 }
James Feistb4383f42018-08-06 16:54:10 -07001171
1172 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1173 globalSchema);
1174 if (!schemaStream.good())
1175 {
1176 std::cerr
1177 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1178 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001179 return false;
James Feistb4383f42018-08-06 16:54:10 -07001180 }
1181 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1182 if (schema.is_discarded())
1183 {
1184 std::cerr
1185 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1186 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001187 return false;
James Feistb4383f42018-08-06 16:54:10 -07001188 }
1189
James Feista465ccc2019-02-08 12:51:01 -08001190 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001191 {
1192 std::ifstream jsonStream(jsonPath.c_str());
1193 if (!jsonStream.good())
1194 {
1195 std::cerr << "unable to open " << jsonPath.string() << "\n";
1196 continue;
1197 }
1198 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1199 if (data.is_discarded())
1200 {
1201 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1202 continue;
1203 }
James Feist8da99192019-01-24 08:20:16 -08001204 /*
1205 * todo(james): reenable this once less things are in flight
1206 *
James Feistb4383f42018-08-06 16:54:10 -07001207 if (!validateJson(schema, data))
1208 {
1209 std::cerr << "Error validating " << jsonPath.string() << "\n";
1210 continue;
1211 }
James Feist8da99192019-01-24 08:20:16 -08001212 */
James Feistb4383f42018-08-06 16:54:10 -07001213
James Feist3cb5fec2018-01-23 14:41:51 -08001214 if (data.type() == nlohmann::json::value_t::array)
1215 {
James Feista465ccc2019-02-08 12:51:01 -08001216 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001217 {
1218 configurations.emplace_back(d);
1219 }
1220 }
1221 else
1222 {
1223 configurations.emplace_back(data);
1224 }
1225 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001226 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001227}
James Feist3cb5fec2018-01-23 14:41:51 -08001228
James Feist94219d12020-03-03 11:52:25 -08001229std::string getRecordName(
1230 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1231 const std::string& probeName)
1232{
1233 if (probe.empty())
1234 {
1235 return probeName;
1236 }
1237
1238 // use an array so alphabetical order from the
1239 // flat_map is maintained
1240 auto device = nlohmann::json::array();
1241 for (auto& devPair : probe)
1242 {
1243 device.push_back(devPair.first);
1244 std::visit([&device](auto&& v) { device.push_back(v); },
1245 devPair.second);
1246 }
1247 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1248 // hashes are hard to distinguish, use the
1249 // non-hashed version if we want debug
1250 if constexpr (DEBUG)
1251 {
1252 return probeName + device.dump();
1253 }
1254 else
1255 {
1256 return std::to_string(hash);
1257 }
1258}
1259
James Feist733f7652019-11-13 14:32:29 -08001260PerformScan::PerformScan(
1261 nlohmann::json& systemConfiguration, nlohmann::json& missingConfigurations,
1262 std::list<nlohmann::json>& configurations,
1263 std::function<void(const DBusProbeObjectT&)>&& callback) :
1264 _systemConfiguration(systemConfiguration),
1265 _missingConfigurations(missingConfigurations),
1266 _configurations(configurations), _callback(std::move(callback))
James Feist75fdeeb2018-02-20 14:26:16 -08001267{
James Feist733f7652019-11-13 14:32:29 -08001268}
1269void PerformScan::run()
1270{
1271 boost::container::flat_set<std::string> dbusProbeInterfaces;
1272 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001273
James Feist733f7652019-11-13 14:32:29 -08001274 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001275 {
James Feist733f7652019-11-13 14:32:29 -08001276 auto findProbe = it->find("Probe");
1277 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001278
James Feist733f7652019-11-13 14:32:29 -08001279 nlohmann::json probeCommand;
1280 // check for poorly formatted fields, probe must be an array
1281 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001282 {
James Feist733f7652019-11-13 14:32:29 -08001283 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1284 it = _configurations.erase(it);
1285 continue;
1286 }
1287 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1288 {
1289 probeCommand = nlohmann::json::array();
1290 probeCommand.push_back(*findProbe);
1291 }
1292 else
1293 {
1294 probeCommand = *findProbe;
1295 }
James Feist3cb5fec2018-01-23 14:41:51 -08001296
James Feist733f7652019-11-13 14:32:29 -08001297 if (findName == it->end())
1298 {
1299 std::cerr << "configuration file missing name:\n " << *it << "\n";
1300 it = _configurations.erase(it);
1301 continue;
1302 }
1303 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001304
James Feist733f7652019-11-13 14:32:29 -08001305 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1306 passedProbes.end())
1307 {
1308 it = _configurations.erase(it);
1309 continue;
1310 }
1311 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001312
James Feist733f7652019-11-13 14:32:29 -08001313 // store reference to this to children to makes sure we don't get
1314 // destroyed too early
1315 auto thisRef = shared_from_this();
1316 auto probePointer = std::make_shared<PerformProbe>(
1317 probeCommand, thisRef,
1318 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001319 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001320
James Feist35f5e0e2020-03-16 14:02:27 -07001321 std::set<nlohmann::json> usedNames;
James Feist733f7652019-11-13 14:32:29 -08001322 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001323 std::list<size_t> indexes(foundDevices.size());
1324 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001325
James Feist35f5e0e2020-03-16 14:02:27 -07001326 size_t indexIdx = probeName.find("$");
1327 bool hasTemplateName = (indexIdx != std::string::npos);
James Feist94219d12020-03-03 11:52:25 -08001328
1329 // copy over persisted configurations and make sure we remove
1330 // indexes that are already used
1331 for (auto itr = foundDevices.begin();
1332 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001333 {
James Feist94219d12020-03-03 11:52:25 -08001334 std::string recordName = getRecordName(*itr, probeName);
James Feistf1b14142019-04-10 15:22:09 -07001335
James Feist08a5b172019-08-28 14:47:47 -07001336 auto fromLastJson = lastJson.find(recordName);
1337 if (fromLastJson != lastJson.end())
1338 {
James Feist02d2b932020-02-06 16:28:48 -08001339 auto findExposes = fromLastJson->find("Exposes");
1340 // delete nulls from any updates
1341 if (findExposes != fromLastJson->end())
1342 {
1343 auto copy = nlohmann::json::array();
1344 for (auto& expose : *findExposes)
1345 {
1346 if (expose.is_null())
1347 {
1348 continue;
1349 }
1350 copy.emplace_back(expose);
1351 }
1352 *findExposes = copy;
1353 }
1354
James Feist08a5b172019-08-28 14:47:47 -07001355 // keep user changes
1356 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001357 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001358 itr = foundDevices.erase(itr);
James Feist35f5e0e2020-03-16 14:02:27 -07001359 if (hasTemplateName)
James Feist94219d12020-03-03 11:52:25 -08001360 {
1361 auto nameIt = fromLastJson->find("Name");
1362 if (nameIt == fromLastJson->end())
1363 {
1364 std::cerr << "Last JSON Illegal\n";
1365 continue;
1366 }
1367
1368 int index = std::stoi(
1369 nameIt->get<std::string>().substr(indexIdx),
1370 nullptr, 0);
James Feist35f5e0e2020-03-16 14:02:27 -07001371 usedNames.insert(nameIt.value());
James Feist94219d12020-03-03 11:52:25 -08001372 auto usedIt = std::find(indexes.begin(),
1373 indexes.end(), index);
1374
1375 if (usedIt == indexes.end())
1376 {
1377 continue; // less items now
1378 }
1379 indexes.erase(usedIt);
1380 }
1381
James Feist08a5b172019-08-28 14:47:47 -07001382 continue;
1383 }
James Feist94219d12020-03-03 11:52:25 -08001384 itr++;
1385 }
James Feist35f5e0e2020-03-16 14:02:27 -07001386
1387 std::optional<std::string> replaceStr;
1388
James Feist94219d12020-03-03 11:52:25 -08001389 for (auto& foundDevice : foundDevices)
1390 {
1391 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();
1407 std::optional<std::string> replaceVal = templateCharReplace(
1408 copyIt, foundDevice, foundDeviceIdx, replaceStr);
1409
1410 if (!replaceStr && replaceVal)
1411 {
1412 if (usedNames.find(copyIt.value()) != usedNames.end())
1413 {
1414 replaceStr = replaceVal;
1415 copyForName = {{"Name", getName.value()}};
1416 copyIt = copyForName.begin();
1417 templateCharReplace(copyIt, foundDevice,
1418 foundDeviceIdx, replaceStr);
1419 }
1420 }
1421
1422 if (replaceStr)
1423 {
1424 std::cerr << "Duplicates found, replacing "
1425 << *replaceStr
1426 << " with found device index.\n Consider "
1427 "fixing template to not have duplicates\n";
1428 }
James Feist08a5b172019-08-28 14:47:47 -07001429
1430 for (auto keyPair = record.begin(); keyPair != record.end();
1431 keyPair++)
1432 {
James Feist35f5e0e2020-03-16 14:02:27 -07001433 if (keyPair.key() == "Name")
1434 {
1435 keyPair.value() = copyIt.value();
1436 usedNames.insert(copyIt.value());
1437
1438 continue; // already covered above
1439 }
James Feist08a5b172019-08-28 14:47:47 -07001440 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001441 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001442 }
1443
James Feist35f5e0e2020-03-16 14:02:27 -07001444 // insert into configuration temporarily to be able to
1445 // reference ourselves
1446
1447 _systemConfiguration[recordName] = record;
1448
James Feist08a5b172019-08-28 14:47:47 -07001449 auto findExpose = record.find("Exposes");
1450 if (findExpose == record.end())
1451 {
James Feistf1b14142019-04-10 15:22:09 -07001452 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001453 continue;
1454 }
James Feistf1b14142019-04-10 15:22:09 -07001455
James Feist08a5b172019-08-28 14:47:47 -07001456 for (auto& expose : *findExpose)
1457 {
1458 for (auto keyPair = expose.begin();
1459 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001460 {
James Feist08a5b172019-08-28 14:47:47 -07001461
1462 templateCharReplace(keyPair, foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -07001463 foundDeviceIdx, replaceStr);
James Feist08a5b172019-08-28 14:47:47 -07001464
James Feist668bbb12020-02-05 14:27:26 -08001465 bool isBind =
1466 boost::starts_with(keyPair.key(), "Bind");
1467 bool isDisable = keyPair.key() == "DisableNode";
1468
1469 // special cases
1470 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001471 {
James Feist668bbb12020-02-05 14:27:26 -08001472 continue;
1473 }
1474
1475 if (keyPair.value().type() !=
1476 nlohmann::json::value_t::string &&
1477 keyPair.value().type() !=
1478 nlohmann::json::value_t::array)
1479 {
1480 std::cerr << "Value is invalid type "
1481 << keyPair.key() << "\n";
1482 continue;
1483 }
1484
1485 std::vector<std::string> matches;
1486 if (keyPair.value().type() ==
1487 nlohmann::json::value_t::string)
1488 {
1489 matches.emplace_back(keyPair.value());
1490 }
1491 else
1492 {
1493 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001494 {
James Feist668bbb12020-02-05 14:27:26 -08001495 if (value.type() !=
1496 nlohmann::json::value_t::string)
1497 {
1498 std::cerr << "Value is invalid type "
1499 << value << "\n";
1500 break;
1501 }
1502 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001503 }
James Feist668bbb12020-02-05 14:27:26 -08001504 }
James Feist08a5b172019-08-28 14:47:47 -07001505
James Feist668bbb12020-02-05 14:27:26 -08001506 std::set<std::string> foundMatches;
1507 for (auto& configurationPair :
1508 _systemConfiguration.items())
1509 {
1510 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001511 {
James Feist668bbb12020-02-05 14:27:26 -08001512 // don't disable ourselves
1513 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001514 {
James Feist1b2e2242018-01-30 13:45:19 -08001515 continue;
1516 }
James Feist668bbb12020-02-05 14:27:26 -08001517 }
1518 auto configListFind =
1519 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001520
James Feist668bbb12020-02-05 14:27:26 -08001521 if (configListFind ==
1522 configurationPair.value().end() ||
1523 configListFind->type() !=
1524 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001525 {
James Feist668bbb12020-02-05 14:27:26 -08001526 continue;
James Feist08a5b172019-08-28 14:47:47 -07001527 }
James Feist668bbb12020-02-05 14:27:26 -08001528 for (auto& exposedObject : *configListFind)
1529 {
1530 auto matchIt = std::find_if(
1531 matches.begin(), matches.end(),
1532 [name = (exposedObject)["Name"]
1533 .get<std::string>()](
1534 const std::string& s) {
1535 return s == name;
1536 });
1537 if (matchIt == matches.end())
1538 {
1539 continue;
1540 }
1541 foundMatches.insert(*matchIt);
1542
1543 if (isBind)
1544 {
1545 std::string bind = keyPair.key().substr(
1546 sizeof("Bind") - 1);
1547
1548 exposedObject["Status"] = "okay";
1549 expose[bind] = exposedObject;
1550 }
1551 else if (isDisable)
1552 {
1553 exposedObject["Status"] = "disabled";
1554 }
1555 }
1556 }
1557 if (foundMatches.size() != matches.size())
1558 {
1559 std::cerr << "configuration file "
1560 "dependency error, "
1561 "could not find "
1562 << keyPair.key() << " "
1563 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001564 }
1565 }
1566 }
James Feist08a5b172019-08-28 14:47:47 -07001567 // overwrite ourselves with cleaned up version
1568 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001569 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001570 }
1571 });
James Feist787c3c32019-11-07 14:42:58 -08001572
James Feist733f7652019-11-13 14:32:29 -08001573 // parse out dbus probes by discarding other probe types, store in a
1574 // map
1575 for (const std::string& probe : probeCommand)
1576 {
1577 bool found = false;
1578 boost::container::flat_map<const char*, probe_type_codes,
1579 cmp_str>::const_iterator probeType;
1580 for (probeType = PROBE_TYPES.begin();
1581 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001582 {
James Feist733f7652019-11-13 14:32:29 -08001583 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001584 {
James Feist733f7652019-11-13 14:32:29 -08001585 found = true;
1586 break;
James Feist787c3c32019-11-07 14:42:58 -08001587 }
James Feist787c3c32019-11-07 14:42:58 -08001588 }
James Feist733f7652019-11-13 14:32:29 -08001589 if (found)
1590 {
1591 continue;
1592 }
1593 // syntax requires probe before first open brace
1594 auto findStart = probe.find("(");
1595 std::string interface = probe.substr(0, findStart);
1596 dbusProbeInterfaces.emplace(interface);
1597 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001598 }
James Feist733f7652019-11-13 14:32:29 -08001599 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001600 }
James Feist75fdeeb2018-02-20 14:26:16 -08001601
James Feist733f7652019-11-13 14:32:29 -08001602 // probe vector stores a shared_ptr to each PerformProbe that cares
1603 // about a dbus interface
James Feistb1728ca2020-04-30 15:40:55 -07001604 findDbusObjects(std::move(dbusProbePointers),
1605 std::move(dbusProbeInterfaces), shared_from_this());
1606 if constexpr (DEBUG)
1607 {
1608 std::cerr << __func__ << " " << __LINE__ << "\n";
1609 }
James Feist733f7652019-11-13 14:32:29 -08001610}
1611
1612PerformScan::~PerformScan()
1613{
1614 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001615 {
James Feist733f7652019-11-13 14:32:29 -08001616 auto nextScan = std::make_shared<PerformScan>(
1617 _systemConfiguration, _missingConfigurations, _configurations,
1618 std::move(_callback));
1619 nextScan->passedProbes = std::move(passedProbes);
1620 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1621 nextScan->run();
James Feistb1728ca2020-04-30 15:40:55 -07001622
1623 if constexpr (DEBUG)
1624 {
1625 std::cerr << __func__ << " " << __LINE__ << "\n";
1626 }
James Feist8f2710a2018-05-09 17:18:55 -07001627 }
James Feist733f7652019-11-13 14:32:29 -08001628 else
1629 {
1630 _callback(dbusProbeObjects);
James Feistb1728ca2020-04-30 15:40:55 -07001631
1632 if constexpr (DEBUG)
1633 {
1634 std::cerr << __func__ << " " << __LINE__ << "\n";
1635 }
James Feist733f7652019-11-13 14:32:29 -08001636 }
1637}
James Feistc95cb142018-02-26 10:41:42 -08001638
James Feistb1728ca2020-04-30 15:40:55 -07001639void startRemovedTimer(boost::asio::steady_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001640 nlohmann::json& systemConfiguration)
1641{
1642 static bool scannedPowerOff = false;
1643 static bool scannedPowerOn = false;
1644
James Feistfb00f392019-06-25 14:16:48 -07001645 if (systemConfiguration.empty() || lastJson.empty())
1646 {
1647 return; // not ready yet
1648 }
James Feist1df06a42019-04-11 14:23:04 -07001649 if (scannedPowerOn)
1650 {
1651 return;
1652 }
1653
1654 if (!isPowerOn() && scannedPowerOff)
1655 {
1656 return;
1657 }
1658
James Feistb1728ca2020-04-30 15:40:55 -07001659 timer.expires_after(std::chrono::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001660 timer.async_wait(
1661 [&systemConfiguration](const boost::system::error_code& ec) {
1662 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001663 {
James Feist1a996582019-05-14 15:10:06 -07001664 // we were cancelled
1665 return;
1666 }
1667
1668 bool powerOff = !isPowerOn();
1669 for (const auto& item : lastJson.items())
1670 {
1671 if (systemConfiguration.find(item.key()) ==
1672 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001673 {
James Feist1a996582019-05-14 15:10:06 -07001674 bool isDetectedPowerOn = false;
1675 auto powerState = item.value().find("PowerState");
1676 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001677 {
James Feist1a996582019-05-14 15:10:06 -07001678 auto ptr = powerState->get_ptr<const std::string*>();
1679 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001680 {
James Feist1a996582019-05-14 15:10:06 -07001681 if (*ptr == "On" || *ptr == "BiosPost")
1682 {
1683 isDetectedPowerOn = true;
1684 }
James Feist1df06a42019-04-11 14:23:04 -07001685 }
1686 }
James Feist1a996582019-05-14 15:10:06 -07001687 if (powerOff && isDetectedPowerOn)
1688 {
1689 // power not on yet, don't know if it's there or not
1690 continue;
1691 }
1692 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1693 {
1694 // already logged it when power was off
1695 continue;
1696 }
James Feist1df06a42019-04-11 14:23:04 -07001697
James Feist1a996582019-05-14 15:10:06 -07001698 logDeviceRemoved(item.value());
1699 }
James Feist1df06a42019-04-11 14:23:04 -07001700 }
James Feist1a996582019-05-14 15:10:06 -07001701 scannedPowerOff = true;
1702 if (!powerOff)
1703 {
1704 scannedPowerOn = true;
1705 }
1706 });
James Feist1df06a42019-04-11 14:23:04 -07001707}
1708
James Feist8f2710a2018-05-09 17:18:55 -07001709// main properties changed entry
1710void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001711 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1712 nlohmann::json& systemConfiguration,
1713 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001714{
James Feistb1728ca2020-04-30 15:40:55 -07001715 static boost::asio::steady_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001716 static size_t instance = 0;
1717 instance++;
1718 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001719
James Feistb1728ca2020-04-30 15:40:55 -07001720 timer.expires_after(std::chrono::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001721
1722 // setup an async wait as we normally get flooded with new requests
James Feist02d2b932020-02-06 16:28:48 -08001723 timer.async_wait([&systemConfiguration, &dbusMatches, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001724 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001725 if (ec == boost::asio::error::operation_aborted)
1726 {
1727 // we were cancelled
1728 return;
1729 }
1730 else if (ec)
1731 {
1732 std::cerr << "async wait error " << ec << "\n";
1733 return;
1734 }
1735
1736 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001737 auto missingConfigurations = std::make_shared<nlohmann::json>();
1738 *missingConfigurations = systemConfiguration;
1739
James Feist8f2710a2018-05-09 17:18:55 -07001740 std::list<nlohmann::json> configurations;
1741 if (!findJsonFiles(configurations))
1742 {
1743 std::cerr << "cannot find json files\n";
1744 return;
1745 }
1746
1747 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001748 systemConfiguration, *missingConfigurations, configurations,
James Feist02d2b932020-02-06 16:28:48 -08001749 [&systemConfiguration, &objServer, &dbusMatches, count,
James Feist733f7652019-11-13 14:32:29 -08001750 oldConfiguration,
1751 missingConfigurations](const DBusProbeObjectT& dbusProbeObjects) {
James Feist899e17f2019-09-13 11:46:29 -07001752 // this is something that since ac has been applied to the bmc
1753 // we saw, and we no longer see it
1754 bool powerOff = !isPowerOn();
1755 for (const auto& item : missingConfigurations->items())
1756 {
1757 bool isDetectedPowerOn = false;
1758 auto powerState = item.value().find("PowerState");
1759 if (powerState != item.value().end())
1760 {
1761 auto ptr = powerState->get_ptr<const std::string*>();
1762 if (ptr)
1763 {
1764 if (*ptr == "On" || *ptr == "BiosPost")
1765 {
1766 isDetectedPowerOn = true;
1767 }
1768 }
1769 }
1770 if (powerOff && isDetectedPowerOn)
1771 {
1772 // power not on yet, don't know if it's there or not
1773 continue;
1774 }
1775 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001776 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001777 ifaces = inventory[name];
1778 for (auto& iface : ifaces)
1779 {
James Feist02d2b932020-02-06 16:28:48 -08001780 auto sharedPtr = iface.lock();
1781 if (!sharedPtr)
1782 {
1783 continue; // was already deleted elsewhere
1784 }
1785 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001786 }
1787 ifaces.clear();
1788 systemConfiguration.erase(item.key());
1789 logDeviceRemoved(item.value());
1790 }
1791
James Feist8f2710a2018-05-09 17:18:55 -07001792 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001793 for (auto it = newConfiguration.begin();
1794 it != newConfiguration.end();)
1795 {
1796 auto findKey = oldConfiguration.find(it.key());
1797 if (findKey != oldConfiguration.end())
1798 {
1799 it = newConfiguration.erase(it);
1800 }
1801 else
1802 {
1803 it++;
1804 }
1805 }
James Feist899e17f2019-09-13 11:46:29 -07001806 for (const auto& item : newConfiguration.items())
1807 {
1808 logDeviceAdded(item.value());
1809 }
1810
James Feist02d2b932020-02-06 16:28:48 -08001811 registerCallbacks(dbusMatches, systemConfiguration, objServer,
1812 dbusProbeObjects);
James Feist8f2710a2018-05-09 17:18:55 -07001813 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001814 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001815
James Feistbb43d022018-06-12 15:44:33 -07001816 io.post([&]() {
1817 if (!writeJsonFiles(systemConfiguration))
1818 {
1819 std::cerr << "Error writing json files\n";
1820 }
1821 });
James Feist8f2710a2018-05-09 17:18:55 -07001822 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001823 postToDbus(newConfiguration, systemConfiguration,
1824 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001825 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001826 {
James Feist899e17f2019-09-13 11:46:29 -07001827 return;
James Feist1df06a42019-04-11 14:23:04 -07001828 }
James Feist899e17f2019-09-13 11:46:29 -07001829 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001830 });
1831 });
1832 });
1833 perfScan->run();
1834 });
James Feist75fdeeb2018-02-20 14:26:16 -08001835}
1836
James Feist02d2b932020-02-06 16:28:48 -08001837void registerCallbacks(std::vector<sdbusplus::bus::match::match>& dbusMatches,
James Feista465ccc2019-02-08 12:51:01 -08001838 nlohmann::json& systemConfiguration,
James Feist733f7652019-11-13 14:32:29 -08001839 sdbusplus::asio::object_server& objServer,
1840 const DBusProbeObjectT& dbusProbeObjects)
James Feist75fdeeb2018-02-20 14:26:16 -08001841{
1842 static boost::container::flat_set<std::string> watchedObjects;
1843
James Feist733f7652019-11-13 14:32:29 -08001844 for (const auto& objectMap : dbusProbeObjects)
James Feist75fdeeb2018-02-20 14:26:16 -08001845 {
James Feist787c3c32019-11-07 14:42:58 -08001846 auto [_, inserted] = watchedObjects.insert(objectMap.first);
1847 if (!inserted)
James Feist75fdeeb2018-02-20 14:26:16 -08001848 {
1849 continue;
1850 }
James Feist8f2710a2018-05-09 17:18:55 -07001851 std::function<void(sdbusplus::message::message & message)>
1852 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001853
James Feista465ccc2019-02-08 12:51:01 -08001854 [&](sdbusplus::message::message&) {
James Feist02d2b932020-02-06 16:28:48 -08001855 propertiesChangedCallback(dbusMatches, systemConfiguration,
1856 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001857 };
1858
1859 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001860 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001861 "type='signal',member='PropertiesChanged',arg0='" +
1862 objectMap.first + "'",
1863 eventHandler);
1864 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001865 }
1866}
1867
James Feist98132792019-07-09 13:29:09 -07001868int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001869{
1870 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001871 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001872 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001873
James Feist8f2710a2018-05-09 17:18:55 -07001874 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001875
James Feist8f2710a2018-05-09 17:18:55 -07001876 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1877 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1878 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001879
James Feist4131aea2018-03-09 09:47:30 -08001880 // to keep reference to the match / filter objects so they don't get
1881 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001882 std::vector<sdbusplus::bus::match::match> dbusMatches;
1883
1884 nlohmann::json systemConfiguration = nlohmann::json::object();
1885
James Feist8f2710a2018-05-09 17:18:55 -07001886 io.post([&]() {
James Feist02d2b932020-02-06 16:28:48 -08001887 propertiesChangedCallback(dbusMatches, systemConfiguration, objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001888 });
James Feist4131aea2018-03-09 09:47:30 -08001889
James Feistfd1264a2018-05-03 12:10:00 -07001890 entityIface->register_method("ReScan", [&]() {
James Feist02d2b932020-02-06 16:28:48 -08001891 propertiesChangedCallback(dbusMatches, systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001892 });
James Feist8f2710a2018-05-09 17:18:55 -07001893 entityIface->initialize();
1894
James Feist1df06a42019-04-11 14:23:04 -07001895 if (fwVersionIsSame())
1896 {
1897 if (std::filesystem::is_regular_file(currentConfiguration))
1898 {
1899 // this file could just be deleted, but it's nice for debug
1900 std::filesystem::create_directory(tempConfigDir);
1901 std::filesystem::remove(lastConfiguration);
1902 std::filesystem::copy(currentConfiguration, lastConfiguration);
1903 std::filesystem::remove(currentConfiguration);
1904
1905 std::ifstream jsonStream(lastConfiguration);
1906 if (jsonStream.good())
1907 {
1908 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1909 if (data.is_discarded())
1910 {
1911 std::cerr << "syntax error in " << lastConfiguration
1912 << "\n";
1913 }
1914 else
1915 {
1916 lastJson = std::move(data);
1917 }
1918 }
1919 else
1920 {
1921 std::cerr << "unable to open " << lastConfiguration << "\n";
1922 }
1923 }
1924 }
1925 else
1926 {
1927 // not an error, just logging at this level to make it in the journal
1928 std::cerr << "Clearing previous configuration\n";
1929 std::filesystem::remove(currentConfiguration);
1930 }
1931
1932 // some boards only show up after power is on, we want to not say they are
1933 // removed until the same state happens
1934 setupPowerMatch(SYSTEM_BUS);
1935
James Feist1b2e2242018-01-30 13:45:19 -08001936 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001937
1938 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001939}