blob: b17711985488e0c88de05324d80228ee756eb6f7 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
James Feist1df06a42019-04-11 14:23:04 -070017#include "EntityManager.hpp"
18
Patrick Venturea49dc332019-10-26 08:32:02 -070019#include "Overlay.hpp"
20#include "Utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070021#include "VariantVisitors.hpp"
22
James Feist11be6672018-04-06 14:05:32 -070023#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070024#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070027#include <boost/algorithm/string/split.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080028#include <boost/container/flat_map.hpp>
29#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070030#include <boost/range/iterator_range.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070031#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080032#include <fstream>
33#include <iostream>
34#include <nlohmann/json.hpp>
35#include <regex>
36#include <sdbusplus/asio/connection.hpp>
37#include <sdbusplus/asio/object_server.hpp>
38#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080039
James Feista465ccc2019-02-08 12:51:01 -080040constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
41constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070042constexpr const char* tempConfigDir = "/tmp/configuration/";
43constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
44constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080045constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070046constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080047
James Feistf1b14142019-04-10 15:22:09 -070048constexpr const bool DEBUG = false;
49
James Feist3cb5fec2018-01-23 14:41:51 -080050struct cmp_str
51{
James Feista465ccc2019-02-08 12:51:01 -080052 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080053 {
54 return std::strcmp(a, b) < 0;
55 }
56};
57
James Feist8f2710a2018-05-09 17:18:55 -070058struct PerformProbe;
59
James Feist3cb5fec2018-01-23 14:41:51 -080060// underscore T for collison with dbus c api
61enum class probe_type_codes
62{
63 FALSE_T,
64 TRUE_T,
65 AND,
66 OR,
James Feist6bd2a022018-03-13 12:30:58 -070067 FOUND,
68 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080069};
James Feista465ccc2019-02-08 12:51:01 -080070const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080071 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
72 {"TRUE", probe_type_codes::TRUE_T},
73 {"AND", probe_type_codes::AND},
74 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070075 {"FOUND", probe_type_codes::FOUND},
76 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080077
James Feist41334262019-03-25 13:30:20 -070078static constexpr std::array<const char*, 5> settableInterfaces = {
79 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070080using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080081 std::variant<std::vector<std::string>, std::vector<double>, std::string,
82 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
83 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080084using GetSubTreeType = std::vector<
85 std::pair<std::string,
86 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
87
88using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070089 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080090 boost::container::flat_map<
91 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070092 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080093
James Feist08a5b172019-08-28 14:47:47 -070094using FoundDeviceT =
95 std::vector<boost::container::flat_map<std::string, BasicVariantType>>;
96
James Feist3cb5fec2018-01-23 14:41:51 -080097boost::container::flat_map<
98 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070099 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -0800100 DBUS_PROBE_OBJECTS;
101std::vector<std::string> PASSED_PROBES;
102
James Feistd58879a2019-09-11 11:26:07 -0700103// store reference to all interfaces so we can destroy them later
104boost::container::flat_map<
105 std::string, std::vector<std::shared_ptr<sdbusplus::asio::dbus_interface>>>
106 inventory;
107
James Feist3cb5fec2018-01-23 14:41:51 -0800108// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700109std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700110static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800111
Johnathan Mantey2015f752019-03-26 15:22:31 -0700112const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
113const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800114
James Feista465ccc2019-02-08 12:51:01 -0800115void registerCallbacks(boost::asio::io_service& io,
116 std::vector<sdbusplus::bus::match::match>& dbusMatches,
117 nlohmann::json& systemConfiguration,
118 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800119
James Feistd58879a2019-09-11 11:26:07 -0700120static std::shared_ptr<sdbusplus::asio::dbus_interface>
121 createInterface(sdbusplus::asio::object_server& objServer,
122 const std::string& path, const std::string& interface,
123 const std::string& parent)
124{
125 return inventory[parent].emplace_back(
126 objServer.add_interface(path, interface));
127}
128
James Feist3cb5fec2018-01-23 14:41:51 -0800129// calls the mapper to find all exposed objects of an interface type
130// and creates a vector<flat_map> that contains all the key value pairs
131// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700132void findDbusObjects(std::shared_ptr<PerformProbe> probe,
133 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800134 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800135{
James Feist8f2710a2018-05-09 17:18:55 -0700136
137 // store reference to pending callbacks so we don't overwhelm services
138 static boost::container::flat_map<
139 std::string, std::vector<std::shared_ptr<PerformProbe>>>
140 pendingProbes;
141
142 if (DBUS_PROBE_OBJECTS[interface].size())
143 {
144 return;
145 }
146
147 // add shared_ptr to vector of Probes waiting for callback from a specific
148 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800149 std::array<const char*, 1> objects = {interface.c_str()};
150 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700151 pendingProbes[interface];
152 auto iter = pending.emplace(pending.end(), probe);
153 // only allow first call to run to not overwhelm processes
154 if (iter != pending.begin())
155 {
156 return;
157 }
158
James Feist3cb5fec2018-01-23 14:41:51 -0800159 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700160 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800161 [connection, interface, probe](boost::system::error_code& ec,
162 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700163 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700164 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700165 {
James Feist0de40152018-07-25 11:56:12 -0700166 pendingProbes[interface].clear();
167 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700168 {
James Feist0de40152018-07-25 11:56:12 -0700169 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700170 }
James Feist0de40152018-07-25 11:56:12 -0700171 std::cerr << "Error communicating to mapper.\n";
172
173 // if we can't communicate to the mapper something is very wrong
174 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700175 }
James Feist8f2710a2018-05-09 17:18:55 -0700176 else
James Feist3cb5fec2018-01-23 14:41:51 -0800177 {
James Feista465ccc2019-02-08 12:51:01 -0800178 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700179 {
James Feista465ccc2019-02-08 12:51:01 -0800180 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700181 {
James Feist0eb40352019-04-09 14:44:04 -0700182 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700183 }
184 }
James Feist3cb5fec2018-01-23 14:41:51 -0800185 }
James Feist63845bf2019-01-24 12:19:51 -0800186 if (interfaceConnections.empty())
187 {
188 pendingProbes[interface].clear();
189 return;
190 }
James Feist8f2710a2018-05-09 17:18:55 -0700191 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800192 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700193 {
194 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700195 [conn,
James Feist98132792019-07-09 13:29:09 -0700196 interface](boost::system::error_code& errc,
James Feista465ccc2019-02-08 12:51:01 -0800197 const ManagedObjectType& managedInterface) {
James Feist98132792019-07-09 13:29:09 -0700198 if (errc)
James Feist8f2710a2018-05-09 17:18:55 -0700199 {
200 std::cerr
201 << "error getting managed object for device "
202 << conn << "\n";
203 pendingProbes[interface].clear();
204 return;
205 }
James Feista465ccc2019-02-08 12:51:01 -0800206 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700207 {
208 auto ifaceObjFind =
209 interfaceManagedObj.second.find(interface);
210 if (ifaceObjFind !=
211 interfaceManagedObj.second.end())
212 {
213 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800214 std::string, BasicVariantType>>&
215 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700216 dbusObject.emplace_back(ifaceObjFind->second);
217 }
218 }
219 pendingProbes[interface].clear();
220 },
221 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
222 "GetManagedObjects");
223 }
224 },
225 "xyz.openbmc_project.ObjectMapper",
226 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700227 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700228 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800229}
James Feist8f2710a2018-05-09 17:18:55 -0700230// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700231bool probeDbus(const std::string& interface,
232 const std::map<std::string, nlohmann::json>& matches,
233 FoundDeviceT& devices, bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800234{
James Feista465ccc2019-02-08 12:51:01 -0800235 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
236 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800237 if (dbusObject.empty())
238 {
James Feist8f2710a2018-05-09 17:18:55 -0700239 foundProbe = false;
240 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800241 }
242 foundProbe = true;
243
244 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800245 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800246 {
247 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800248 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800249 {
250 auto deviceValue = device.find(match.first);
251 if (deviceValue != device.end())
252 {
253 switch (match.second.type())
254 {
James Feist9eb0b582018-04-27 12:15:46 -0700255 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800256 {
James Feist9eb0b582018-04-27 12:15:46 -0700257 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700258 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700259
260 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800261 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700262 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700263 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700264 {
265 deviceMatches = false;
266 break;
267 }
James Feist3cb5fec2018-01-23 14:41:51 -0800268 break;
269 }
James Feist9eb0b582018-04-27 12:15:46 -0700270 case nlohmann::json::value_t::boolean:
271 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800272 {
James Feista465ccc2019-02-08 12:51:01 -0800273 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700274 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800275
James Feist9eb0b582018-04-27 12:15:46 -0700276 if (probeValue != match.second.get<unsigned int>())
277 {
278 deviceMatches = false;
279 }
280 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800281 }
James Feist9eb0b582018-04-27 12:15:46 -0700282 case nlohmann::json::value_t::number_integer:
283 {
James Feista465ccc2019-02-08 12:51:01 -0800284 int probeValue = std::visit(VariantToIntVisitor(),
285 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800286
James Feist9eb0b582018-04-27 12:15:46 -0700287 if (probeValue != match.second.get<int>())
288 {
289 deviceMatches = false;
290 }
291 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800292 }
James Feist9eb0b582018-04-27 12:15:46 -0700293 case nlohmann::json::value_t::number_float:
294 {
James Feista465ccc2019-02-08 12:51:01 -0800295 float probeValue = std::visit(VariantToFloatVisitor(),
296 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700297
298 if (probeValue != match.second.get<float>())
299 {
300 deviceMatches = false;
301 }
302 break;
303 }
James Feist0eb40352019-04-09 14:44:04 -0700304 default:
305 {
306 std::cerr << "unexpected dbus probe type "
307 << match.second.type_name() << "\n";
308 }
James Feist3cb5fec2018-01-23 14:41:51 -0800309 }
310 }
311 else
312 {
313 deviceMatches = false;
314 break;
315 }
316 }
317 if (deviceMatches)
318 {
James Feistf5125b02019-06-06 11:27:43 -0700319 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800320 foundMatch = true;
321 deviceMatches = false; // for next iteration
322 }
323 }
324 return foundMatch;
325}
326
327// default probe entry point, iterates a list looking for specific types to
328// call specific probe functions
329bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800330 const std::vector<std::string>& probeCommand,
James Feist08a5b172019-08-28 14:47:47 -0700331 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
332 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800333{
334 const static std::regex command(R"(\((.*)\))");
335 std::smatch match;
336 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700337 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800338 bool cur = true;
339 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700340 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800341
James Feista465ccc2019-02-08 12:51:01 -0800342 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800343 {
344 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800345 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800346 cmp_str>::const_iterator probeType;
347
348 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700349 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800350 {
351 if (probe.find(probeType->first) != std::string::npos)
352 {
353 foundProbe = true;
354 break;
355 }
356 }
357 if (foundProbe)
358 {
359 switch (probeType->second)
360 {
James Feist9eb0b582018-04-27 12:15:46 -0700361 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800362 {
James Feiste31e00a2019-07-24 10:45:43 -0700363 cur = false;
364 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800365 }
James Feist9eb0b582018-04-27 12:15:46 -0700366 case probe_type_codes::TRUE_T:
367 {
James Feiste31e00a2019-07-24 10:45:43 -0700368 cur = true;
369 break;
James Feist9eb0b582018-04-27 12:15:46 -0700370 }
371 case probe_type_codes::MATCH_ONE:
372 {
373 // set current value to last, this probe type shouldn't
374 // affect the outcome
375 cur = ret;
376 matchOne = true;
377 break;
378 }
379 /*case probe_type_codes::AND:
380 break;
381 case probe_type_codes::OR:
382 break;
383 // these are no-ops until the last command switch
384 */
385 case probe_type_codes::FOUND:
386 {
387 if (!std::regex_search(probe, match, command))
388 {
James Feist0eb40352019-04-09 14:44:04 -0700389 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700390 << "\n";
391 return false;
392 }
393 std::string commandStr = *(match.begin() + 1);
394 boost::replace_all(commandStr, "'", "");
395 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
396 commandStr) != PASSED_PROBES.end());
397 break;
398 }
James Feist0eb40352019-04-09 14:44:04 -0700399 default:
400 {
401 break;
402 }
James Feist3cb5fec2018-01-23 14:41:51 -0800403 }
404 }
405 // look on dbus for object
406 else
407 {
408 if (!std::regex_search(probe, match, command))
409 {
James Feist0eb40352019-04-09 14:44:04 -0700410 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800411 return false;
412 }
413 std::string commandStr = *(match.begin() + 1);
414 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700415 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800416 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800417 auto json = nlohmann::json::parse(commandStr, nullptr, false);
418 if (json.is_discarded())
419 {
James Feist0eb40352019-04-09 14:44:04 -0700420 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800421 return false;
422 }
423 // we can match any (string, variant) property. (string, string)
424 // does a regex
425 std::map<std::string, nlohmann::json> dbusProbeMap =
426 json.get<std::map<std::string, nlohmann::json>>();
427 auto findStart = probe.find("(");
428 if (findStart == std::string::npos)
429 {
430 return false;
431 }
432 std::string probeInterface = probe.substr(0, findStart);
433 cur =
434 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
435 }
436
437 // some functions like AND and OR only take affect after the
438 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700439 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800440 {
James Feist54a0dca2019-06-26 10:34:54 -0700441 ret = cur && ret;
442 }
443 else if (lastCommand == probe_type_codes::OR)
444 {
445 ret = cur || ret;
446 }
447
448 if (first)
449 {
450 ret = cur;
451 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800452 }
453 lastCommand = probeType != PROBE_TYPES.end()
454 ? probeType->second
455 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800456 }
457
458 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800459 if (ret && foundDevs.size() == 0)
460 {
James Feist08a5b172019-08-28 14:47:47 -0700461 foundDevs.emplace_back(
462 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800463 }
James Feist0eb40352019-04-09 14:44:04 -0700464 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700465 {
James Feist71f295f2019-06-20 13:35:12 -0700466 // match the last one
467 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700468 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700469
470 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700471 }
James Feist3cb5fec2018-01-23 14:41:51 -0800472 return ret;
473}
James Feist8f2710a2018-05-09 17:18:55 -0700474// this class finds the needed dbus fields and on destruction runs the probe
475struct PerformProbe : std::enable_shared_from_this<PerformProbe>
476{
James Feist3cb5fec2018-01-23 14:41:51 -0800477
James Feist08a5b172019-08-28 14:47:47 -0700478 PerformProbe(const std::vector<std::string>& probeCommand,
479 std::function<void(FoundDeviceT&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700480 _probeCommand(probeCommand),
481 _callback(std::move(callback))
482 {
483 }
484 ~PerformProbe()
485 {
James Feist08a5b172019-08-28 14:47:47 -0700486 FoundDeviceT foundDevs;
James Feist0eb40352019-04-09 14:44:04 -0700487 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700488 {
James Feist0eb40352019-04-09 14:44:04 -0700489 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700490 }
491 }
492 void run()
493 {
494 // parse out dbus probes by discarding other probe types
James Feist8f2710a2018-05-09 17:18:55 -0700495
James Feista465ccc2019-02-08 12:51:01 -0800496 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700497 {
498 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800499 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700500 cmp_str>::const_iterator probeType;
501 for (probeType = PROBE_TYPES.begin();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700502 probeType != PROBE_TYPES.end(); ++probeType)
James Feist8f2710a2018-05-09 17:18:55 -0700503 {
504 if (probe.find(probeType->first) != std::string::npos)
505 {
506 found = true;
507 break;
508 }
509 }
510 if (found)
511 {
512 continue;
513 }
514 // syntax requires probe before first open brace
515 auto findStart = probe.find("(");
516 std::string interface = probe.substr(0, findStart);
517
518 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
519 }
520 }
521 std::vector<std::string> _probeCommand;
James Feist08a5b172019-08-28 14:47:47 -0700522 std::function<void(FoundDeviceT&)> _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700523};
524
525// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800526bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800527{
James Feist1df06a42019-04-11 14:23:04 -0700528 std::filesystem::create_directory(configurationOutDir);
529 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700530 if (!output.good())
531 {
532 return false;
533 }
James Feist1b2e2242018-01-30 13:45:19 -0800534 output << systemConfiguration.dump(4);
535 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700536 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700537}
James Feist1b2e2242018-01-30 13:45:19 -0800538
James Feist97a63f12018-05-17 13:50:57 -0700539template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800540bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
541 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700542{
543 try
544 {
545 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800546 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700547 ref = value;
548 return true;
549 }
James Feist98132792019-07-09 13:29:09 -0700550 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700551 {
552 return false;
553 }
554}
James Feistbb43d022018-06-12 15:44:33 -0700555
James Feistebcc26b2019-03-22 12:30:43 -0700556// template function to add array as dbus property
557template <typename PropertyType>
558void addArrayToDbus(const std::string& name, const nlohmann::json& array,
559 sdbusplus::asio::dbus_interface* iface,
560 sdbusplus::asio::PropertyPermission permission,
561 nlohmann::json& systemConfiguration,
562 const std::string& jsonPointerString)
563{
564 std::vector<PropertyType> values;
565 for (const auto& property : array)
566 {
567 auto ptr = property.get_ptr<const PropertyType*>();
568 if (ptr != nullptr)
569 {
570 values.emplace_back(*ptr);
571 }
572 }
573
574 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
575 {
576 iface->register_property(name, values);
577 }
578 else
579 {
580 iface->register_property(
581 name, values,
582 [&systemConfiguration,
583 jsonPointerString{std::string(jsonPointerString)}](
584 const std::vector<PropertyType>& newVal,
585 std::vector<PropertyType>& val) {
586 val = newVal;
587 if (!setJsonFromPointer(jsonPointerString, val,
588 systemConfiguration))
589 {
590 std::cerr << "error setting json field\n";
591 return -1;
592 }
593 if (!writeJsonFiles(systemConfiguration))
594 {
595 std::cerr << "error setting json file\n";
596 return -1;
597 }
598 return 1;
599 });
600 }
601}
602
James Feistbb43d022018-06-12 15:44:33 -0700603template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800604void addProperty(const std::string& propertyName, const PropertyType& value,
605 sdbusplus::asio::dbus_interface* iface,
606 nlohmann::json& systemConfiguration,
607 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700608 sdbusplus::asio::PropertyPermission permission)
609{
610 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
611 {
612 iface->register_property(propertyName, value);
613 return;
614 }
James Feist68500ff2018-08-08 15:40:42 -0700615 iface->register_property(
616 propertyName, value,
617 [&systemConfiguration,
618 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800619 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700620 val = newVal;
621 if (!setJsonFromPointer(jsonPointerString, val,
622 systemConfiguration))
623 {
624 std::cerr << "error setting json field\n";
625 return -1;
626 }
James Feistc6248a52018-08-14 10:09:45 -0700627 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700628 {
629 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700630 return -1;
631 }
632 return 1;
633 });
634}
635
636void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800637 const std::string& jsonPointerPath,
638 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
639 sdbusplus::asio::object_server& objServer,
640 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700641{
642 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
643 iface->register_method(
644 "Delete", [&objServer, &systemConfiguration, interface,
645 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700646 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700647 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700648 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700649 {
650 // this technically can't happen as the pointer is pointing to
651 // us
652 throw DBusInternalError();
653 }
654 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feist98132792019-07-09 13:29:09 -0700655 if (!objServer.remove_interface(dbusInterface))
James Feistc6248a52018-08-14 10:09:45 -0700656 {
657 std::cerr << "Can't delete interface " << jsonPointerPath
658 << "\n";
659 throw DBusInternalError();
660 }
661 systemConfiguration[ptr] = nullptr;
662
663 if (!writeJsonFiles(systemConfiguration))
664 {
665 std::cerr << "error setting json file\n";
666 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700667 }
James Feistbb43d022018-06-12 15:44:33 -0700668 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700669 });
James Feistbb43d022018-06-12 15:44:33 -0700670}
671
James Feist1b2e2242018-01-30 13:45:19 -0800672// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700673void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800674 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
675 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
676 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700677 sdbusplus::asio::PropertyPermission permission =
678 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800679{
James Feista465ccc2019-02-08 12:51:01 -0800680 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800681 {
James Feist8f2710a2018-05-09 17:18:55 -0700682 auto type = dictPair.value().type();
683 bool array = false;
684 if (dictPair.value().type() == nlohmann::json::value_t::array)
685 {
686 array = true;
687 if (!dictPair.value().size())
688 {
689 continue;
690 }
691 type = dictPair.value()[0].type();
692 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800693 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700694 {
695 if (arrayItem.type() != type)
696 {
697 isLegal = false;
698 break;
699 }
700 }
701 if (!isLegal)
702 {
703 std::cerr << "dbus format error" << dictPair.value() << "\n";
704 continue;
705 }
James Feista218ddb2019-04-11 14:01:31 -0700706 }
707 if (type == nlohmann::json::value_t::object)
708 {
709 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700710 }
James Feist97a63f12018-05-17 13:50:57 -0700711 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700712 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
713 {
714 // all setable numbers are doubles as it is difficult to always
715 // create a configuration file with all whole numbers as decimals
716 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700717 if (array)
718 {
719 if (dictPair.value()[0].is_number())
720 {
721 type = nlohmann::json::value_t::number_float;
722 }
723 }
724 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700725 {
726 type = nlohmann::json::value_t::number_float;
727 }
728 }
729
James Feist8f2710a2018-05-09 17:18:55 -0700730 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800731 {
James Feist9eb0b582018-04-27 12:15:46 -0700732 case (nlohmann::json::value_t::boolean):
733 {
James Feist8f2710a2018-05-09 17:18:55 -0700734 if (array)
735 {
736 // todo: array of bool isn't detected correctly by
737 // sdbusplus, change it to numbers
738 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700739 iface.get(), permission,
740 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700741 }
James Feistbb43d022018-06-12 15:44:33 -0700742
James Feist97a63f12018-05-17 13:50:57 -0700743 else
744 {
James Feistbb43d022018-06-12 15:44:33 -0700745 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700746 iface.get(), systemConfiguration, key,
747 permission);
James Feist97a63f12018-05-17 13:50:57 -0700748 }
James Feist9eb0b582018-04-27 12:15:46 -0700749 break;
750 }
751 case (nlohmann::json::value_t::number_integer):
752 {
James Feist8f2710a2018-05-09 17:18:55 -0700753 if (array)
754 {
755 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700756 iface.get(), permission,
757 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700758 }
759 else
760 {
James Feistbb43d022018-06-12 15:44:33 -0700761 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700762 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700763 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700764 }
James Feist9eb0b582018-04-27 12:15:46 -0700765 break;
766 }
767 case (nlohmann::json::value_t::number_unsigned):
768 {
James Feist8f2710a2018-05-09 17:18:55 -0700769 if (array)
770 {
771 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700772 iface.get(), permission,
773 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700774 }
775 else
776 {
James Feistbb43d022018-06-12 15:44:33 -0700777 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700778 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700779 systemConfiguration, key,
780 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700781 }
James Feist9eb0b582018-04-27 12:15:46 -0700782 break;
783 }
784 case (nlohmann::json::value_t::number_float):
785 {
James Feist8f2710a2018-05-09 17:18:55 -0700786 if (array)
787 {
788 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700789 iface.get(), permission,
790 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700791 }
James Feistbb43d022018-06-12 15:44:33 -0700792
James Feist97a63f12018-05-17 13:50:57 -0700793 else
794 {
James Feistbb43d022018-06-12 15:44:33 -0700795 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700796 iface.get(), systemConfiguration, key,
797 permission);
James Feist97a63f12018-05-17 13:50:57 -0700798 }
James Feist9eb0b582018-04-27 12:15:46 -0700799 break;
800 }
801 case (nlohmann::json::value_t::string):
802 {
James Feist8f2710a2018-05-09 17:18:55 -0700803 if (array)
804 {
James Feistebcc26b2019-03-22 12:30:43 -0700805 addArrayToDbus<std::string>(
806 dictPair.key(), dictPair.value(), iface.get(),
807 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700808 }
809 else
810 {
James Feistc6248a52018-08-14 10:09:45 -0700811 addProperty(
812 dictPair.key(), dictPair.value().get<std::string>(),
813 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700814 }
James Feist9eb0b582018-04-27 12:15:46 -0700815 break;
816 }
James Feist0eb40352019-04-09 14:44:04 -0700817 default:
818 {
James Feista218ddb2019-04-11 14:01:31 -0700819 std::cerr << "Unexpected json type in system configuration "
820 << dictPair.key() << ": "
821 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700822 break;
823 }
James Feist1b2e2242018-01-30 13:45:19 -0800824 }
825 }
James Feistc6248a52018-08-14 10:09:45 -0700826 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
827 {
828 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
829 systemConfiguration);
830 }
James Feist8f2710a2018-05-09 17:18:55 -0700831 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800832}
833
James Feista465ccc2019-02-08 12:51:01 -0800834sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700835{
836 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
837 interface) != settableInterfaces.end()
838 ? sdbusplus::asio::PropertyPermission::readWrite
839 : sdbusplus::asio::PropertyPermission::readOnly;
840}
841
James Feista465ccc2019-02-08 12:51:01 -0800842void createAddObjectMethod(const std::string& jsonPointerPath,
843 const std::string& path,
844 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700845 sdbusplus::asio::object_server& objServer,
846 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700847{
James Feistd58879a2019-09-11 11:26:07 -0700848 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
849 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700850
851 iface->register_method(
852 "AddObject",
853 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700854 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
855 board](const boost::container::flat_map<std::string, JsonVariantType>&
856 data) {
James Feist68500ff2018-08-08 15:40:42 -0700857 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800858 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700859 auto findExposes = base.find("Exposes");
860
861 if (findExposes == base.end())
862 {
863 throw std::invalid_argument("Entity must have children.");
864 }
865
866 // this will throw invalid-argument to sdbusplus if invalid json
867 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800868 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700869 {
James Feista465ccc2019-02-08 12:51:01 -0800870 nlohmann::json& newJson = newData[item.first];
871 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
872 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700873 }
874
875 auto findName = newData.find("Name");
876 auto findType = newData.find("Type");
877 if (findName == newData.end() || findType == newData.end())
878 {
879 throw std::invalid_argument("AddObject missing Name or Type");
880 }
James Feista465ccc2019-02-08 12:51:01 -0800881 const std::string* type = findType->get_ptr<const std::string*>();
882 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700883 if (type == nullptr || name == nullptr)
884 {
885 throw std::invalid_argument("Type and Name must be a string.");
886 }
887
888 size_t lastIndex = 0;
889 // we add in the "exposes"
890 for (; lastIndex < findExposes->size(); lastIndex++)
891 {
892 if (findExposes->at(lastIndex)["Name"] == *name &&
893 findExposes->at(lastIndex)["Type"] == *type)
894 {
895 throw std::invalid_argument(
896 "Field already in JSON, not adding");
897 }
898 lastIndex++;
899 }
900
901 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
902 *type + ".json");
903 // todo(james) we might want to also make a list of 'can add'
904 // interfaces but for now I think the assumption if there is a
905 // schema avaliable that it is allowed to update is fine
906 if (!schemaFile.good())
907 {
908 throw std::invalid_argument(
909 "No schema avaliable, cannot validate.");
910 }
911 nlohmann::json schema =
912 nlohmann::json::parse(schemaFile, nullptr, false);
913 if (schema.is_discarded())
914 {
915 std::cerr << "Schema not legal" << *type << ".json\n";
916 throw DBusInternalError();
917 }
918 if (!validateJson(schema, newData))
919 {
920 throw std::invalid_argument("Data does not match schema");
921 }
922
James Feist16a02f22019-05-13 15:21:37 -0700923 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700924 if (!writeJsonFiles(systemConfiguration))
925 {
926 std::cerr << "Error writing json files\n";
927 throw DBusInternalError();
928 }
929 std::string dbusName = *name;
930
931 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700932 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700933
934 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
935 createInterface(objServer, path + "/" + dbusName,
936 "xyz.openbmc_project.Configuration." + *type,
937 board);
James Feist68500ff2018-08-08 15:40:42 -0700938 // permission is read-write, as since we just created it, must be
939 // runtime modifiable
940 populateInterfaceFromJson(
941 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700942 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700943 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700944 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700945 });
946 iface->initialize();
947}
948
James Feista465ccc2019-02-08 12:51:01 -0800949void postToDbus(const nlohmann::json& newConfiguration,
950 nlohmann::json& systemConfiguration,
951 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800952
James Feist1b2e2242018-01-30 13:45:19 -0800953{
James Feist97a63f12018-05-17 13:50:57 -0700954 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800955 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800956 {
James Feistf1b14142019-04-10 15:22:09 -0700957 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700958 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700959 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700960 // loop through newConfiguration, but use values from system
961 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700962 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700963 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800964 std::string boardType;
965 if (findBoardType != boardValues.end() &&
966 findBoardType->type() == nlohmann::json::value_t::string)
967 {
968 boardType = findBoardType->get<std::string>();
969 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700970 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800971 }
972 else
973 {
974 std::cerr << "Unable to find type for " << boardKey
975 << " reverting to Chassis.\n";
976 boardType = "Chassis";
977 }
James Feist11be6672018-04-06 14:05:32 -0700978 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800979
980 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700981 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700982 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
983 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800984
James Feistd58879a2019-09-11 11:26:07 -0700985 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
986 createInterface(objServer, boardName,
987 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700988
James Feistd58879a2019-09-11 11:26:07 -0700989 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
990 createInterface(objServer, boardName,
991 "xyz.openbmc_project.Inventory.Item." + boardType,
992 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700993
James Feist68500ff2018-08-08 15:40:42 -0700994 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700995 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700996
James Feist97a63f12018-05-17 13:50:57 -0700997 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700998 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700999 jsonPointerPath += "/";
1000 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -08001001 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -07001002 {
1003 if (boardField.value().type() == nlohmann::json::value_t::object)
1004 {
James Feistd58879a2019-09-11 11:26:07 -07001005 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
1006 createInterface(objServer, boardName, boardField.key(),
1007 boardKeyOrig);
1008
James Feistc6248a52018-08-14 10:09:45 -07001009 populateInterfaceFromJson(systemConfiguration,
1010 jsonPointerPath + boardField.key(),
1011 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -07001012 }
1013 }
James Feist97a63f12018-05-17 13:50:57 -07001014
James Feist1e3e6982018-08-03 16:09:28 -07001015 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001016 if (exposes == boardValues.end())
1017 {
1018 continue;
1019 }
James Feist97a63f12018-05-17 13:50:57 -07001020 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001021 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001022
1023 // store the board level pointer so we can modify it on the way down
1024 std::string jsonPointerPathBoard = jsonPointerPath;
1025 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001026 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001027 {
James Feist97a63f12018-05-17 13:50:57 -07001028 exposesIndex++;
1029 jsonPointerPath = jsonPointerPathBoard;
1030 jsonPointerPath += std::to_string(exposesIndex);
1031
James Feistd63d18a2018-07-19 15:23:45 -07001032 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001033 if (findName == item.end())
1034 {
1035 std::cerr << "cannot find name in field " << item << "\n";
1036 continue;
1037 }
James Feist1e3e6982018-08-03 16:09:28 -07001038 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001039 // if status is not found it is assumed to be status = 'okay'
1040 if (findStatus != item.end())
1041 {
1042 if (*findStatus == "disabled")
1043 {
1044 continue;
1045 }
1046 }
James Feistd63d18a2018-07-19 15:23:45 -07001047 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001048 std::string itemType;
1049 if (findType != item.end())
1050 {
1051 itemType = findType->get<std::string>();
1052 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001053 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1054 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001055 }
1056 else
1057 {
1058 itemType = "unknown";
1059 }
1060 std::string itemName = findName->get<std::string>();
1061 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001062 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001063
James Feistd58879a2019-09-11 11:26:07 -07001064 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1065 createInterface(objServer, boardName + "/" + itemName,
1066 "xyz.openbmc_project.Configuration." + itemType,
1067 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001068
James Feist97a63f12018-05-17 13:50:57 -07001069 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001070 itemIface, item, objServer,
1071 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001072
James Feista465ccc2019-02-08 12:51:01 -08001073 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001074 {
James Feist97a63f12018-05-17 13:50:57 -07001075 jsonPointerPath = jsonPointerPathBoard +
1076 std::to_string(exposesIndex) + "/" +
1077 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001078 if (objectPair.value().type() ==
1079 nlohmann::json::value_t::object)
1080 {
James Feistd58879a2019-09-11 11:26:07 -07001081 std::shared_ptr<sdbusplus::asio::dbus_interface>
1082 objectIface = createInterface(
1083 objServer, boardName + "/" + itemName,
1084 "xyz.openbmc_project.Configuration." + itemType +
1085 "." + objectPair.key(),
1086 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001087
1088 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001089 systemConfiguration, jsonPointerPath, objectIface,
1090 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001091 }
1092 else if (objectPair.value().type() ==
1093 nlohmann::json::value_t::array)
1094 {
1095 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001096 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001097 {
James Feist8f2710a2018-05-09 17:18:55 -07001098 continue;
1099 }
1100 bool isLegal = true;
1101 auto type = objectPair.value()[0].type();
1102 if (type != nlohmann::json::value_t::object)
1103 {
1104 continue;
1105 }
1106
1107 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001108 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001109 {
1110 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001111 {
James Feist8f2710a2018-05-09 17:18:55 -07001112 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001113 break;
1114 }
James Feist8f2710a2018-05-09 17:18:55 -07001115 }
1116 if (!isLegal)
1117 {
1118 std::cerr << "dbus format error" << objectPair.value()
1119 << "\n";
1120 break;
1121 }
1122
James Feista465ccc2019-02-08 12:51:01 -08001123 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001124 {
James Feist97a63f12018-05-17 13:50:57 -07001125
James Feistd58879a2019-09-11 11:26:07 -07001126 std::shared_ptr<sdbusplus::asio::dbus_interface>
1127 objectIface = createInterface(
1128 objServer, boardName + "/" + itemName,
1129 "xyz.openbmc_project.Configuration." +
1130 itemType + "." + objectPair.key() +
1131 std::to_string(index),
1132 boardKeyOrig);
1133
James Feistc6248a52018-08-14 10:09:45 -07001134 populateInterfaceFromJson(
1135 systemConfiguration,
1136 jsonPointerPath + "/" + std::to_string(index),
1137 objectIface, arrayItem, objServer,
1138 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001139 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001140 }
1141 }
1142 }
1143 }
1144 }
1145}
1146
James Feist8f2710a2018-05-09 17:18:55 -07001147// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001148bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001149{
1150 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001151 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001152 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1153 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001154 {
1155 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001156 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001157 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001158 }
James Feistb4383f42018-08-06 16:54:10 -07001159
1160 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1161 globalSchema);
1162 if (!schemaStream.good())
1163 {
1164 std::cerr
1165 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1166 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001167 return false;
James Feistb4383f42018-08-06 16:54:10 -07001168 }
1169 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1170 if (schema.is_discarded())
1171 {
1172 std::cerr
1173 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1174 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001175 return false;
James Feistb4383f42018-08-06 16:54:10 -07001176 }
1177
James Feista465ccc2019-02-08 12:51:01 -08001178 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001179 {
1180 std::ifstream jsonStream(jsonPath.c_str());
1181 if (!jsonStream.good())
1182 {
1183 std::cerr << "unable to open " << jsonPath.string() << "\n";
1184 continue;
1185 }
1186 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1187 if (data.is_discarded())
1188 {
1189 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1190 continue;
1191 }
James Feist8da99192019-01-24 08:20:16 -08001192 /*
1193 * todo(james): reenable this once less things are in flight
1194 *
James Feistb4383f42018-08-06 16:54:10 -07001195 if (!validateJson(schema, data))
1196 {
1197 std::cerr << "Error validating " << jsonPath.string() << "\n";
1198 continue;
1199 }
James Feist8da99192019-01-24 08:20:16 -08001200 */
James Feistb4383f42018-08-06 16:54:10 -07001201
James Feist3cb5fec2018-01-23 14:41:51 -08001202 if (data.type() == nlohmann::json::value_t::array)
1203 {
James Feista465ccc2019-02-08 12:51:01 -08001204 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001205 {
1206 configurations.emplace_back(d);
1207 }
1208 }
1209 else
1210 {
1211 configurations.emplace_back(data);
1212 }
1213 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001214 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001215}
James Feist3cb5fec2018-01-23 14:41:51 -08001216
James Feist8f2710a2018-05-09 17:18:55 -07001217struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001218{
James Feist75fdeeb2018-02-20 14:26:16 -08001219
James Feista465ccc2019-02-08 12:51:01 -08001220 PerformScan(nlohmann::json& systemConfiguration,
James Feist899e17f2019-09-13 11:46:29 -07001221 nlohmann::json& missingConfigurations,
James Feista465ccc2019-02-08 12:51:01 -08001222 std::list<nlohmann::json>& configurations,
1223 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001224 _systemConfiguration(systemConfiguration),
James Feist899e17f2019-09-13 11:46:29 -07001225 _missingConfigurations(missingConfigurations),
James Feist8f2710a2018-05-09 17:18:55 -07001226 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001227 {
James Feist8f2710a2018-05-09 17:18:55 -07001228 }
1229 void run()
1230 {
1231 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001232 {
James Feist1e3e6982018-08-03 16:09:28 -07001233 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001234 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001235
James Feist1b2e2242018-01-30 13:45:19 -08001236 nlohmann::json probeCommand;
1237 // check for poorly formatted fields, probe must be an array
1238 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001239 {
1240 std::cerr << "configuration file missing probe:\n " << *it
1241 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001242 it = _configurations.erase(it);
1243 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001244 }
James Feist1b2e2242018-01-30 13:45:19 -08001245 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001246 {
1247 probeCommand = nlohmann::json::array();
1248 probeCommand.push_back(*findProbe);
1249 }
1250 else
1251 {
1252 probeCommand = *findProbe;
1253 }
James Feist1b2e2242018-01-30 13:45:19 -08001254
1255 if (findName == it->end())
1256 {
1257 std::cerr << "configuration file missing name:\n " << *it
1258 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001259 it = _configurations.erase(it);
1260 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001261 }
James Feistf1b14142019-04-10 15:22:09 -07001262 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001263
James Feistf1b14142019-04-10 15:22:09 -07001264 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1265 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001266 {
James Feist8f2710a2018-05-09 17:18:55 -07001267 it = _configurations.erase(it);
1268 continue;
1269 }
James Feistf1b14142019-04-10 15:22:09 -07001270 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001271
James Feist8f2710a2018-05-09 17:18:55 -07001272 // store reference to this to children to makes sure we don't get
1273 // destroyed too early
1274 auto thisRef = shared_from_this();
James Feist08a5b172019-08-28 14:47:47 -07001275 auto p = std::make_shared<
1276 PerformProbe>(probeCommand, [&, recordPtr, probeName, thisRef](
1277 FoundDeviceT& foundDevices) {
1278 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001279
James Feist08a5b172019-08-28 14:47:47 -07001280 PASSED_PROBES.push_back(probeName);
1281 size_t foundDeviceIdx = 1;
James Feist8f2710a2018-05-09 17:18:55 -07001282
James Feist08a5b172019-08-28 14:47:47 -07001283 for (auto& foundDevice : foundDevices)
1284 {
1285 nlohmann::json record = *recordPtr;
1286 std::string recordName;
1287 size_t hash = 0;
1288 if (foundDevice.size())
James Feist3cb5fec2018-01-23 14:41:51 -08001289 {
James Feist08a5b172019-08-28 14:47:47 -07001290 // use an array so alphabetical order from the
1291 // flat_map is maintained
1292 auto device = nlohmann::json::array();
1293 for (auto& devPair : foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001294 {
James Feist08a5b172019-08-28 14:47:47 -07001295 device.push_back(devPair.first);
1296 std::visit(
1297 [&device](auto&& v) { device.push_back(v); },
1298 devPair.second);
1299 }
1300 hash =
1301 std::hash<std::string>{}(probeName + device.dump());
1302 // hashes are hard to distinguish, use the
1303 // non-hashed version if we want debug
1304 if constexpr (DEBUG)
1305 {
1306 recordName = probeName + device.dump();
James Feist8f2710a2018-05-09 17:18:55 -07001307 }
James Feistf1b14142019-04-10 15:22:09 -07001308 else
1309 {
James Feist08a5b172019-08-28 14:47:47 -07001310 recordName = std::to_string(hash);
James Feistf1b14142019-04-10 15:22:09 -07001311 }
James Feist08a5b172019-08-28 14:47:47 -07001312 }
1313 else
1314 {
1315 recordName = probeName;
1316 }
James Feistf1b14142019-04-10 15:22:09 -07001317
James Feist08a5b172019-08-28 14:47:47 -07001318 auto fromLastJson = lastJson.find(recordName);
1319 if (fromLastJson != lastJson.end())
1320 {
1321 // keep user changes
1322 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001323 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001324 continue;
1325 }
James Feist1df06a42019-04-11 14:23:04 -07001326
James Feist08a5b172019-08-28 14:47:47 -07001327 // insert into configuration temporarily to be able to
1328 // reference ourselves
James Feistf1b14142019-04-10 15:22:09 -07001329
James Feist08a5b172019-08-28 14:47:47 -07001330 _systemConfiguration[recordName] = record;
1331
1332 for (auto keyPair = record.begin(); keyPair != record.end();
1333 keyPair++)
1334 {
1335 templateCharReplace(keyPair, foundDevice,
1336 foundDeviceIdx);
1337 }
1338
1339 auto findExpose = record.find("Exposes");
1340 if (findExpose == record.end())
1341 {
James Feistf1b14142019-04-10 15:22:09 -07001342 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001343 foundDeviceIdx++;
1344 continue;
1345 }
James Feistf1b14142019-04-10 15:22:09 -07001346
James Feist08a5b172019-08-28 14:47:47 -07001347 for (auto& expose : *findExpose)
1348 {
1349 for (auto keyPair = expose.begin();
1350 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001351 {
James Feist08a5b172019-08-28 14:47:47 -07001352
1353 templateCharReplace(keyPair, foundDevice,
1354 foundDeviceIdx);
1355
1356 // special case bind
1357 if (boost::starts_with(keyPair.key(), "Bind"))
James Feistf1b14142019-04-10 15:22:09 -07001358 {
James Feist08a5b172019-08-28 14:47:47 -07001359 if (keyPair.value().type() !=
1360 nlohmann::json::value_t::string)
James Feistf1b14142019-04-10 15:22:09 -07001361 {
James Feist08a5b172019-08-28 14:47:47 -07001362 std::cerr << "bind_ value must be of "
1363 "type string "
1364 << keyPair.key() << "\n";
1365 continue;
James Feistf1b14142019-04-10 15:22:09 -07001366 }
James Feist08a5b172019-08-28 14:47:47 -07001367 bool foundBind = false;
1368 std::string bind =
1369 keyPair.key().substr(sizeof("Bind") - 1);
1370
1371 for (auto& configurationPair :
1372 _systemConfiguration.items())
James Feist8f2710a2018-05-09 17:18:55 -07001373 {
James Feist08a5b172019-08-28 14:47:47 -07001374
1375 auto configListFind =
1376 configurationPair.value().find(
1377 "Exposes");
1378
1379 if (configListFind ==
1380 configurationPair.value().end() ||
1381 configListFind->type() !=
1382 nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001383 {
James Feist1b2e2242018-01-30 13:45:19 -08001384 continue;
1385 }
James Feist08a5b172019-08-28 14:47:47 -07001386 for (auto& exposedObject : *configListFind)
James Feist1b2e2242018-01-30 13:45:19 -08001387 {
James Feist08a5b172019-08-28 14:47:47 -07001388 std::string foundObjectName =
1389 (exposedObject)["Name"];
1390 if (boost::iequals(
1391 foundObjectName,
1392 keyPair.value()
1393 .get<std::string>()))
James Feist8f2710a2018-05-09 17:18:55 -07001394 {
James Feist08a5b172019-08-28 14:47:47 -07001395 exposedObject["Status"] = "okay";
1396 expose[bind] = exposedObject;
James Feist8f2710a2018-05-09 17:18:55 -07001397
James Feist08a5b172019-08-28 14:47:47 -07001398 foundBind = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001399 break;
1400 }
1401 }
James Feist08a5b172019-08-28 14:47:47 -07001402 if (foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001403 {
James Feist08a5b172019-08-28 14:47:47 -07001404 break;
James Feist3cb5fec2018-01-23 14:41:51 -08001405 }
1406 }
James Feist08a5b172019-08-28 14:47:47 -07001407 if (!foundBind)
1408 {
1409 std::cerr << "configuration file "
1410 "dependency error, "
1411 "could not find bind "
1412 << keyPair.value() << "\n";
1413 }
James Feist3cb5fec2018-01-23 14:41:51 -08001414 }
1415 }
1416 }
James Feist08a5b172019-08-28 14:47:47 -07001417 // overwrite ourselves with cleaned up version
1418 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001419 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001420
1421 foundDeviceIdx++;
1422 }
1423 });
James Feist8f2710a2018-05-09 17:18:55 -07001424 p->run();
1425 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001426 }
1427 }
James Feist75fdeeb2018-02-20 14:26:16 -08001428
James Feist8f2710a2018-05-09 17:18:55 -07001429 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001430 {
James Feist8f2710a2018-05-09 17:18:55 -07001431 if (_passed)
1432 {
1433 auto nextScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001434 _systemConfiguration, _missingConfigurations, _configurations,
1435 std::move(_callback));
James Feist8f2710a2018-05-09 17:18:55 -07001436 nextScan->run();
1437 }
1438 else
1439 {
1440 _callback();
1441 }
1442 }
James Feista465ccc2019-02-08 12:51:01 -08001443 nlohmann::json& _systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001444 nlohmann::json& _missingConfigurations;
James Feist8f2710a2018-05-09 17:18:55 -07001445 std::list<nlohmann::json> _configurations;
1446 std::function<void(void)> _callback;
1447 std::vector<std::shared_ptr<PerformProbe>> _probes;
1448 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001449 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001450};
James Feistc95cb142018-02-26 10:41:42 -08001451
James Feist1df06a42019-04-11 14:23:04 -07001452void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001453 nlohmann::json& systemConfiguration)
1454{
1455 static bool scannedPowerOff = false;
1456 static bool scannedPowerOn = false;
1457
James Feistfb00f392019-06-25 14:16:48 -07001458 if (systemConfiguration.empty() || lastJson.empty())
1459 {
1460 return; // not ready yet
1461 }
James Feist1df06a42019-04-11 14:23:04 -07001462 if (scannedPowerOn)
1463 {
1464 return;
1465 }
1466
1467 if (!isPowerOn() && scannedPowerOff)
1468 {
1469 return;
1470 }
1471
1472 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001473 timer.async_wait(
1474 [&systemConfiguration](const boost::system::error_code& ec) {
1475 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001476 {
James Feist1a996582019-05-14 15:10:06 -07001477 // we were cancelled
1478 return;
1479 }
1480
1481 bool powerOff = !isPowerOn();
1482 for (const auto& item : lastJson.items())
1483 {
1484 if (systemConfiguration.find(item.key()) ==
1485 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001486 {
James Feist1a996582019-05-14 15:10:06 -07001487 bool isDetectedPowerOn = false;
1488 auto powerState = item.value().find("PowerState");
1489 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001490 {
James Feist1a996582019-05-14 15:10:06 -07001491 auto ptr = powerState->get_ptr<const std::string*>();
1492 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001493 {
James Feist1a996582019-05-14 15:10:06 -07001494 if (*ptr == "On" || *ptr == "BiosPost")
1495 {
1496 isDetectedPowerOn = true;
1497 }
James Feist1df06a42019-04-11 14:23:04 -07001498 }
1499 }
James Feist1a996582019-05-14 15:10:06 -07001500 if (powerOff && isDetectedPowerOn)
1501 {
1502 // power not on yet, don't know if it's there or not
1503 continue;
1504 }
1505 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1506 {
1507 // already logged it when power was off
1508 continue;
1509 }
James Feist1df06a42019-04-11 14:23:04 -07001510
James Feist1a996582019-05-14 15:10:06 -07001511 logDeviceRemoved(item.value());
1512 }
James Feist1df06a42019-04-11 14:23:04 -07001513 }
James Feist1a996582019-05-14 15:10:06 -07001514 scannedPowerOff = true;
1515 if (!powerOff)
1516 {
1517 scannedPowerOn = true;
1518 }
1519 });
James Feist1df06a42019-04-11 14:23:04 -07001520}
1521
James Feist8f2710a2018-05-09 17:18:55 -07001522// main properties changed entry
1523void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001524 boost::asio::io_service& io,
1525 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1526 nlohmann::json& systemConfiguration,
1527 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001528{
1529 static boost::asio::deadline_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001530 static size_t instance = 0;
1531 instance++;
1532 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001533
James Feist899e17f2019-09-13 11:46:29 -07001534 timer.expires_from_now(boost::posix_time::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001535
1536 // setup an async wait as we normally get flooded with new requests
James Feist899e17f2019-09-13 11:46:29 -07001537 timer.async_wait([&systemConfiguration, &dbusMatches, &io, &objServer,
1538 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001539 if (ec == boost::asio::error::operation_aborted)
1540 {
1541 // we were cancelled
1542 return;
1543 }
1544 else if (ec)
1545 {
1546 std::cerr << "async wait error " << ec << "\n";
1547 return;
1548 }
1549
1550 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001551 auto missingConfigurations = std::make_shared<nlohmann::json>();
1552 *missingConfigurations = systemConfiguration;
1553
James Feist8f2710a2018-05-09 17:18:55 -07001554 DBUS_PROBE_OBJECTS.clear();
James Feist899e17f2019-09-13 11:46:29 -07001555 PASSED_PROBES.clear();
James Feist8f2710a2018-05-09 17:18:55 -07001556
1557 std::list<nlohmann::json> configurations;
1558 if (!findJsonFiles(configurations))
1559 {
1560 std::cerr << "cannot find json files\n";
1561 return;
1562 }
1563
1564 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001565 systemConfiguration, *missingConfigurations, configurations,
1566 [&systemConfiguration, &io, &objServer, &dbusMatches, count,
1567 oldConfiguration, missingConfigurations]() {
1568 // this is something that since ac has been applied to the bmc
1569 // we saw, and we no longer see it
1570 bool powerOff = !isPowerOn();
1571 for (const auto& item : missingConfigurations->items())
1572 {
1573 bool isDetectedPowerOn = false;
1574 auto powerState = item.value().find("PowerState");
1575 if (powerState != item.value().end())
1576 {
1577 auto ptr = powerState->get_ptr<const std::string*>();
1578 if (ptr)
1579 {
1580 if (*ptr == "On" || *ptr == "BiosPost")
1581 {
1582 isDetectedPowerOn = true;
1583 }
1584 }
1585 }
1586 if (powerOff && isDetectedPowerOn)
1587 {
1588 // power not on yet, don't know if it's there or not
1589 continue;
1590 }
1591 std::string name = item.value()["Name"].get<std::string>();
1592 std::vector<
1593 std::shared_ptr<sdbusplus::asio::dbus_interface>>&
1594 ifaces = inventory[name];
1595 for (auto& iface : ifaces)
1596 {
1597 objServer.remove_interface(iface);
1598 }
1599 ifaces.clear();
1600 systemConfiguration.erase(item.key());
1601 logDeviceRemoved(item.value());
1602 }
1603
James Feist8f2710a2018-05-09 17:18:55 -07001604 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001605 for (auto it = newConfiguration.begin();
1606 it != newConfiguration.end();)
1607 {
1608 auto findKey = oldConfiguration.find(it.key());
1609 if (findKey != oldConfiguration.end())
1610 {
1611 it = newConfiguration.erase(it);
1612 }
1613 else
1614 {
1615 it++;
1616 }
1617 }
James Feist899e17f2019-09-13 11:46:29 -07001618 for (const auto& item : newConfiguration.items())
1619 {
1620 logDeviceAdded(item.value());
1621 }
1622
James Feist8f2710a2018-05-09 17:18:55 -07001623 registerCallbacks(io, dbusMatches, systemConfiguration,
1624 objServer);
1625 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001626 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001627
James Feistbb43d022018-06-12 15:44:33 -07001628 io.post([&]() {
1629 if (!writeJsonFiles(systemConfiguration))
1630 {
1631 std::cerr << "Error writing json files\n";
1632 }
1633 });
James Feist8f2710a2018-05-09 17:18:55 -07001634 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001635 postToDbus(newConfiguration, systemConfiguration,
1636 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001637 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001638 {
James Feist899e17f2019-09-13 11:46:29 -07001639 return;
James Feist1df06a42019-04-11 14:23:04 -07001640 }
James Feist899e17f2019-09-13 11:46:29 -07001641 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001642 });
1643 });
1644 });
1645 perfScan->run();
1646 });
James Feist75fdeeb2018-02-20 14:26:16 -08001647}
1648
James Feista465ccc2019-02-08 12:51:01 -08001649void registerCallbacks(boost::asio::io_service& io,
1650 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1651 nlohmann::json& systemConfiguration,
1652 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001653{
1654 static boost::container::flat_set<std::string> watchedObjects;
1655
James Feista465ccc2019-02-08 12:51:01 -08001656 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001657 {
1658 auto findObject = watchedObjects.find(objectMap.first);
1659 if (findObject != watchedObjects.end())
1660 {
1661 continue;
1662 }
James Feist8f2710a2018-05-09 17:18:55 -07001663 std::function<void(sdbusplus::message::message & message)>
1664 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001665
James Feista465ccc2019-02-08 12:51:01 -08001666 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001667 propertiesChangedCallback(io, dbusMatches,
1668 systemConfiguration, objServer);
1669 };
1670
1671 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001672 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001673 "type='signal',member='PropertiesChanged',arg0='" +
1674 objectMap.first + "'",
1675 eventHandler);
1676 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001677 }
1678}
1679
James Feist98132792019-07-09 13:29:09 -07001680int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001681{
1682 // setup connection to dbus
1683 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001684 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001685 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001686
James Feist8f2710a2018-05-09 17:18:55 -07001687 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001688
James Feist8f2710a2018-05-09 17:18:55 -07001689 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1690 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1691 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001692
James Feist8f2710a2018-05-09 17:18:55 -07001693 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1694 objServer.add_interface("/xyz/openbmc_project/inventory",
1695 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001696
1697 // to keep reference to the match / filter objects so they don't get
1698 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001699 std::vector<sdbusplus::bus::match::match> dbusMatches;
1700
1701 nlohmann::json systemConfiguration = nlohmann::json::object();
1702
1703 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001704 "Notify",
1705 [](const boost::container::flat_map<
1706 std::string,
James Feist98132792019-07-09 13:29:09 -07001707 boost::container::flat_map<std::string, BasicVariantType>>&) {
1708 return;
1709 });
James Feist8f2710a2018-05-09 17:18:55 -07001710 inventoryIface->initialize();
1711
1712 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001713#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001714 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001715#endif
James Feist8f2710a2018-05-09 17:18:55 -07001716 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1717 objServer);
1718 });
James Feist4131aea2018-03-09 09:47:30 -08001719
James Feistfd1264a2018-05-03 12:10:00 -07001720 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001721 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1722 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001723 });
James Feist8f2710a2018-05-09 17:18:55 -07001724 entityIface->initialize();
1725
James Feist1df06a42019-04-11 14:23:04 -07001726 if (fwVersionIsSame())
1727 {
1728 if (std::filesystem::is_regular_file(currentConfiguration))
1729 {
1730 // this file could just be deleted, but it's nice for debug
1731 std::filesystem::create_directory(tempConfigDir);
1732 std::filesystem::remove(lastConfiguration);
1733 std::filesystem::copy(currentConfiguration, lastConfiguration);
1734 std::filesystem::remove(currentConfiguration);
1735
1736 std::ifstream jsonStream(lastConfiguration);
1737 if (jsonStream.good())
1738 {
1739 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1740 if (data.is_discarded())
1741 {
1742 std::cerr << "syntax error in " << lastConfiguration
1743 << "\n";
1744 }
1745 else
1746 {
1747 lastJson = std::move(data);
1748 }
1749 }
1750 else
1751 {
1752 std::cerr << "unable to open " << lastConfiguration << "\n";
1753 }
1754 }
1755 }
1756 else
1757 {
1758 // not an error, just logging at this level to make it in the journal
1759 std::cerr << "Clearing previous configuration\n";
1760 std::filesystem::remove(currentConfiguration);
1761 }
1762
1763 // some boards only show up after power is on, we want to not say they are
1764 // removed until the same state happens
1765 setupPowerMatch(SYSTEM_BUS);
1766
James Feist1b2e2242018-01-30 13:45:19 -08001767 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001768
1769 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001770}