blob: 2f49e69e02919849a1025efa7013db0d396ec2fe [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 Feist3cb5fec2018-01-23 14:41:51 -080029#include <boost/container/flat_map.hpp>
30#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070031#include <boost/range/iterator_range.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070032#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080033#include <fstream>
34#include <iostream>
35#include <nlohmann/json.hpp>
36#include <regex>
37#include <sdbusplus/asio/connection.hpp>
38#include <sdbusplus/asio/object_server.hpp>
39#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080040
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 Feist3cb5fec2018-01-23 14:41:51 -0800137// calls the mapper to find all exposed objects of an interface type
138// and creates a vector<flat_map> that contains all the key value pairs
139// getManagedObjects
James Feist787c3c32019-11-07 14:42:58 -0800140void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>& probeVector,
James Feist8f2710a2018-05-09 17:18:55 -0700141 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feist733f7652019-11-13 14:32:29 -0800142 boost::container::flat_set<std::string>& interfaces,
143 std::shared_ptr<PerformScan> scan)
James Feist3cb5fec2018-01-23 14:41:51 -0800144{
James Feist8f2710a2018-05-09 17:18:55 -0700145
James Feist733f7652019-11-13 14:32:29 -0800146 for (const auto& [interface, _] : scan->dbusProbeObjects)
147 {
148 interfaces.erase(interface);
149 }
150 if (interfaces.empty())
151 {
152 return;
153 }
154
James Feist3cb5fec2018-01-23 14:41:51 -0800155 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700156 connection->async_method_call(
James Feist733f7652019-11-13 14:32:29 -0800157 [connection, interfaces, probeVector,
158 scan](boost::system::error_code& ec,
159 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700160 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700161 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700162 {
James Feist0de40152018-07-25 11:56:12 -0700163 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700164 {
James Feist0de40152018-07-25 11:56:12 -0700165 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700166 }
James Feist0de40152018-07-25 11:56:12 -0700167 std::cerr << "Error communicating to mapper.\n";
168
169 // if we can't communicate to the mapper something is very wrong
170 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700171 }
James Feist787c3c32019-11-07 14:42:58 -0800172
173 for (auto& object : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800174 {
James Feist787c3c32019-11-07 14:42:58 -0800175 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700176 {
James Feist787c3c32019-11-07 14:42:58 -0800177 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700178 }
James Feist3cb5fec2018-01-23 14:41:51 -0800179 }
James Feist787c3c32019-11-07 14:42:58 -0800180
James Feist63845bf2019-01-24 12:19:51 -0800181 if (interfaceConnections.empty())
182 {
James Feist63845bf2019-01-24 12:19:51 -0800183 return;
184 }
James Feist8f2710a2018-05-09 17:18:55 -0700185 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800186 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700187 {
188 connection->async_method_call(
James Feist733f7652019-11-13 14:32:29 -0800189 [conn, interfaces, probeVector,
190 scan](boost::system::error_code& errc,
191 const ManagedObjectType& managedInterface) {
James Feist98132792019-07-09 13:29:09 -0700192 if (errc)
James Feist8f2710a2018-05-09 17:18:55 -0700193 {
194 std::cerr
195 << "error getting managed object for device "
196 << conn << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700197 return;
198 }
James Feista465ccc2019-02-08 12:51:01 -0800199 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700200 {
James Feist787c3c32019-11-07 14:42:58 -0800201 // we could match multiple interfaces with one owner
202 for (auto& [interface, object] :
203 interfaceManagedObj.second)
James Feist8f2710a2018-05-09 17:18:55 -0700204 {
James Feist787c3c32019-11-07 14:42:58 -0800205 auto ifaceObjFind = interfaces.find(interface);
206
207 if (ifaceObjFind != interfaces.end())
208 {
James Feist733f7652019-11-13 14:32:29 -0800209 scan->dbusProbeObjects[interface]
210 .emplace_back(object);
James Feist787c3c32019-11-07 14:42:58 -0800211 }
James Feist8f2710a2018-05-09 17:18:55 -0700212 }
213 }
James Feist8f2710a2018-05-09 17:18:55 -0700214 },
James Feist787c3c32019-11-07 14:42:58 -0800215 conn, "/", "org.freedesktop.DBus.ObjectManager",
James Feist8f2710a2018-05-09 17:18:55 -0700216 "GetManagedObjects");
217 }
218 },
219 "xyz.openbmc_project.ObjectMapper",
220 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700221 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800222 interfaces);
James Feist3cb5fec2018-01-23 14:41:51 -0800223}
James Feist8f2710a2018-05-09 17:18:55 -0700224// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700225bool probeDbus(const std::string& interface,
226 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800227 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
228 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800229{
James Feista465ccc2019-02-08 12:51:01 -0800230 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
James Feist733f7652019-11-13 14:32:29 -0800231 dbusObject = scan->dbusProbeObjects[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800232 if (dbusObject.empty())
233 {
James Feist8f2710a2018-05-09 17:18:55 -0700234 foundProbe = false;
235 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800236 }
237 foundProbe = true;
238
239 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800240 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800241 {
242 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800243 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800244 {
245 auto deviceValue = device.find(match.first);
246 if (deviceValue != device.end())
247 {
248 switch (match.second.type())
249 {
James Feist9eb0b582018-04-27 12:15:46 -0700250 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800251 {
James Feist9eb0b582018-04-27 12:15:46 -0700252 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700253 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700254
255 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800256 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700257 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700258 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700259 {
260 deviceMatches = false;
261 break;
262 }
James Feist3cb5fec2018-01-23 14:41:51 -0800263 break;
264 }
James Feist9eb0b582018-04-27 12:15:46 -0700265 case nlohmann::json::value_t::boolean:
266 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800267 {
James Feista465ccc2019-02-08 12:51:01 -0800268 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700269 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800270
James Feist9eb0b582018-04-27 12:15:46 -0700271 if (probeValue != match.second.get<unsigned int>())
272 {
273 deviceMatches = false;
274 }
275 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800276 }
James Feist9eb0b582018-04-27 12:15:46 -0700277 case nlohmann::json::value_t::number_integer:
278 {
James Feista465ccc2019-02-08 12:51:01 -0800279 int probeValue = std::visit(VariantToIntVisitor(),
280 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800281
James Feist9eb0b582018-04-27 12:15:46 -0700282 if (probeValue != match.second.get<int>())
283 {
284 deviceMatches = false;
285 }
286 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800287 }
James Feist9eb0b582018-04-27 12:15:46 -0700288 case nlohmann::json::value_t::number_float:
289 {
James Feista465ccc2019-02-08 12:51:01 -0800290 float probeValue = std::visit(VariantToFloatVisitor(),
291 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700292
293 if (probeValue != match.second.get<float>())
294 {
295 deviceMatches = false;
296 }
297 break;
298 }
James Feist0eb40352019-04-09 14:44:04 -0700299 default:
300 {
301 std::cerr << "unexpected dbus probe type "
302 << match.second.type_name() << "\n";
303 }
James Feist3cb5fec2018-01-23 14:41:51 -0800304 }
305 }
306 else
307 {
308 deviceMatches = false;
309 break;
310 }
311 }
312 if (deviceMatches)
313 {
James Feistf5125b02019-06-06 11:27:43 -0700314 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800315 foundMatch = true;
316 deviceMatches = false; // for next iteration
317 }
318 }
319 return foundMatch;
320}
321
322// default probe entry point, iterates a list looking for specific types to
323// call specific probe functions
324bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800325 const std::vector<std::string>& probeCommand,
James Feist733f7652019-11-13 14:32:29 -0800326 std::shared_ptr<PerformScan> scan,
James Feist08a5b172019-08-28 14:47:47 -0700327 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
328 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800329{
330 const static std::regex command(R"(\((.*)\))");
331 std::smatch match;
332 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700333 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800334 bool cur = true;
335 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700336 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800337
James Feista465ccc2019-02-08 12:51:01 -0800338 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800339 {
340 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800341 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800342 cmp_str>::const_iterator probeType;
343
344 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700345 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800346 {
347 if (probe.find(probeType->first) != std::string::npos)
348 {
349 foundProbe = true;
350 break;
351 }
352 }
353 if (foundProbe)
354 {
355 switch (probeType->second)
356 {
James Feist9eb0b582018-04-27 12:15:46 -0700357 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800358 {
James Feiste31e00a2019-07-24 10:45:43 -0700359 cur = false;
360 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800361 }
James Feist9eb0b582018-04-27 12:15:46 -0700362 case probe_type_codes::TRUE_T:
363 {
James Feiste31e00a2019-07-24 10:45:43 -0700364 cur = true;
365 break;
James Feist9eb0b582018-04-27 12:15:46 -0700366 }
367 case probe_type_codes::MATCH_ONE:
368 {
369 // set current value to last, this probe type shouldn't
370 // affect the outcome
371 cur = ret;
372 matchOne = true;
373 break;
374 }
375 /*case probe_type_codes::AND:
376 break;
377 case probe_type_codes::OR:
378 break;
379 // these are no-ops until the last command switch
380 */
381 case probe_type_codes::FOUND:
382 {
383 if (!std::regex_search(probe, match, command))
384 {
James Feist0eb40352019-04-09 14:44:04 -0700385 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700386 << "\n";
387 return false;
388 }
389 std::string commandStr = *(match.begin() + 1);
390 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800391 cur = (std::find(scan->passedProbes.begin(),
392 scan->passedProbes.end(),
393 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700394 break;
395 }
James Feist0eb40352019-04-09 14:44:04 -0700396 default:
397 {
398 break;
399 }
James Feist3cb5fec2018-01-23 14:41:51 -0800400 }
401 }
402 // look on dbus for object
403 else
404 {
405 if (!std::regex_search(probe, match, command))
406 {
James Feist0eb40352019-04-09 14:44:04 -0700407 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800408 return false;
409 }
410 std::string commandStr = *(match.begin() + 1);
411 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700412 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800413 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800414 auto json = nlohmann::json::parse(commandStr, nullptr, false);
415 if (json.is_discarded())
416 {
James Feist0eb40352019-04-09 14:44:04 -0700417 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800418 return false;
419 }
420 // we can match any (string, variant) property. (string, string)
421 // does a regex
422 std::map<std::string, nlohmann::json> dbusProbeMap =
423 json.get<std::map<std::string, nlohmann::json>>();
424 auto findStart = probe.find("(");
425 if (findStart == std::string::npos)
426 {
427 return false;
428 }
429 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800430 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
431 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800432 }
433
434 // some functions like AND and OR only take affect after the
435 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700436 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800437 {
James Feist54a0dca2019-06-26 10:34:54 -0700438 ret = cur && ret;
439 }
440 else if (lastCommand == probe_type_codes::OR)
441 {
442 ret = cur || ret;
443 }
444
445 if (first)
446 {
447 ret = cur;
448 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800449 }
450 lastCommand = probeType != PROBE_TYPES.end()
451 ? probeType->second
452 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800453 }
454
455 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800456 if (ret && foundDevs.size() == 0)
457 {
James Feist08a5b172019-08-28 14:47:47 -0700458 foundDevs.emplace_back(
459 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800460 }
James Feist0eb40352019-04-09 14:44:04 -0700461 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700462 {
James Feist71f295f2019-06-20 13:35:12 -0700463 // match the last one
464 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700465 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700466
467 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700468 }
James Feist3cb5fec2018-01-23 14:41:51 -0800469 return ret;
470}
471
James Feist7d807752019-11-13 14:47:57 -0800472PerformProbe::PerformProbe(const std::vector<std::string>& probeCommand,
473 std::shared_ptr<PerformScan>& scanPtr,
474 std::function<void(FoundDeviceT&)>&& callback) :
475 _probeCommand(probeCommand),
476 scan(scanPtr), _callback(std::move(callback))
477{
478}
479PerformProbe::~PerformProbe()
480{
481 FoundDeviceT foundDevs;
482 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700483 {
James Feist7d807752019-11-13 14:47:57 -0800484 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700485 }
James Feist7d807752019-11-13 14:47:57 -0800486}
James Feist8f2710a2018-05-09 17:18:55 -0700487
488// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800489bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800490{
James Feist1df06a42019-04-11 14:23:04 -0700491 std::filesystem::create_directory(configurationOutDir);
492 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700493 if (!output.good())
494 {
495 return false;
496 }
James Feist1b2e2242018-01-30 13:45:19 -0800497 output << systemConfiguration.dump(4);
498 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700499 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700500}
James Feist1b2e2242018-01-30 13:45:19 -0800501
James Feist97a63f12018-05-17 13:50:57 -0700502template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800503bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
504 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700505{
506 try
507 {
508 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800509 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700510 ref = value;
511 return true;
512 }
James Feist98132792019-07-09 13:29:09 -0700513 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700514 {
515 return false;
516 }
517}
James Feistbb43d022018-06-12 15:44:33 -0700518
James Feistebcc26b2019-03-22 12:30:43 -0700519// template function to add array as dbus property
520template <typename PropertyType>
521void addArrayToDbus(const std::string& name, const nlohmann::json& array,
522 sdbusplus::asio::dbus_interface* iface,
523 sdbusplus::asio::PropertyPermission permission,
524 nlohmann::json& systemConfiguration,
525 const std::string& jsonPointerString)
526{
527 std::vector<PropertyType> values;
528 for (const auto& property : array)
529 {
530 auto ptr = property.get_ptr<const PropertyType*>();
531 if (ptr != nullptr)
532 {
533 values.emplace_back(*ptr);
534 }
535 }
536
537 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
538 {
539 iface->register_property(name, values);
540 }
541 else
542 {
543 iface->register_property(
544 name, values,
545 [&systemConfiguration,
546 jsonPointerString{std::string(jsonPointerString)}](
547 const std::vector<PropertyType>& newVal,
548 std::vector<PropertyType>& val) {
549 val = newVal;
550 if (!setJsonFromPointer(jsonPointerString, val,
551 systemConfiguration))
552 {
553 std::cerr << "error setting json field\n";
554 return -1;
555 }
556 if (!writeJsonFiles(systemConfiguration))
557 {
558 std::cerr << "error setting json file\n";
559 return -1;
560 }
561 return 1;
562 });
563 }
564}
565
James Feistbb43d022018-06-12 15:44:33 -0700566template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800567void addProperty(const std::string& propertyName, const PropertyType& value,
568 sdbusplus::asio::dbus_interface* iface,
569 nlohmann::json& systemConfiguration,
570 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700571 sdbusplus::asio::PropertyPermission permission)
572{
573 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
574 {
575 iface->register_property(propertyName, value);
576 return;
577 }
James Feist68500ff2018-08-08 15:40:42 -0700578 iface->register_property(
579 propertyName, value,
580 [&systemConfiguration,
581 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800582 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700583 val = newVal;
584 if (!setJsonFromPointer(jsonPointerString, val,
585 systemConfiguration))
586 {
587 std::cerr << "error setting json field\n";
588 return -1;
589 }
James Feistc6248a52018-08-14 10:09:45 -0700590 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700591 {
592 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700593 return -1;
594 }
595 return 1;
596 });
597}
598
599void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800600 const std::string& jsonPointerPath,
601 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
602 sdbusplus::asio::object_server& objServer,
603 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700604{
605 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
606 iface->register_method(
607 "Delete", [&objServer, &systemConfiguration, interface,
608 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700609 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700610 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700611 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700612 {
613 // this technically can't happen as the pointer is pointing to
614 // us
615 throw DBusInternalError();
616 }
617 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feistc6248a52018-08-14 10:09:45 -0700618 systemConfiguration[ptr] = nullptr;
619
James Feist02d2b932020-02-06 16:28:48 -0800620 // todo(james): dig through sdbusplus to find out why we can't
621 // delete it in a method call
622 io.post([&objServer, dbusInterface]() mutable {
623 objServer.remove_interface(dbusInterface);
624 });
625
James Feistc6248a52018-08-14 10:09:45 -0700626 if (!writeJsonFiles(systemConfiguration))
627 {
628 std::cerr << "error setting json file\n";
629 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700630 }
James Feist68500ff2018-08-08 15:40:42 -0700631 });
James Feistbb43d022018-06-12 15:44:33 -0700632}
633
James Feist1b2e2242018-01-30 13:45:19 -0800634// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700635void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800636 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
637 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
638 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700639 sdbusplus::asio::PropertyPermission permission =
640 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800641{
James Feista465ccc2019-02-08 12:51:01 -0800642 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800643 {
James Feist8f2710a2018-05-09 17:18:55 -0700644 auto type = dictPair.value().type();
645 bool array = false;
646 if (dictPair.value().type() == nlohmann::json::value_t::array)
647 {
648 array = true;
649 if (!dictPair.value().size())
650 {
651 continue;
652 }
653 type = dictPair.value()[0].type();
654 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800655 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700656 {
657 if (arrayItem.type() != type)
658 {
659 isLegal = false;
660 break;
661 }
662 }
663 if (!isLegal)
664 {
665 std::cerr << "dbus format error" << dictPair.value() << "\n";
666 continue;
667 }
James Feista218ddb2019-04-11 14:01:31 -0700668 }
669 if (type == nlohmann::json::value_t::object)
670 {
671 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700672 }
James Feist97a63f12018-05-17 13:50:57 -0700673 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700674 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
675 {
676 // all setable numbers are doubles as it is difficult to always
677 // create a configuration file with all whole numbers as decimals
678 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700679 if (array)
680 {
681 if (dictPair.value()[0].is_number())
682 {
683 type = nlohmann::json::value_t::number_float;
684 }
685 }
686 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700687 {
688 type = nlohmann::json::value_t::number_float;
689 }
690 }
691
James Feist8f2710a2018-05-09 17:18:55 -0700692 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800693 {
James Feist9eb0b582018-04-27 12:15:46 -0700694 case (nlohmann::json::value_t::boolean):
695 {
James Feist8f2710a2018-05-09 17:18:55 -0700696 if (array)
697 {
698 // todo: array of bool isn't detected correctly by
699 // sdbusplus, change it to numbers
700 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700701 iface.get(), permission,
702 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700703 }
James Feistbb43d022018-06-12 15:44:33 -0700704
James Feist97a63f12018-05-17 13:50:57 -0700705 else
706 {
James Feistbb43d022018-06-12 15:44:33 -0700707 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700708 iface.get(), systemConfiguration, key,
709 permission);
James Feist97a63f12018-05-17 13:50:57 -0700710 }
James Feist9eb0b582018-04-27 12:15:46 -0700711 break;
712 }
713 case (nlohmann::json::value_t::number_integer):
714 {
James Feist8f2710a2018-05-09 17:18:55 -0700715 if (array)
716 {
717 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700718 iface.get(), permission,
719 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700720 }
721 else
722 {
James Feistbb43d022018-06-12 15:44:33 -0700723 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700724 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700725 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700726 }
James Feist9eb0b582018-04-27 12:15:46 -0700727 break;
728 }
729 case (nlohmann::json::value_t::number_unsigned):
730 {
James Feist8f2710a2018-05-09 17:18:55 -0700731 if (array)
732 {
733 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700734 iface.get(), permission,
735 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700736 }
737 else
738 {
James Feistbb43d022018-06-12 15:44:33 -0700739 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700740 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700741 systemConfiguration, key,
742 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700743 }
James Feist9eb0b582018-04-27 12:15:46 -0700744 break;
745 }
746 case (nlohmann::json::value_t::number_float):
747 {
James Feist8f2710a2018-05-09 17:18:55 -0700748 if (array)
749 {
750 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700751 iface.get(), permission,
752 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700753 }
James Feistbb43d022018-06-12 15:44:33 -0700754
James Feist97a63f12018-05-17 13:50:57 -0700755 else
756 {
James Feistbb43d022018-06-12 15:44:33 -0700757 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700758 iface.get(), systemConfiguration, key,
759 permission);
James Feist97a63f12018-05-17 13:50:57 -0700760 }
James Feist9eb0b582018-04-27 12:15:46 -0700761 break;
762 }
763 case (nlohmann::json::value_t::string):
764 {
James Feist8f2710a2018-05-09 17:18:55 -0700765 if (array)
766 {
James Feistebcc26b2019-03-22 12:30:43 -0700767 addArrayToDbus<std::string>(
768 dictPair.key(), dictPair.value(), iface.get(),
769 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700770 }
771 else
772 {
James Feistc6248a52018-08-14 10:09:45 -0700773 addProperty(
774 dictPair.key(), dictPair.value().get<std::string>(),
775 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700776 }
James Feist9eb0b582018-04-27 12:15:46 -0700777 break;
778 }
James Feist0eb40352019-04-09 14:44:04 -0700779 default:
780 {
James Feista218ddb2019-04-11 14:01:31 -0700781 std::cerr << "Unexpected json type in system configuration "
782 << dictPair.key() << ": "
783 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700784 break;
785 }
James Feist1b2e2242018-01-30 13:45:19 -0800786 }
787 }
James Feistc6248a52018-08-14 10:09:45 -0700788 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
789 {
790 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
791 systemConfiguration);
792 }
James Feist8f2710a2018-05-09 17:18:55 -0700793 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800794}
795
James Feista465ccc2019-02-08 12:51:01 -0800796sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700797{
798 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
799 interface) != settableInterfaces.end()
800 ? sdbusplus::asio::PropertyPermission::readWrite
801 : sdbusplus::asio::PropertyPermission::readOnly;
802}
803
James Feista465ccc2019-02-08 12:51:01 -0800804void createAddObjectMethod(const std::string& jsonPointerPath,
805 const std::string& path,
806 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700807 sdbusplus::asio::object_server& objServer,
808 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700809{
James Feistd58879a2019-09-11 11:26:07 -0700810 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
811 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700812
813 iface->register_method(
814 "AddObject",
815 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700816 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
817 board](const boost::container::flat_map<std::string, JsonVariantType>&
818 data) {
James Feist68500ff2018-08-08 15:40:42 -0700819 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800820 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700821 auto findExposes = base.find("Exposes");
822
823 if (findExposes == base.end())
824 {
825 throw std::invalid_argument("Entity must have children.");
826 }
827
828 // this will throw invalid-argument to sdbusplus if invalid json
829 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800830 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700831 {
James Feista465ccc2019-02-08 12:51:01 -0800832 nlohmann::json& newJson = newData[item.first];
833 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
834 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700835 }
836
837 auto findName = newData.find("Name");
838 auto findType = newData.find("Type");
839 if (findName == newData.end() || findType == newData.end())
840 {
841 throw std::invalid_argument("AddObject missing Name or Type");
842 }
James Feista465ccc2019-02-08 12:51:01 -0800843 const std::string* type = findType->get_ptr<const std::string*>();
844 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700845 if (type == nullptr || name == nullptr)
846 {
847 throw std::invalid_argument("Type and Name must be a string.");
848 }
849
James Feist02d2b932020-02-06 16:28:48 -0800850 bool foundNull = false;
James Feist68500ff2018-08-08 15:40:42 -0700851 size_t lastIndex = 0;
852 // we add in the "exposes"
James Feist02d2b932020-02-06 16:28:48 -0800853 for (const auto& expose : *findExposes)
James Feist68500ff2018-08-08 15:40:42 -0700854 {
James Feist02d2b932020-02-06 16:28:48 -0800855 if (expose.is_null())
856 {
857 foundNull = true;
858 continue;
859 }
860
861 if (expose["Name"] == *name && expose["Type"] == *type)
James Feist68500ff2018-08-08 15:40:42 -0700862 {
863 throw std::invalid_argument(
864 "Field already in JSON, not adding");
865 }
James Feist02d2b932020-02-06 16:28:48 -0800866
867 if (foundNull)
868 {
869 continue;
870 }
871
James Feist68500ff2018-08-08 15:40:42 -0700872 lastIndex++;
873 }
874
875 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
876 *type + ".json");
877 // todo(james) we might want to also make a list of 'can add'
878 // interfaces but for now I think the assumption if there is a
879 // schema avaliable that it is allowed to update is fine
880 if (!schemaFile.good())
881 {
882 throw std::invalid_argument(
883 "No schema avaliable, cannot validate.");
884 }
885 nlohmann::json schema =
886 nlohmann::json::parse(schemaFile, nullptr, false);
887 if (schema.is_discarded())
888 {
889 std::cerr << "Schema not legal" << *type << ".json\n";
890 throw DBusInternalError();
891 }
892 if (!validateJson(schema, newData))
893 {
894 throw std::invalid_argument("Data does not match schema");
895 }
James Feist02d2b932020-02-06 16:28:48 -0800896 if (foundNull)
897 {
898 findExposes->at(lastIndex) = newData;
899 }
900 else
901 {
902 findExposes->push_back(newData);
903 }
James Feist68500ff2018-08-08 15:40:42 -0700904 if (!writeJsonFiles(systemConfiguration))
905 {
906 std::cerr << "Error writing json files\n";
907 throw DBusInternalError();
908 }
909 std::string dbusName = *name;
910
911 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700912 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700913
914 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
915 createInterface(objServer, path + "/" + dbusName,
916 "xyz.openbmc_project.Configuration." + *type,
James Feist02d2b932020-02-06 16:28:48 -0800917 board, true);
James Feist68500ff2018-08-08 15:40:42 -0700918 // permission is read-write, as since we just created it, must be
919 // runtime modifiable
920 populateInterfaceFromJson(
921 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700922 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700923 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700924 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700925 });
926 iface->initialize();
927}
928
James Feista465ccc2019-02-08 12:51:01 -0800929void postToDbus(const nlohmann::json& newConfiguration,
930 nlohmann::json& systemConfiguration,
931 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800932
James Feist1b2e2242018-01-30 13:45:19 -0800933{
James Feist97a63f12018-05-17 13:50:57 -0700934 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800935 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800936 {
James Feistf1b14142019-04-10 15:22:09 -0700937 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700938 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700939 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700940 // loop through newConfiguration, but use values from system
941 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700942 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700943 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800944 std::string boardType;
945 if (findBoardType != boardValues.end() &&
946 findBoardType->type() == nlohmann::json::value_t::string)
947 {
948 boardType = findBoardType->get<std::string>();
949 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700950 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800951 }
952 else
953 {
954 std::cerr << "Unable to find type for " << boardKey
955 << " reverting to Chassis.\n";
956 boardType = "Chassis";
957 }
James Feist11be6672018-04-06 14:05:32 -0700958 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800959
960 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700961 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700962 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
963 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800964
James Feistd58879a2019-09-11 11:26:07 -0700965 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
966 createInterface(objServer, boardName,
967 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700968
James Feistd58879a2019-09-11 11:26:07 -0700969 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
970 createInterface(objServer, boardName,
971 "xyz.openbmc_project.Inventory.Item." + boardType,
972 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700973
James Feist68500ff2018-08-08 15:40:42 -0700974 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700975 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700976
James Feist97a63f12018-05-17 13:50:57 -0700977 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700978 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700979 jsonPointerPath += "/";
980 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800981 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700982 {
983 if (boardField.value().type() == nlohmann::json::value_t::object)
984 {
James Feistd58879a2019-09-11 11:26:07 -0700985 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
986 createInterface(objServer, boardName, boardField.key(),
987 boardKeyOrig);
988
James Feistc6248a52018-08-14 10:09:45 -0700989 populateInterfaceFromJson(systemConfiguration,
990 jsonPointerPath + boardField.key(),
991 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700992 }
993 }
James Feist97a63f12018-05-17 13:50:57 -0700994
James Feist1e3e6982018-08-03 16:09:28 -0700995 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800996 if (exposes == boardValues.end())
997 {
998 continue;
999 }
James Feist97a63f12018-05-17 13:50:57 -07001000 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001001 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001002
1003 // store the board level pointer so we can modify it on the way down
1004 std::string jsonPointerPathBoard = jsonPointerPath;
1005 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001006 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001007 {
James Feist97a63f12018-05-17 13:50:57 -07001008 exposesIndex++;
1009 jsonPointerPath = jsonPointerPathBoard;
1010 jsonPointerPath += std::to_string(exposesIndex);
1011
James Feistd63d18a2018-07-19 15:23:45 -07001012 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001013 if (findName == item.end())
1014 {
1015 std::cerr << "cannot find name in field " << item << "\n";
1016 continue;
1017 }
James Feist1e3e6982018-08-03 16:09:28 -07001018 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001019 // if status is not found it is assumed to be status = 'okay'
1020 if (findStatus != item.end())
1021 {
1022 if (*findStatus == "disabled")
1023 {
1024 continue;
1025 }
1026 }
James Feistd63d18a2018-07-19 15:23:45 -07001027 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001028 std::string itemType;
1029 if (findType != item.end())
1030 {
1031 itemType = findType->get<std::string>();
1032 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001033 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1034 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001035 }
1036 else
1037 {
1038 itemType = "unknown";
1039 }
1040 std::string itemName = findName->get<std::string>();
1041 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001042 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001043
James Feistd58879a2019-09-11 11:26:07 -07001044 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1045 createInterface(objServer, boardName + "/" + itemName,
1046 "xyz.openbmc_project.Configuration." + itemType,
1047 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001048
James Feist97a63f12018-05-17 13:50:57 -07001049 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001050 itemIface, item, objServer,
1051 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001052
James Feista465ccc2019-02-08 12:51:01 -08001053 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001054 {
James Feist97a63f12018-05-17 13:50:57 -07001055 jsonPointerPath = jsonPointerPathBoard +
1056 std::to_string(exposesIndex) + "/" +
1057 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001058 if (objectPair.value().type() ==
1059 nlohmann::json::value_t::object)
1060 {
James Feistd58879a2019-09-11 11:26:07 -07001061 std::shared_ptr<sdbusplus::asio::dbus_interface>
1062 objectIface = createInterface(
1063 objServer, boardName + "/" + itemName,
1064 "xyz.openbmc_project.Configuration." + itemType +
1065 "." + objectPair.key(),
1066 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001067
1068 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001069 systemConfiguration, jsonPointerPath, objectIface,
1070 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001071 }
1072 else if (objectPair.value().type() ==
1073 nlohmann::json::value_t::array)
1074 {
1075 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001076 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001077 {
James Feist8f2710a2018-05-09 17:18:55 -07001078 continue;
1079 }
1080 bool isLegal = true;
1081 auto type = objectPair.value()[0].type();
1082 if (type != nlohmann::json::value_t::object)
1083 {
1084 continue;
1085 }
1086
1087 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001088 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001089 {
1090 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001091 {
James Feist8f2710a2018-05-09 17:18:55 -07001092 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001093 break;
1094 }
James Feist8f2710a2018-05-09 17:18:55 -07001095 }
1096 if (!isLegal)
1097 {
1098 std::cerr << "dbus format error" << objectPair.value()
1099 << "\n";
1100 break;
1101 }
1102
James Feista465ccc2019-02-08 12:51:01 -08001103 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001104 {
James Feist97a63f12018-05-17 13:50:57 -07001105
James Feistd58879a2019-09-11 11:26:07 -07001106 std::shared_ptr<sdbusplus::asio::dbus_interface>
1107 objectIface = createInterface(
1108 objServer, boardName + "/" + itemName,
1109 "xyz.openbmc_project.Configuration." +
1110 itemType + "." + objectPair.key() +
1111 std::to_string(index),
1112 boardKeyOrig);
1113
James Feistc6248a52018-08-14 10:09:45 -07001114 populateInterfaceFromJson(
1115 systemConfiguration,
1116 jsonPointerPath + "/" + std::to_string(index),
1117 objectIface, arrayItem, objServer,
1118 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001119 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001120 }
1121 }
1122 }
1123 }
1124 }
1125}
1126
James Feist8f2710a2018-05-09 17:18:55 -07001127// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001128bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001129{
1130 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001131 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001132 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1133 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001134 {
1135 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001136 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001137 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001138 }
James Feistb4383f42018-08-06 16:54:10 -07001139
1140 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1141 globalSchema);
1142 if (!schemaStream.good())
1143 {
1144 std::cerr
1145 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1146 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001147 return false;
James Feistb4383f42018-08-06 16:54:10 -07001148 }
1149 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1150 if (schema.is_discarded())
1151 {
1152 std::cerr
1153 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1154 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001155 return false;
James Feistb4383f42018-08-06 16:54:10 -07001156 }
1157
James Feista465ccc2019-02-08 12:51:01 -08001158 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001159 {
1160 std::ifstream jsonStream(jsonPath.c_str());
1161 if (!jsonStream.good())
1162 {
1163 std::cerr << "unable to open " << jsonPath.string() << "\n";
1164 continue;
1165 }
1166 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1167 if (data.is_discarded())
1168 {
1169 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1170 continue;
1171 }
James Feist8da99192019-01-24 08:20:16 -08001172 /*
1173 * todo(james): reenable this once less things are in flight
1174 *
James Feistb4383f42018-08-06 16:54:10 -07001175 if (!validateJson(schema, data))
1176 {
1177 std::cerr << "Error validating " << jsonPath.string() << "\n";
1178 continue;
1179 }
James Feist8da99192019-01-24 08:20:16 -08001180 */
James Feistb4383f42018-08-06 16:54:10 -07001181
James Feist3cb5fec2018-01-23 14:41:51 -08001182 if (data.type() == nlohmann::json::value_t::array)
1183 {
James Feista465ccc2019-02-08 12:51:01 -08001184 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001185 {
1186 configurations.emplace_back(d);
1187 }
1188 }
1189 else
1190 {
1191 configurations.emplace_back(data);
1192 }
1193 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001194 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001195}
James Feist3cb5fec2018-01-23 14:41:51 -08001196
James Feist94219d12020-03-03 11:52:25 -08001197std::string getRecordName(
1198 const boost::container::flat_map<std::string, BasicVariantType>& probe,
1199 const std::string& probeName)
1200{
1201 if (probe.empty())
1202 {
1203 return probeName;
1204 }
1205
1206 // use an array so alphabetical order from the
1207 // flat_map is maintained
1208 auto device = nlohmann::json::array();
1209 for (auto& devPair : probe)
1210 {
1211 device.push_back(devPair.first);
1212 std::visit([&device](auto&& v) { device.push_back(v); },
1213 devPair.second);
1214 }
1215 size_t hash = std::hash<std::string>{}(probeName + device.dump());
1216 // hashes are hard to distinguish, use the
1217 // non-hashed version if we want debug
1218 if constexpr (DEBUG)
1219 {
1220 return probeName + device.dump();
1221 }
1222 else
1223 {
1224 return std::to_string(hash);
1225 }
1226}
1227
James Feist733f7652019-11-13 14:32:29 -08001228PerformScan::PerformScan(
1229 nlohmann::json& systemConfiguration, nlohmann::json& missingConfigurations,
1230 std::list<nlohmann::json>& configurations,
1231 std::function<void(const DBusProbeObjectT&)>&& callback) :
1232 _systemConfiguration(systemConfiguration),
1233 _missingConfigurations(missingConfigurations),
1234 _configurations(configurations), _callback(std::move(callback))
James Feist75fdeeb2018-02-20 14:26:16 -08001235{
James Feist733f7652019-11-13 14:32:29 -08001236}
1237void PerformScan::run()
1238{
1239 boost::container::flat_set<std::string> dbusProbeInterfaces;
1240 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001241
James Feist733f7652019-11-13 14:32:29 -08001242 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001243 {
James Feist733f7652019-11-13 14:32:29 -08001244 auto findProbe = it->find("Probe");
1245 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001246
James Feist733f7652019-11-13 14:32:29 -08001247 nlohmann::json probeCommand;
1248 // check for poorly formatted fields, probe must be an array
1249 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001250 {
James Feist733f7652019-11-13 14:32:29 -08001251 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1252 it = _configurations.erase(it);
1253 continue;
1254 }
1255 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1256 {
1257 probeCommand = nlohmann::json::array();
1258 probeCommand.push_back(*findProbe);
1259 }
1260 else
1261 {
1262 probeCommand = *findProbe;
1263 }
James Feist3cb5fec2018-01-23 14:41:51 -08001264
James Feist733f7652019-11-13 14:32:29 -08001265 if (findName == it->end())
1266 {
1267 std::cerr << "configuration file missing name:\n " << *it << "\n";
1268 it = _configurations.erase(it);
1269 continue;
1270 }
1271 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001272
James Feist733f7652019-11-13 14:32:29 -08001273 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1274 passedProbes.end())
1275 {
1276 it = _configurations.erase(it);
1277 continue;
1278 }
1279 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001280
James Feist733f7652019-11-13 14:32:29 -08001281 // store reference to this to children to makes sure we don't get
1282 // destroyed too early
1283 auto thisRef = shared_from_this();
1284 auto probePointer = std::make_shared<PerformProbe>(
1285 probeCommand, thisRef,
1286 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001287 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001288
James Feist733f7652019-11-13 14:32:29 -08001289 passedProbes.push_back(probeName);
James Feist94219d12020-03-03 11:52:25 -08001290 std::list<size_t> indexes(foundDevices.size());
1291 std::iota(indexes.begin(), indexes.end(), 1);
James Feist8f2710a2018-05-09 17:18:55 -07001292
James Feist94219d12020-03-03 11:52:25 -08001293 size_t indexIdx = probeName.find("$index");
1294 bool hasIndex = (indexIdx != std::string::npos);
1295
1296 // copy over persisted configurations and make sure we remove
1297 // indexes that are already used
1298 for (auto itr = foundDevices.begin();
1299 itr != foundDevices.end();)
James Feist08a5b172019-08-28 14:47:47 -07001300 {
James Feist94219d12020-03-03 11:52:25 -08001301 std::string recordName = getRecordName(*itr, probeName);
James Feistf1b14142019-04-10 15:22:09 -07001302
James Feist08a5b172019-08-28 14:47:47 -07001303 auto fromLastJson = lastJson.find(recordName);
1304 if (fromLastJson != lastJson.end())
1305 {
James Feist02d2b932020-02-06 16:28:48 -08001306 auto findExposes = fromLastJson->find("Exposes");
1307 // delete nulls from any updates
1308 if (findExposes != fromLastJson->end())
1309 {
1310 auto copy = nlohmann::json::array();
1311 for (auto& expose : *findExposes)
1312 {
1313 if (expose.is_null())
1314 {
1315 continue;
1316 }
1317 copy.emplace_back(expose);
1318 }
1319 *findExposes = copy;
1320 }
1321
James Feist08a5b172019-08-28 14:47:47 -07001322 // keep user changes
1323 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001324 _missingConfigurations.erase(recordName);
James Feist94219d12020-03-03 11:52:25 -08001325 itr = foundDevices.erase(itr);
1326 if (hasIndex)
1327 {
1328 auto nameIt = fromLastJson->find("Name");
1329 if (nameIt == fromLastJson->end())
1330 {
1331 std::cerr << "Last JSON Illegal\n";
1332 continue;
1333 }
1334
1335 int index = std::stoi(
1336 nameIt->get<std::string>().substr(indexIdx),
1337 nullptr, 0);
1338 auto usedIt = std::find(indexes.begin(),
1339 indexes.end(), index);
1340
1341 if (usedIt == indexes.end())
1342 {
1343 continue; // less items now
1344 }
1345 indexes.erase(usedIt);
1346 }
1347
James Feist08a5b172019-08-28 14:47:47 -07001348 continue;
1349 }
James Feist94219d12020-03-03 11:52:25 -08001350 itr++;
1351 }
1352 for (auto& foundDevice : foundDevices)
1353 {
1354 nlohmann::json record = *recordPtr;
1355 std::string recordName =
1356 getRecordName(foundDevice, probeName);
1357 size_t foundDeviceIdx = indexes.front();
1358 indexes.pop_front();
James Feist08a5b172019-08-28 14:47:47 -07001359 // insert into configuration temporarily to be able to
1360 // reference ourselves
James Feistf1b14142019-04-10 15:22:09 -07001361
James Feist08a5b172019-08-28 14:47:47 -07001362 _systemConfiguration[recordName] = record;
1363
1364 for (auto keyPair = record.begin(); keyPair != record.end();
1365 keyPair++)
1366 {
1367 templateCharReplace(keyPair, foundDevice,
1368 foundDeviceIdx);
1369 }
1370
1371 auto findExpose = record.find("Exposes");
1372 if (findExpose == record.end())
1373 {
James Feistf1b14142019-04-10 15:22:09 -07001374 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001375 continue;
1376 }
James Feistf1b14142019-04-10 15:22:09 -07001377
James Feist08a5b172019-08-28 14:47:47 -07001378 for (auto& expose : *findExpose)
1379 {
1380 for (auto keyPair = expose.begin();
1381 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001382 {
James Feist08a5b172019-08-28 14:47:47 -07001383
1384 templateCharReplace(keyPair, foundDevice,
1385 foundDeviceIdx);
1386
James Feist668bbb12020-02-05 14:27:26 -08001387 bool isBind =
1388 boost::starts_with(keyPair.key(), "Bind");
1389 bool isDisable = keyPair.key() == "DisableNode";
1390
1391 // special cases
1392 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001393 {
James Feist668bbb12020-02-05 14:27:26 -08001394 continue;
1395 }
1396
1397 if (keyPair.value().type() !=
1398 nlohmann::json::value_t::string &&
1399 keyPair.value().type() !=
1400 nlohmann::json::value_t::array)
1401 {
1402 std::cerr << "Value is invalid type "
1403 << keyPair.key() << "\n";
1404 continue;
1405 }
1406
1407 std::vector<std::string> matches;
1408 if (keyPair.value().type() ==
1409 nlohmann::json::value_t::string)
1410 {
1411 matches.emplace_back(keyPair.value());
1412 }
1413 else
1414 {
1415 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001416 {
James Feist668bbb12020-02-05 14:27:26 -08001417 if (value.type() !=
1418 nlohmann::json::value_t::string)
1419 {
1420 std::cerr << "Value is invalid type "
1421 << value << "\n";
1422 break;
1423 }
1424 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001425 }
James Feist668bbb12020-02-05 14:27:26 -08001426 }
James Feist08a5b172019-08-28 14:47:47 -07001427
James Feist668bbb12020-02-05 14:27:26 -08001428 std::set<std::string> foundMatches;
1429 for (auto& configurationPair :
1430 _systemConfiguration.items())
1431 {
1432 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001433 {
James Feist668bbb12020-02-05 14:27:26 -08001434 // don't disable ourselves
1435 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001436 {
James Feist1b2e2242018-01-30 13:45:19 -08001437 continue;
1438 }
James Feist668bbb12020-02-05 14:27:26 -08001439 }
1440 auto configListFind =
1441 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001442
James Feist668bbb12020-02-05 14:27:26 -08001443 if (configListFind ==
1444 configurationPair.value().end() ||
1445 configListFind->type() !=
1446 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001447 {
James Feist668bbb12020-02-05 14:27:26 -08001448 continue;
James Feist08a5b172019-08-28 14:47:47 -07001449 }
James Feist668bbb12020-02-05 14:27:26 -08001450 for (auto& exposedObject : *configListFind)
1451 {
1452 auto matchIt = std::find_if(
1453 matches.begin(), matches.end(),
1454 [name = (exposedObject)["Name"]
1455 .get<std::string>()](
1456 const std::string& s) {
1457 return s == name;
1458 });
1459 if (matchIt == matches.end())
1460 {
1461 continue;
1462 }
1463 foundMatches.insert(*matchIt);
1464
1465 if (isBind)
1466 {
1467 std::string bind = keyPair.key().substr(
1468 sizeof("Bind") - 1);
1469
1470 exposedObject["Status"] = "okay";
1471 expose[bind] = exposedObject;
1472 }
1473 else if (isDisable)
1474 {
1475 exposedObject["Status"] = "disabled";
1476 }
1477 }
1478 }
1479 if (foundMatches.size() != matches.size())
1480 {
1481 std::cerr << "configuration file "
1482 "dependency error, "
1483 "could not find "
1484 << keyPair.key() << " "
1485 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001486 }
1487 }
1488 }
James Feist08a5b172019-08-28 14:47:47 -07001489 // overwrite ourselves with cleaned up version
1490 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001491 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001492 }
1493 });
James Feist787c3c32019-11-07 14:42:58 -08001494
James Feist733f7652019-11-13 14:32:29 -08001495 // parse out dbus probes by discarding other probe types, store in a
1496 // map
1497 for (const std::string& probe : probeCommand)
1498 {
1499 bool found = false;
1500 boost::container::flat_map<const char*, probe_type_codes,
1501 cmp_str>::const_iterator probeType;
1502 for (probeType = PROBE_TYPES.begin();
1503 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001504 {
James Feist733f7652019-11-13 14:32:29 -08001505 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001506 {
James Feist733f7652019-11-13 14:32:29 -08001507 found = true;
1508 break;
James Feist787c3c32019-11-07 14:42:58 -08001509 }
James Feist787c3c32019-11-07 14:42:58 -08001510 }
James Feist733f7652019-11-13 14:32:29 -08001511 if (found)
1512 {
1513 continue;
1514 }
1515 // syntax requires probe before first open brace
1516 auto findStart = probe.find("(");
1517 std::string interface = probe.substr(0, findStart);
1518 dbusProbeInterfaces.emplace(interface);
1519 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001520 }
James Feist733f7652019-11-13 14:32:29 -08001521 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001522 }
James Feist75fdeeb2018-02-20 14:26:16 -08001523
James Feist733f7652019-11-13 14:32:29 -08001524 // probe vector stores a shared_ptr to each PerformProbe that cares
1525 // about a dbus interface
1526 findDbusObjects(dbusProbePointers, SYSTEM_BUS, dbusProbeInterfaces,
1527 shared_from_this());
1528}
1529
1530PerformScan::~PerformScan()
1531{
1532 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001533 {
James Feist733f7652019-11-13 14:32:29 -08001534 auto nextScan = std::make_shared<PerformScan>(
1535 _systemConfiguration, _missingConfigurations, _configurations,
1536 std::move(_callback));
1537 nextScan->passedProbes = std::move(passedProbes);
1538 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1539 nextScan->run();
James Feist8f2710a2018-05-09 17:18:55 -07001540 }
James Feist733f7652019-11-13 14:32:29 -08001541 else
1542 {
1543 _callback(dbusProbeObjects);
1544 }
1545}
James Feistc95cb142018-02-26 10:41:42 -08001546
James Feist1df06a42019-04-11 14:23:04 -07001547void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001548 nlohmann::json& systemConfiguration)
1549{
1550 static bool scannedPowerOff = false;
1551 static bool scannedPowerOn = false;
1552
James Feistfb00f392019-06-25 14:16:48 -07001553 if (systemConfiguration.empty() || lastJson.empty())
1554 {
1555 return; // not ready yet
1556 }
James Feist1df06a42019-04-11 14:23:04 -07001557 if (scannedPowerOn)
1558 {
1559 return;
1560 }
1561
1562 if (!isPowerOn() && scannedPowerOff)
1563 {
1564 return;
1565 }
1566
1567 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001568 timer.async_wait(
1569 [&systemConfiguration](const boost::system::error_code& ec) {
1570 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001571 {
James Feist1a996582019-05-14 15:10:06 -07001572 // we were cancelled
1573 return;
1574 }
1575
1576 bool powerOff = !isPowerOn();
1577 for (const auto& item : lastJson.items())
1578 {
1579 if (systemConfiguration.find(item.key()) ==
1580 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001581 {
James Feist1a996582019-05-14 15:10:06 -07001582 bool isDetectedPowerOn = false;
1583 auto powerState = item.value().find("PowerState");
1584 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001585 {
James Feist1a996582019-05-14 15:10:06 -07001586 auto ptr = powerState->get_ptr<const std::string*>();
1587 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001588 {
James Feist1a996582019-05-14 15:10:06 -07001589 if (*ptr == "On" || *ptr == "BiosPost")
1590 {
1591 isDetectedPowerOn = true;
1592 }
James Feist1df06a42019-04-11 14:23:04 -07001593 }
1594 }
James Feist1a996582019-05-14 15:10:06 -07001595 if (powerOff && isDetectedPowerOn)
1596 {
1597 // power not on yet, don't know if it's there or not
1598 continue;
1599 }
1600 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1601 {
1602 // already logged it when power was off
1603 continue;
1604 }
James Feist1df06a42019-04-11 14:23:04 -07001605
James Feist1a996582019-05-14 15:10:06 -07001606 logDeviceRemoved(item.value());
1607 }
James Feist1df06a42019-04-11 14:23:04 -07001608 }
James Feist1a996582019-05-14 15:10:06 -07001609 scannedPowerOff = true;
1610 if (!powerOff)
1611 {
1612 scannedPowerOn = true;
1613 }
1614 });
James Feist1df06a42019-04-11 14:23:04 -07001615}
1616
James Feist8f2710a2018-05-09 17:18:55 -07001617// main properties changed entry
1618void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001619 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1620 nlohmann::json& systemConfiguration,
1621 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001622{
1623 static boost::asio::deadline_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001624 static size_t instance = 0;
1625 instance++;
1626 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001627
James Feist899e17f2019-09-13 11:46:29 -07001628 timer.expires_from_now(boost::posix_time::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001629
1630 // setup an async wait as we normally get flooded with new requests
James Feist02d2b932020-02-06 16:28:48 -08001631 timer.async_wait([&systemConfiguration, &dbusMatches, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001632 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001633 if (ec == boost::asio::error::operation_aborted)
1634 {
1635 // we were cancelled
1636 return;
1637 }
1638 else if (ec)
1639 {
1640 std::cerr << "async wait error " << ec << "\n";
1641 return;
1642 }
1643
1644 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001645 auto missingConfigurations = std::make_shared<nlohmann::json>();
1646 *missingConfigurations = systemConfiguration;
1647
James Feist8f2710a2018-05-09 17:18:55 -07001648 std::list<nlohmann::json> configurations;
1649 if (!findJsonFiles(configurations))
1650 {
1651 std::cerr << "cannot find json files\n";
1652 return;
1653 }
1654
1655 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001656 systemConfiguration, *missingConfigurations, configurations,
James Feist02d2b932020-02-06 16:28:48 -08001657 [&systemConfiguration, &objServer, &dbusMatches, count,
James Feist733f7652019-11-13 14:32:29 -08001658 oldConfiguration,
1659 missingConfigurations](const DBusProbeObjectT& dbusProbeObjects) {
James Feist899e17f2019-09-13 11:46:29 -07001660 // this is something that since ac has been applied to the bmc
1661 // we saw, and we no longer see it
1662 bool powerOff = !isPowerOn();
1663 for (const auto& item : missingConfigurations->items())
1664 {
1665 bool isDetectedPowerOn = false;
1666 auto powerState = item.value().find("PowerState");
1667 if (powerState != item.value().end())
1668 {
1669 auto ptr = powerState->get_ptr<const std::string*>();
1670 if (ptr)
1671 {
1672 if (*ptr == "On" || *ptr == "BiosPost")
1673 {
1674 isDetectedPowerOn = true;
1675 }
1676 }
1677 }
1678 if (powerOff && isDetectedPowerOn)
1679 {
1680 // power not on yet, don't know if it's there or not
1681 continue;
1682 }
1683 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001684 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001685 ifaces = inventory[name];
1686 for (auto& iface : ifaces)
1687 {
James Feist02d2b932020-02-06 16:28:48 -08001688 auto sharedPtr = iface.lock();
1689 if (!sharedPtr)
1690 {
1691 continue; // was already deleted elsewhere
1692 }
1693 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001694 }
1695 ifaces.clear();
1696 systemConfiguration.erase(item.key());
1697 logDeviceRemoved(item.value());
1698 }
1699
James Feist8f2710a2018-05-09 17:18:55 -07001700 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001701 for (auto it = newConfiguration.begin();
1702 it != newConfiguration.end();)
1703 {
1704 auto findKey = oldConfiguration.find(it.key());
1705 if (findKey != oldConfiguration.end())
1706 {
1707 it = newConfiguration.erase(it);
1708 }
1709 else
1710 {
1711 it++;
1712 }
1713 }
James Feist899e17f2019-09-13 11:46:29 -07001714 for (const auto& item : newConfiguration.items())
1715 {
1716 logDeviceAdded(item.value());
1717 }
1718
James Feist02d2b932020-02-06 16:28:48 -08001719 registerCallbacks(dbusMatches, systemConfiguration, objServer,
1720 dbusProbeObjects);
James Feist8f2710a2018-05-09 17:18:55 -07001721 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001722 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001723
James Feistbb43d022018-06-12 15:44:33 -07001724 io.post([&]() {
1725 if (!writeJsonFiles(systemConfiguration))
1726 {
1727 std::cerr << "Error writing json files\n";
1728 }
1729 });
James Feist8f2710a2018-05-09 17:18:55 -07001730 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001731 postToDbus(newConfiguration, systemConfiguration,
1732 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001733 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001734 {
James Feist899e17f2019-09-13 11:46:29 -07001735 return;
James Feist1df06a42019-04-11 14:23:04 -07001736 }
James Feist899e17f2019-09-13 11:46:29 -07001737 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001738 });
1739 });
1740 });
1741 perfScan->run();
1742 });
James Feist75fdeeb2018-02-20 14:26:16 -08001743}
1744
James Feist02d2b932020-02-06 16:28:48 -08001745void registerCallbacks(std::vector<sdbusplus::bus::match::match>& dbusMatches,
James Feista465ccc2019-02-08 12:51:01 -08001746 nlohmann::json& systemConfiguration,
James Feist733f7652019-11-13 14:32:29 -08001747 sdbusplus::asio::object_server& objServer,
1748 const DBusProbeObjectT& dbusProbeObjects)
James Feist75fdeeb2018-02-20 14:26:16 -08001749{
1750 static boost::container::flat_set<std::string> watchedObjects;
1751
James Feist733f7652019-11-13 14:32:29 -08001752 for (const auto& objectMap : dbusProbeObjects)
James Feist75fdeeb2018-02-20 14:26:16 -08001753 {
James Feist787c3c32019-11-07 14:42:58 -08001754 auto [_, inserted] = watchedObjects.insert(objectMap.first);
1755 if (!inserted)
James Feist75fdeeb2018-02-20 14:26:16 -08001756 {
1757 continue;
1758 }
James Feist8f2710a2018-05-09 17:18:55 -07001759 std::function<void(sdbusplus::message::message & message)>
1760 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001761
James Feista465ccc2019-02-08 12:51:01 -08001762 [&](sdbusplus::message::message&) {
James Feist02d2b932020-02-06 16:28:48 -08001763 propertiesChangedCallback(dbusMatches, systemConfiguration,
1764 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001765 };
1766
1767 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001768 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001769 "type='signal',member='PropertiesChanged',arg0='" +
1770 objectMap.first + "'",
1771 eventHandler);
1772 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001773 }
1774}
1775
James Feist98132792019-07-09 13:29:09 -07001776int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001777{
1778 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001779 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001780 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001781
James Feist8f2710a2018-05-09 17:18:55 -07001782 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001783
James Feist8f2710a2018-05-09 17:18:55 -07001784 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1785 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1786 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001787
James Feist8f2710a2018-05-09 17:18:55 -07001788 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1789 objServer.add_interface("/xyz/openbmc_project/inventory",
1790 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001791
1792 // to keep reference to the match / filter objects so they don't get
1793 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001794 std::vector<sdbusplus::bus::match::match> dbusMatches;
1795
1796 nlohmann::json systemConfiguration = nlohmann::json::object();
1797
1798 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001799 "Notify",
1800 [](const boost::container::flat_map<
1801 std::string,
James Feist98132792019-07-09 13:29:09 -07001802 boost::container::flat_map<std::string, BasicVariantType>>&) {
1803 return;
1804 });
James Feist8f2710a2018-05-09 17:18:55 -07001805 inventoryIface->initialize();
1806
1807 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001808#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001809 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001810#endif
James Feist02d2b932020-02-06 16:28:48 -08001811 propertiesChangedCallback(dbusMatches, systemConfiguration, objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001812 });
James Feist4131aea2018-03-09 09:47:30 -08001813
James Feistfd1264a2018-05-03 12:10:00 -07001814 entityIface->register_method("ReScan", [&]() {
James Feist02d2b932020-02-06 16:28:48 -08001815 propertiesChangedCallback(dbusMatches, systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001816 });
James Feist8f2710a2018-05-09 17:18:55 -07001817 entityIface->initialize();
1818
James Feist1df06a42019-04-11 14:23:04 -07001819 if (fwVersionIsSame())
1820 {
1821 if (std::filesystem::is_regular_file(currentConfiguration))
1822 {
1823 // this file could just be deleted, but it's nice for debug
1824 std::filesystem::create_directory(tempConfigDir);
1825 std::filesystem::remove(lastConfiguration);
1826 std::filesystem::copy(currentConfiguration, lastConfiguration);
1827 std::filesystem::remove(currentConfiguration);
1828
1829 std::ifstream jsonStream(lastConfiguration);
1830 if (jsonStream.good())
1831 {
1832 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1833 if (data.is_discarded())
1834 {
1835 std::cerr << "syntax error in " << lastConfiguration
1836 << "\n";
1837 }
1838 else
1839 {
1840 lastJson = std::move(data);
1841 }
1842 }
1843 else
1844 {
1845 std::cerr << "unable to open " << lastConfiguration << "\n";
1846 }
1847 }
1848 }
1849 else
1850 {
1851 // not an error, just logging at this level to make it in the journal
1852 std::cerr << "Clearing previous configuration\n";
1853 std::filesystem::remove(currentConfiguration);
1854 }
1855
1856 // some boards only show up after power is on, we want to not say they are
1857 // removed until the same state happens
1858 setupPowerMatch(SYSTEM_BUS);
1859
James Feist1b2e2242018-01-30 13:45:19 -08001860 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001861
1862 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001863}