blob: 8928b823a95eb9ceb2b797be55130cc8df02d10b [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 Feist733f7652019-11-13 14:32:29 -08001197PerformScan::PerformScan(
1198 nlohmann::json& systemConfiguration, nlohmann::json& missingConfigurations,
1199 std::list<nlohmann::json>& configurations,
1200 std::function<void(const DBusProbeObjectT&)>&& callback) :
1201 _systemConfiguration(systemConfiguration),
1202 _missingConfigurations(missingConfigurations),
1203 _configurations(configurations), _callback(std::move(callback))
James Feist75fdeeb2018-02-20 14:26:16 -08001204{
James Feist733f7652019-11-13 14:32:29 -08001205}
1206void PerformScan::run()
1207{
1208 boost::container::flat_set<std::string> dbusProbeInterfaces;
1209 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001210
James Feist733f7652019-11-13 14:32:29 -08001211 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001212 {
James Feist733f7652019-11-13 14:32:29 -08001213 auto findProbe = it->find("Probe");
1214 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001215
James Feist733f7652019-11-13 14:32:29 -08001216 nlohmann::json probeCommand;
1217 // check for poorly formatted fields, probe must be an array
1218 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001219 {
James Feist733f7652019-11-13 14:32:29 -08001220 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1221 it = _configurations.erase(it);
1222 continue;
1223 }
1224 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1225 {
1226 probeCommand = nlohmann::json::array();
1227 probeCommand.push_back(*findProbe);
1228 }
1229 else
1230 {
1231 probeCommand = *findProbe;
1232 }
James Feist3cb5fec2018-01-23 14:41:51 -08001233
James Feist733f7652019-11-13 14:32:29 -08001234 if (findName == it->end())
1235 {
1236 std::cerr << "configuration file missing name:\n " << *it << "\n";
1237 it = _configurations.erase(it);
1238 continue;
1239 }
1240 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001241
James Feist733f7652019-11-13 14:32:29 -08001242 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1243 passedProbes.end())
1244 {
1245 it = _configurations.erase(it);
1246 continue;
1247 }
1248 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001249
James Feist733f7652019-11-13 14:32:29 -08001250 // store reference to this to children to makes sure we don't get
1251 // destroyed too early
1252 auto thisRef = shared_from_this();
1253 auto probePointer = std::make_shared<PerformProbe>(
1254 probeCommand, thisRef,
1255 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001256 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001257
James Feist733f7652019-11-13 14:32:29 -08001258 passedProbes.push_back(probeName);
James Feist08a5b172019-08-28 14:47:47 -07001259 size_t foundDeviceIdx = 1;
James Feist8f2710a2018-05-09 17:18:55 -07001260
James Feist08a5b172019-08-28 14:47:47 -07001261 for (auto& foundDevice : foundDevices)
1262 {
1263 nlohmann::json record = *recordPtr;
1264 std::string recordName;
1265 size_t hash = 0;
1266 if (foundDevice.size())
James Feist3cb5fec2018-01-23 14:41:51 -08001267 {
James Feist08a5b172019-08-28 14:47:47 -07001268 // use an array so alphabetical order from the
1269 // flat_map is maintained
1270 auto device = nlohmann::json::array();
1271 for (auto& devPair : foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001272 {
James Feist08a5b172019-08-28 14:47:47 -07001273 device.push_back(devPair.first);
1274 std::visit(
1275 [&device](auto&& v) { device.push_back(v); },
1276 devPair.second);
1277 }
1278 hash =
1279 std::hash<std::string>{}(probeName + device.dump());
1280 // hashes are hard to distinguish, use the
1281 // non-hashed version if we want debug
1282 if constexpr (DEBUG)
1283 {
1284 recordName = probeName + device.dump();
James Feist8f2710a2018-05-09 17:18:55 -07001285 }
James Feistf1b14142019-04-10 15:22:09 -07001286 else
1287 {
James Feist08a5b172019-08-28 14:47:47 -07001288 recordName = std::to_string(hash);
James Feistf1b14142019-04-10 15:22:09 -07001289 }
James Feist08a5b172019-08-28 14:47:47 -07001290 }
1291 else
1292 {
1293 recordName = probeName;
1294 }
James Feistf1b14142019-04-10 15:22:09 -07001295
James Feist08a5b172019-08-28 14:47:47 -07001296 auto fromLastJson = lastJson.find(recordName);
1297 if (fromLastJson != lastJson.end())
1298 {
James Feist02d2b932020-02-06 16:28:48 -08001299 auto findExposes = fromLastJson->find("Exposes");
1300 // delete nulls from any updates
1301 if (findExposes != fromLastJson->end())
1302 {
1303 auto copy = nlohmann::json::array();
1304 for (auto& expose : *findExposes)
1305 {
1306 if (expose.is_null())
1307 {
1308 continue;
1309 }
1310 copy.emplace_back(expose);
1311 }
1312 *findExposes = copy;
1313 }
1314
James Feist08a5b172019-08-28 14:47:47 -07001315 // keep user changes
1316 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001317 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001318 continue;
1319 }
James Feist1df06a42019-04-11 14:23:04 -07001320
James Feist08a5b172019-08-28 14:47:47 -07001321 // insert into configuration temporarily to be able to
1322 // reference ourselves
James Feistf1b14142019-04-10 15:22:09 -07001323
James Feist08a5b172019-08-28 14:47:47 -07001324 _systemConfiguration[recordName] = record;
1325
1326 for (auto keyPair = record.begin(); keyPair != record.end();
1327 keyPair++)
1328 {
1329 templateCharReplace(keyPair, foundDevice,
1330 foundDeviceIdx);
1331 }
1332
1333 auto findExpose = record.find("Exposes");
1334 if (findExpose == record.end())
1335 {
James Feistf1b14142019-04-10 15:22:09 -07001336 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001337 foundDeviceIdx++;
1338 continue;
1339 }
James Feistf1b14142019-04-10 15:22:09 -07001340
James Feist08a5b172019-08-28 14:47:47 -07001341 for (auto& expose : *findExpose)
1342 {
1343 for (auto keyPair = expose.begin();
1344 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001345 {
James Feist08a5b172019-08-28 14:47:47 -07001346
1347 templateCharReplace(keyPair, foundDevice,
1348 foundDeviceIdx);
1349
James Feist668bbb12020-02-05 14:27:26 -08001350 bool isBind =
1351 boost::starts_with(keyPair.key(), "Bind");
1352 bool isDisable = keyPair.key() == "DisableNode";
1353
1354 // special cases
1355 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001356 {
James Feist668bbb12020-02-05 14:27:26 -08001357 continue;
1358 }
1359
1360 if (keyPair.value().type() !=
1361 nlohmann::json::value_t::string &&
1362 keyPair.value().type() !=
1363 nlohmann::json::value_t::array)
1364 {
1365 std::cerr << "Value is invalid type "
1366 << keyPair.key() << "\n";
1367 continue;
1368 }
1369
1370 std::vector<std::string> matches;
1371 if (keyPair.value().type() ==
1372 nlohmann::json::value_t::string)
1373 {
1374 matches.emplace_back(keyPair.value());
1375 }
1376 else
1377 {
1378 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001379 {
James Feist668bbb12020-02-05 14:27:26 -08001380 if (value.type() !=
1381 nlohmann::json::value_t::string)
1382 {
1383 std::cerr << "Value is invalid type "
1384 << value << "\n";
1385 break;
1386 }
1387 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001388 }
James Feist668bbb12020-02-05 14:27:26 -08001389 }
James Feist08a5b172019-08-28 14:47:47 -07001390
James Feist668bbb12020-02-05 14:27:26 -08001391 std::set<std::string> foundMatches;
1392 for (auto& configurationPair :
1393 _systemConfiguration.items())
1394 {
1395 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001396 {
James Feist668bbb12020-02-05 14:27:26 -08001397 // don't disable ourselves
1398 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001399 {
James Feist1b2e2242018-01-30 13:45:19 -08001400 continue;
1401 }
James Feist668bbb12020-02-05 14:27:26 -08001402 }
1403 auto configListFind =
1404 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001405
James Feist668bbb12020-02-05 14:27:26 -08001406 if (configListFind ==
1407 configurationPair.value().end() ||
1408 configListFind->type() !=
1409 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001410 {
James Feist668bbb12020-02-05 14:27:26 -08001411 continue;
James Feist08a5b172019-08-28 14:47:47 -07001412 }
James Feist668bbb12020-02-05 14:27:26 -08001413 for (auto& exposedObject : *configListFind)
1414 {
1415 auto matchIt = std::find_if(
1416 matches.begin(), matches.end(),
1417 [name = (exposedObject)["Name"]
1418 .get<std::string>()](
1419 const std::string& s) {
1420 return s == name;
1421 });
1422 if (matchIt == matches.end())
1423 {
1424 continue;
1425 }
1426 foundMatches.insert(*matchIt);
1427
1428 if (isBind)
1429 {
1430 std::string bind = keyPair.key().substr(
1431 sizeof("Bind") - 1);
1432
1433 exposedObject["Status"] = "okay";
1434 expose[bind] = exposedObject;
1435 }
1436 else if (isDisable)
1437 {
1438 exposedObject["Status"] = "disabled";
1439 }
1440 }
1441 }
1442 if (foundMatches.size() != matches.size())
1443 {
1444 std::cerr << "configuration file "
1445 "dependency error, "
1446 "could not find "
1447 << keyPair.key() << " "
1448 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001449 }
1450 }
1451 }
James Feist08a5b172019-08-28 14:47:47 -07001452 // overwrite ourselves with cleaned up version
1453 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001454 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001455
1456 foundDeviceIdx++;
1457 }
1458 });
James Feist787c3c32019-11-07 14:42:58 -08001459
James Feist733f7652019-11-13 14:32:29 -08001460 // parse out dbus probes by discarding other probe types, store in a
1461 // map
1462 for (const std::string& probe : probeCommand)
1463 {
1464 bool found = false;
1465 boost::container::flat_map<const char*, probe_type_codes,
1466 cmp_str>::const_iterator probeType;
1467 for (probeType = PROBE_TYPES.begin();
1468 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001469 {
James Feist733f7652019-11-13 14:32:29 -08001470 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001471 {
James Feist733f7652019-11-13 14:32:29 -08001472 found = true;
1473 break;
James Feist787c3c32019-11-07 14:42:58 -08001474 }
James Feist787c3c32019-11-07 14:42:58 -08001475 }
James Feist733f7652019-11-13 14:32:29 -08001476 if (found)
1477 {
1478 continue;
1479 }
1480 // syntax requires probe before first open brace
1481 auto findStart = probe.find("(");
1482 std::string interface = probe.substr(0, findStart);
1483 dbusProbeInterfaces.emplace(interface);
1484 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001485 }
James Feist733f7652019-11-13 14:32:29 -08001486 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001487 }
James Feist75fdeeb2018-02-20 14:26:16 -08001488
James Feist733f7652019-11-13 14:32:29 -08001489 // probe vector stores a shared_ptr to each PerformProbe that cares
1490 // about a dbus interface
1491 findDbusObjects(dbusProbePointers, SYSTEM_BUS, dbusProbeInterfaces,
1492 shared_from_this());
1493}
1494
1495PerformScan::~PerformScan()
1496{
1497 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001498 {
James Feist733f7652019-11-13 14:32:29 -08001499 auto nextScan = std::make_shared<PerformScan>(
1500 _systemConfiguration, _missingConfigurations, _configurations,
1501 std::move(_callback));
1502 nextScan->passedProbes = std::move(passedProbes);
1503 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1504 nextScan->run();
James Feist8f2710a2018-05-09 17:18:55 -07001505 }
James Feist733f7652019-11-13 14:32:29 -08001506 else
1507 {
1508 _callback(dbusProbeObjects);
1509 }
1510}
James Feistc95cb142018-02-26 10:41:42 -08001511
James Feist1df06a42019-04-11 14:23:04 -07001512void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001513 nlohmann::json& systemConfiguration)
1514{
1515 static bool scannedPowerOff = false;
1516 static bool scannedPowerOn = false;
1517
James Feistfb00f392019-06-25 14:16:48 -07001518 if (systemConfiguration.empty() || lastJson.empty())
1519 {
1520 return; // not ready yet
1521 }
James Feist1df06a42019-04-11 14:23:04 -07001522 if (scannedPowerOn)
1523 {
1524 return;
1525 }
1526
1527 if (!isPowerOn() && scannedPowerOff)
1528 {
1529 return;
1530 }
1531
1532 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001533 timer.async_wait(
1534 [&systemConfiguration](const boost::system::error_code& ec) {
1535 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001536 {
James Feist1a996582019-05-14 15:10:06 -07001537 // we were cancelled
1538 return;
1539 }
1540
1541 bool powerOff = !isPowerOn();
1542 for (const auto& item : lastJson.items())
1543 {
1544 if (systemConfiguration.find(item.key()) ==
1545 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001546 {
James Feist1a996582019-05-14 15:10:06 -07001547 bool isDetectedPowerOn = false;
1548 auto powerState = item.value().find("PowerState");
1549 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001550 {
James Feist1a996582019-05-14 15:10:06 -07001551 auto ptr = powerState->get_ptr<const std::string*>();
1552 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001553 {
James Feist1a996582019-05-14 15:10:06 -07001554 if (*ptr == "On" || *ptr == "BiosPost")
1555 {
1556 isDetectedPowerOn = true;
1557 }
James Feist1df06a42019-04-11 14:23:04 -07001558 }
1559 }
James Feist1a996582019-05-14 15:10:06 -07001560 if (powerOff && isDetectedPowerOn)
1561 {
1562 // power not on yet, don't know if it's there or not
1563 continue;
1564 }
1565 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1566 {
1567 // already logged it when power was off
1568 continue;
1569 }
James Feist1df06a42019-04-11 14:23:04 -07001570
James Feist1a996582019-05-14 15:10:06 -07001571 logDeviceRemoved(item.value());
1572 }
James Feist1df06a42019-04-11 14:23:04 -07001573 }
James Feist1a996582019-05-14 15:10:06 -07001574 scannedPowerOff = true;
1575 if (!powerOff)
1576 {
1577 scannedPowerOn = true;
1578 }
1579 });
James Feist1df06a42019-04-11 14:23:04 -07001580}
1581
James Feist8f2710a2018-05-09 17:18:55 -07001582// main properties changed entry
1583void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001584 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1585 nlohmann::json& systemConfiguration,
1586 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001587{
1588 static boost::asio::deadline_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001589 static size_t instance = 0;
1590 instance++;
1591 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001592
James Feist899e17f2019-09-13 11:46:29 -07001593 timer.expires_from_now(boost::posix_time::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001594
1595 // setup an async wait as we normally get flooded with new requests
James Feist02d2b932020-02-06 16:28:48 -08001596 timer.async_wait([&systemConfiguration, &dbusMatches, &objServer,
James Feist899e17f2019-09-13 11:46:29 -07001597 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001598 if (ec == boost::asio::error::operation_aborted)
1599 {
1600 // we were cancelled
1601 return;
1602 }
1603 else if (ec)
1604 {
1605 std::cerr << "async wait error " << ec << "\n";
1606 return;
1607 }
1608
1609 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001610 auto missingConfigurations = std::make_shared<nlohmann::json>();
1611 *missingConfigurations = systemConfiguration;
1612
James Feist8f2710a2018-05-09 17:18:55 -07001613 std::list<nlohmann::json> configurations;
1614 if (!findJsonFiles(configurations))
1615 {
1616 std::cerr << "cannot find json files\n";
1617 return;
1618 }
1619
1620 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001621 systemConfiguration, *missingConfigurations, configurations,
James Feist02d2b932020-02-06 16:28:48 -08001622 [&systemConfiguration, &objServer, &dbusMatches, count,
James Feist733f7652019-11-13 14:32:29 -08001623 oldConfiguration,
1624 missingConfigurations](const DBusProbeObjectT& dbusProbeObjects) {
James Feist899e17f2019-09-13 11:46:29 -07001625 // this is something that since ac has been applied to the bmc
1626 // we saw, and we no longer see it
1627 bool powerOff = !isPowerOn();
1628 for (const auto& item : missingConfigurations->items())
1629 {
1630 bool isDetectedPowerOn = false;
1631 auto powerState = item.value().find("PowerState");
1632 if (powerState != item.value().end())
1633 {
1634 auto ptr = powerState->get_ptr<const std::string*>();
1635 if (ptr)
1636 {
1637 if (*ptr == "On" || *ptr == "BiosPost")
1638 {
1639 isDetectedPowerOn = true;
1640 }
1641 }
1642 }
1643 if (powerOff && isDetectedPowerOn)
1644 {
1645 // power not on yet, don't know if it's there or not
1646 continue;
1647 }
1648 std::string name = item.value()["Name"].get<std::string>();
James Feist02d2b932020-02-06 16:28:48 -08001649 std::vector<std::weak_ptr<sdbusplus::asio::dbus_interface>>&
James Feist899e17f2019-09-13 11:46:29 -07001650 ifaces = inventory[name];
1651 for (auto& iface : ifaces)
1652 {
James Feist02d2b932020-02-06 16:28:48 -08001653 auto sharedPtr = iface.lock();
1654 if (!sharedPtr)
1655 {
1656 continue; // was already deleted elsewhere
1657 }
1658 objServer.remove_interface(sharedPtr);
James Feist899e17f2019-09-13 11:46:29 -07001659 }
1660 ifaces.clear();
1661 systemConfiguration.erase(item.key());
1662 logDeviceRemoved(item.value());
1663 }
1664
James Feist8f2710a2018-05-09 17:18:55 -07001665 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001666 for (auto it = newConfiguration.begin();
1667 it != newConfiguration.end();)
1668 {
1669 auto findKey = oldConfiguration.find(it.key());
1670 if (findKey != oldConfiguration.end())
1671 {
1672 it = newConfiguration.erase(it);
1673 }
1674 else
1675 {
1676 it++;
1677 }
1678 }
James Feist899e17f2019-09-13 11:46:29 -07001679 for (const auto& item : newConfiguration.items())
1680 {
1681 logDeviceAdded(item.value());
1682 }
1683
James Feist02d2b932020-02-06 16:28:48 -08001684 registerCallbacks(dbusMatches, systemConfiguration, objServer,
1685 dbusProbeObjects);
James Feist8f2710a2018-05-09 17:18:55 -07001686 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001687 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001688
James Feistbb43d022018-06-12 15:44:33 -07001689 io.post([&]() {
1690 if (!writeJsonFiles(systemConfiguration))
1691 {
1692 std::cerr << "Error writing json files\n";
1693 }
1694 });
James Feist8f2710a2018-05-09 17:18:55 -07001695 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001696 postToDbus(newConfiguration, systemConfiguration,
1697 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001698 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001699 {
James Feist899e17f2019-09-13 11:46:29 -07001700 return;
James Feist1df06a42019-04-11 14:23:04 -07001701 }
James Feist899e17f2019-09-13 11:46:29 -07001702 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001703 });
1704 });
1705 });
1706 perfScan->run();
1707 });
James Feist75fdeeb2018-02-20 14:26:16 -08001708}
1709
James Feist02d2b932020-02-06 16:28:48 -08001710void registerCallbacks(std::vector<sdbusplus::bus::match::match>& dbusMatches,
James Feista465ccc2019-02-08 12:51:01 -08001711 nlohmann::json& systemConfiguration,
James Feist733f7652019-11-13 14:32:29 -08001712 sdbusplus::asio::object_server& objServer,
1713 const DBusProbeObjectT& dbusProbeObjects)
James Feist75fdeeb2018-02-20 14:26:16 -08001714{
1715 static boost::container::flat_set<std::string> watchedObjects;
1716
James Feist733f7652019-11-13 14:32:29 -08001717 for (const auto& objectMap : dbusProbeObjects)
James Feist75fdeeb2018-02-20 14:26:16 -08001718 {
James Feist787c3c32019-11-07 14:42:58 -08001719 auto [_, inserted] = watchedObjects.insert(objectMap.first);
1720 if (!inserted)
James Feist75fdeeb2018-02-20 14:26:16 -08001721 {
1722 continue;
1723 }
James Feist8f2710a2018-05-09 17:18:55 -07001724 std::function<void(sdbusplus::message::message & message)>
1725 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001726
James Feista465ccc2019-02-08 12:51:01 -08001727 [&](sdbusplus::message::message&) {
James Feist02d2b932020-02-06 16:28:48 -08001728 propertiesChangedCallback(dbusMatches, systemConfiguration,
1729 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001730 };
1731
1732 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001733 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001734 "type='signal',member='PropertiesChanged',arg0='" +
1735 objectMap.first + "'",
1736 eventHandler);
1737 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001738 }
1739}
1740
James Feist98132792019-07-09 13:29:09 -07001741int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001742{
1743 // setup connection to dbus
James Feist8f2710a2018-05-09 17:18:55 -07001744 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001745 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001746
James Feist8f2710a2018-05-09 17:18:55 -07001747 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001748
James Feist8f2710a2018-05-09 17:18:55 -07001749 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1750 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1751 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001752
James Feist8f2710a2018-05-09 17:18:55 -07001753 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1754 objServer.add_interface("/xyz/openbmc_project/inventory",
1755 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001756
1757 // to keep reference to the match / filter objects so they don't get
1758 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001759 std::vector<sdbusplus::bus::match::match> dbusMatches;
1760
1761 nlohmann::json systemConfiguration = nlohmann::json::object();
1762
1763 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001764 "Notify",
1765 [](const boost::container::flat_map<
1766 std::string,
James Feist98132792019-07-09 13:29:09 -07001767 boost::container::flat_map<std::string, BasicVariantType>>&) {
1768 return;
1769 });
James Feist8f2710a2018-05-09 17:18:55 -07001770 inventoryIface->initialize();
1771
1772 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001773#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001774 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001775#endif
James Feist02d2b932020-02-06 16:28:48 -08001776 propertiesChangedCallback(dbusMatches, systemConfiguration, objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001777 });
James Feist4131aea2018-03-09 09:47:30 -08001778
James Feistfd1264a2018-05-03 12:10:00 -07001779 entityIface->register_method("ReScan", [&]() {
James Feist02d2b932020-02-06 16:28:48 -08001780 propertiesChangedCallback(dbusMatches, systemConfiguration, objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001781 });
James Feist8f2710a2018-05-09 17:18:55 -07001782 entityIface->initialize();
1783
James Feist1df06a42019-04-11 14:23:04 -07001784 if (fwVersionIsSame())
1785 {
1786 if (std::filesystem::is_regular_file(currentConfiguration))
1787 {
1788 // this file could just be deleted, but it's nice for debug
1789 std::filesystem::create_directory(tempConfigDir);
1790 std::filesystem::remove(lastConfiguration);
1791 std::filesystem::copy(currentConfiguration, lastConfiguration);
1792 std::filesystem::remove(currentConfiguration);
1793
1794 std::ifstream jsonStream(lastConfiguration);
1795 if (jsonStream.good())
1796 {
1797 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1798 if (data.is_discarded())
1799 {
1800 std::cerr << "syntax error in " << lastConfiguration
1801 << "\n";
1802 }
1803 else
1804 {
1805 lastJson = std::move(data);
1806 }
1807 }
1808 else
1809 {
1810 std::cerr << "unable to open " << lastConfiguration << "\n";
1811 }
1812 }
1813 }
1814 else
1815 {
1816 // not an error, just logging at this level to make it in the journal
1817 std::cerr << "Clearing previous configuration\n";
1818 std::filesystem::remove(currentConfiguration);
1819 }
1820
1821 // some boards only show up after power is on, we want to not say they are
1822 // removed until the same state happens
1823 setupPowerMatch(SYSTEM_BUS);
1824
James Feist1b2e2242018-01-30 13:45:19 -08001825 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001826
1827 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001828}