blob: 3e3e21b8ead563c856d59cb80eb2bcd76ea907dc [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
James Feist481c5d52019-08-13 14:40:40 -070019#include "VariantVisitors.hpp"
20
James Feistc95cb142018-02-26 10:41:42 -080021#include <Overlay.hpp>
James Feista465ccc2019-02-08 12:51:01 -080022#include <Utils.hpp>
23#include <VariantVisitors.hpp>
James Feist11be6672018-04-06 14:05:32 -070024#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070025#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070028#include <boost/algorithm/string/split.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
James Feist8f2710a2018-05-09 17:18:55 -070059struct PerformProbe;
60
James Feist3cb5fec2018-01-23 14:41:51 -080061// underscore T for collison with dbus c api
62enum class probe_type_codes
63{
64 FALSE_T,
65 TRUE_T,
66 AND,
67 OR,
James Feist6bd2a022018-03-13 12:30:58 -070068 FOUND,
69 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080070};
James Feista465ccc2019-02-08 12:51:01 -080071const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080072 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
73 {"TRUE", probe_type_codes::TRUE_T},
74 {"AND", probe_type_codes::AND},
75 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070076 {"FOUND", probe_type_codes::FOUND},
77 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080078
James Feist41334262019-03-25 13:30:20 -070079static constexpr std::array<const char*, 5> settableInterfaces = {
80 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070081using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080082 std::variant<std::vector<std::string>, std::vector<double>, std::string,
83 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
84 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080085using GetSubTreeType = std::vector<
86 std::pair<std::string,
87 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
88
89using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070090 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080091 boost::container::flat_map<
92 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070093 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080094
James Feist08a5b172019-08-28 14:47:47 -070095using FoundDeviceT =
96 std::vector<boost::container::flat_map<std::string, BasicVariantType>>;
97
James Feist3cb5fec2018-01-23 14:41:51 -080098boost::container::flat_map<
99 std::string,
James Feist8f2710a2018-05-09 17:18:55 -0700100 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -0800101 DBUS_PROBE_OBJECTS;
102std::vector<std::string> PASSED_PROBES;
103
James Feistd58879a2019-09-11 11:26:07 -0700104// store reference to all interfaces so we can destroy them later
105boost::container::flat_map<
106 std::string, std::vector<std::shared_ptr<sdbusplus::asio::dbus_interface>>>
107 inventory;
108
James Feist3cb5fec2018-01-23 14:41:51 -0800109// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700110std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700111static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800112
Johnathan Mantey2015f752019-03-26 15:22:31 -0700113const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
114const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800115
James Feista465ccc2019-02-08 12:51:01 -0800116void registerCallbacks(boost::asio::io_service& io,
117 std::vector<sdbusplus::bus::match::match>& dbusMatches,
118 nlohmann::json& systemConfiguration,
119 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800120
James Feistd58879a2019-09-11 11:26:07 -0700121static std::shared_ptr<sdbusplus::asio::dbus_interface>
122 createInterface(sdbusplus::asio::object_server& objServer,
123 const std::string& path, const std::string& interface,
124 const std::string& parent)
125{
126 return inventory[parent].emplace_back(
127 objServer.add_interface(path, interface));
128}
129
James Feist3cb5fec2018-01-23 14:41:51 -0800130// calls the mapper to find all exposed objects of an interface type
131// and creates a vector<flat_map> that contains all the key value pairs
132// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700133void findDbusObjects(std::shared_ptr<PerformProbe> probe,
134 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800135 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800136{
James Feist8f2710a2018-05-09 17:18:55 -0700137
138 // store reference to pending callbacks so we don't overwhelm services
139 static boost::container::flat_map<
140 std::string, std::vector<std::shared_ptr<PerformProbe>>>
141 pendingProbes;
142
143 if (DBUS_PROBE_OBJECTS[interface].size())
144 {
145 return;
146 }
147
148 // add shared_ptr to vector of Probes waiting for callback from a specific
149 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800150 std::array<const char*, 1> objects = {interface.c_str()};
151 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700152 pendingProbes[interface];
153 auto iter = pending.emplace(pending.end(), probe);
154 // only allow first call to run to not overwhelm processes
155 if (iter != pending.begin())
156 {
157 return;
158 }
159
James Feist3cb5fec2018-01-23 14:41:51 -0800160 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700161 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800162 [connection, interface, probe](boost::system::error_code& ec,
163 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700164 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700165 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700166 {
James Feist0de40152018-07-25 11:56:12 -0700167 pendingProbes[interface].clear();
168 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700169 {
James Feist0de40152018-07-25 11:56:12 -0700170 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700171 }
James Feist0de40152018-07-25 11:56:12 -0700172 std::cerr << "Error communicating to mapper.\n";
173
174 // if we can't communicate to the mapper something is very wrong
175 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700176 }
James Feist8f2710a2018-05-09 17:18:55 -0700177 else
James Feist3cb5fec2018-01-23 14:41:51 -0800178 {
James Feista465ccc2019-02-08 12:51:01 -0800179 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700180 {
James Feista465ccc2019-02-08 12:51:01 -0800181 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700182 {
James Feist0eb40352019-04-09 14:44:04 -0700183 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700184 }
185 }
James Feist3cb5fec2018-01-23 14:41:51 -0800186 }
James Feist63845bf2019-01-24 12:19:51 -0800187 if (interfaceConnections.empty())
188 {
189 pendingProbes[interface].clear();
190 return;
191 }
James Feist8f2710a2018-05-09 17:18:55 -0700192 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800193 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700194 {
195 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700196 [conn,
James Feist98132792019-07-09 13:29:09 -0700197 interface](boost::system::error_code& errc,
James Feista465ccc2019-02-08 12:51:01 -0800198 const ManagedObjectType& managedInterface) {
James Feist98132792019-07-09 13:29:09 -0700199 if (errc)
James Feist8f2710a2018-05-09 17:18:55 -0700200 {
201 std::cerr
202 << "error getting managed object for device "
203 << conn << "\n";
204 pendingProbes[interface].clear();
205 return;
206 }
James Feista465ccc2019-02-08 12:51:01 -0800207 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700208 {
209 auto ifaceObjFind =
210 interfaceManagedObj.second.find(interface);
211 if (ifaceObjFind !=
212 interfaceManagedObj.second.end())
213 {
214 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800215 std::string, BasicVariantType>>&
216 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700217 dbusObject.emplace_back(ifaceObjFind->second);
218 }
219 }
220 pendingProbes[interface].clear();
221 },
222 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
223 "GetManagedObjects");
224 }
225 },
226 "xyz.openbmc_project.ObjectMapper",
227 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700228 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700229 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800230}
James Feist8f2710a2018-05-09 17:18:55 -0700231// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700232bool probeDbus(const std::string& interface,
233 const std::map<std::string, nlohmann::json>& matches,
234 FoundDeviceT& devices, bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800235{
James Feista465ccc2019-02-08 12:51:01 -0800236 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
237 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800238 if (dbusObject.empty())
239 {
James Feist8f2710a2018-05-09 17:18:55 -0700240 foundProbe = false;
241 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800242 }
243 foundProbe = true;
244
245 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800246 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800247 {
248 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800249 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800250 {
251 auto deviceValue = device.find(match.first);
252 if (deviceValue != device.end())
253 {
254 switch (match.second.type())
255 {
James Feist9eb0b582018-04-27 12:15:46 -0700256 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800257 {
James Feist9eb0b582018-04-27 12:15:46 -0700258 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700259 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700260
261 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800262 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700263 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700264 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700265 {
266 deviceMatches = false;
267 break;
268 }
James Feist3cb5fec2018-01-23 14:41:51 -0800269 break;
270 }
James Feist9eb0b582018-04-27 12:15:46 -0700271 case nlohmann::json::value_t::boolean:
272 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800273 {
James Feista465ccc2019-02-08 12:51:01 -0800274 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700275 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800276
James Feist9eb0b582018-04-27 12:15:46 -0700277 if (probeValue != match.second.get<unsigned int>())
278 {
279 deviceMatches = false;
280 }
281 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800282 }
James Feist9eb0b582018-04-27 12:15:46 -0700283 case nlohmann::json::value_t::number_integer:
284 {
James Feista465ccc2019-02-08 12:51:01 -0800285 int probeValue = std::visit(VariantToIntVisitor(),
286 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800287
James Feist9eb0b582018-04-27 12:15:46 -0700288 if (probeValue != match.second.get<int>())
289 {
290 deviceMatches = false;
291 }
292 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800293 }
James Feist9eb0b582018-04-27 12:15:46 -0700294 case nlohmann::json::value_t::number_float:
295 {
James Feista465ccc2019-02-08 12:51:01 -0800296 float probeValue = std::visit(VariantToFloatVisitor(),
297 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700298
299 if (probeValue != match.second.get<float>())
300 {
301 deviceMatches = false;
302 }
303 break;
304 }
James Feist0eb40352019-04-09 14:44:04 -0700305 default:
306 {
307 std::cerr << "unexpected dbus probe type "
308 << match.second.type_name() << "\n";
309 }
James Feist3cb5fec2018-01-23 14:41:51 -0800310 }
311 }
312 else
313 {
314 deviceMatches = false;
315 break;
316 }
317 }
318 if (deviceMatches)
319 {
James Feistf5125b02019-06-06 11:27:43 -0700320 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800321 foundMatch = true;
322 deviceMatches = false; // for next iteration
323 }
324 }
325 return foundMatch;
326}
327
328// default probe entry point, iterates a list looking for specific types to
329// call specific probe functions
330bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800331 const std::vector<std::string>& probeCommand,
James Feist08a5b172019-08-28 14:47:47 -0700332 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
333 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800334{
335 const static std::regex command(R"(\((.*)\))");
336 std::smatch match;
337 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700338 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800339 bool cur = true;
340 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700341 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800342
James Feista465ccc2019-02-08 12:51:01 -0800343 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800344 {
345 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800346 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800347 cmp_str>::const_iterator probeType;
348
349 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
350 probeType++)
351 {
352 if (probe.find(probeType->first) != std::string::npos)
353 {
354 foundProbe = true;
355 break;
356 }
357 }
358 if (foundProbe)
359 {
360 switch (probeType->second)
361 {
James Feist9eb0b582018-04-27 12:15:46 -0700362 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800363 {
James Feiste31e00a2019-07-24 10:45:43 -0700364 cur = false;
365 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800366 }
James Feist9eb0b582018-04-27 12:15:46 -0700367 case probe_type_codes::TRUE_T:
368 {
James Feiste31e00a2019-07-24 10:45:43 -0700369 cur = true;
370 break;
James Feist9eb0b582018-04-27 12:15:46 -0700371 }
372 case probe_type_codes::MATCH_ONE:
373 {
374 // set current value to last, this probe type shouldn't
375 // affect the outcome
376 cur = ret;
377 matchOne = true;
378 break;
379 }
380 /*case probe_type_codes::AND:
381 break;
382 case probe_type_codes::OR:
383 break;
384 // these are no-ops until the last command switch
385 */
386 case probe_type_codes::FOUND:
387 {
388 if (!std::regex_search(probe, match, command))
389 {
James Feist0eb40352019-04-09 14:44:04 -0700390 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700391 << "\n";
392 return false;
393 }
394 std::string commandStr = *(match.begin() + 1);
395 boost::replace_all(commandStr, "'", "");
396 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
397 commandStr) != PASSED_PROBES.end());
398 break;
399 }
James Feist0eb40352019-04-09 14:44:04 -0700400 default:
401 {
402 break;
403 }
James Feist3cb5fec2018-01-23 14:41:51 -0800404 }
405 }
406 // look on dbus for object
407 else
408 {
409 if (!std::regex_search(probe, match, command))
410 {
James Feist0eb40352019-04-09 14:44:04 -0700411 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800412 return false;
413 }
414 std::string commandStr = *(match.begin() + 1);
415 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700416 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800417 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800418 auto json = nlohmann::json::parse(commandStr, nullptr, false);
419 if (json.is_discarded())
420 {
James Feist0eb40352019-04-09 14:44:04 -0700421 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800422 return false;
423 }
424 // we can match any (string, variant) property. (string, string)
425 // does a regex
426 std::map<std::string, nlohmann::json> dbusProbeMap =
427 json.get<std::map<std::string, nlohmann::json>>();
428 auto findStart = probe.find("(");
429 if (findStart == std::string::npos)
430 {
431 return false;
432 }
433 std::string probeInterface = probe.substr(0, findStart);
434 cur =
435 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
436 }
437
438 // some functions like AND and OR only take affect after the
439 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700440 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800441 {
James Feist54a0dca2019-06-26 10:34:54 -0700442 ret = cur && ret;
443 }
444 else if (lastCommand == probe_type_codes::OR)
445 {
446 ret = cur || ret;
447 }
448
449 if (first)
450 {
451 ret = cur;
452 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800453 }
454 lastCommand = probeType != PROBE_TYPES.end()
455 ? probeType->second
456 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800457 }
458
459 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800460 if (ret && foundDevs.size() == 0)
461 {
James Feist08a5b172019-08-28 14:47:47 -0700462 foundDevs.emplace_back(
463 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800464 }
James Feist0eb40352019-04-09 14:44:04 -0700465 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700466 {
James Feist71f295f2019-06-20 13:35:12 -0700467 // match the last one
468 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700469 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700470
471 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700472 }
James Feist3cb5fec2018-01-23 14:41:51 -0800473 return ret;
474}
James Feist8f2710a2018-05-09 17:18:55 -0700475// this class finds the needed dbus fields and on destruction runs the probe
476struct PerformProbe : std::enable_shared_from_this<PerformProbe>
477{
James Feist3cb5fec2018-01-23 14:41:51 -0800478
James Feist08a5b172019-08-28 14:47:47 -0700479 PerformProbe(const std::vector<std::string>& probeCommand,
480 std::function<void(FoundDeviceT&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700481 _probeCommand(probeCommand),
482 _callback(std::move(callback))
483 {
484 }
485 ~PerformProbe()
486 {
James Feist08a5b172019-08-28 14:47:47 -0700487 FoundDeviceT foundDevs;
James Feist0eb40352019-04-09 14:44:04 -0700488 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700489 {
James Feist0eb40352019-04-09 14:44:04 -0700490 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700491 }
492 }
493 void run()
494 {
495 // parse out dbus probes by discarding other probe types
James Feist8f2710a2018-05-09 17:18:55 -0700496
James Feista465ccc2019-02-08 12:51:01 -0800497 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700498 {
499 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800500 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700501 cmp_str>::const_iterator probeType;
502 for (probeType = PROBE_TYPES.begin();
503 probeType != PROBE_TYPES.end(); probeType++)
504 {
505 if (probe.find(probeType->first) != std::string::npos)
506 {
507 found = true;
508 break;
509 }
510 }
511 if (found)
512 {
513 continue;
514 }
515 // syntax requires probe before first open brace
516 auto findStart = probe.find("(");
517 std::string interface = probe.substr(0, findStart);
518
519 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
520 }
521 }
522 std::vector<std::string> _probeCommand;
James Feist08a5b172019-08-28 14:47:47 -0700523 std::function<void(FoundDeviceT&)> _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700524};
525
526// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800527bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800528{
James Feist1df06a42019-04-11 14:23:04 -0700529 std::filesystem::create_directory(configurationOutDir);
530 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700531 if (!output.good())
532 {
533 return false;
534 }
James Feist1b2e2242018-01-30 13:45:19 -0800535 output << systemConfiguration.dump(4);
536 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700537 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700538}
James Feist1b2e2242018-01-30 13:45:19 -0800539
James Feist97a63f12018-05-17 13:50:57 -0700540template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800541bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
542 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700543{
544 try
545 {
546 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800547 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700548 ref = value;
549 return true;
550 }
James Feist98132792019-07-09 13:29:09 -0700551 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700552 {
553 return false;
554 }
555}
James Feistbb43d022018-06-12 15:44:33 -0700556
James Feistebcc26b2019-03-22 12:30:43 -0700557// template function to add array as dbus property
558template <typename PropertyType>
559void addArrayToDbus(const std::string& name, const nlohmann::json& array,
560 sdbusplus::asio::dbus_interface* iface,
561 sdbusplus::asio::PropertyPermission permission,
562 nlohmann::json& systemConfiguration,
563 const std::string& jsonPointerString)
564{
565 std::vector<PropertyType> values;
566 for (const auto& property : array)
567 {
568 auto ptr = property.get_ptr<const PropertyType*>();
569 if (ptr != nullptr)
570 {
571 values.emplace_back(*ptr);
572 }
573 }
574
575 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
576 {
577 iface->register_property(name, values);
578 }
579 else
580 {
581 iface->register_property(
582 name, values,
583 [&systemConfiguration,
584 jsonPointerString{std::string(jsonPointerString)}](
585 const std::vector<PropertyType>& newVal,
586 std::vector<PropertyType>& val) {
587 val = newVal;
588 if (!setJsonFromPointer(jsonPointerString, val,
589 systemConfiguration))
590 {
591 std::cerr << "error setting json field\n";
592 return -1;
593 }
594 if (!writeJsonFiles(systemConfiguration))
595 {
596 std::cerr << "error setting json file\n";
597 return -1;
598 }
599 return 1;
600 });
601 }
602}
603
James Feistbb43d022018-06-12 15:44:33 -0700604template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800605void addProperty(const std::string& propertyName, const PropertyType& value,
606 sdbusplus::asio::dbus_interface* iface,
607 nlohmann::json& systemConfiguration,
608 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700609 sdbusplus::asio::PropertyPermission permission)
610{
611 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
612 {
613 iface->register_property(propertyName, value);
614 return;
615 }
James Feist68500ff2018-08-08 15:40:42 -0700616 iface->register_property(
617 propertyName, value,
618 [&systemConfiguration,
619 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800620 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700621 val = newVal;
622 if (!setJsonFromPointer(jsonPointerString, val,
623 systemConfiguration))
624 {
625 std::cerr << "error setting json field\n";
626 return -1;
627 }
James Feistc6248a52018-08-14 10:09:45 -0700628 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700629 {
630 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700631 return -1;
632 }
633 return 1;
634 });
635}
636
637void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800638 const std::string& jsonPointerPath,
639 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
640 sdbusplus::asio::object_server& objServer,
641 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700642{
643 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
644 iface->register_method(
645 "Delete", [&objServer, &systemConfiguration, interface,
646 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700647 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700648 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700649 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700650 {
651 // this technically can't happen as the pointer is pointing to
652 // us
653 throw DBusInternalError();
654 }
655 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feist98132792019-07-09 13:29:09 -0700656 if (!objServer.remove_interface(dbusInterface))
James Feistc6248a52018-08-14 10:09:45 -0700657 {
658 std::cerr << "Can't delete interface " << jsonPointerPath
659 << "\n";
660 throw DBusInternalError();
661 }
662 systemConfiguration[ptr] = nullptr;
663
664 if (!writeJsonFiles(systemConfiguration))
665 {
666 std::cerr << "error setting json file\n";
667 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700668 }
James Feistbb43d022018-06-12 15:44:33 -0700669 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700670 });
James Feistbb43d022018-06-12 15:44:33 -0700671}
672
James Feist1b2e2242018-01-30 13:45:19 -0800673// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700674void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800675 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
676 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
677 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700678 sdbusplus::asio::PropertyPermission permission =
679 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800680{
James Feista465ccc2019-02-08 12:51:01 -0800681 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800682 {
James Feist8f2710a2018-05-09 17:18:55 -0700683 auto type = dictPair.value().type();
684 bool array = false;
685 if (dictPair.value().type() == nlohmann::json::value_t::array)
686 {
687 array = true;
688 if (!dictPair.value().size())
689 {
690 continue;
691 }
692 type = dictPair.value()[0].type();
693 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800694 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700695 {
696 if (arrayItem.type() != type)
697 {
698 isLegal = false;
699 break;
700 }
701 }
702 if (!isLegal)
703 {
704 std::cerr << "dbus format error" << dictPair.value() << "\n";
705 continue;
706 }
James Feista218ddb2019-04-11 14:01:31 -0700707 }
708 if (type == nlohmann::json::value_t::object)
709 {
710 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700711 }
James Feist97a63f12018-05-17 13:50:57 -0700712 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700713 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
714 {
715 // all setable numbers are doubles as it is difficult to always
716 // create a configuration file with all whole numbers as decimals
717 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700718 if (array)
719 {
720 if (dictPair.value()[0].is_number())
721 {
722 type = nlohmann::json::value_t::number_float;
723 }
724 }
725 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700726 {
727 type = nlohmann::json::value_t::number_float;
728 }
729 }
730
James Feist8f2710a2018-05-09 17:18:55 -0700731 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800732 {
James Feist9eb0b582018-04-27 12:15:46 -0700733 case (nlohmann::json::value_t::boolean):
734 {
James Feist8f2710a2018-05-09 17:18:55 -0700735 if (array)
736 {
737 // todo: array of bool isn't detected correctly by
738 // sdbusplus, change it to numbers
739 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700740 iface.get(), permission,
741 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700742 }
James Feistbb43d022018-06-12 15:44:33 -0700743
James Feist97a63f12018-05-17 13:50:57 -0700744 else
745 {
James Feistbb43d022018-06-12 15:44:33 -0700746 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700747 iface.get(), systemConfiguration, key,
748 permission);
James Feist97a63f12018-05-17 13:50:57 -0700749 }
James Feist9eb0b582018-04-27 12:15:46 -0700750 break;
751 }
752 case (nlohmann::json::value_t::number_integer):
753 {
James Feist8f2710a2018-05-09 17:18:55 -0700754 if (array)
755 {
756 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700757 iface.get(), permission,
758 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700759 }
760 else
761 {
James Feistbb43d022018-06-12 15:44:33 -0700762 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700763 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700764 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700765 }
James Feist9eb0b582018-04-27 12:15:46 -0700766 break;
767 }
768 case (nlohmann::json::value_t::number_unsigned):
769 {
James Feist8f2710a2018-05-09 17:18:55 -0700770 if (array)
771 {
772 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700773 iface.get(), permission,
774 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700775 }
776 else
777 {
James Feistbb43d022018-06-12 15:44:33 -0700778 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700779 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700780 systemConfiguration, key,
781 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700782 }
James Feist9eb0b582018-04-27 12:15:46 -0700783 break;
784 }
785 case (nlohmann::json::value_t::number_float):
786 {
James Feist8f2710a2018-05-09 17:18:55 -0700787 if (array)
788 {
789 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700790 iface.get(), permission,
791 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700792 }
James Feistbb43d022018-06-12 15:44:33 -0700793
James Feist97a63f12018-05-17 13:50:57 -0700794 else
795 {
James Feistbb43d022018-06-12 15:44:33 -0700796 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700797 iface.get(), systemConfiguration, key,
798 permission);
James Feist97a63f12018-05-17 13:50:57 -0700799 }
James Feist9eb0b582018-04-27 12:15:46 -0700800 break;
801 }
802 case (nlohmann::json::value_t::string):
803 {
James Feist8f2710a2018-05-09 17:18:55 -0700804 if (array)
805 {
James Feistebcc26b2019-03-22 12:30:43 -0700806 addArrayToDbus<std::string>(
807 dictPair.key(), dictPair.value(), iface.get(),
808 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700809 }
810 else
811 {
James Feistc6248a52018-08-14 10:09:45 -0700812 addProperty(
813 dictPair.key(), dictPair.value().get<std::string>(),
814 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700815 }
James Feist9eb0b582018-04-27 12:15:46 -0700816 break;
817 }
James Feist0eb40352019-04-09 14:44:04 -0700818 default:
819 {
James Feista218ddb2019-04-11 14:01:31 -0700820 std::cerr << "Unexpected json type in system configuration "
821 << dictPair.key() << ": "
822 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700823 break;
824 }
James Feist1b2e2242018-01-30 13:45:19 -0800825 }
826 }
James Feistc6248a52018-08-14 10:09:45 -0700827 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
828 {
829 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
830 systemConfiguration);
831 }
James Feist8f2710a2018-05-09 17:18:55 -0700832 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800833}
834
James Feista465ccc2019-02-08 12:51:01 -0800835sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700836{
837 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
838 interface) != settableInterfaces.end()
839 ? sdbusplus::asio::PropertyPermission::readWrite
840 : sdbusplus::asio::PropertyPermission::readOnly;
841}
842
James Feista465ccc2019-02-08 12:51:01 -0800843void createAddObjectMethod(const std::string& jsonPointerPath,
844 const std::string& path,
845 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700846 sdbusplus::asio::object_server& objServer,
847 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700848{
James Feistd58879a2019-09-11 11:26:07 -0700849 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
850 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700851
852 iface->register_method(
853 "AddObject",
854 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700855 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
856 board](const boost::container::flat_map<std::string, JsonVariantType>&
857 data) {
James Feist68500ff2018-08-08 15:40:42 -0700858 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800859 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700860 auto findExposes = base.find("Exposes");
861
862 if (findExposes == base.end())
863 {
864 throw std::invalid_argument("Entity must have children.");
865 }
866
867 // this will throw invalid-argument to sdbusplus if invalid json
868 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800869 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700870 {
James Feista465ccc2019-02-08 12:51:01 -0800871 nlohmann::json& newJson = newData[item.first];
872 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
873 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700874 }
875
876 auto findName = newData.find("Name");
877 auto findType = newData.find("Type");
878 if (findName == newData.end() || findType == newData.end())
879 {
880 throw std::invalid_argument("AddObject missing Name or Type");
881 }
James Feista465ccc2019-02-08 12:51:01 -0800882 const std::string* type = findType->get_ptr<const std::string*>();
883 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700884 if (type == nullptr || name == nullptr)
885 {
886 throw std::invalid_argument("Type and Name must be a string.");
887 }
888
889 size_t lastIndex = 0;
890 // we add in the "exposes"
891 for (; lastIndex < findExposes->size(); lastIndex++)
892 {
893 if (findExposes->at(lastIndex)["Name"] == *name &&
894 findExposes->at(lastIndex)["Type"] == *type)
895 {
896 throw std::invalid_argument(
897 "Field already in JSON, not adding");
898 }
899 lastIndex++;
900 }
901
902 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
903 *type + ".json");
904 // todo(james) we might want to also make a list of 'can add'
905 // interfaces but for now I think the assumption if there is a
906 // schema avaliable that it is allowed to update is fine
907 if (!schemaFile.good())
908 {
909 throw std::invalid_argument(
910 "No schema avaliable, cannot validate.");
911 }
912 nlohmann::json schema =
913 nlohmann::json::parse(schemaFile, nullptr, false);
914 if (schema.is_discarded())
915 {
916 std::cerr << "Schema not legal" << *type << ".json\n";
917 throw DBusInternalError();
918 }
919 if (!validateJson(schema, newData))
920 {
921 throw std::invalid_argument("Data does not match schema");
922 }
923
James Feist16a02f22019-05-13 15:21:37 -0700924 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700925 if (!writeJsonFiles(systemConfiguration))
926 {
927 std::cerr << "Error writing json files\n";
928 throw DBusInternalError();
929 }
930 std::string dbusName = *name;
931
932 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700933 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700934
935 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
936 createInterface(objServer, path + "/" + dbusName,
937 "xyz.openbmc_project.Configuration." + *type,
938 board);
James Feist68500ff2018-08-08 15:40:42 -0700939 // permission is read-write, as since we just created it, must be
940 // runtime modifiable
941 populateInterfaceFromJson(
942 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700943 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700944 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700945 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700946 });
947 iface->initialize();
948}
949
James Feista465ccc2019-02-08 12:51:01 -0800950void postToDbus(const nlohmann::json& newConfiguration,
951 nlohmann::json& systemConfiguration,
952 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800953
James Feist1b2e2242018-01-30 13:45:19 -0800954{
James Feist97a63f12018-05-17 13:50:57 -0700955 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800956 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800957 {
James Feistf1b14142019-04-10 15:22:09 -0700958 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700959 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700960 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700961 // loop through newConfiguration, but use values from system
962 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700963 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700964 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800965 std::string boardType;
966 if (findBoardType != boardValues.end() &&
967 findBoardType->type() == nlohmann::json::value_t::string)
968 {
969 boardType = findBoardType->get<std::string>();
970 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700971 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800972 }
973 else
974 {
975 std::cerr << "Unable to find type for " << boardKey
976 << " reverting to Chassis.\n";
977 boardType = "Chassis";
978 }
James Feist11be6672018-04-06 14:05:32 -0700979 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800980
981 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700982 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700983 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
984 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800985
James Feistd58879a2019-09-11 11:26:07 -0700986 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
987 createInterface(objServer, boardName,
988 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700989
James Feistd58879a2019-09-11 11:26:07 -0700990 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
991 createInterface(objServer, boardName,
992 "xyz.openbmc_project.Inventory.Item." + boardType,
993 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700994
James Feist68500ff2018-08-08 15:40:42 -0700995 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700996 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700997
James Feist97a63f12018-05-17 13:50:57 -0700998 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700999 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -07001000 jsonPointerPath += "/";
1001 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -08001002 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -07001003 {
1004 if (boardField.value().type() == nlohmann::json::value_t::object)
1005 {
James Feistd58879a2019-09-11 11:26:07 -07001006 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
1007 createInterface(objServer, boardName, boardField.key(),
1008 boardKeyOrig);
1009
James Feistc6248a52018-08-14 10:09:45 -07001010 populateInterfaceFromJson(systemConfiguration,
1011 jsonPointerPath + boardField.key(),
1012 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -07001013 }
1014 }
James Feist97a63f12018-05-17 13:50:57 -07001015
James Feist1e3e6982018-08-03 16:09:28 -07001016 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001017 if (exposes == boardValues.end())
1018 {
1019 continue;
1020 }
James Feist97a63f12018-05-17 13:50:57 -07001021 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001022 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001023
1024 // store the board level pointer so we can modify it on the way down
1025 std::string jsonPointerPathBoard = jsonPointerPath;
1026 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001027 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001028 {
James Feist97a63f12018-05-17 13:50:57 -07001029 exposesIndex++;
1030 jsonPointerPath = jsonPointerPathBoard;
1031 jsonPointerPath += std::to_string(exposesIndex);
1032
James Feistd63d18a2018-07-19 15:23:45 -07001033 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001034 if (findName == item.end())
1035 {
1036 std::cerr << "cannot find name in field " << item << "\n";
1037 continue;
1038 }
James Feist1e3e6982018-08-03 16:09:28 -07001039 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001040 // if status is not found it is assumed to be status = 'okay'
1041 if (findStatus != item.end())
1042 {
1043 if (*findStatus == "disabled")
1044 {
1045 continue;
1046 }
1047 }
James Feistd63d18a2018-07-19 15:23:45 -07001048 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001049 std::string itemType;
1050 if (findType != item.end())
1051 {
1052 itemType = findType->get<std::string>();
1053 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001054 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1055 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001056 }
1057 else
1058 {
1059 itemType = "unknown";
1060 }
1061 std::string itemName = findName->get<std::string>();
1062 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001063 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001064
James Feistd58879a2019-09-11 11:26:07 -07001065 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1066 createInterface(objServer, boardName + "/" + itemName,
1067 "xyz.openbmc_project.Configuration." + itemType,
1068 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001069
James Feist97a63f12018-05-17 13:50:57 -07001070 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001071 itemIface, item, objServer,
1072 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001073
James Feista465ccc2019-02-08 12:51:01 -08001074 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001075 {
James Feist97a63f12018-05-17 13:50:57 -07001076 jsonPointerPath = jsonPointerPathBoard +
1077 std::to_string(exposesIndex) + "/" +
1078 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001079 if (objectPair.value().type() ==
1080 nlohmann::json::value_t::object)
1081 {
James Feistd58879a2019-09-11 11:26:07 -07001082 std::shared_ptr<sdbusplus::asio::dbus_interface>
1083 objectIface = createInterface(
1084 objServer, boardName + "/" + itemName,
1085 "xyz.openbmc_project.Configuration." + itemType +
1086 "." + objectPair.key(),
1087 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001088
1089 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001090 systemConfiguration, jsonPointerPath, objectIface,
1091 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001092 }
1093 else if (objectPair.value().type() ==
1094 nlohmann::json::value_t::array)
1095 {
1096 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001097 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001098 {
James Feist8f2710a2018-05-09 17:18:55 -07001099 continue;
1100 }
1101 bool isLegal = true;
1102 auto type = objectPair.value()[0].type();
1103 if (type != nlohmann::json::value_t::object)
1104 {
1105 continue;
1106 }
1107
1108 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001109 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001110 {
1111 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001112 {
James Feist8f2710a2018-05-09 17:18:55 -07001113 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001114 break;
1115 }
James Feist8f2710a2018-05-09 17:18:55 -07001116 }
1117 if (!isLegal)
1118 {
1119 std::cerr << "dbus format error" << objectPair.value()
1120 << "\n";
1121 break;
1122 }
1123
James Feista465ccc2019-02-08 12:51:01 -08001124 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001125 {
James Feist97a63f12018-05-17 13:50:57 -07001126
James Feistd58879a2019-09-11 11:26:07 -07001127 std::shared_ptr<sdbusplus::asio::dbus_interface>
1128 objectIface = createInterface(
1129 objServer, boardName + "/" + itemName,
1130 "xyz.openbmc_project.Configuration." +
1131 itemType + "." + objectPair.key() +
1132 std::to_string(index),
1133 boardKeyOrig);
1134
James Feistc6248a52018-08-14 10:09:45 -07001135 populateInterfaceFromJson(
1136 systemConfiguration,
1137 jsonPointerPath + "/" + std::to_string(index),
1138 objectIface, arrayItem, objServer,
1139 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001140 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001141 }
1142 }
1143 }
1144 }
1145 }
1146}
1147
James Feist8f2710a2018-05-09 17:18:55 -07001148// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001149bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001150{
1151 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001152 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001153 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1154 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001155 {
1156 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001157 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001158 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001159 }
James Feistb4383f42018-08-06 16:54:10 -07001160
1161 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1162 globalSchema);
1163 if (!schemaStream.good())
1164 {
1165 std::cerr
1166 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1167 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001168 return false;
James Feistb4383f42018-08-06 16:54:10 -07001169 }
1170 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1171 if (schema.is_discarded())
1172 {
1173 std::cerr
1174 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1175 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001176 return false;
James Feistb4383f42018-08-06 16:54:10 -07001177 }
1178
James Feista465ccc2019-02-08 12:51:01 -08001179 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001180 {
1181 std::ifstream jsonStream(jsonPath.c_str());
1182 if (!jsonStream.good())
1183 {
1184 std::cerr << "unable to open " << jsonPath.string() << "\n";
1185 continue;
1186 }
1187 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1188 if (data.is_discarded())
1189 {
1190 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1191 continue;
1192 }
James Feist8da99192019-01-24 08:20:16 -08001193 /*
1194 * todo(james): reenable this once less things are in flight
1195 *
James Feistb4383f42018-08-06 16:54:10 -07001196 if (!validateJson(schema, data))
1197 {
1198 std::cerr << "Error validating " << jsonPath.string() << "\n";
1199 continue;
1200 }
James Feist8da99192019-01-24 08:20:16 -08001201 */
James Feistb4383f42018-08-06 16:54:10 -07001202
James Feist3cb5fec2018-01-23 14:41:51 -08001203 if (data.type() == nlohmann::json::value_t::array)
1204 {
James Feista465ccc2019-02-08 12:51:01 -08001205 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001206 {
1207 configurations.emplace_back(d);
1208 }
1209 }
1210 else
1211 {
1212 configurations.emplace_back(data);
1213 }
1214 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001215 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001216}
James Feist3cb5fec2018-01-23 14:41:51 -08001217
James Feist8f2710a2018-05-09 17:18:55 -07001218struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001219{
James Feist75fdeeb2018-02-20 14:26:16 -08001220
James Feista465ccc2019-02-08 12:51:01 -08001221 PerformScan(nlohmann::json& systemConfiguration,
James Feist899e17f2019-09-13 11:46:29 -07001222 nlohmann::json& missingConfigurations,
James Feista465ccc2019-02-08 12:51:01 -08001223 std::list<nlohmann::json>& configurations,
1224 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001225 _systemConfiguration(systemConfiguration),
James Feist899e17f2019-09-13 11:46:29 -07001226 _missingConfigurations(missingConfigurations),
James Feist8f2710a2018-05-09 17:18:55 -07001227 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001228 {
James Feist8f2710a2018-05-09 17:18:55 -07001229 }
1230 void run()
1231 {
1232 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001233 {
James Feist1e3e6982018-08-03 16:09:28 -07001234 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001235 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001236
James Feist1b2e2242018-01-30 13:45:19 -08001237 nlohmann::json probeCommand;
1238 // check for poorly formatted fields, probe must be an array
1239 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001240 {
1241 std::cerr << "configuration file missing probe:\n " << *it
1242 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001243 it = _configurations.erase(it);
1244 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001245 }
James Feist1b2e2242018-01-30 13:45:19 -08001246 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001247 {
1248 probeCommand = nlohmann::json::array();
1249 probeCommand.push_back(*findProbe);
1250 }
1251 else
1252 {
1253 probeCommand = *findProbe;
1254 }
James Feist1b2e2242018-01-30 13:45:19 -08001255
1256 if (findName == it->end())
1257 {
1258 std::cerr << "configuration file missing name:\n " << *it
1259 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001260 it = _configurations.erase(it);
1261 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001262 }
James Feistf1b14142019-04-10 15:22:09 -07001263 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001264
James Feistf1b14142019-04-10 15:22:09 -07001265 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1266 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001267 {
James Feist8f2710a2018-05-09 17:18:55 -07001268 it = _configurations.erase(it);
1269 continue;
1270 }
James Feistf1b14142019-04-10 15:22:09 -07001271 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001272
James Feist8f2710a2018-05-09 17:18:55 -07001273 // store reference to this to children to makes sure we don't get
1274 // destroyed too early
1275 auto thisRef = shared_from_this();
James Feist08a5b172019-08-28 14:47:47 -07001276 auto p = std::make_shared<
1277 PerformProbe>(probeCommand, [&, recordPtr, probeName, thisRef](
1278 FoundDeviceT& foundDevices) {
1279 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001280
James Feist08a5b172019-08-28 14:47:47 -07001281 PASSED_PROBES.push_back(probeName);
1282 size_t foundDeviceIdx = 1;
James Feist8f2710a2018-05-09 17:18:55 -07001283
James Feist08a5b172019-08-28 14:47:47 -07001284 for (auto& foundDevice : foundDevices)
1285 {
1286 nlohmann::json record = *recordPtr;
1287 std::string recordName;
1288 size_t hash = 0;
1289 if (foundDevice.size())
James Feist3cb5fec2018-01-23 14:41:51 -08001290 {
James Feist08a5b172019-08-28 14:47:47 -07001291 // use an array so alphabetical order from the
1292 // flat_map is maintained
1293 auto device = nlohmann::json::array();
1294 for (auto& devPair : foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001295 {
James Feist08a5b172019-08-28 14:47:47 -07001296 device.push_back(devPair.first);
1297 std::visit(
1298 [&device](auto&& v) { device.push_back(v); },
1299 devPair.second);
1300 }
1301 hash =
1302 std::hash<std::string>{}(probeName + device.dump());
1303 // hashes are hard to distinguish, use the
1304 // non-hashed version if we want debug
1305 if constexpr (DEBUG)
1306 {
1307 recordName = probeName + device.dump();
James Feist8f2710a2018-05-09 17:18:55 -07001308 }
James Feistf1b14142019-04-10 15:22:09 -07001309 else
1310 {
James Feist08a5b172019-08-28 14:47:47 -07001311 recordName = std::to_string(hash);
James Feistf1b14142019-04-10 15:22:09 -07001312 }
James Feist08a5b172019-08-28 14:47:47 -07001313 }
1314 else
1315 {
1316 recordName = probeName;
1317 }
James Feistf1b14142019-04-10 15:22:09 -07001318
James Feist08a5b172019-08-28 14:47:47 -07001319 auto fromLastJson = lastJson.find(recordName);
1320 if (fromLastJson != lastJson.end())
1321 {
1322 // keep user changes
1323 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001324 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001325 continue;
1326 }
James Feist1df06a42019-04-11 14:23:04 -07001327
James Feist08a5b172019-08-28 14:47:47 -07001328 // insert into configuration temporarily to be able to
1329 // reference ourselves
James Feistf1b14142019-04-10 15:22:09 -07001330
James Feist08a5b172019-08-28 14:47:47 -07001331 _systemConfiguration[recordName] = record;
1332
1333 for (auto keyPair = record.begin(); keyPair != record.end();
1334 keyPair++)
1335 {
1336 templateCharReplace(keyPair, foundDevice,
1337 foundDeviceIdx);
1338 }
1339
1340 auto findExpose = record.find("Exposes");
1341 if (findExpose == record.end())
1342 {
James Feistf1b14142019-04-10 15:22:09 -07001343 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001344 foundDeviceIdx++;
1345 continue;
1346 }
James Feistf1b14142019-04-10 15:22:09 -07001347
James Feist08a5b172019-08-28 14:47:47 -07001348 for (auto& expose : *findExpose)
1349 {
1350 for (auto keyPair = expose.begin();
1351 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001352 {
James Feist08a5b172019-08-28 14:47:47 -07001353
1354 templateCharReplace(keyPair, foundDevice,
1355 foundDeviceIdx);
1356
1357 // special case bind
1358 if (boost::starts_with(keyPair.key(), "Bind"))
James Feistf1b14142019-04-10 15:22:09 -07001359 {
James Feist08a5b172019-08-28 14:47:47 -07001360 if (keyPair.value().type() !=
1361 nlohmann::json::value_t::string)
James Feistf1b14142019-04-10 15:22:09 -07001362 {
James Feist08a5b172019-08-28 14:47:47 -07001363 std::cerr << "bind_ value must be of "
1364 "type string "
1365 << keyPair.key() << "\n";
1366 continue;
James Feistf1b14142019-04-10 15:22:09 -07001367 }
James Feist08a5b172019-08-28 14:47:47 -07001368 bool foundBind = false;
1369 std::string bind =
1370 keyPair.key().substr(sizeof("Bind") - 1);
1371
1372 for (auto& configurationPair :
1373 _systemConfiguration.items())
James Feist8f2710a2018-05-09 17:18:55 -07001374 {
James Feist08a5b172019-08-28 14:47:47 -07001375
1376 auto configListFind =
1377 configurationPair.value().find(
1378 "Exposes");
1379
1380 if (configListFind ==
1381 configurationPair.value().end() ||
1382 configListFind->type() !=
1383 nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001384 {
James Feist1b2e2242018-01-30 13:45:19 -08001385 continue;
1386 }
James Feist08a5b172019-08-28 14:47:47 -07001387 for (auto& exposedObject : *configListFind)
James Feist1b2e2242018-01-30 13:45:19 -08001388 {
James Feist08a5b172019-08-28 14:47:47 -07001389 std::string foundObjectName =
1390 (exposedObject)["Name"];
1391 if (boost::iequals(
1392 foundObjectName,
1393 keyPair.value()
1394 .get<std::string>()))
James Feist8f2710a2018-05-09 17:18:55 -07001395 {
James Feist08a5b172019-08-28 14:47:47 -07001396 exposedObject["Status"] = "okay";
1397 expose[bind] = exposedObject;
James Feist8f2710a2018-05-09 17:18:55 -07001398
James Feist08a5b172019-08-28 14:47:47 -07001399 foundBind = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001400 break;
1401 }
1402 }
James Feist08a5b172019-08-28 14:47:47 -07001403 if (foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001404 {
James Feist08a5b172019-08-28 14:47:47 -07001405 break;
James Feist3cb5fec2018-01-23 14:41:51 -08001406 }
1407 }
James Feist08a5b172019-08-28 14:47:47 -07001408 if (!foundBind)
1409 {
1410 std::cerr << "configuration file "
1411 "dependency error, "
1412 "could not find bind "
1413 << keyPair.value() << "\n";
1414 }
James Feist3cb5fec2018-01-23 14:41:51 -08001415 }
1416 }
1417 }
James Feist08a5b172019-08-28 14:47:47 -07001418 // overwrite ourselves with cleaned up version
1419 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001420 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001421
1422 foundDeviceIdx++;
1423 }
1424 });
James Feist8f2710a2018-05-09 17:18:55 -07001425 p->run();
1426 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001427 }
1428 }
James Feist75fdeeb2018-02-20 14:26:16 -08001429
James Feist8f2710a2018-05-09 17:18:55 -07001430 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001431 {
James Feist8f2710a2018-05-09 17:18:55 -07001432 if (_passed)
1433 {
1434 auto nextScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001435 _systemConfiguration, _missingConfigurations, _configurations,
1436 std::move(_callback));
James Feist8f2710a2018-05-09 17:18:55 -07001437 nextScan->run();
1438 }
1439 else
1440 {
1441 _callback();
1442 }
1443 }
James Feista465ccc2019-02-08 12:51:01 -08001444 nlohmann::json& _systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001445 nlohmann::json& _missingConfigurations;
James Feist8f2710a2018-05-09 17:18:55 -07001446 std::list<nlohmann::json> _configurations;
1447 std::function<void(void)> _callback;
1448 std::vector<std::shared_ptr<PerformProbe>> _probes;
1449 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001450 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001451};
James Feistc95cb142018-02-26 10:41:42 -08001452
James Feist1df06a42019-04-11 14:23:04 -07001453void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001454 nlohmann::json& systemConfiguration)
1455{
1456 static bool scannedPowerOff = false;
1457 static bool scannedPowerOn = false;
1458
James Feistfb00f392019-06-25 14:16:48 -07001459 if (systemConfiguration.empty() || lastJson.empty())
1460 {
1461 return; // not ready yet
1462 }
James Feist1df06a42019-04-11 14:23:04 -07001463 if (scannedPowerOn)
1464 {
1465 return;
1466 }
1467
1468 if (!isPowerOn() && scannedPowerOff)
1469 {
1470 return;
1471 }
1472
1473 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001474 timer.async_wait(
1475 [&systemConfiguration](const boost::system::error_code& ec) {
1476 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001477 {
James Feist1a996582019-05-14 15:10:06 -07001478 // we were cancelled
1479 return;
1480 }
1481
1482 bool powerOff = !isPowerOn();
1483 for (const auto& item : lastJson.items())
1484 {
1485 if (systemConfiguration.find(item.key()) ==
1486 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001487 {
James Feist1a996582019-05-14 15:10:06 -07001488 bool isDetectedPowerOn = false;
1489 auto powerState = item.value().find("PowerState");
1490 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001491 {
James Feist1a996582019-05-14 15:10:06 -07001492 auto ptr = powerState->get_ptr<const std::string*>();
1493 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001494 {
James Feist1a996582019-05-14 15:10:06 -07001495 if (*ptr == "On" || *ptr == "BiosPost")
1496 {
1497 isDetectedPowerOn = true;
1498 }
James Feist1df06a42019-04-11 14:23:04 -07001499 }
1500 }
James Feist1a996582019-05-14 15:10:06 -07001501 if (powerOff && isDetectedPowerOn)
1502 {
1503 // power not on yet, don't know if it's there or not
1504 continue;
1505 }
1506 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1507 {
1508 // already logged it when power was off
1509 continue;
1510 }
James Feist1df06a42019-04-11 14:23:04 -07001511
James Feist1a996582019-05-14 15:10:06 -07001512 logDeviceRemoved(item.value());
1513 }
James Feist1df06a42019-04-11 14:23:04 -07001514 }
James Feist1a996582019-05-14 15:10:06 -07001515 scannedPowerOff = true;
1516 if (!powerOff)
1517 {
1518 scannedPowerOn = true;
1519 }
1520 });
James Feist1df06a42019-04-11 14:23:04 -07001521}
1522
James Feist8f2710a2018-05-09 17:18:55 -07001523// main properties changed entry
1524void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001525 boost::asio::io_service& io,
1526 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1527 nlohmann::json& systemConfiguration,
1528 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001529{
1530 static boost::asio::deadline_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001531 static size_t instance = 0;
1532 instance++;
1533 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001534
James Feist899e17f2019-09-13 11:46:29 -07001535 timer.expires_from_now(boost::posix_time::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001536
1537 // setup an async wait as we normally get flooded with new requests
James Feist899e17f2019-09-13 11:46:29 -07001538 timer.async_wait([&systemConfiguration, &dbusMatches, &io, &objServer,
1539 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001540 if (ec == boost::asio::error::operation_aborted)
1541 {
1542 // we were cancelled
1543 return;
1544 }
1545 else if (ec)
1546 {
1547 std::cerr << "async wait error " << ec << "\n";
1548 return;
1549 }
1550
1551 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001552 auto missingConfigurations = std::make_shared<nlohmann::json>();
1553 *missingConfigurations = systemConfiguration;
1554
James Feist8f2710a2018-05-09 17:18:55 -07001555 DBUS_PROBE_OBJECTS.clear();
James Feist899e17f2019-09-13 11:46:29 -07001556 PASSED_PROBES.clear();
James Feist8f2710a2018-05-09 17:18:55 -07001557
1558 std::list<nlohmann::json> configurations;
1559 if (!findJsonFiles(configurations))
1560 {
1561 std::cerr << "cannot find json files\n";
1562 return;
1563 }
1564
1565 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001566 systemConfiguration, *missingConfigurations, configurations,
1567 [&systemConfiguration, &io, &objServer, &dbusMatches, count,
1568 oldConfiguration, missingConfigurations]() {
1569 // this is something that since ac has been applied to the bmc
1570 // we saw, and we no longer see it
1571 bool powerOff = !isPowerOn();
1572 for (const auto& item : missingConfigurations->items())
1573 {
1574 bool isDetectedPowerOn = false;
1575 auto powerState = item.value().find("PowerState");
1576 if (powerState != item.value().end())
1577 {
1578 auto ptr = powerState->get_ptr<const std::string*>();
1579 if (ptr)
1580 {
1581 if (*ptr == "On" || *ptr == "BiosPost")
1582 {
1583 isDetectedPowerOn = true;
1584 }
1585 }
1586 }
1587 if (powerOff && isDetectedPowerOn)
1588 {
1589 // power not on yet, don't know if it's there or not
1590 continue;
1591 }
1592 std::string name = item.value()["Name"].get<std::string>();
1593 std::vector<
1594 std::shared_ptr<sdbusplus::asio::dbus_interface>>&
1595 ifaces = inventory[name];
1596 for (auto& iface : ifaces)
1597 {
1598 objServer.remove_interface(iface);
1599 }
1600 ifaces.clear();
1601 systemConfiguration.erase(item.key());
1602 logDeviceRemoved(item.value());
1603 }
1604
James Feist8f2710a2018-05-09 17:18:55 -07001605 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001606 for (auto it = newConfiguration.begin();
1607 it != newConfiguration.end();)
1608 {
1609 auto findKey = oldConfiguration.find(it.key());
1610 if (findKey != oldConfiguration.end())
1611 {
1612 it = newConfiguration.erase(it);
1613 }
1614 else
1615 {
1616 it++;
1617 }
1618 }
James Feist899e17f2019-09-13 11:46:29 -07001619 for (const auto& item : newConfiguration.items())
1620 {
1621 logDeviceAdded(item.value());
1622 }
1623
James Feist8f2710a2018-05-09 17:18:55 -07001624 registerCallbacks(io, dbusMatches, systemConfiguration,
1625 objServer);
1626 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001627 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001628
James Feistbb43d022018-06-12 15:44:33 -07001629 io.post([&]() {
1630 if (!writeJsonFiles(systemConfiguration))
1631 {
1632 std::cerr << "Error writing json files\n";
1633 }
1634 });
James Feist8f2710a2018-05-09 17:18:55 -07001635 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001636 postToDbus(newConfiguration, systemConfiguration,
1637 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001638 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001639 {
James Feist899e17f2019-09-13 11:46:29 -07001640 return;
James Feist1df06a42019-04-11 14:23:04 -07001641 }
James Feist899e17f2019-09-13 11:46:29 -07001642 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001643 });
1644 });
1645 });
1646 perfScan->run();
1647 });
James Feist75fdeeb2018-02-20 14:26:16 -08001648}
1649
James Feista465ccc2019-02-08 12:51:01 -08001650void registerCallbacks(boost::asio::io_service& io,
1651 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1652 nlohmann::json& systemConfiguration,
1653 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001654{
1655 static boost::container::flat_set<std::string> watchedObjects;
1656
James Feista465ccc2019-02-08 12:51:01 -08001657 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001658 {
1659 auto findObject = watchedObjects.find(objectMap.first);
1660 if (findObject != watchedObjects.end())
1661 {
1662 continue;
1663 }
James Feist8f2710a2018-05-09 17:18:55 -07001664 std::function<void(sdbusplus::message::message & message)>
1665 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001666
James Feista465ccc2019-02-08 12:51:01 -08001667 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001668 propertiesChangedCallback(io, dbusMatches,
1669 systemConfiguration, objServer);
1670 };
1671
1672 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001673 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001674 "type='signal',member='PropertiesChanged',arg0='" +
1675 objectMap.first + "'",
1676 eventHandler);
1677 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001678 }
1679}
1680
James Feist98132792019-07-09 13:29:09 -07001681int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001682{
1683 // setup connection to dbus
1684 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001685 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001686 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001687
James Feist8f2710a2018-05-09 17:18:55 -07001688 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001689
James Feist8f2710a2018-05-09 17:18:55 -07001690 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1691 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1692 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001693
James Feist8f2710a2018-05-09 17:18:55 -07001694 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1695 objServer.add_interface("/xyz/openbmc_project/inventory",
1696 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001697
1698 // to keep reference to the match / filter objects so they don't get
1699 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001700 std::vector<sdbusplus::bus::match::match> dbusMatches;
1701
1702 nlohmann::json systemConfiguration = nlohmann::json::object();
1703
1704 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001705 "Notify",
1706 [](const boost::container::flat_map<
1707 std::string,
James Feist98132792019-07-09 13:29:09 -07001708 boost::container::flat_map<std::string, BasicVariantType>>&) {
1709 return;
1710 });
James Feist8f2710a2018-05-09 17:18:55 -07001711 inventoryIface->initialize();
1712
1713 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001714#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001715 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001716#endif
James Feist8f2710a2018-05-09 17:18:55 -07001717 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1718 objServer);
1719 });
James Feist4131aea2018-03-09 09:47:30 -08001720
James Feistfd1264a2018-05-03 12:10:00 -07001721 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001722 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1723 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001724 });
James Feist8f2710a2018-05-09 17:18:55 -07001725 entityIface->initialize();
1726
James Feist1df06a42019-04-11 14:23:04 -07001727 if (fwVersionIsSame())
1728 {
1729 if (std::filesystem::is_regular_file(currentConfiguration))
1730 {
1731 // this file could just be deleted, but it's nice for debug
1732 std::filesystem::create_directory(tempConfigDir);
1733 std::filesystem::remove(lastConfiguration);
1734 std::filesystem::copy(currentConfiguration, lastConfiguration);
1735 std::filesystem::remove(currentConfiguration);
1736
1737 std::ifstream jsonStream(lastConfiguration);
1738 if (jsonStream.good())
1739 {
1740 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1741 if (data.is_discarded())
1742 {
1743 std::cerr << "syntax error in " << lastConfiguration
1744 << "\n";
1745 }
1746 else
1747 {
1748 lastJson = std::move(data);
1749 }
1750 }
1751 else
1752 {
1753 std::cerr << "unable to open " << lastConfiguration << "\n";
1754 }
1755 }
1756 }
1757 else
1758 {
1759 // not an error, just logging at this level to make it in the journal
1760 std::cerr << "Clearing previous configuration\n";
1761 std::filesystem::remove(currentConfiguration);
1762 }
1763
1764 // some boards only show up after power is on, we want to not say they are
1765 // removed until the same state happens
1766 setupPowerMatch(SYSTEM_BUS);
1767
James Feist1b2e2242018-01-30 13:45:19 -08001768 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001769
1770 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001771}