blob: 784ef54e3fade14fd0637bf335f3b0464b5c64c9 [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 Feist3cb5fec2018-01-23 14:41:51 -080028#include <boost/container/flat_map.hpp>
29#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070030#include <boost/range/iterator_range.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070031#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080032#include <fstream>
33#include <iostream>
34#include <nlohmann/json.hpp>
35#include <regex>
36#include <sdbusplus/asio/connection.hpp>
37#include <sdbusplus/asio/object_server.hpp>
38#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080039
James Feista465ccc2019-02-08 12:51:01 -080040constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
41constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070042constexpr const char* tempConfigDir = "/tmp/configuration/";
43constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
44constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080045constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070046constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080047
James Feistf1b14142019-04-10 15:22:09 -070048constexpr const bool DEBUG = false;
49
James Feist3cb5fec2018-01-23 14:41:51 -080050struct cmp_str
51{
James Feista465ccc2019-02-08 12:51:01 -080052 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080053 {
54 return std::strcmp(a, b) < 0;
55 }
56};
57
James Feist8f2710a2018-05-09 17:18:55 -070058struct PerformProbe;
59
James Feist3cb5fec2018-01-23 14:41:51 -080060// underscore T for collison with dbus c api
61enum class probe_type_codes
62{
63 FALSE_T,
64 TRUE_T,
65 AND,
66 OR,
James Feist6bd2a022018-03-13 12:30:58 -070067 FOUND,
68 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080069};
James Feista465ccc2019-02-08 12:51:01 -080070const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080071 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
72 {"TRUE", probe_type_codes::TRUE_T},
73 {"AND", probe_type_codes::AND},
74 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070075 {"FOUND", probe_type_codes::FOUND},
76 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080077
James Feist41334262019-03-25 13:30:20 -070078static constexpr std::array<const char*, 5> settableInterfaces = {
79 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070080using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080081 std::variant<std::vector<std::string>, std::vector<double>, std::string,
82 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
83 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080084using GetSubTreeType = std::vector<
85 std::pair<std::string,
86 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
87
88using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070089 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080090 boost::container::flat_map<
91 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070092 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080093
James Feist08a5b172019-08-28 14:47:47 -070094using FoundDeviceT =
95 std::vector<boost::container::flat_map<std::string, BasicVariantType>>;
96
James Feist3cb5fec2018-01-23 14:41:51 -080097boost::container::flat_map<
98 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070099 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -0800100 DBUS_PROBE_OBJECTS;
101std::vector<std::string> PASSED_PROBES;
102
James Feistd58879a2019-09-11 11:26:07 -0700103// store reference to all interfaces so we can destroy them later
104boost::container::flat_map<
105 std::string, std::vector<std::shared_ptr<sdbusplus::asio::dbus_interface>>>
106 inventory;
107
James Feist3cb5fec2018-01-23 14:41:51 -0800108// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700109std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700110static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800111
Johnathan Mantey2015f752019-03-26 15:22:31 -0700112const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
113const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800114
James Feista465ccc2019-02-08 12:51:01 -0800115void registerCallbacks(boost::asio::io_service& io,
116 std::vector<sdbusplus::bus::match::match>& dbusMatches,
117 nlohmann::json& systemConfiguration,
118 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800119
James Feistd58879a2019-09-11 11:26:07 -0700120static std::shared_ptr<sdbusplus::asio::dbus_interface>
121 createInterface(sdbusplus::asio::object_server& objServer,
122 const std::string& path, const std::string& interface,
123 const std::string& parent)
124{
125 return inventory[parent].emplace_back(
126 objServer.add_interface(path, interface));
127}
128
James Feist3cb5fec2018-01-23 14:41:51 -0800129// calls the mapper to find all exposed objects of an interface type
130// and creates a vector<flat_map> that contains all the key value pairs
131// getManagedObjects
James Feist787c3c32019-11-07 14:42:58 -0800132void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>& probeVector,
James Feist8f2710a2018-05-09 17:18:55 -0700133 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feist787c3c32019-11-07 14:42:58 -0800134 boost::container::flat_set<std::string>& interfaces)
James Feist3cb5fec2018-01-23 14:41:51 -0800135{
James Feist8f2710a2018-05-09 17:18:55 -0700136
James Feist3cb5fec2018-01-23 14:41:51 -0800137 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700138 connection->async_method_call(
James Feist787c3c32019-11-07 14:42:58 -0800139 [connection, interfaces,
140 probeVector](boost::system::error_code& ec,
141 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700142 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700143 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700144 {
James Feist0de40152018-07-25 11:56:12 -0700145 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700146 {
James Feist0de40152018-07-25 11:56:12 -0700147 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700148 }
James Feist0de40152018-07-25 11:56:12 -0700149 std::cerr << "Error communicating to mapper.\n";
150
151 // if we can't communicate to the mapper something is very wrong
152 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700153 }
James Feist787c3c32019-11-07 14:42:58 -0800154
155 for (auto& object : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800156 {
James Feist787c3c32019-11-07 14:42:58 -0800157 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700158 {
James Feist787c3c32019-11-07 14:42:58 -0800159 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700160 }
James Feist3cb5fec2018-01-23 14:41:51 -0800161 }
James Feist787c3c32019-11-07 14:42:58 -0800162
James Feist63845bf2019-01-24 12:19:51 -0800163 if (interfaceConnections.empty())
164 {
James Feist63845bf2019-01-24 12:19:51 -0800165 return;
166 }
James Feist8f2710a2018-05-09 17:18:55 -0700167 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800168 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700169 {
170 connection->async_method_call(
James Feist787c3c32019-11-07 14:42:58 -0800171 [conn, interfaces,
172 probeVector](boost::system::error_code& errc,
173 const ManagedObjectType& managedInterface) {
James Feist98132792019-07-09 13:29:09 -0700174 if (errc)
James Feist8f2710a2018-05-09 17:18:55 -0700175 {
176 std::cerr
177 << "error getting managed object for device "
178 << conn << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700179 return;
180 }
James Feista465ccc2019-02-08 12:51:01 -0800181 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700182 {
James Feist787c3c32019-11-07 14:42:58 -0800183 // we could match multiple interfaces with one owner
184 for (auto& [interface, object] :
185 interfaceManagedObj.second)
James Feist8f2710a2018-05-09 17:18:55 -0700186 {
James Feist787c3c32019-11-07 14:42:58 -0800187 auto ifaceObjFind = interfaces.find(interface);
188
189 if (ifaceObjFind != interfaces.end())
190 {
191 DBUS_PROBE_OBJECTS[interface].emplace_back(
192 object);
193 }
James Feist8f2710a2018-05-09 17:18:55 -0700194 }
195 }
James Feist8f2710a2018-05-09 17:18:55 -0700196 },
James Feist787c3c32019-11-07 14:42:58 -0800197 conn, "/", "org.freedesktop.DBus.ObjectManager",
James Feist8f2710a2018-05-09 17:18:55 -0700198 "GetManagedObjects");
199 }
200 },
201 "xyz.openbmc_project.ObjectMapper",
202 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700203 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800204 interfaces);
James Feist3cb5fec2018-01-23 14:41:51 -0800205}
James Feist8f2710a2018-05-09 17:18:55 -0700206// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700207bool probeDbus(const std::string& interface,
208 const std::map<std::string, nlohmann::json>& matches,
209 FoundDeviceT& devices, bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800210{
James Feista465ccc2019-02-08 12:51:01 -0800211 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
212 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800213 if (dbusObject.empty())
214 {
James Feist8f2710a2018-05-09 17:18:55 -0700215 foundProbe = false;
216 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800217 }
218 foundProbe = true;
219
220 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800221 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800222 {
223 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800224 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800225 {
226 auto deviceValue = device.find(match.first);
227 if (deviceValue != device.end())
228 {
229 switch (match.second.type())
230 {
James Feist9eb0b582018-04-27 12:15:46 -0700231 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800232 {
James Feist9eb0b582018-04-27 12:15:46 -0700233 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700234 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700235
236 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800237 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700238 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700239 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700240 {
241 deviceMatches = false;
242 break;
243 }
James Feist3cb5fec2018-01-23 14:41:51 -0800244 break;
245 }
James Feist9eb0b582018-04-27 12:15:46 -0700246 case nlohmann::json::value_t::boolean:
247 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800248 {
James Feista465ccc2019-02-08 12:51:01 -0800249 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700250 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800251
James Feist9eb0b582018-04-27 12:15:46 -0700252 if (probeValue != match.second.get<unsigned int>())
253 {
254 deviceMatches = false;
255 }
256 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800257 }
James Feist9eb0b582018-04-27 12:15:46 -0700258 case nlohmann::json::value_t::number_integer:
259 {
James Feista465ccc2019-02-08 12:51:01 -0800260 int probeValue = std::visit(VariantToIntVisitor(),
261 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800262
James Feist9eb0b582018-04-27 12:15:46 -0700263 if (probeValue != match.second.get<int>())
264 {
265 deviceMatches = false;
266 }
267 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800268 }
James Feist9eb0b582018-04-27 12:15:46 -0700269 case nlohmann::json::value_t::number_float:
270 {
James Feista465ccc2019-02-08 12:51:01 -0800271 float probeValue = std::visit(VariantToFloatVisitor(),
272 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700273
274 if (probeValue != match.second.get<float>())
275 {
276 deviceMatches = false;
277 }
278 break;
279 }
James Feist0eb40352019-04-09 14:44:04 -0700280 default:
281 {
282 std::cerr << "unexpected dbus probe type "
283 << match.second.type_name() << "\n";
284 }
James Feist3cb5fec2018-01-23 14:41:51 -0800285 }
286 }
287 else
288 {
289 deviceMatches = false;
290 break;
291 }
292 }
293 if (deviceMatches)
294 {
James Feistf5125b02019-06-06 11:27:43 -0700295 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800296 foundMatch = true;
297 deviceMatches = false; // for next iteration
298 }
299 }
300 return foundMatch;
301}
302
303// default probe entry point, iterates a list looking for specific types to
304// call specific probe functions
305bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800306 const std::vector<std::string>& probeCommand,
James Feist08a5b172019-08-28 14:47:47 -0700307 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
308 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800309{
310 const static std::regex command(R"(\((.*)\))");
311 std::smatch match;
312 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700313 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800314 bool cur = true;
315 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700316 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800317
James Feista465ccc2019-02-08 12:51:01 -0800318 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800319 {
320 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800321 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800322 cmp_str>::const_iterator probeType;
323
324 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700325 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800326 {
327 if (probe.find(probeType->first) != std::string::npos)
328 {
329 foundProbe = true;
330 break;
331 }
332 }
333 if (foundProbe)
334 {
335 switch (probeType->second)
336 {
James Feist9eb0b582018-04-27 12:15:46 -0700337 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800338 {
James Feiste31e00a2019-07-24 10:45:43 -0700339 cur = false;
340 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800341 }
James Feist9eb0b582018-04-27 12:15:46 -0700342 case probe_type_codes::TRUE_T:
343 {
James Feiste31e00a2019-07-24 10:45:43 -0700344 cur = true;
345 break;
James Feist9eb0b582018-04-27 12:15:46 -0700346 }
347 case probe_type_codes::MATCH_ONE:
348 {
349 // set current value to last, this probe type shouldn't
350 // affect the outcome
351 cur = ret;
352 matchOne = true;
353 break;
354 }
355 /*case probe_type_codes::AND:
356 break;
357 case probe_type_codes::OR:
358 break;
359 // these are no-ops until the last command switch
360 */
361 case probe_type_codes::FOUND:
362 {
363 if (!std::regex_search(probe, match, command))
364 {
James Feist0eb40352019-04-09 14:44:04 -0700365 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700366 << "\n";
367 return false;
368 }
369 std::string commandStr = *(match.begin() + 1);
370 boost::replace_all(commandStr, "'", "");
371 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
372 commandStr) != PASSED_PROBES.end());
373 break;
374 }
James Feist0eb40352019-04-09 14:44:04 -0700375 default:
376 {
377 break;
378 }
James Feist3cb5fec2018-01-23 14:41:51 -0800379 }
380 }
381 // look on dbus for object
382 else
383 {
384 if (!std::regex_search(probe, match, command))
385 {
James Feist0eb40352019-04-09 14:44:04 -0700386 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800387 return false;
388 }
389 std::string commandStr = *(match.begin() + 1);
390 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700391 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800392 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800393 auto json = nlohmann::json::parse(commandStr, nullptr, false);
394 if (json.is_discarded())
395 {
James Feist0eb40352019-04-09 14:44:04 -0700396 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800397 return false;
398 }
399 // we can match any (string, variant) property. (string, string)
400 // does a regex
401 std::map<std::string, nlohmann::json> dbusProbeMap =
402 json.get<std::map<std::string, nlohmann::json>>();
403 auto findStart = probe.find("(");
404 if (findStart == std::string::npos)
405 {
406 return false;
407 }
408 std::string probeInterface = probe.substr(0, findStart);
409 cur =
410 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
411 }
412
413 // some functions like AND and OR only take affect after the
414 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700415 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800416 {
James Feist54a0dca2019-06-26 10:34:54 -0700417 ret = cur && ret;
418 }
419 else if (lastCommand == probe_type_codes::OR)
420 {
421 ret = cur || ret;
422 }
423
424 if (first)
425 {
426 ret = cur;
427 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800428 }
429 lastCommand = probeType != PROBE_TYPES.end()
430 ? probeType->second
431 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800432 }
433
434 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800435 if (ret && foundDevs.size() == 0)
436 {
James Feist08a5b172019-08-28 14:47:47 -0700437 foundDevs.emplace_back(
438 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800439 }
James Feist0eb40352019-04-09 14:44:04 -0700440 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700441 {
James Feist71f295f2019-06-20 13:35:12 -0700442 // match the last one
443 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700444 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700445
446 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700447 }
James Feist3cb5fec2018-01-23 14:41:51 -0800448 return ret;
449}
James Feist8f2710a2018-05-09 17:18:55 -0700450// this class finds the needed dbus fields and on destruction runs the probe
451struct PerformProbe : std::enable_shared_from_this<PerformProbe>
452{
James Feist3cb5fec2018-01-23 14:41:51 -0800453
James Feist08a5b172019-08-28 14:47:47 -0700454 PerformProbe(const std::vector<std::string>& probeCommand,
455 std::function<void(FoundDeviceT&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700456 _probeCommand(probeCommand),
457 _callback(std::move(callback))
458 {
459 }
460 ~PerformProbe()
461 {
James Feist08a5b172019-08-28 14:47:47 -0700462 FoundDeviceT foundDevs;
James Feist0eb40352019-04-09 14:44:04 -0700463 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700464 {
James Feist0eb40352019-04-09 14:44:04 -0700465 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700466 }
467 }
James Feist8f2710a2018-05-09 17:18:55 -0700468 std::vector<std::string> _probeCommand;
James Feist08a5b172019-08-28 14:47:47 -0700469 std::function<void(FoundDeviceT&)> _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700470};
471
472// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800473bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800474{
James Feist1df06a42019-04-11 14:23:04 -0700475 std::filesystem::create_directory(configurationOutDir);
476 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700477 if (!output.good())
478 {
479 return false;
480 }
James Feist1b2e2242018-01-30 13:45:19 -0800481 output << systemConfiguration.dump(4);
482 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700483 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700484}
James Feist1b2e2242018-01-30 13:45:19 -0800485
James Feist97a63f12018-05-17 13:50:57 -0700486template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800487bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
488 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700489{
490 try
491 {
492 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800493 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700494 ref = value;
495 return true;
496 }
James Feist98132792019-07-09 13:29:09 -0700497 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700498 {
499 return false;
500 }
501}
James Feistbb43d022018-06-12 15:44:33 -0700502
James Feistebcc26b2019-03-22 12:30:43 -0700503// template function to add array as dbus property
504template <typename PropertyType>
505void addArrayToDbus(const std::string& name, const nlohmann::json& array,
506 sdbusplus::asio::dbus_interface* iface,
507 sdbusplus::asio::PropertyPermission permission,
508 nlohmann::json& systemConfiguration,
509 const std::string& jsonPointerString)
510{
511 std::vector<PropertyType> values;
512 for (const auto& property : array)
513 {
514 auto ptr = property.get_ptr<const PropertyType*>();
515 if (ptr != nullptr)
516 {
517 values.emplace_back(*ptr);
518 }
519 }
520
521 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
522 {
523 iface->register_property(name, values);
524 }
525 else
526 {
527 iface->register_property(
528 name, values,
529 [&systemConfiguration,
530 jsonPointerString{std::string(jsonPointerString)}](
531 const std::vector<PropertyType>& newVal,
532 std::vector<PropertyType>& val) {
533 val = newVal;
534 if (!setJsonFromPointer(jsonPointerString, val,
535 systemConfiguration))
536 {
537 std::cerr << "error setting json field\n";
538 return -1;
539 }
540 if (!writeJsonFiles(systemConfiguration))
541 {
542 std::cerr << "error setting json file\n";
543 return -1;
544 }
545 return 1;
546 });
547 }
548}
549
James Feistbb43d022018-06-12 15:44:33 -0700550template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800551void addProperty(const std::string& propertyName, const PropertyType& value,
552 sdbusplus::asio::dbus_interface* iface,
553 nlohmann::json& systemConfiguration,
554 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700555 sdbusplus::asio::PropertyPermission permission)
556{
557 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
558 {
559 iface->register_property(propertyName, value);
560 return;
561 }
James Feist68500ff2018-08-08 15:40:42 -0700562 iface->register_property(
563 propertyName, value,
564 [&systemConfiguration,
565 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800566 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700567 val = newVal;
568 if (!setJsonFromPointer(jsonPointerString, val,
569 systemConfiguration))
570 {
571 std::cerr << "error setting json field\n";
572 return -1;
573 }
James Feistc6248a52018-08-14 10:09:45 -0700574 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700575 {
576 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700577 return -1;
578 }
579 return 1;
580 });
581}
582
583void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800584 const std::string& jsonPointerPath,
585 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
586 sdbusplus::asio::object_server& objServer,
587 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700588{
589 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
590 iface->register_method(
591 "Delete", [&objServer, &systemConfiguration, interface,
592 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700593 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700594 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700595 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700596 {
597 // this technically can't happen as the pointer is pointing to
598 // us
599 throw DBusInternalError();
600 }
601 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feist98132792019-07-09 13:29:09 -0700602 if (!objServer.remove_interface(dbusInterface))
James Feistc6248a52018-08-14 10:09:45 -0700603 {
604 std::cerr << "Can't delete interface " << jsonPointerPath
605 << "\n";
606 throw DBusInternalError();
607 }
608 systemConfiguration[ptr] = nullptr;
609
610 if (!writeJsonFiles(systemConfiguration))
611 {
612 std::cerr << "error setting json file\n";
613 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700614 }
James Feistbb43d022018-06-12 15:44:33 -0700615 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700616 });
James Feistbb43d022018-06-12 15:44:33 -0700617}
618
James Feist1b2e2242018-01-30 13:45:19 -0800619// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700620void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800621 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
622 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
623 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700624 sdbusplus::asio::PropertyPermission permission =
625 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800626{
James Feista465ccc2019-02-08 12:51:01 -0800627 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800628 {
James Feist8f2710a2018-05-09 17:18:55 -0700629 auto type = dictPair.value().type();
630 bool array = false;
631 if (dictPair.value().type() == nlohmann::json::value_t::array)
632 {
633 array = true;
634 if (!dictPair.value().size())
635 {
636 continue;
637 }
638 type = dictPair.value()[0].type();
639 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800640 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700641 {
642 if (arrayItem.type() != type)
643 {
644 isLegal = false;
645 break;
646 }
647 }
648 if (!isLegal)
649 {
650 std::cerr << "dbus format error" << dictPair.value() << "\n";
651 continue;
652 }
James Feista218ddb2019-04-11 14:01:31 -0700653 }
654 if (type == nlohmann::json::value_t::object)
655 {
656 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700657 }
James Feist97a63f12018-05-17 13:50:57 -0700658 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700659 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
660 {
661 // all setable numbers are doubles as it is difficult to always
662 // create a configuration file with all whole numbers as decimals
663 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700664 if (array)
665 {
666 if (dictPair.value()[0].is_number())
667 {
668 type = nlohmann::json::value_t::number_float;
669 }
670 }
671 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700672 {
673 type = nlohmann::json::value_t::number_float;
674 }
675 }
676
James Feist8f2710a2018-05-09 17:18:55 -0700677 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800678 {
James Feist9eb0b582018-04-27 12:15:46 -0700679 case (nlohmann::json::value_t::boolean):
680 {
James Feist8f2710a2018-05-09 17:18:55 -0700681 if (array)
682 {
683 // todo: array of bool isn't detected correctly by
684 // sdbusplus, change it to numbers
685 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700686 iface.get(), permission,
687 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700688 }
James Feistbb43d022018-06-12 15:44:33 -0700689
James Feist97a63f12018-05-17 13:50:57 -0700690 else
691 {
James Feistbb43d022018-06-12 15:44:33 -0700692 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700693 iface.get(), systemConfiguration, key,
694 permission);
James Feist97a63f12018-05-17 13:50:57 -0700695 }
James Feist9eb0b582018-04-27 12:15:46 -0700696 break;
697 }
698 case (nlohmann::json::value_t::number_integer):
699 {
James Feist8f2710a2018-05-09 17:18:55 -0700700 if (array)
701 {
702 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700703 iface.get(), permission,
704 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700705 }
706 else
707 {
James Feistbb43d022018-06-12 15:44:33 -0700708 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700709 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700710 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700711 }
James Feist9eb0b582018-04-27 12:15:46 -0700712 break;
713 }
714 case (nlohmann::json::value_t::number_unsigned):
715 {
James Feist8f2710a2018-05-09 17:18:55 -0700716 if (array)
717 {
718 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700719 iface.get(), permission,
720 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700721 }
722 else
723 {
James Feistbb43d022018-06-12 15:44:33 -0700724 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700725 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700726 systemConfiguration, key,
727 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700728 }
James Feist9eb0b582018-04-27 12:15:46 -0700729 break;
730 }
731 case (nlohmann::json::value_t::number_float):
732 {
James Feist8f2710a2018-05-09 17:18:55 -0700733 if (array)
734 {
735 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700736 iface.get(), permission,
737 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700738 }
James Feistbb43d022018-06-12 15:44:33 -0700739
James Feist97a63f12018-05-17 13:50:57 -0700740 else
741 {
James Feistbb43d022018-06-12 15:44:33 -0700742 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700743 iface.get(), systemConfiguration, key,
744 permission);
James Feist97a63f12018-05-17 13:50:57 -0700745 }
James Feist9eb0b582018-04-27 12:15:46 -0700746 break;
747 }
748 case (nlohmann::json::value_t::string):
749 {
James Feist8f2710a2018-05-09 17:18:55 -0700750 if (array)
751 {
James Feistebcc26b2019-03-22 12:30:43 -0700752 addArrayToDbus<std::string>(
753 dictPair.key(), dictPair.value(), iface.get(),
754 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700755 }
756 else
757 {
James Feistc6248a52018-08-14 10:09:45 -0700758 addProperty(
759 dictPair.key(), dictPair.value().get<std::string>(),
760 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700761 }
James Feist9eb0b582018-04-27 12:15:46 -0700762 break;
763 }
James Feist0eb40352019-04-09 14:44:04 -0700764 default:
765 {
James Feista218ddb2019-04-11 14:01:31 -0700766 std::cerr << "Unexpected json type in system configuration "
767 << dictPair.key() << ": "
768 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700769 break;
770 }
James Feist1b2e2242018-01-30 13:45:19 -0800771 }
772 }
James Feistc6248a52018-08-14 10:09:45 -0700773 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
774 {
775 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
776 systemConfiguration);
777 }
James Feist8f2710a2018-05-09 17:18:55 -0700778 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800779}
780
James Feista465ccc2019-02-08 12:51:01 -0800781sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700782{
783 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
784 interface) != settableInterfaces.end()
785 ? sdbusplus::asio::PropertyPermission::readWrite
786 : sdbusplus::asio::PropertyPermission::readOnly;
787}
788
James Feista465ccc2019-02-08 12:51:01 -0800789void createAddObjectMethod(const std::string& jsonPointerPath,
790 const std::string& path,
791 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700792 sdbusplus::asio::object_server& objServer,
793 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700794{
James Feistd58879a2019-09-11 11:26:07 -0700795 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
796 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700797
798 iface->register_method(
799 "AddObject",
800 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700801 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
802 board](const boost::container::flat_map<std::string, JsonVariantType>&
803 data) {
James Feist68500ff2018-08-08 15:40:42 -0700804 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800805 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700806 auto findExposes = base.find("Exposes");
807
808 if (findExposes == base.end())
809 {
810 throw std::invalid_argument("Entity must have children.");
811 }
812
813 // this will throw invalid-argument to sdbusplus if invalid json
814 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800815 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700816 {
James Feista465ccc2019-02-08 12:51:01 -0800817 nlohmann::json& newJson = newData[item.first];
818 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
819 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700820 }
821
822 auto findName = newData.find("Name");
823 auto findType = newData.find("Type");
824 if (findName == newData.end() || findType == newData.end())
825 {
826 throw std::invalid_argument("AddObject missing Name or Type");
827 }
James Feista465ccc2019-02-08 12:51:01 -0800828 const std::string* type = findType->get_ptr<const std::string*>();
829 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700830 if (type == nullptr || name == nullptr)
831 {
832 throw std::invalid_argument("Type and Name must be a string.");
833 }
834
835 size_t lastIndex = 0;
836 // we add in the "exposes"
837 for (; lastIndex < findExposes->size(); lastIndex++)
838 {
839 if (findExposes->at(lastIndex)["Name"] == *name &&
840 findExposes->at(lastIndex)["Type"] == *type)
841 {
842 throw std::invalid_argument(
843 "Field already in JSON, not adding");
844 }
845 lastIndex++;
846 }
847
848 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
849 *type + ".json");
850 // todo(james) we might want to also make a list of 'can add'
851 // interfaces but for now I think the assumption if there is a
852 // schema avaliable that it is allowed to update is fine
853 if (!schemaFile.good())
854 {
855 throw std::invalid_argument(
856 "No schema avaliable, cannot validate.");
857 }
858 nlohmann::json schema =
859 nlohmann::json::parse(schemaFile, nullptr, false);
860 if (schema.is_discarded())
861 {
862 std::cerr << "Schema not legal" << *type << ".json\n";
863 throw DBusInternalError();
864 }
865 if (!validateJson(schema, newData))
866 {
867 throw std::invalid_argument("Data does not match schema");
868 }
869
James Feist16a02f22019-05-13 15:21:37 -0700870 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700871 if (!writeJsonFiles(systemConfiguration))
872 {
873 std::cerr << "Error writing json files\n";
874 throw DBusInternalError();
875 }
876 std::string dbusName = *name;
877
878 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700879 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700880
881 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
882 createInterface(objServer, path + "/" + dbusName,
883 "xyz.openbmc_project.Configuration." + *type,
884 board);
James Feist68500ff2018-08-08 15:40:42 -0700885 // permission is read-write, as since we just created it, must be
886 // runtime modifiable
887 populateInterfaceFromJson(
888 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700889 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700890 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700891 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700892 });
893 iface->initialize();
894}
895
James Feista465ccc2019-02-08 12:51:01 -0800896void postToDbus(const nlohmann::json& newConfiguration,
897 nlohmann::json& systemConfiguration,
898 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800899
James Feist1b2e2242018-01-30 13:45:19 -0800900{
James Feist97a63f12018-05-17 13:50:57 -0700901 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800902 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800903 {
James Feistf1b14142019-04-10 15:22:09 -0700904 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700905 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700906 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700907 // loop through newConfiguration, but use values from system
908 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700909 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700910 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800911 std::string boardType;
912 if (findBoardType != boardValues.end() &&
913 findBoardType->type() == nlohmann::json::value_t::string)
914 {
915 boardType = findBoardType->get<std::string>();
916 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700917 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800918 }
919 else
920 {
921 std::cerr << "Unable to find type for " << boardKey
922 << " reverting to Chassis.\n";
923 boardType = "Chassis";
924 }
James Feist11be6672018-04-06 14:05:32 -0700925 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800926
927 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700928 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700929 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
930 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800931
James Feistd58879a2019-09-11 11:26:07 -0700932 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
933 createInterface(objServer, boardName,
934 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700935
James Feistd58879a2019-09-11 11:26:07 -0700936 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
937 createInterface(objServer, boardName,
938 "xyz.openbmc_project.Inventory.Item." + boardType,
939 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700940
James Feist68500ff2018-08-08 15:40:42 -0700941 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700942 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700943
James Feist97a63f12018-05-17 13:50:57 -0700944 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700945 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700946 jsonPointerPath += "/";
947 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800948 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700949 {
950 if (boardField.value().type() == nlohmann::json::value_t::object)
951 {
James Feistd58879a2019-09-11 11:26:07 -0700952 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
953 createInterface(objServer, boardName, boardField.key(),
954 boardKeyOrig);
955
James Feistc6248a52018-08-14 10:09:45 -0700956 populateInterfaceFromJson(systemConfiguration,
957 jsonPointerPath + boardField.key(),
958 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700959 }
960 }
James Feist97a63f12018-05-17 13:50:57 -0700961
James Feist1e3e6982018-08-03 16:09:28 -0700962 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800963 if (exposes == boardValues.end())
964 {
965 continue;
966 }
James Feist97a63f12018-05-17 13:50:57 -0700967 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700968 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700969
970 // store the board level pointer so we can modify it on the way down
971 std::string jsonPointerPathBoard = jsonPointerPath;
972 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800973 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800974 {
James Feist97a63f12018-05-17 13:50:57 -0700975 exposesIndex++;
976 jsonPointerPath = jsonPointerPathBoard;
977 jsonPointerPath += std::to_string(exposesIndex);
978
James Feistd63d18a2018-07-19 15:23:45 -0700979 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800980 if (findName == item.end())
981 {
982 std::cerr << "cannot find name in field " << item << "\n";
983 continue;
984 }
James Feist1e3e6982018-08-03 16:09:28 -0700985 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800986 // if status is not found it is assumed to be status = 'okay'
987 if (findStatus != item.end())
988 {
989 if (*findStatus == "disabled")
990 {
991 continue;
992 }
993 }
James Feistd63d18a2018-07-19 15:23:45 -0700994 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800995 std::string itemType;
996 if (findType != item.end())
997 {
998 itemType = findType->get<std::string>();
999 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001000 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1001 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001002 }
1003 else
1004 {
1005 itemType = "unknown";
1006 }
1007 std::string itemName = findName->get<std::string>();
1008 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001009 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001010
James Feistd58879a2019-09-11 11:26:07 -07001011 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1012 createInterface(objServer, boardName + "/" + itemName,
1013 "xyz.openbmc_project.Configuration." + itemType,
1014 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001015
James Feist97a63f12018-05-17 13:50:57 -07001016 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001017 itemIface, item, objServer,
1018 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001019
James Feista465ccc2019-02-08 12:51:01 -08001020 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001021 {
James Feist97a63f12018-05-17 13:50:57 -07001022 jsonPointerPath = jsonPointerPathBoard +
1023 std::to_string(exposesIndex) + "/" +
1024 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001025 if (objectPair.value().type() ==
1026 nlohmann::json::value_t::object)
1027 {
James Feistd58879a2019-09-11 11:26:07 -07001028 std::shared_ptr<sdbusplus::asio::dbus_interface>
1029 objectIface = createInterface(
1030 objServer, boardName + "/" + itemName,
1031 "xyz.openbmc_project.Configuration." + itemType +
1032 "." + objectPair.key(),
1033 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001034
1035 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001036 systemConfiguration, jsonPointerPath, objectIface,
1037 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001038 }
1039 else if (objectPair.value().type() ==
1040 nlohmann::json::value_t::array)
1041 {
1042 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001043 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001044 {
James Feist8f2710a2018-05-09 17:18:55 -07001045 continue;
1046 }
1047 bool isLegal = true;
1048 auto type = objectPair.value()[0].type();
1049 if (type != nlohmann::json::value_t::object)
1050 {
1051 continue;
1052 }
1053
1054 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001055 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001056 {
1057 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001058 {
James Feist8f2710a2018-05-09 17:18:55 -07001059 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001060 break;
1061 }
James Feist8f2710a2018-05-09 17:18:55 -07001062 }
1063 if (!isLegal)
1064 {
1065 std::cerr << "dbus format error" << objectPair.value()
1066 << "\n";
1067 break;
1068 }
1069
James Feista465ccc2019-02-08 12:51:01 -08001070 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001071 {
James Feist97a63f12018-05-17 13:50:57 -07001072
James Feistd58879a2019-09-11 11:26:07 -07001073 std::shared_ptr<sdbusplus::asio::dbus_interface>
1074 objectIface = createInterface(
1075 objServer, boardName + "/" + itemName,
1076 "xyz.openbmc_project.Configuration." +
1077 itemType + "." + objectPair.key() +
1078 std::to_string(index),
1079 boardKeyOrig);
1080
James Feistc6248a52018-08-14 10:09:45 -07001081 populateInterfaceFromJson(
1082 systemConfiguration,
1083 jsonPointerPath + "/" + std::to_string(index),
1084 objectIface, arrayItem, objServer,
1085 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001086 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001087 }
1088 }
1089 }
1090 }
1091 }
1092}
1093
James Feist8f2710a2018-05-09 17:18:55 -07001094// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001095bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001096{
1097 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001098 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001099 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1100 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001101 {
1102 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001103 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001104 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001105 }
James Feistb4383f42018-08-06 16:54:10 -07001106
1107 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1108 globalSchema);
1109 if (!schemaStream.good())
1110 {
1111 std::cerr
1112 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1113 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001114 return false;
James Feistb4383f42018-08-06 16:54:10 -07001115 }
1116 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1117 if (schema.is_discarded())
1118 {
1119 std::cerr
1120 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1121 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001122 return false;
James Feistb4383f42018-08-06 16:54:10 -07001123 }
1124
James Feista465ccc2019-02-08 12:51:01 -08001125 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001126 {
1127 std::ifstream jsonStream(jsonPath.c_str());
1128 if (!jsonStream.good())
1129 {
1130 std::cerr << "unable to open " << jsonPath.string() << "\n";
1131 continue;
1132 }
1133 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1134 if (data.is_discarded())
1135 {
1136 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1137 continue;
1138 }
James Feist8da99192019-01-24 08:20:16 -08001139 /*
1140 * todo(james): reenable this once less things are in flight
1141 *
James Feistb4383f42018-08-06 16:54:10 -07001142 if (!validateJson(schema, data))
1143 {
1144 std::cerr << "Error validating " << jsonPath.string() << "\n";
1145 continue;
1146 }
James Feist8da99192019-01-24 08:20:16 -08001147 */
James Feistb4383f42018-08-06 16:54:10 -07001148
James Feist3cb5fec2018-01-23 14:41:51 -08001149 if (data.type() == nlohmann::json::value_t::array)
1150 {
James Feista465ccc2019-02-08 12:51:01 -08001151 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001152 {
1153 configurations.emplace_back(d);
1154 }
1155 }
1156 else
1157 {
1158 configurations.emplace_back(data);
1159 }
1160 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001161 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001162}
James Feist3cb5fec2018-01-23 14:41:51 -08001163
James Feist8f2710a2018-05-09 17:18:55 -07001164struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001165{
James Feist75fdeeb2018-02-20 14:26:16 -08001166
James Feista465ccc2019-02-08 12:51:01 -08001167 PerformScan(nlohmann::json& systemConfiguration,
James Feist899e17f2019-09-13 11:46:29 -07001168 nlohmann::json& missingConfigurations,
James Feista465ccc2019-02-08 12:51:01 -08001169 std::list<nlohmann::json>& configurations,
1170 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001171 _systemConfiguration(systemConfiguration),
James Feist899e17f2019-09-13 11:46:29 -07001172 _missingConfigurations(missingConfigurations),
James Feist8f2710a2018-05-09 17:18:55 -07001173 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001174 {
James Feist8f2710a2018-05-09 17:18:55 -07001175 }
1176 void run()
1177 {
James Feist787c3c32019-11-07 14:42:58 -08001178 boost::container::flat_set<std::string> dbusProbeInterfaces;
1179 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
1180
James Feist8f2710a2018-05-09 17:18:55 -07001181 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001182 {
James Feist1e3e6982018-08-03 16:09:28 -07001183 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001184 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001185
James Feist1b2e2242018-01-30 13:45:19 -08001186 nlohmann::json probeCommand;
1187 // check for poorly formatted fields, probe must be an array
1188 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001189 {
1190 std::cerr << "configuration file missing probe:\n " << *it
1191 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001192 it = _configurations.erase(it);
1193 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001194 }
James Feist1b2e2242018-01-30 13:45:19 -08001195 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001196 {
1197 probeCommand = nlohmann::json::array();
1198 probeCommand.push_back(*findProbe);
1199 }
1200 else
1201 {
1202 probeCommand = *findProbe;
1203 }
James Feist1b2e2242018-01-30 13:45:19 -08001204
1205 if (findName == it->end())
1206 {
1207 std::cerr << "configuration file missing name:\n " << *it
1208 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001209 it = _configurations.erase(it);
1210 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001211 }
James Feistf1b14142019-04-10 15:22:09 -07001212 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001213
James Feistf1b14142019-04-10 15:22:09 -07001214 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1215 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001216 {
James Feist8f2710a2018-05-09 17:18:55 -07001217 it = _configurations.erase(it);
1218 continue;
1219 }
James Feistf1b14142019-04-10 15:22:09 -07001220 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001221
James Feist8f2710a2018-05-09 17:18:55 -07001222 // store reference to this to children to makes sure we don't get
1223 // destroyed too early
1224 auto thisRef = shared_from_this();
James Feist787c3c32019-11-07 14:42:58 -08001225 auto probePointer = std::make_shared<
James Feist08a5b172019-08-28 14:47:47 -07001226 PerformProbe>(probeCommand, [&, recordPtr, probeName, thisRef](
1227 FoundDeviceT& foundDevices) {
1228 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001229
James Feist08a5b172019-08-28 14:47:47 -07001230 PASSED_PROBES.push_back(probeName);
1231 size_t foundDeviceIdx = 1;
James Feist8f2710a2018-05-09 17:18:55 -07001232
James Feist08a5b172019-08-28 14:47:47 -07001233 for (auto& foundDevice : foundDevices)
1234 {
1235 nlohmann::json record = *recordPtr;
1236 std::string recordName;
1237 size_t hash = 0;
1238 if (foundDevice.size())
James Feist3cb5fec2018-01-23 14:41:51 -08001239 {
James Feist08a5b172019-08-28 14:47:47 -07001240 // use an array so alphabetical order from the
1241 // flat_map is maintained
1242 auto device = nlohmann::json::array();
1243 for (auto& devPair : foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001244 {
James Feist08a5b172019-08-28 14:47:47 -07001245 device.push_back(devPair.first);
1246 std::visit(
1247 [&device](auto&& v) { device.push_back(v); },
1248 devPair.second);
1249 }
1250 hash =
1251 std::hash<std::string>{}(probeName + device.dump());
1252 // hashes are hard to distinguish, use the
1253 // non-hashed version if we want debug
1254 if constexpr (DEBUG)
1255 {
1256 recordName = probeName + device.dump();
James Feist8f2710a2018-05-09 17:18:55 -07001257 }
James Feistf1b14142019-04-10 15:22:09 -07001258 else
1259 {
James Feist08a5b172019-08-28 14:47:47 -07001260 recordName = std::to_string(hash);
James Feistf1b14142019-04-10 15:22:09 -07001261 }
James Feist08a5b172019-08-28 14:47:47 -07001262 }
1263 else
1264 {
1265 recordName = probeName;
1266 }
James Feistf1b14142019-04-10 15:22:09 -07001267
James Feist08a5b172019-08-28 14:47:47 -07001268 auto fromLastJson = lastJson.find(recordName);
1269 if (fromLastJson != lastJson.end())
1270 {
1271 // keep user changes
1272 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001273 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001274 continue;
1275 }
James Feist1df06a42019-04-11 14:23:04 -07001276
James Feist08a5b172019-08-28 14:47:47 -07001277 // insert into configuration temporarily to be able to
1278 // reference ourselves
James Feistf1b14142019-04-10 15:22:09 -07001279
James Feist08a5b172019-08-28 14:47:47 -07001280 _systemConfiguration[recordName] = record;
1281
1282 for (auto keyPair = record.begin(); keyPair != record.end();
1283 keyPair++)
1284 {
1285 templateCharReplace(keyPair, foundDevice,
1286 foundDeviceIdx);
1287 }
1288
1289 auto findExpose = record.find("Exposes");
1290 if (findExpose == record.end())
1291 {
James Feistf1b14142019-04-10 15:22:09 -07001292 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001293 foundDeviceIdx++;
1294 continue;
1295 }
James Feistf1b14142019-04-10 15:22:09 -07001296
James Feist08a5b172019-08-28 14:47:47 -07001297 for (auto& expose : *findExpose)
1298 {
1299 for (auto keyPair = expose.begin();
1300 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001301 {
James Feist08a5b172019-08-28 14:47:47 -07001302
1303 templateCharReplace(keyPair, foundDevice,
1304 foundDeviceIdx);
1305
1306 // special case bind
1307 if (boost::starts_with(keyPair.key(), "Bind"))
James Feistf1b14142019-04-10 15:22:09 -07001308 {
James Feist08a5b172019-08-28 14:47:47 -07001309 if (keyPair.value().type() !=
1310 nlohmann::json::value_t::string)
James Feistf1b14142019-04-10 15:22:09 -07001311 {
James Feist08a5b172019-08-28 14:47:47 -07001312 std::cerr << "bind_ value must be of "
1313 "type string "
1314 << keyPair.key() << "\n";
1315 continue;
James Feistf1b14142019-04-10 15:22:09 -07001316 }
James Feist08a5b172019-08-28 14:47:47 -07001317 bool foundBind = false;
1318 std::string bind =
1319 keyPair.key().substr(sizeof("Bind") - 1);
1320
1321 for (auto& configurationPair :
1322 _systemConfiguration.items())
James Feist8f2710a2018-05-09 17:18:55 -07001323 {
James Feist08a5b172019-08-28 14:47:47 -07001324
1325 auto configListFind =
1326 configurationPair.value().find(
1327 "Exposes");
1328
1329 if (configListFind ==
1330 configurationPair.value().end() ||
1331 configListFind->type() !=
1332 nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001333 {
James Feist1b2e2242018-01-30 13:45:19 -08001334 continue;
1335 }
James Feist08a5b172019-08-28 14:47:47 -07001336 for (auto& exposedObject : *configListFind)
James Feist1b2e2242018-01-30 13:45:19 -08001337 {
James Feist08a5b172019-08-28 14:47:47 -07001338 std::string foundObjectName =
1339 (exposedObject)["Name"];
1340 if (boost::iequals(
1341 foundObjectName,
1342 keyPair.value()
1343 .get<std::string>()))
James Feist8f2710a2018-05-09 17:18:55 -07001344 {
James Feist08a5b172019-08-28 14:47:47 -07001345 exposedObject["Status"] = "okay";
1346 expose[bind] = exposedObject;
James Feist8f2710a2018-05-09 17:18:55 -07001347
James Feist08a5b172019-08-28 14:47:47 -07001348 foundBind = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001349 break;
1350 }
1351 }
James Feist08a5b172019-08-28 14:47:47 -07001352 if (foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001353 {
James Feist08a5b172019-08-28 14:47:47 -07001354 break;
James Feist3cb5fec2018-01-23 14:41:51 -08001355 }
1356 }
James Feist08a5b172019-08-28 14:47:47 -07001357 if (!foundBind)
1358 {
1359 std::cerr << "configuration file "
1360 "dependency error, "
1361 "could not find bind "
1362 << keyPair.value() << "\n";
1363 }
James Feist3cb5fec2018-01-23 14:41:51 -08001364 }
1365 }
1366 }
James Feist08a5b172019-08-28 14:47:47 -07001367 // overwrite ourselves with cleaned up version
1368 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001369 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001370
1371 foundDeviceIdx++;
1372 }
1373 });
James Feist787c3c32019-11-07 14:42:58 -08001374
1375 // parse out dbus probes by discarding other probe types, store in a
1376 // map
1377 for (const std::string& probe : probeCommand)
1378 {
1379 bool found = false;
1380 boost::container::flat_map<const char*, probe_type_codes,
1381 cmp_str>::const_iterator probeType;
1382 for (probeType = PROBE_TYPES.begin();
1383 probeType != PROBE_TYPES.end(); ++probeType)
1384 {
1385 if (probe.find(probeType->first) != std::string::npos)
1386 {
1387 found = true;
1388 break;
1389 }
1390 }
1391 if (found)
1392 {
1393 continue;
1394 }
1395 // syntax requires probe before first open brace
1396 auto findStart = probe.find("(");
1397 std::string interface = probe.substr(0, findStart);
1398 dbusProbeInterfaces.emplace(interface);
1399 dbusProbePointers.emplace_back(probePointer);
1400 }
James Feist8f2710a2018-05-09 17:18:55 -07001401 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001402 }
James Feist787c3c32019-11-07 14:42:58 -08001403
1404 // probe vector stores a shared_ptr to each PerformProbe that cares
1405 // about a dbus interface
1406 findDbusObjects(dbusProbePointers, SYSTEM_BUS, dbusProbeInterfaces);
James Feist3cb5fec2018-01-23 14:41:51 -08001407 }
James Feist75fdeeb2018-02-20 14:26:16 -08001408
James Feist8f2710a2018-05-09 17:18:55 -07001409 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001410 {
James Feist8f2710a2018-05-09 17:18:55 -07001411 if (_passed)
1412 {
1413 auto nextScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001414 _systemConfiguration, _missingConfigurations, _configurations,
1415 std::move(_callback));
James Feist8f2710a2018-05-09 17:18:55 -07001416 nextScan->run();
1417 }
1418 else
1419 {
1420 _callback();
1421 }
1422 }
James Feista465ccc2019-02-08 12:51:01 -08001423 nlohmann::json& _systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001424 nlohmann::json& _missingConfigurations;
James Feist8f2710a2018-05-09 17:18:55 -07001425 std::list<nlohmann::json> _configurations;
1426 std::function<void(void)> _callback;
James Feist8f2710a2018-05-09 17:18:55 -07001427 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001428 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001429};
James Feistc95cb142018-02-26 10:41:42 -08001430
James Feist1df06a42019-04-11 14:23:04 -07001431void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001432 nlohmann::json& systemConfiguration)
1433{
1434 static bool scannedPowerOff = false;
1435 static bool scannedPowerOn = false;
1436
James Feistfb00f392019-06-25 14:16:48 -07001437 if (systemConfiguration.empty() || lastJson.empty())
1438 {
1439 return; // not ready yet
1440 }
James Feist1df06a42019-04-11 14:23:04 -07001441 if (scannedPowerOn)
1442 {
1443 return;
1444 }
1445
1446 if (!isPowerOn() && scannedPowerOff)
1447 {
1448 return;
1449 }
1450
1451 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001452 timer.async_wait(
1453 [&systemConfiguration](const boost::system::error_code& ec) {
1454 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001455 {
James Feist1a996582019-05-14 15:10:06 -07001456 // we were cancelled
1457 return;
1458 }
1459
1460 bool powerOff = !isPowerOn();
1461 for (const auto& item : lastJson.items())
1462 {
1463 if (systemConfiguration.find(item.key()) ==
1464 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001465 {
James Feist1a996582019-05-14 15:10:06 -07001466 bool isDetectedPowerOn = false;
1467 auto powerState = item.value().find("PowerState");
1468 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001469 {
James Feist1a996582019-05-14 15:10:06 -07001470 auto ptr = powerState->get_ptr<const std::string*>();
1471 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001472 {
James Feist1a996582019-05-14 15:10:06 -07001473 if (*ptr == "On" || *ptr == "BiosPost")
1474 {
1475 isDetectedPowerOn = true;
1476 }
James Feist1df06a42019-04-11 14:23:04 -07001477 }
1478 }
James Feist1a996582019-05-14 15:10:06 -07001479 if (powerOff && isDetectedPowerOn)
1480 {
1481 // power not on yet, don't know if it's there or not
1482 continue;
1483 }
1484 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1485 {
1486 // already logged it when power was off
1487 continue;
1488 }
James Feist1df06a42019-04-11 14:23:04 -07001489
James Feist1a996582019-05-14 15:10:06 -07001490 logDeviceRemoved(item.value());
1491 }
James Feist1df06a42019-04-11 14:23:04 -07001492 }
James Feist1a996582019-05-14 15:10:06 -07001493 scannedPowerOff = true;
1494 if (!powerOff)
1495 {
1496 scannedPowerOn = true;
1497 }
1498 });
James Feist1df06a42019-04-11 14:23:04 -07001499}
1500
James Feist8f2710a2018-05-09 17:18:55 -07001501// main properties changed entry
1502void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001503 boost::asio::io_service& io,
1504 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1505 nlohmann::json& systemConfiguration,
1506 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001507{
1508 static boost::asio::deadline_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001509 static size_t instance = 0;
1510 instance++;
1511 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001512
James Feist899e17f2019-09-13 11:46:29 -07001513 timer.expires_from_now(boost::posix_time::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001514
1515 // setup an async wait as we normally get flooded with new requests
James Feist899e17f2019-09-13 11:46:29 -07001516 timer.async_wait([&systemConfiguration, &dbusMatches, &io, &objServer,
1517 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001518 if (ec == boost::asio::error::operation_aborted)
1519 {
1520 // we were cancelled
1521 return;
1522 }
1523 else if (ec)
1524 {
1525 std::cerr << "async wait error " << ec << "\n";
1526 return;
1527 }
1528
1529 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001530 auto missingConfigurations = std::make_shared<nlohmann::json>();
1531 *missingConfigurations = systemConfiguration;
1532
James Feist8f2710a2018-05-09 17:18:55 -07001533 DBUS_PROBE_OBJECTS.clear();
James Feist899e17f2019-09-13 11:46:29 -07001534 PASSED_PROBES.clear();
James Feist8f2710a2018-05-09 17:18:55 -07001535
1536 std::list<nlohmann::json> configurations;
1537 if (!findJsonFiles(configurations))
1538 {
1539 std::cerr << "cannot find json files\n";
1540 return;
1541 }
1542
1543 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001544 systemConfiguration, *missingConfigurations, configurations,
1545 [&systemConfiguration, &io, &objServer, &dbusMatches, count,
1546 oldConfiguration, missingConfigurations]() {
1547 // this is something that since ac has been applied to the bmc
1548 // we saw, and we no longer see it
1549 bool powerOff = !isPowerOn();
1550 for (const auto& item : missingConfigurations->items())
1551 {
1552 bool isDetectedPowerOn = false;
1553 auto powerState = item.value().find("PowerState");
1554 if (powerState != item.value().end())
1555 {
1556 auto ptr = powerState->get_ptr<const std::string*>();
1557 if (ptr)
1558 {
1559 if (*ptr == "On" || *ptr == "BiosPost")
1560 {
1561 isDetectedPowerOn = true;
1562 }
1563 }
1564 }
1565 if (powerOff && isDetectedPowerOn)
1566 {
1567 // power not on yet, don't know if it's there or not
1568 continue;
1569 }
1570 std::string name = item.value()["Name"].get<std::string>();
1571 std::vector<
1572 std::shared_ptr<sdbusplus::asio::dbus_interface>>&
1573 ifaces = inventory[name];
1574 for (auto& iface : ifaces)
1575 {
1576 objServer.remove_interface(iface);
1577 }
1578 ifaces.clear();
1579 systemConfiguration.erase(item.key());
1580 logDeviceRemoved(item.value());
1581 }
1582
James Feist8f2710a2018-05-09 17:18:55 -07001583 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001584 for (auto it = newConfiguration.begin();
1585 it != newConfiguration.end();)
1586 {
1587 auto findKey = oldConfiguration.find(it.key());
1588 if (findKey != oldConfiguration.end())
1589 {
1590 it = newConfiguration.erase(it);
1591 }
1592 else
1593 {
1594 it++;
1595 }
1596 }
James Feist899e17f2019-09-13 11:46:29 -07001597 for (const auto& item : newConfiguration.items())
1598 {
1599 logDeviceAdded(item.value());
1600 }
1601
James Feist8f2710a2018-05-09 17:18:55 -07001602 registerCallbacks(io, dbusMatches, systemConfiguration,
1603 objServer);
1604 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001605 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001606
James Feistbb43d022018-06-12 15:44:33 -07001607 io.post([&]() {
1608 if (!writeJsonFiles(systemConfiguration))
1609 {
1610 std::cerr << "Error writing json files\n";
1611 }
1612 });
James Feist8f2710a2018-05-09 17:18:55 -07001613 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001614 postToDbus(newConfiguration, systemConfiguration,
1615 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001616 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001617 {
James Feist899e17f2019-09-13 11:46:29 -07001618 return;
James Feist1df06a42019-04-11 14:23:04 -07001619 }
James Feist899e17f2019-09-13 11:46:29 -07001620 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001621 });
1622 });
1623 });
1624 perfScan->run();
1625 });
James Feist75fdeeb2018-02-20 14:26:16 -08001626}
1627
James Feista465ccc2019-02-08 12:51:01 -08001628void registerCallbacks(boost::asio::io_service& io,
1629 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1630 nlohmann::json& systemConfiguration,
1631 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001632{
1633 static boost::container::flat_set<std::string> watchedObjects;
1634
James Feista465ccc2019-02-08 12:51:01 -08001635 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001636 {
James Feist787c3c32019-11-07 14:42:58 -08001637 auto [_, inserted] = watchedObjects.insert(objectMap.first);
1638 if (!inserted)
James Feist75fdeeb2018-02-20 14:26:16 -08001639 {
1640 continue;
1641 }
James Feist8f2710a2018-05-09 17:18:55 -07001642 std::function<void(sdbusplus::message::message & message)>
1643 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001644
James Feista465ccc2019-02-08 12:51:01 -08001645 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001646 propertiesChangedCallback(io, dbusMatches,
1647 systemConfiguration, objServer);
1648 };
1649
1650 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001651 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001652 "type='signal',member='PropertiesChanged',arg0='" +
1653 objectMap.first + "'",
1654 eventHandler);
1655 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001656 }
1657}
1658
James Feist98132792019-07-09 13:29:09 -07001659int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001660{
1661 // setup connection to dbus
1662 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001663 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001664 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001665
James Feist8f2710a2018-05-09 17:18:55 -07001666 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001667
James Feist8f2710a2018-05-09 17:18:55 -07001668 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1669 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1670 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001671
James Feist8f2710a2018-05-09 17:18:55 -07001672 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1673 objServer.add_interface("/xyz/openbmc_project/inventory",
1674 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001675
1676 // to keep reference to the match / filter objects so they don't get
1677 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001678 std::vector<sdbusplus::bus::match::match> dbusMatches;
1679
1680 nlohmann::json systemConfiguration = nlohmann::json::object();
1681
1682 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001683 "Notify",
1684 [](const boost::container::flat_map<
1685 std::string,
James Feist98132792019-07-09 13:29:09 -07001686 boost::container::flat_map<std::string, BasicVariantType>>&) {
1687 return;
1688 });
James Feist8f2710a2018-05-09 17:18:55 -07001689 inventoryIface->initialize();
1690
1691 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001692#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001693 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001694#endif
James Feist8f2710a2018-05-09 17:18:55 -07001695 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1696 objServer);
1697 });
James Feist4131aea2018-03-09 09:47:30 -08001698
James Feistfd1264a2018-05-03 12:10:00 -07001699 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001700 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1701 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001702 });
James Feist8f2710a2018-05-09 17:18:55 -07001703 entityIface->initialize();
1704
James Feist1df06a42019-04-11 14:23:04 -07001705 if (fwVersionIsSame())
1706 {
1707 if (std::filesystem::is_regular_file(currentConfiguration))
1708 {
1709 // this file could just be deleted, but it's nice for debug
1710 std::filesystem::create_directory(tempConfigDir);
1711 std::filesystem::remove(lastConfiguration);
1712 std::filesystem::copy(currentConfiguration, lastConfiguration);
1713 std::filesystem::remove(currentConfiguration);
1714
1715 std::ifstream jsonStream(lastConfiguration);
1716 if (jsonStream.good())
1717 {
1718 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1719 if (data.is_discarded())
1720 {
1721 std::cerr << "syntax error in " << lastConfiguration
1722 << "\n";
1723 }
1724 else
1725 {
1726 lastJson = std::move(data);
1727 }
1728 }
1729 else
1730 {
1731 std::cerr << "unable to open " << lastConfiguration << "\n";
1732 }
1733 }
1734 }
1735 else
1736 {
1737 // not an error, just logging at this level to make it in the journal
1738 std::cerr << "Clearing previous configuration\n";
1739 std::filesystem::remove(currentConfiguration);
1740 }
1741
1742 // some boards only show up after power is on, we want to not say they are
1743 // removed until the same state happens
1744 setupPowerMatch(SYSTEM_BUS);
1745
James Feist1b2e2242018-01-30 13:45:19 -08001746 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001747
1748 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001749}