blob: 31163d180925684f74e0aba3d3e277e65dd4662e [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
104// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700105std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700106static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800107
Johnathan Mantey2015f752019-03-26 15:22:31 -0700108const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
109const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800110
James Feista465ccc2019-02-08 12:51:01 -0800111void registerCallbacks(boost::asio::io_service& io,
112 std::vector<sdbusplus::bus::match::match>& dbusMatches,
113 nlohmann::json& systemConfiguration,
114 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800115
James Feist3cb5fec2018-01-23 14:41:51 -0800116// calls the mapper to find all exposed objects of an interface type
117// and creates a vector<flat_map> that contains all the key value pairs
118// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700119void findDbusObjects(std::shared_ptr<PerformProbe> probe,
120 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800121 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800122{
James Feist8f2710a2018-05-09 17:18:55 -0700123
124 // store reference to pending callbacks so we don't overwhelm services
125 static boost::container::flat_map<
126 std::string, std::vector<std::shared_ptr<PerformProbe>>>
127 pendingProbes;
128
129 if (DBUS_PROBE_OBJECTS[interface].size())
130 {
131 return;
132 }
133
134 // add shared_ptr to vector of Probes waiting for callback from a specific
135 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800136 std::array<const char*, 1> objects = {interface.c_str()};
137 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700138 pendingProbes[interface];
139 auto iter = pending.emplace(pending.end(), probe);
140 // only allow first call to run to not overwhelm processes
141 if (iter != pending.begin())
142 {
143 return;
144 }
145
James Feist3cb5fec2018-01-23 14:41:51 -0800146 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700147 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800148 [connection, interface, probe](boost::system::error_code& ec,
149 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700150 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700151 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700152 {
James Feist0de40152018-07-25 11:56:12 -0700153 pendingProbes[interface].clear();
154 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700155 {
James Feist0de40152018-07-25 11:56:12 -0700156 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700157 }
James Feist0de40152018-07-25 11:56:12 -0700158 std::cerr << "Error communicating to mapper.\n";
159
160 // if we can't communicate to the mapper something is very wrong
161 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700162 }
James Feist8f2710a2018-05-09 17:18:55 -0700163 else
James Feist3cb5fec2018-01-23 14:41:51 -0800164 {
James Feista465ccc2019-02-08 12:51:01 -0800165 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700166 {
James Feista465ccc2019-02-08 12:51:01 -0800167 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700168 {
James Feist0eb40352019-04-09 14:44:04 -0700169 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700170 }
171 }
James Feist3cb5fec2018-01-23 14:41:51 -0800172 }
James Feist63845bf2019-01-24 12:19:51 -0800173 if (interfaceConnections.empty())
174 {
175 pendingProbes[interface].clear();
176 return;
177 }
James Feist8f2710a2018-05-09 17:18:55 -0700178 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800179 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700180 {
181 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700182 [conn,
James Feist98132792019-07-09 13:29:09 -0700183 interface](boost::system::error_code& errc,
James Feista465ccc2019-02-08 12:51:01 -0800184 const ManagedObjectType& managedInterface) {
James Feist98132792019-07-09 13:29:09 -0700185 if (errc)
James Feist8f2710a2018-05-09 17:18:55 -0700186 {
187 std::cerr
188 << "error getting managed object for device "
189 << conn << "\n";
190 pendingProbes[interface].clear();
191 return;
192 }
James Feista465ccc2019-02-08 12:51:01 -0800193 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700194 {
195 auto ifaceObjFind =
196 interfaceManagedObj.second.find(interface);
197 if (ifaceObjFind !=
198 interfaceManagedObj.second.end())
199 {
200 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800201 std::string, BasicVariantType>>&
202 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700203 dbusObject.emplace_back(ifaceObjFind->second);
204 }
205 }
206 pendingProbes[interface].clear();
207 },
208 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
209 "GetManagedObjects");
210 }
211 },
212 "xyz.openbmc_project.ObjectMapper",
213 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700214 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700215 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800216}
James Feist8f2710a2018-05-09 17:18:55 -0700217// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700218bool probeDbus(const std::string& interface,
219 const std::map<std::string, nlohmann::json>& matches,
220 FoundDeviceT& devices, bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800221{
James Feista465ccc2019-02-08 12:51:01 -0800222 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
223 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800224 if (dbusObject.empty())
225 {
James Feist8f2710a2018-05-09 17:18:55 -0700226 foundProbe = false;
227 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800228 }
229 foundProbe = true;
230
231 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800232 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800233 {
234 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800235 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800236 {
237 auto deviceValue = device.find(match.first);
238 if (deviceValue != device.end())
239 {
240 switch (match.second.type())
241 {
James Feist9eb0b582018-04-27 12:15:46 -0700242 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800243 {
James Feist9eb0b582018-04-27 12:15:46 -0700244 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700245 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700246
247 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800248 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700249 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700250 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700251 {
252 deviceMatches = false;
253 break;
254 }
James Feist3cb5fec2018-01-23 14:41:51 -0800255 break;
256 }
James Feist9eb0b582018-04-27 12:15:46 -0700257 case nlohmann::json::value_t::boolean:
258 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800259 {
James Feista465ccc2019-02-08 12:51:01 -0800260 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700261 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800262
James Feist9eb0b582018-04-27 12:15:46 -0700263 if (probeValue != match.second.get<unsigned int>())
264 {
265 deviceMatches = false;
266 }
267 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800268 }
James Feist9eb0b582018-04-27 12:15:46 -0700269 case nlohmann::json::value_t::number_integer:
270 {
James Feista465ccc2019-02-08 12:51:01 -0800271 int probeValue = std::visit(VariantToIntVisitor(),
272 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800273
James Feist9eb0b582018-04-27 12:15:46 -0700274 if (probeValue != match.second.get<int>())
275 {
276 deviceMatches = false;
277 }
278 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800279 }
James Feist9eb0b582018-04-27 12:15:46 -0700280 case nlohmann::json::value_t::number_float:
281 {
James Feista465ccc2019-02-08 12:51:01 -0800282 float probeValue = std::visit(VariantToFloatVisitor(),
283 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700284
285 if (probeValue != match.second.get<float>())
286 {
287 deviceMatches = false;
288 }
289 break;
290 }
James Feist0eb40352019-04-09 14:44:04 -0700291 default:
292 {
293 std::cerr << "unexpected dbus probe type "
294 << match.second.type_name() << "\n";
295 }
James Feist3cb5fec2018-01-23 14:41:51 -0800296 }
297 }
298 else
299 {
300 deviceMatches = false;
301 break;
302 }
303 }
304 if (deviceMatches)
305 {
James Feistf5125b02019-06-06 11:27:43 -0700306 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800307 foundMatch = true;
308 deviceMatches = false; // for next iteration
309 }
310 }
311 return foundMatch;
312}
313
314// default probe entry point, iterates a list looking for specific types to
315// call specific probe functions
316bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800317 const std::vector<std::string>& probeCommand,
James Feist08a5b172019-08-28 14:47:47 -0700318 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
319 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800320{
321 const static std::regex command(R"(\((.*)\))");
322 std::smatch match;
323 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700324 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800325 bool cur = true;
326 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700327 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800328
James Feista465ccc2019-02-08 12:51:01 -0800329 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800330 {
331 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800332 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800333 cmp_str>::const_iterator probeType;
334
335 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
336 probeType++)
337 {
338 if (probe.find(probeType->first) != std::string::npos)
339 {
340 foundProbe = true;
341 break;
342 }
343 }
344 if (foundProbe)
345 {
346 switch (probeType->second)
347 {
James Feist9eb0b582018-04-27 12:15:46 -0700348 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800349 {
James Feiste31e00a2019-07-24 10:45:43 -0700350 cur = false;
351 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800352 }
James Feist9eb0b582018-04-27 12:15:46 -0700353 case probe_type_codes::TRUE_T:
354 {
James Feiste31e00a2019-07-24 10:45:43 -0700355 cur = true;
356 break;
James Feist9eb0b582018-04-27 12:15:46 -0700357 }
358 case probe_type_codes::MATCH_ONE:
359 {
360 // set current value to last, this probe type shouldn't
361 // affect the outcome
362 cur = ret;
363 matchOne = true;
364 break;
365 }
366 /*case probe_type_codes::AND:
367 break;
368 case probe_type_codes::OR:
369 break;
370 // these are no-ops until the last command switch
371 */
372 case probe_type_codes::FOUND:
373 {
374 if (!std::regex_search(probe, match, command))
375 {
James Feist0eb40352019-04-09 14:44:04 -0700376 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700377 << "\n";
378 return false;
379 }
380 std::string commandStr = *(match.begin() + 1);
381 boost::replace_all(commandStr, "'", "");
382 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
383 commandStr) != PASSED_PROBES.end());
384 break;
385 }
James Feist0eb40352019-04-09 14:44:04 -0700386 default:
387 {
388 break;
389 }
James Feist3cb5fec2018-01-23 14:41:51 -0800390 }
391 }
392 // look on dbus for object
393 else
394 {
395 if (!std::regex_search(probe, match, command))
396 {
James Feist0eb40352019-04-09 14:44:04 -0700397 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800398 return false;
399 }
400 std::string commandStr = *(match.begin() + 1);
401 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700402 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800403 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800404 auto json = nlohmann::json::parse(commandStr, nullptr, false);
405 if (json.is_discarded())
406 {
James Feist0eb40352019-04-09 14:44:04 -0700407 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800408 return false;
409 }
410 // we can match any (string, variant) property. (string, string)
411 // does a regex
412 std::map<std::string, nlohmann::json> dbusProbeMap =
413 json.get<std::map<std::string, nlohmann::json>>();
414 auto findStart = probe.find("(");
415 if (findStart == std::string::npos)
416 {
417 return false;
418 }
419 std::string probeInterface = probe.substr(0, findStart);
420 cur =
421 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
422 }
423
424 // some functions like AND and OR only take affect after the
425 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700426 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800427 {
James Feist54a0dca2019-06-26 10:34:54 -0700428 ret = cur && ret;
429 }
430 else if (lastCommand == probe_type_codes::OR)
431 {
432 ret = cur || ret;
433 }
434
435 if (first)
436 {
437 ret = cur;
438 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800439 }
440 lastCommand = probeType != PROBE_TYPES.end()
441 ? probeType->second
442 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800443 }
444
445 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800446 if (ret && foundDevs.size() == 0)
447 {
James Feist08a5b172019-08-28 14:47:47 -0700448 foundDevs.emplace_back(
449 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800450 }
James Feist0eb40352019-04-09 14:44:04 -0700451 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700452 {
James Feist71f295f2019-06-20 13:35:12 -0700453 // match the last one
454 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700455 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700456
457 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700458 }
James Feist3cb5fec2018-01-23 14:41:51 -0800459 return ret;
460}
James Feist8f2710a2018-05-09 17:18:55 -0700461// this class finds the needed dbus fields and on destruction runs the probe
462struct PerformProbe : std::enable_shared_from_this<PerformProbe>
463{
James Feist3cb5fec2018-01-23 14:41:51 -0800464
James Feist08a5b172019-08-28 14:47:47 -0700465 PerformProbe(const std::vector<std::string>& probeCommand,
466 std::function<void(FoundDeviceT&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700467 _probeCommand(probeCommand),
468 _callback(std::move(callback))
469 {
470 }
471 ~PerformProbe()
472 {
James Feist08a5b172019-08-28 14:47:47 -0700473 FoundDeviceT foundDevs;
James Feist0eb40352019-04-09 14:44:04 -0700474 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700475 {
James Feist0eb40352019-04-09 14:44:04 -0700476 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700477 }
478 }
479 void run()
480 {
481 // parse out dbus probes by discarding other probe types
James Feist8f2710a2018-05-09 17:18:55 -0700482
James Feista465ccc2019-02-08 12:51:01 -0800483 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700484 {
485 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800486 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700487 cmp_str>::const_iterator probeType;
488 for (probeType = PROBE_TYPES.begin();
489 probeType != PROBE_TYPES.end(); probeType++)
490 {
491 if (probe.find(probeType->first) != std::string::npos)
492 {
493 found = true;
494 break;
495 }
496 }
497 if (found)
498 {
499 continue;
500 }
501 // syntax requires probe before first open brace
502 auto findStart = probe.find("(");
503 std::string interface = probe.substr(0, findStart);
504
505 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
506 }
507 }
508 std::vector<std::string> _probeCommand;
James Feist08a5b172019-08-28 14:47:47 -0700509 std::function<void(FoundDeviceT&)> _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700510};
511
512// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800513bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800514{
James Feist1df06a42019-04-11 14:23:04 -0700515 std::filesystem::create_directory(configurationOutDir);
516 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700517 if (!output.good())
518 {
519 return false;
520 }
James Feist1b2e2242018-01-30 13:45:19 -0800521 output << systemConfiguration.dump(4);
522 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700523 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700524}
James Feist1b2e2242018-01-30 13:45:19 -0800525
James Feist97a63f12018-05-17 13:50:57 -0700526template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800527bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
528 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700529{
530 try
531 {
532 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800533 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700534 ref = value;
535 return true;
536 }
James Feist98132792019-07-09 13:29:09 -0700537 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700538 {
539 return false;
540 }
541}
James Feistbb43d022018-06-12 15:44:33 -0700542
James Feistebcc26b2019-03-22 12:30:43 -0700543// template function to add array as dbus property
544template <typename PropertyType>
545void addArrayToDbus(const std::string& name, const nlohmann::json& array,
546 sdbusplus::asio::dbus_interface* iface,
547 sdbusplus::asio::PropertyPermission permission,
548 nlohmann::json& systemConfiguration,
549 const std::string& jsonPointerString)
550{
551 std::vector<PropertyType> values;
552 for (const auto& property : array)
553 {
554 auto ptr = property.get_ptr<const PropertyType*>();
555 if (ptr != nullptr)
556 {
557 values.emplace_back(*ptr);
558 }
559 }
560
561 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
562 {
563 iface->register_property(name, values);
564 }
565 else
566 {
567 iface->register_property(
568 name, values,
569 [&systemConfiguration,
570 jsonPointerString{std::string(jsonPointerString)}](
571 const std::vector<PropertyType>& newVal,
572 std::vector<PropertyType>& val) {
573 val = newVal;
574 if (!setJsonFromPointer(jsonPointerString, val,
575 systemConfiguration))
576 {
577 std::cerr << "error setting json field\n";
578 return -1;
579 }
580 if (!writeJsonFiles(systemConfiguration))
581 {
582 std::cerr << "error setting json file\n";
583 return -1;
584 }
585 return 1;
586 });
587 }
588}
589
James Feistbb43d022018-06-12 15:44:33 -0700590template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800591void addProperty(const std::string& propertyName, const PropertyType& value,
592 sdbusplus::asio::dbus_interface* iface,
593 nlohmann::json& systemConfiguration,
594 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700595 sdbusplus::asio::PropertyPermission permission)
596{
597 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
598 {
599 iface->register_property(propertyName, value);
600 return;
601 }
James Feist68500ff2018-08-08 15:40:42 -0700602 iface->register_property(
603 propertyName, value,
604 [&systemConfiguration,
605 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800606 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700607 val = newVal;
608 if (!setJsonFromPointer(jsonPointerString, val,
609 systemConfiguration))
610 {
611 std::cerr << "error setting json field\n";
612 return -1;
613 }
James Feistc6248a52018-08-14 10:09:45 -0700614 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700615 {
616 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700617 return -1;
618 }
619 return 1;
620 });
621}
622
623void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800624 const std::string& jsonPointerPath,
625 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
626 sdbusplus::asio::object_server& objServer,
627 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700628{
629 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
630 iface->register_method(
631 "Delete", [&objServer, &systemConfiguration, interface,
632 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700633 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700634 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700635 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700636 {
637 // this technically can't happen as the pointer is pointing to
638 // us
639 throw DBusInternalError();
640 }
641 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feist98132792019-07-09 13:29:09 -0700642 if (!objServer.remove_interface(dbusInterface))
James Feistc6248a52018-08-14 10:09:45 -0700643 {
644 std::cerr << "Can't delete interface " << jsonPointerPath
645 << "\n";
646 throw DBusInternalError();
647 }
648 systemConfiguration[ptr] = nullptr;
649
650 if (!writeJsonFiles(systemConfiguration))
651 {
652 std::cerr << "error setting json file\n";
653 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700654 }
James Feistbb43d022018-06-12 15:44:33 -0700655 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700656 });
James Feistbb43d022018-06-12 15:44:33 -0700657}
658
James Feist1b2e2242018-01-30 13:45:19 -0800659// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700660void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800661 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
662 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
663 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700664 sdbusplus::asio::PropertyPermission permission =
665 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800666{
James Feista465ccc2019-02-08 12:51:01 -0800667 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800668 {
James Feist8f2710a2018-05-09 17:18:55 -0700669 auto type = dictPair.value().type();
670 bool array = false;
671 if (dictPair.value().type() == nlohmann::json::value_t::array)
672 {
673 array = true;
674 if (!dictPair.value().size())
675 {
676 continue;
677 }
678 type = dictPair.value()[0].type();
679 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800680 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700681 {
682 if (arrayItem.type() != type)
683 {
684 isLegal = false;
685 break;
686 }
687 }
688 if (!isLegal)
689 {
690 std::cerr << "dbus format error" << dictPair.value() << "\n";
691 continue;
692 }
James Feista218ddb2019-04-11 14:01:31 -0700693 }
694 if (type == nlohmann::json::value_t::object)
695 {
696 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700697 }
James Feist97a63f12018-05-17 13:50:57 -0700698 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700699 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
700 {
701 // all setable numbers are doubles as it is difficult to always
702 // create a configuration file with all whole numbers as decimals
703 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700704 if (array)
705 {
706 if (dictPair.value()[0].is_number())
707 {
708 type = nlohmann::json::value_t::number_float;
709 }
710 }
711 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700712 {
713 type = nlohmann::json::value_t::number_float;
714 }
715 }
716
James Feist8f2710a2018-05-09 17:18:55 -0700717 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800718 {
James Feist9eb0b582018-04-27 12:15:46 -0700719 case (nlohmann::json::value_t::boolean):
720 {
James Feist8f2710a2018-05-09 17:18:55 -0700721 if (array)
722 {
723 // todo: array of bool isn't detected correctly by
724 // sdbusplus, change it to numbers
725 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700726 iface.get(), permission,
727 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700728 }
James Feistbb43d022018-06-12 15:44:33 -0700729
James Feist97a63f12018-05-17 13:50:57 -0700730 else
731 {
James Feistbb43d022018-06-12 15:44:33 -0700732 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700733 iface.get(), systemConfiguration, key,
734 permission);
James Feist97a63f12018-05-17 13:50:57 -0700735 }
James Feist9eb0b582018-04-27 12:15:46 -0700736 break;
737 }
738 case (nlohmann::json::value_t::number_integer):
739 {
James Feist8f2710a2018-05-09 17:18:55 -0700740 if (array)
741 {
742 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700743 iface.get(), permission,
744 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700745 }
746 else
747 {
James Feistbb43d022018-06-12 15:44:33 -0700748 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700749 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700750 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700751 }
James Feist9eb0b582018-04-27 12:15:46 -0700752 break;
753 }
754 case (nlohmann::json::value_t::number_unsigned):
755 {
James Feist8f2710a2018-05-09 17:18:55 -0700756 if (array)
757 {
758 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700759 iface.get(), permission,
760 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700761 }
762 else
763 {
James Feistbb43d022018-06-12 15:44:33 -0700764 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700765 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700766 systemConfiguration, key,
767 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700768 }
James Feist9eb0b582018-04-27 12:15:46 -0700769 break;
770 }
771 case (nlohmann::json::value_t::number_float):
772 {
James Feist8f2710a2018-05-09 17:18:55 -0700773 if (array)
774 {
775 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700776 iface.get(), permission,
777 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700778 }
James Feistbb43d022018-06-12 15:44:33 -0700779
James Feist97a63f12018-05-17 13:50:57 -0700780 else
781 {
James Feistbb43d022018-06-12 15:44:33 -0700782 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700783 iface.get(), systemConfiguration, key,
784 permission);
James Feist97a63f12018-05-17 13:50:57 -0700785 }
James Feist9eb0b582018-04-27 12:15:46 -0700786 break;
787 }
788 case (nlohmann::json::value_t::string):
789 {
James Feist8f2710a2018-05-09 17:18:55 -0700790 if (array)
791 {
James Feistebcc26b2019-03-22 12:30:43 -0700792 addArrayToDbus<std::string>(
793 dictPair.key(), dictPair.value(), iface.get(),
794 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700795 }
796 else
797 {
James Feistc6248a52018-08-14 10:09:45 -0700798 addProperty(
799 dictPair.key(), dictPair.value().get<std::string>(),
800 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700801 }
James Feist9eb0b582018-04-27 12:15:46 -0700802 break;
803 }
James Feist0eb40352019-04-09 14:44:04 -0700804 default:
805 {
James Feista218ddb2019-04-11 14:01:31 -0700806 std::cerr << "Unexpected json type in system configuration "
807 << dictPair.key() << ": "
808 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700809 break;
810 }
James Feist1b2e2242018-01-30 13:45:19 -0800811 }
812 }
James Feistc6248a52018-08-14 10:09:45 -0700813 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
814 {
815 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
816 systemConfiguration);
817 }
James Feist8f2710a2018-05-09 17:18:55 -0700818 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800819}
820
James Feista465ccc2019-02-08 12:51:01 -0800821sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700822{
823 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
824 interface) != settableInterfaces.end()
825 ? sdbusplus::asio::PropertyPermission::readWrite
826 : sdbusplus::asio::PropertyPermission::readOnly;
827}
828
James Feista465ccc2019-02-08 12:51:01 -0800829void createAddObjectMethod(const std::string& jsonPointerPath,
830 const std::string& path,
831 nlohmann::json& systemConfiguration,
832 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700833{
834 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
835
836 iface->register_method(
837 "AddObject",
838 [&systemConfiguration, &objServer,
839 jsonPointerPath{std::string(jsonPointerPath)},
840 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800841 const boost::container::flat_map<std::string, JsonVariantType>&
842 data) {
James Feist68500ff2018-08-08 15:40:42 -0700843 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800844 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700845 auto findExposes = base.find("Exposes");
846
847 if (findExposes == base.end())
848 {
849 throw std::invalid_argument("Entity must have children.");
850 }
851
852 // this will throw invalid-argument to sdbusplus if invalid json
853 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800854 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700855 {
James Feista465ccc2019-02-08 12:51:01 -0800856 nlohmann::json& newJson = newData[item.first];
857 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
858 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700859 }
860
861 auto findName = newData.find("Name");
862 auto findType = newData.find("Type");
863 if (findName == newData.end() || findType == newData.end())
864 {
865 throw std::invalid_argument("AddObject missing Name or Type");
866 }
James Feista465ccc2019-02-08 12:51:01 -0800867 const std::string* type = findType->get_ptr<const std::string*>();
868 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700869 if (type == nullptr || name == nullptr)
870 {
871 throw std::invalid_argument("Type and Name must be a string.");
872 }
873
874 size_t lastIndex = 0;
875 // we add in the "exposes"
876 for (; lastIndex < findExposes->size(); lastIndex++)
877 {
878 if (findExposes->at(lastIndex)["Name"] == *name &&
879 findExposes->at(lastIndex)["Type"] == *type)
880 {
881 throw std::invalid_argument(
882 "Field already in JSON, not adding");
883 }
884 lastIndex++;
885 }
886
887 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
888 *type + ".json");
889 // todo(james) we might want to also make a list of 'can add'
890 // interfaces but for now I think the assumption if there is a
891 // schema avaliable that it is allowed to update is fine
892 if (!schemaFile.good())
893 {
894 throw std::invalid_argument(
895 "No schema avaliable, cannot validate.");
896 }
897 nlohmann::json schema =
898 nlohmann::json::parse(schemaFile, nullptr, false);
899 if (schema.is_discarded())
900 {
901 std::cerr << "Schema not legal" << *type << ".json\n";
902 throw DBusInternalError();
903 }
904 if (!validateJson(schema, newData))
905 {
906 throw std::invalid_argument("Data does not match schema");
907 }
908
James Feist16a02f22019-05-13 15:21:37 -0700909 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700910 if (!writeJsonFiles(systemConfiguration))
911 {
912 std::cerr << "Error writing json files\n";
913 throw DBusInternalError();
914 }
915 std::string dbusName = *name;
916
917 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700918 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist98132792019-07-09 13:29:09 -0700919 auto interface = objServer.add_interface(
James Feist68500ff2018-08-08 15:40:42 -0700920 path + "/" + dbusName,
921 "xyz.openbmc_project.Configuration." + *type);
922 // permission is read-write, as since we just created it, must be
923 // runtime modifiable
924 populateInterfaceFromJson(
925 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700926 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700927 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700928 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700929 });
930 iface->initialize();
931}
932
James Feista465ccc2019-02-08 12:51:01 -0800933void postToDbus(const nlohmann::json& newConfiguration,
934 nlohmann::json& systemConfiguration,
935 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800936
James Feist1b2e2242018-01-30 13:45:19 -0800937{
James Feist97a63f12018-05-17 13:50:57 -0700938 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800939 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800940 {
James Feistf1b14142019-04-10 15:22:09 -0700941 std::string boardKey = boardPair.value()["Name"];
942 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700943 // loop through newConfiguration, but use values from system
944 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700945 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700946 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800947 std::string boardType;
948 if (findBoardType != boardValues.end() &&
949 findBoardType->type() == nlohmann::json::value_t::string)
950 {
951 boardType = findBoardType->get<std::string>();
952 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700953 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800954 }
955 else
956 {
957 std::cerr << "Unable to find type for " << boardKey
958 << " reverting to Chassis.\n";
959 boardType = "Chassis";
960 }
James Feist11be6672018-04-06 14:05:32 -0700961 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800962
963 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700964 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700965 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
966 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800967
James Feist8f2710a2018-05-09 17:18:55 -0700968 auto inventoryIface = objServer.add_interface(
969 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700970
James Feist8f2710a2018-05-09 17:18:55 -0700971 auto boardIface = objServer.add_interface(
972 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700973
James Feist68500ff2018-08-08 15:40:42 -0700974 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
975 objServer);
976
James Feist97a63f12018-05-17 13:50:57 -0700977 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700978 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700979 jsonPointerPath += "/";
980 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800981 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700982 {
983 if (boardField.value().type() == nlohmann::json::value_t::object)
984 {
James Feist8f2710a2018-05-09 17:18:55 -0700985 auto iface =
986 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700987 populateInterfaceFromJson(systemConfiguration,
988 jsonPointerPath + boardField.key(),
989 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700990 }
991 }
James Feist97a63f12018-05-17 13:50:57 -0700992
James Feist1e3e6982018-08-03 16:09:28 -0700993 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800994 if (exposes == boardValues.end())
995 {
996 continue;
997 }
James Feist97a63f12018-05-17 13:50:57 -0700998 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700999 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001000
1001 // store the board level pointer so we can modify it on the way down
1002 std::string jsonPointerPathBoard = jsonPointerPath;
1003 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001004 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001005 {
James Feist97a63f12018-05-17 13:50:57 -07001006 exposesIndex++;
1007 jsonPointerPath = jsonPointerPathBoard;
1008 jsonPointerPath += std::to_string(exposesIndex);
1009
James Feistd63d18a2018-07-19 15:23:45 -07001010 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001011 if (findName == item.end())
1012 {
1013 std::cerr << "cannot find name in field " << item << "\n";
1014 continue;
1015 }
James Feist1e3e6982018-08-03 16:09:28 -07001016 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001017 // if status is not found it is assumed to be status = 'okay'
1018 if (findStatus != item.end())
1019 {
1020 if (*findStatus == "disabled")
1021 {
1022 continue;
1023 }
1024 }
James Feistd63d18a2018-07-19 15:23:45 -07001025 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001026 std::string itemType;
1027 if (findType != item.end())
1028 {
1029 itemType = findType->get<std::string>();
1030 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001031 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1032 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001033 }
1034 else
1035 {
1036 itemType = "unknown";
1037 }
1038 std::string itemName = findName->get<std::string>();
1039 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001040 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001041
James Feist8f2710a2018-05-09 17:18:55 -07001042 auto itemIface = objServer.add_interface(
1043 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001044 "xyz.openbmc_project.Configuration." + itemType);
1045
James Feist97a63f12018-05-17 13:50:57 -07001046 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001047 itemIface, item, objServer,
1048 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001049
James Feista465ccc2019-02-08 12:51:01 -08001050 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001051 {
James Feist97a63f12018-05-17 13:50:57 -07001052 jsonPointerPath = jsonPointerPathBoard +
1053 std::to_string(exposesIndex) + "/" +
1054 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001055 if (objectPair.value().type() ==
1056 nlohmann::json::value_t::object)
1057 {
James Feist8f2710a2018-05-09 17:18:55 -07001058 auto objectIface = objServer.add_interface(
1059 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001060 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001061 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001062
1063 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001064 systemConfiguration, jsonPointerPath, objectIface,
1065 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001066 }
1067 else if (objectPair.value().type() ==
1068 nlohmann::json::value_t::array)
1069 {
1070 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001071 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001072 {
James Feist8f2710a2018-05-09 17:18:55 -07001073 continue;
1074 }
1075 bool isLegal = true;
1076 auto type = objectPair.value()[0].type();
1077 if (type != nlohmann::json::value_t::object)
1078 {
1079 continue;
1080 }
1081
1082 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001083 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001084 {
1085 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001086 {
James Feist8f2710a2018-05-09 17:18:55 -07001087 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001088 break;
1089 }
James Feist8f2710a2018-05-09 17:18:55 -07001090 }
1091 if (!isLegal)
1092 {
1093 std::cerr << "dbus format error" << objectPair.value()
1094 << "\n";
1095 break;
1096 }
1097
James Feista465ccc2019-02-08 12:51:01 -08001098 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001099 {
James Feist97a63f12018-05-17 13:50:57 -07001100
James Feist8f2710a2018-05-09 17:18:55 -07001101 auto objectIface = objServer.add_interface(
1102 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001103 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001104 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001105 populateInterfaceFromJson(
1106 systemConfiguration,
1107 jsonPointerPath + "/" + std::to_string(index),
1108 objectIface, arrayItem, objServer,
1109 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001110 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001111 }
1112 }
1113 }
1114 }
1115 }
1116}
1117
James Feist8f2710a2018-05-09 17:18:55 -07001118// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001119bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001120{
1121 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001122 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001123 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1124 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001125 {
1126 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001127 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001128 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001129 }
James Feistb4383f42018-08-06 16:54:10 -07001130
1131 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1132 globalSchema);
1133 if (!schemaStream.good())
1134 {
1135 std::cerr
1136 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1137 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001138 return false;
James Feistb4383f42018-08-06 16:54:10 -07001139 }
1140 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1141 if (schema.is_discarded())
1142 {
1143 std::cerr
1144 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1145 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001146 return false;
James Feistb4383f42018-08-06 16:54:10 -07001147 }
1148
James Feista465ccc2019-02-08 12:51:01 -08001149 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001150 {
1151 std::ifstream jsonStream(jsonPath.c_str());
1152 if (!jsonStream.good())
1153 {
1154 std::cerr << "unable to open " << jsonPath.string() << "\n";
1155 continue;
1156 }
1157 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1158 if (data.is_discarded())
1159 {
1160 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1161 continue;
1162 }
James Feist8da99192019-01-24 08:20:16 -08001163 /*
1164 * todo(james): reenable this once less things are in flight
1165 *
James Feistb4383f42018-08-06 16:54:10 -07001166 if (!validateJson(schema, data))
1167 {
1168 std::cerr << "Error validating " << jsonPath.string() << "\n";
1169 continue;
1170 }
James Feist8da99192019-01-24 08:20:16 -08001171 */
James Feistb4383f42018-08-06 16:54:10 -07001172
James Feist3cb5fec2018-01-23 14:41:51 -08001173 if (data.type() == nlohmann::json::value_t::array)
1174 {
James Feista465ccc2019-02-08 12:51:01 -08001175 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001176 {
1177 configurations.emplace_back(d);
1178 }
1179 }
1180 else
1181 {
1182 configurations.emplace_back(data);
1183 }
1184 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001185 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001186}
James Feist3cb5fec2018-01-23 14:41:51 -08001187
James Feist8f2710a2018-05-09 17:18:55 -07001188struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001189{
James Feist75fdeeb2018-02-20 14:26:16 -08001190
James Feista465ccc2019-02-08 12:51:01 -08001191 PerformScan(nlohmann::json& systemConfiguration,
1192 std::list<nlohmann::json>& configurations,
1193 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001194 _systemConfiguration(systemConfiguration),
1195 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001196 {
James Feist8f2710a2018-05-09 17:18:55 -07001197 }
1198 void run()
1199 {
1200 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001201 {
James Feist1e3e6982018-08-03 16:09:28 -07001202 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001203 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001204
James Feist1b2e2242018-01-30 13:45:19 -08001205 nlohmann::json probeCommand;
1206 // check for poorly formatted fields, probe must be an array
1207 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001208 {
1209 std::cerr << "configuration file missing probe:\n " << *it
1210 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001211 it = _configurations.erase(it);
1212 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001213 }
James Feist1b2e2242018-01-30 13:45:19 -08001214 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001215 {
1216 probeCommand = nlohmann::json::array();
1217 probeCommand.push_back(*findProbe);
1218 }
1219 else
1220 {
1221 probeCommand = *findProbe;
1222 }
James Feist1b2e2242018-01-30 13:45:19 -08001223
1224 if (findName == it->end())
1225 {
1226 std::cerr << "configuration file missing name:\n " << *it
1227 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001228 it = _configurations.erase(it);
1229 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001230 }
James Feistf1b14142019-04-10 15:22:09 -07001231 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001232
James Feistf1b14142019-04-10 15:22:09 -07001233 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1234 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001235 {
James Feist8f2710a2018-05-09 17:18:55 -07001236 it = _configurations.erase(it);
1237 continue;
1238 }
James Feistf1b14142019-04-10 15:22:09 -07001239 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001240
James Feist8f2710a2018-05-09 17:18:55 -07001241 // store reference to this to children to makes sure we don't get
1242 // destroyed too early
1243 auto thisRef = shared_from_this();
James Feist08a5b172019-08-28 14:47:47 -07001244 auto p = std::make_shared<
1245 PerformProbe>(probeCommand, [&, recordPtr, probeName, thisRef](
1246 FoundDeviceT& foundDevices) {
1247 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001248
James Feist08a5b172019-08-28 14:47:47 -07001249 PASSED_PROBES.push_back(probeName);
1250 size_t foundDeviceIdx = 1;
James Feist8f2710a2018-05-09 17:18:55 -07001251
James Feist08a5b172019-08-28 14:47:47 -07001252 for (auto& foundDevice : foundDevices)
1253 {
1254 nlohmann::json record = *recordPtr;
1255 std::string recordName;
1256 size_t hash = 0;
1257 if (foundDevice.size())
James Feist3cb5fec2018-01-23 14:41:51 -08001258 {
James Feist08a5b172019-08-28 14:47:47 -07001259 // use an array so alphabetical order from the
1260 // flat_map is maintained
1261 auto device = nlohmann::json::array();
1262 for (auto& devPair : foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001263 {
James Feist08a5b172019-08-28 14:47:47 -07001264 device.push_back(devPair.first);
1265 std::visit(
1266 [&device](auto&& v) { device.push_back(v); },
1267 devPair.second);
1268 }
1269 hash =
1270 std::hash<std::string>{}(probeName + device.dump());
1271 // hashes are hard to distinguish, use the
1272 // non-hashed version if we want debug
1273 if constexpr (DEBUG)
1274 {
1275 recordName = probeName + device.dump();
James Feist8f2710a2018-05-09 17:18:55 -07001276 }
James Feistf1b14142019-04-10 15:22:09 -07001277 else
1278 {
James Feist08a5b172019-08-28 14:47:47 -07001279 recordName = std::to_string(hash);
James Feistf1b14142019-04-10 15:22:09 -07001280 }
James Feist08a5b172019-08-28 14:47:47 -07001281 }
1282 else
1283 {
1284 recordName = probeName;
1285 }
James Feistf1b14142019-04-10 15:22:09 -07001286
James Feist08a5b172019-08-28 14:47:47 -07001287 auto fromLastJson = lastJson.find(recordName);
1288 if (fromLastJson != lastJson.end())
1289 {
1290 // keep user changes
1291 _systemConfiguration[recordName] = *fromLastJson;
1292 continue;
1293 }
James Feist1df06a42019-04-11 14:23:04 -07001294
James Feist08a5b172019-08-28 14:47:47 -07001295 // insert into configuration temporarily to be able to
1296 // reference ourselves
James Feistf1b14142019-04-10 15:22:09 -07001297
James Feist08a5b172019-08-28 14:47:47 -07001298 _systemConfiguration[recordName] = record;
1299
1300 for (auto keyPair = record.begin(); keyPair != record.end();
1301 keyPair++)
1302 {
1303 templateCharReplace(keyPair, foundDevice,
1304 foundDeviceIdx);
1305 }
1306
1307 auto findExpose = record.find("Exposes");
1308 if (findExpose == record.end())
1309 {
James Feistf1b14142019-04-10 15:22:09 -07001310 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001311 logDeviceAdded(record);
1312 foundDeviceIdx++;
1313 continue;
1314 }
James Feistf1b14142019-04-10 15:22:09 -07001315
James Feist08a5b172019-08-28 14:47:47 -07001316 for (auto& expose : *findExpose)
1317 {
1318 for (auto keyPair = expose.begin();
1319 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001320 {
James Feist08a5b172019-08-28 14:47:47 -07001321
1322 templateCharReplace(keyPair, foundDevice,
1323 foundDeviceIdx);
1324
1325 // special case bind
1326 if (boost::starts_with(keyPair.key(), "Bind"))
James Feistf1b14142019-04-10 15:22:09 -07001327 {
James Feist08a5b172019-08-28 14:47:47 -07001328 if (keyPair.value().type() !=
1329 nlohmann::json::value_t::string)
James Feistf1b14142019-04-10 15:22:09 -07001330 {
James Feist08a5b172019-08-28 14:47:47 -07001331 std::cerr << "bind_ value must be of "
1332 "type string "
1333 << keyPair.key() << "\n";
1334 continue;
James Feistf1b14142019-04-10 15:22:09 -07001335 }
James Feist08a5b172019-08-28 14:47:47 -07001336 bool foundBind = false;
1337 std::string bind =
1338 keyPair.key().substr(sizeof("Bind") - 1);
1339
1340 for (auto& configurationPair :
1341 _systemConfiguration.items())
James Feist8f2710a2018-05-09 17:18:55 -07001342 {
James Feist08a5b172019-08-28 14:47:47 -07001343
1344 auto configListFind =
1345 configurationPair.value().find(
1346 "Exposes");
1347
1348 if (configListFind ==
1349 configurationPair.value().end() ||
1350 configListFind->type() !=
1351 nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001352 {
James Feist1b2e2242018-01-30 13:45:19 -08001353 continue;
1354 }
James Feist08a5b172019-08-28 14:47:47 -07001355 for (auto& exposedObject : *configListFind)
James Feist1b2e2242018-01-30 13:45:19 -08001356 {
James Feist08a5b172019-08-28 14:47:47 -07001357 std::string foundObjectName =
1358 (exposedObject)["Name"];
1359 if (boost::iequals(
1360 foundObjectName,
1361 keyPair.value()
1362 .get<std::string>()))
James Feist8f2710a2018-05-09 17:18:55 -07001363 {
James Feist08a5b172019-08-28 14:47:47 -07001364 exposedObject["Status"] = "okay";
1365 expose[bind] = exposedObject;
James Feist8f2710a2018-05-09 17:18:55 -07001366
James Feist08a5b172019-08-28 14:47:47 -07001367 foundBind = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001368 break;
1369 }
1370 }
James Feist08a5b172019-08-28 14:47:47 -07001371 if (foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001372 {
James Feist08a5b172019-08-28 14:47:47 -07001373 break;
James Feist3cb5fec2018-01-23 14:41:51 -08001374 }
1375 }
James Feist08a5b172019-08-28 14:47:47 -07001376 if (!foundBind)
1377 {
1378 std::cerr << "configuration file "
1379 "dependency error, "
1380 "could not find bind "
1381 << keyPair.value() << "\n";
1382 }
James Feist3cb5fec2018-01-23 14:41:51 -08001383 }
1384 }
1385 }
James Feist08a5b172019-08-28 14:47:47 -07001386 // overwrite ourselves with cleaned up version
1387 _systemConfiguration[recordName] = record;
1388
1389 logDeviceAdded(record);
1390
1391 foundDeviceIdx++;
1392 }
1393 });
James Feist8f2710a2018-05-09 17:18:55 -07001394 p->run();
1395 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001396 }
1397 }
James Feist75fdeeb2018-02-20 14:26:16 -08001398
James Feist8f2710a2018-05-09 17:18:55 -07001399 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001400 {
James Feist8f2710a2018-05-09 17:18:55 -07001401 if (_passed)
1402 {
1403 auto nextScan = std::make_shared<PerformScan>(
1404 _systemConfiguration, _configurations, std::move(_callback));
1405 nextScan->run();
1406 }
1407 else
1408 {
1409 _callback();
1410 }
1411 }
James Feista465ccc2019-02-08 12:51:01 -08001412 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001413 std::list<nlohmann::json> _configurations;
1414 std::function<void(void)> _callback;
1415 std::vector<std::shared_ptr<PerformProbe>> _probes;
1416 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001417 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001418};
James Feistc95cb142018-02-26 10:41:42 -08001419
James Feist1df06a42019-04-11 14:23:04 -07001420void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001421 nlohmann::json& systemConfiguration)
1422{
1423 static bool scannedPowerOff = false;
1424 static bool scannedPowerOn = false;
1425
James Feistfb00f392019-06-25 14:16:48 -07001426 if (systemConfiguration.empty() || lastJson.empty())
1427 {
1428 return; // not ready yet
1429 }
James Feist1df06a42019-04-11 14:23:04 -07001430 if (scannedPowerOn)
1431 {
1432 return;
1433 }
1434
1435 if (!isPowerOn() && scannedPowerOff)
1436 {
1437 return;
1438 }
1439
1440 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001441 timer.async_wait(
1442 [&systemConfiguration](const boost::system::error_code& ec) {
1443 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001444 {
James Feist1a996582019-05-14 15:10:06 -07001445 // we were cancelled
1446 return;
1447 }
1448
1449 bool powerOff = !isPowerOn();
1450 for (const auto& item : lastJson.items())
1451 {
1452 if (systemConfiguration.find(item.key()) ==
1453 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001454 {
James Feist1a996582019-05-14 15:10:06 -07001455 bool isDetectedPowerOn = false;
1456 auto powerState = item.value().find("PowerState");
1457 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001458 {
James Feist1a996582019-05-14 15:10:06 -07001459 auto ptr = powerState->get_ptr<const std::string*>();
1460 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001461 {
James Feist1a996582019-05-14 15:10:06 -07001462 if (*ptr == "On" || *ptr == "BiosPost")
1463 {
1464 isDetectedPowerOn = true;
1465 }
James Feist1df06a42019-04-11 14:23:04 -07001466 }
1467 }
James Feist1a996582019-05-14 15:10:06 -07001468 if (powerOff && isDetectedPowerOn)
1469 {
1470 // power not on yet, don't know if it's there or not
1471 continue;
1472 }
1473 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1474 {
1475 // already logged it when power was off
1476 continue;
1477 }
James Feist1df06a42019-04-11 14:23:04 -07001478
James Feist1a996582019-05-14 15:10:06 -07001479 logDeviceRemoved(item.value());
1480 }
James Feist1df06a42019-04-11 14:23:04 -07001481 }
James Feist1a996582019-05-14 15:10:06 -07001482 scannedPowerOff = true;
1483 if (!powerOff)
1484 {
1485 scannedPowerOn = true;
1486 }
1487 });
James Feist1df06a42019-04-11 14:23:04 -07001488}
1489
James Feist8f2710a2018-05-09 17:18:55 -07001490// main properties changed entry
1491void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001492 boost::asio::io_service& io,
1493 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1494 nlohmann::json& systemConfiguration,
1495 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001496{
1497 static boost::asio::deadline_timer timer(io);
James Feist1df06a42019-04-11 14:23:04 -07001498 static bool timerRunning;
1499
1500 timerRunning = true;
James Feist8f2710a2018-05-09 17:18:55 -07001501 timer.expires_from_now(boost::posix_time::seconds(1));
1502
1503 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001504 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001505 if (ec == boost::asio::error::operation_aborted)
1506 {
1507 // we were cancelled
1508 return;
1509 }
1510 else if (ec)
1511 {
1512 std::cerr << "async wait error " << ec << "\n";
1513 return;
1514 }
James Feist1df06a42019-04-11 14:23:04 -07001515 timerRunning = false;
James Feist8f2710a2018-05-09 17:18:55 -07001516
1517 nlohmann::json oldConfiguration = systemConfiguration;
1518 DBUS_PROBE_OBJECTS.clear();
1519
1520 std::list<nlohmann::json> configurations;
1521 if (!findJsonFiles(configurations))
1522 {
1523 std::cerr << "cannot find json files\n";
1524 return;
1525 }
1526
1527 auto perfScan = std::make_shared<PerformScan>(
1528 systemConfiguration, configurations, [&, oldConfiguration]() {
1529 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001530 for (auto it = newConfiguration.begin();
1531 it != newConfiguration.end();)
1532 {
1533 auto findKey = oldConfiguration.find(it.key());
1534 if (findKey != oldConfiguration.end())
1535 {
1536 it = newConfiguration.erase(it);
1537 }
1538 else
1539 {
1540 it++;
1541 }
1542 }
James Feist8f2710a2018-05-09 17:18:55 -07001543 registerCallbacks(io, dbusMatches, systemConfiguration,
1544 objServer);
1545 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001546 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001547
James Feistbb43d022018-06-12 15:44:33 -07001548 io.post([&]() {
1549 if (!writeJsonFiles(systemConfiguration))
1550 {
1551 std::cerr << "Error writing json files\n";
1552 }
1553 });
James Feist8f2710a2018-05-09 17:18:55 -07001554 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001555 postToDbus(newConfiguration, systemConfiguration,
1556 objServer);
James Feist1df06a42019-04-11 14:23:04 -07001557 if (!timerRunning)
1558 {
James Feist98132792019-07-09 13:29:09 -07001559 startRemovedTimer(timer, systemConfiguration);
James Feist1df06a42019-04-11 14:23:04 -07001560 }
James Feist8f2710a2018-05-09 17:18:55 -07001561 });
1562 });
1563 });
1564 perfScan->run();
1565 });
James Feist75fdeeb2018-02-20 14:26:16 -08001566}
1567
James Feista465ccc2019-02-08 12:51:01 -08001568void registerCallbacks(boost::asio::io_service& io,
1569 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1570 nlohmann::json& systemConfiguration,
1571 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001572{
1573 static boost::container::flat_set<std::string> watchedObjects;
1574
James Feista465ccc2019-02-08 12:51:01 -08001575 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001576 {
1577 auto findObject = watchedObjects.find(objectMap.first);
1578 if (findObject != watchedObjects.end())
1579 {
1580 continue;
1581 }
James Feist8f2710a2018-05-09 17:18:55 -07001582 std::function<void(sdbusplus::message::message & message)>
1583 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001584
James Feista465ccc2019-02-08 12:51:01 -08001585 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001586 propertiesChangedCallback(io, dbusMatches,
1587 systemConfiguration, objServer);
1588 };
1589
1590 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001591 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001592 "type='signal',member='PropertiesChanged',arg0='" +
1593 objectMap.first + "'",
1594 eventHandler);
1595 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001596 }
1597}
1598
James Feist98132792019-07-09 13:29:09 -07001599int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001600{
1601 // setup connection to dbus
1602 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001603 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001604 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001605
James Feist8f2710a2018-05-09 17:18:55 -07001606 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001607
James Feist8f2710a2018-05-09 17:18:55 -07001608 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1609 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1610 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001611
James Feist8f2710a2018-05-09 17:18:55 -07001612 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1613 objServer.add_interface("/xyz/openbmc_project/inventory",
1614 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001615
1616 // to keep reference to the match / filter objects so they don't get
1617 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001618 std::vector<sdbusplus::bus::match::match> dbusMatches;
1619
1620 nlohmann::json systemConfiguration = nlohmann::json::object();
1621
1622 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001623 "Notify",
1624 [](const boost::container::flat_map<
1625 std::string,
James Feist98132792019-07-09 13:29:09 -07001626 boost::container::flat_map<std::string, BasicVariantType>>&) {
1627 return;
1628 });
James Feist8f2710a2018-05-09 17:18:55 -07001629 inventoryIface->initialize();
1630
1631 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001632#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001633 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001634#endif
James Feist8f2710a2018-05-09 17:18:55 -07001635 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1636 objServer);
1637 });
James Feist4131aea2018-03-09 09:47:30 -08001638
James Feistfd1264a2018-05-03 12:10:00 -07001639 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001640 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1641 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001642 });
James Feist8f2710a2018-05-09 17:18:55 -07001643 entityIface->initialize();
1644
James Feist1df06a42019-04-11 14:23:04 -07001645 if (fwVersionIsSame())
1646 {
1647 if (std::filesystem::is_regular_file(currentConfiguration))
1648 {
1649 // this file could just be deleted, but it's nice for debug
1650 std::filesystem::create_directory(tempConfigDir);
1651 std::filesystem::remove(lastConfiguration);
1652 std::filesystem::copy(currentConfiguration, lastConfiguration);
1653 std::filesystem::remove(currentConfiguration);
1654
1655 std::ifstream jsonStream(lastConfiguration);
1656 if (jsonStream.good())
1657 {
1658 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1659 if (data.is_discarded())
1660 {
1661 std::cerr << "syntax error in " << lastConfiguration
1662 << "\n";
1663 }
1664 else
1665 {
1666 lastJson = std::move(data);
1667 }
1668 }
1669 else
1670 {
1671 std::cerr << "unable to open " << lastConfiguration << "\n";
1672 }
1673 }
1674 }
1675 else
1676 {
1677 // not an error, just logging at this level to make it in the journal
1678 std::cerr << "Clearing previous configuration\n";
1679 std::filesystem::remove(currentConfiguration);
1680 }
1681
1682 // some boards only show up after power is on, we want to not say they are
1683 // removed until the same state happens
1684 setupPowerMatch(SYSTEM_BUS);
1685
James Feist1b2e2242018-01-30 13:45:19 -08001686 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001687
1688 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001689}