blob: a71d5532c6b549c4c26d7f7c7bea33a838a21ea0 [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 Feista465ccc2019-02-08 12:51:01 -080019#include "filesystem.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 Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/replace.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080027#include <boost/container/flat_map.hpp>
28#include <boost/container/flat_set.hpp>
James Feista465ccc2019-02-08 12:51:01 -080029#include <boost/lexical_cast.hpp>
30#include <fstream>
31#include <iostream>
32#include <nlohmann/json.hpp>
33#include <regex>
34#include <sdbusplus/asio/connection.hpp>
35#include <sdbusplus/asio/object_server.hpp>
36#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080037
James Feista465ccc2019-02-08 12:51:01 -080038constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
39constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070040constexpr const char* tempConfigDir = "/tmp/configuration/";
41constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
42constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080043constexpr const char* globalSchema = "global.json";
44constexpr const char* TEMPLATE_CHAR = "$";
James Feist8f2710a2018-05-09 17:18:55 -070045constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080046
James Feistf1b14142019-04-10 15:22:09 -070047constexpr const bool DEBUG = false;
48
James Feist3cb5fec2018-01-23 14:41:51 -080049struct cmp_str
50{
James Feista465ccc2019-02-08 12:51:01 -080051 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080052 {
53 return std::strcmp(a, b) < 0;
54 }
55};
56
James Feist8f2710a2018-05-09 17:18:55 -070057struct PerformProbe;
58
James Feist3cb5fec2018-01-23 14:41:51 -080059// underscore T for collison with dbus c api
60enum class probe_type_codes
61{
62 FALSE_T,
63 TRUE_T,
64 AND,
65 OR,
James Feist6bd2a022018-03-13 12:30:58 -070066 FOUND,
67 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080068};
James Feista465ccc2019-02-08 12:51:01 -080069const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080070 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
71 {"TRUE", probe_type_codes::TRUE_T},
72 {"AND", probe_type_codes::AND},
73 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070074 {"FOUND", probe_type_codes::FOUND},
75 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080076
James Feist41334262019-03-25 13:30:20 -070077static constexpr std::array<const char*, 5> settableInterfaces = {
78 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070079using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080080 std::variant<std::vector<std::string>, std::vector<double>, std::string,
81 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
82 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070083using BasicVariantType =
James Feista465ccc2019-02-08 12:51:01 -080084 std::variant<std::string, int64_t, uint64_t, double, int32_t, uint32_t,
85 int16_t, uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070086
James Feist3cb5fec2018-01-23 14:41:51 -080087using GetSubTreeType = std::vector<
88 std::pair<std::string,
89 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
90
91using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070092 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080093 boost::container::flat_map<
94 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070095 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080096
97boost::container::flat_map<
98 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070099 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -0800100 DBUS_PROBE_OBJECTS;
101std::vector<std::string> PASSED_PROBES;
102
103// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700104std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700105static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800106
Johnathan Mantey2015f752019-03-26 15:22:31 -0700107const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
108const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800109
James Feista465ccc2019-02-08 12:51:01 -0800110void registerCallbacks(boost::asio::io_service& io,
111 std::vector<sdbusplus::bus::match::match>& dbusMatches,
112 nlohmann::json& systemConfiguration,
113 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800114
James Feist3cb5fec2018-01-23 14:41:51 -0800115// calls the mapper to find all exposed objects of an interface type
116// and creates a vector<flat_map> that contains all the key value pairs
117// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700118void findDbusObjects(std::shared_ptr<PerformProbe> probe,
119 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800120 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800121{
James Feist8f2710a2018-05-09 17:18:55 -0700122
123 // store reference to pending callbacks so we don't overwhelm services
124 static boost::container::flat_map<
125 std::string, std::vector<std::shared_ptr<PerformProbe>>>
126 pendingProbes;
127
128 if (DBUS_PROBE_OBJECTS[interface].size())
129 {
130 return;
131 }
132
133 // add shared_ptr to vector of Probes waiting for callback from a specific
134 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800135 std::array<const char*, 1> objects = {interface.c_str()};
136 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700137 pendingProbes[interface];
138 auto iter = pending.emplace(pending.end(), probe);
139 // only allow first call to run to not overwhelm processes
140 if (iter != pending.begin())
141 {
142 return;
143 }
144
James Feist3cb5fec2018-01-23 14:41:51 -0800145 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700146 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800147 [connection, interface, probe](boost::system::error_code& ec,
148 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700149 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700150 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700151 {
James Feist0de40152018-07-25 11:56:12 -0700152 pendingProbes[interface].clear();
153 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700154 {
James Feist0de40152018-07-25 11:56:12 -0700155 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700156 }
James Feist0de40152018-07-25 11:56:12 -0700157 std::cerr << "Error communicating to mapper.\n";
158
159 // if we can't communicate to the mapper something is very wrong
160 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700161 }
James Feist8f2710a2018-05-09 17:18:55 -0700162 else
James Feist3cb5fec2018-01-23 14:41:51 -0800163 {
James Feista465ccc2019-02-08 12:51:01 -0800164 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700165 {
James Feista465ccc2019-02-08 12:51:01 -0800166 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700167 {
James Feist0eb40352019-04-09 14:44:04 -0700168 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700169 }
170 }
James Feist3cb5fec2018-01-23 14:41:51 -0800171 }
James Feist63845bf2019-01-24 12:19:51 -0800172 if (interfaceConnections.empty())
173 {
174 pendingProbes[interface].clear();
175 return;
176 }
James Feist8f2710a2018-05-09 17:18:55 -0700177 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800178 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700179 {
180 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700181 [conn,
James Feista465ccc2019-02-08 12:51:01 -0800182 interface](boost::system::error_code& ec,
183 const ManagedObjectType& managedInterface) {
James Feist8f2710a2018-05-09 17:18:55 -0700184 if (ec)
185 {
186 std::cerr
187 << "error getting managed object for device "
188 << conn << "\n";
189 pendingProbes[interface].clear();
190 return;
191 }
James Feista465ccc2019-02-08 12:51:01 -0800192 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700193 {
194 auto ifaceObjFind =
195 interfaceManagedObj.second.find(interface);
196 if (ifaceObjFind !=
197 interfaceManagedObj.second.end())
198 {
199 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800200 std::string, BasicVariantType>>&
201 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700202 dbusObject.emplace_back(ifaceObjFind->second);
203 }
204 }
205 pendingProbes[interface].clear();
206 },
207 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
208 "GetManagedObjects");
209 }
210 },
211 "xyz.openbmc_project.ObjectMapper",
212 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700213 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700214 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800215}
James Feist8f2710a2018-05-09 17:18:55 -0700216// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800217bool probeDbus(
James Feista465ccc2019-02-08 12:51:01 -0800218 const std::string& interface,
219 const std::map<std::string, nlohmann::json>& matches,
James Feistf1b14142019-04-10 15:22:09 -0700220 std::vector<std::optional<
221 boost::container::flat_map<std::string, BasicVariantType>>>& devices,
James Feista465ccc2019-02-08 12:51:01 -0800222 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800223{
James Feista465ccc2019-02-08 12:51:01 -0800224 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
225 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800226 if (dbusObject.empty())
227 {
James Feist8f2710a2018-05-09 17:18:55 -0700228 foundProbe = false;
229 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800230 }
231 foundProbe = true;
232
233 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800234 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800235 {
236 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800237 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800238 {
239 auto deviceValue = device.find(match.first);
240 if (deviceValue != device.end())
241 {
242 switch (match.second.type())
243 {
James Feist9eb0b582018-04-27 12:15:46 -0700244 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800245 {
James Feist9eb0b582018-04-27 12:15:46 -0700246 std::regex search(match.second.get<std::string>());
247 std::smatch match;
248
249 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800250 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700251 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700252 if (!std::regex_search(probeValue, match, search))
253 {
254 deviceMatches = false;
255 break;
256 }
James Feist3cb5fec2018-01-23 14:41:51 -0800257 break;
258 }
James Feist9eb0b582018-04-27 12:15:46 -0700259 case nlohmann::json::value_t::boolean:
260 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800261 {
James Feista465ccc2019-02-08 12:51:01 -0800262 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700263 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800264
James Feist9eb0b582018-04-27 12:15:46 -0700265 if (probeValue != match.second.get<unsigned int>())
266 {
267 deviceMatches = false;
268 }
269 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800270 }
James Feist9eb0b582018-04-27 12:15:46 -0700271 case nlohmann::json::value_t::number_integer:
272 {
James Feista465ccc2019-02-08 12:51:01 -0800273 int probeValue = std::visit(VariantToIntVisitor(),
274 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800275
James Feist9eb0b582018-04-27 12:15:46 -0700276 if (probeValue != match.second.get<int>())
277 {
278 deviceMatches = false;
279 }
280 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800281 }
James Feist9eb0b582018-04-27 12:15:46 -0700282 case nlohmann::json::value_t::number_float:
283 {
James Feista465ccc2019-02-08 12:51:01 -0800284 float probeValue = std::visit(VariantToFloatVisitor(),
285 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700286
287 if (probeValue != match.second.get<float>())
288 {
289 deviceMatches = false;
290 }
291 break;
292 }
James Feist0eb40352019-04-09 14:44:04 -0700293 default:
294 {
295 std::cerr << "unexpected dbus probe type "
296 << match.second.type_name() << "\n";
297 }
James Feist3cb5fec2018-01-23 14:41:51 -0800298 }
299 }
300 else
301 {
302 deviceMatches = false;
303 break;
304 }
305 }
306 if (deviceMatches)
307 {
308 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700309 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800310 device));
311 foundMatch = true;
312 deviceMatches = false; // for next iteration
313 }
314 }
315 return foundMatch;
316}
317
318// default probe entry point, iterates a list looking for specific types to
319// call specific probe functions
320bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800321 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700322 std::vector<std::optional<
323 boost::container::flat_map<std::string, BasicVariantType>>>& foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800324{
325 const static std::regex command(R"(\((.*)\))");
326 std::smatch match;
327 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700328 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800329 bool cur = true;
330 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
331
James Feista465ccc2019-02-08 12:51:01 -0800332 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800333 {
334 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800335 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800336 cmp_str>::const_iterator probeType;
337
338 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
339 probeType++)
340 {
341 if (probe.find(probeType->first) != std::string::npos)
342 {
343 foundProbe = true;
344 break;
345 }
346 }
347 if (foundProbe)
348 {
349 switch (probeType->second)
350 {
James Feist9eb0b582018-04-27 12:15:46 -0700351 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800352 {
James Feist8f2710a2018-05-09 17:18:55 -0700353 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800354 }
James Feist9eb0b582018-04-27 12:15:46 -0700355 case probe_type_codes::TRUE_T:
356 {
James Feist8f2710a2018-05-09 17:18:55 -0700357 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700358 }
359 case probe_type_codes::MATCH_ONE:
360 {
361 // set current value to last, this probe type shouldn't
362 // affect the outcome
363 cur = ret;
364 matchOne = true;
365 break;
366 }
367 /*case probe_type_codes::AND:
368 break;
369 case probe_type_codes::OR:
370 break;
371 // these are no-ops until the last command switch
372 */
373 case probe_type_codes::FOUND:
374 {
375 if (!std::regex_search(probe, match, command))
376 {
James Feist0eb40352019-04-09 14:44:04 -0700377 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700378 << "\n";
379 return false;
380 }
381 std::string commandStr = *(match.begin() + 1);
382 boost::replace_all(commandStr, "'", "");
383 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
384 commandStr) != PASSED_PROBES.end());
385 break;
386 }
James Feist0eb40352019-04-09 14:44:04 -0700387 default:
388 {
389 break;
390 }
James Feist3cb5fec2018-01-23 14:41:51 -0800391 }
392 }
393 // look on dbus for object
394 else
395 {
396 if (!std::regex_search(probe, match, command))
397 {
James Feist0eb40352019-04-09 14:44:04 -0700398 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800399 return false;
400 }
401 std::string commandStr = *(match.begin() + 1);
402 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700403 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800404 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800405 auto json = nlohmann::json::parse(commandStr, nullptr, false);
406 if (json.is_discarded())
407 {
James Feist0eb40352019-04-09 14:44:04 -0700408 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800409 return false;
410 }
411 // we can match any (string, variant) property. (string, string)
412 // does a regex
413 std::map<std::string, nlohmann::json> dbusProbeMap =
414 json.get<std::map<std::string, nlohmann::json>>();
415 auto findStart = probe.find("(");
416 if (findStart == std::string::npos)
417 {
418 return false;
419 }
420 std::string probeInterface = probe.substr(0, findStart);
421 cur =
422 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
423 }
424
425 // some functions like AND and OR only take affect after the
426 // fact
427 switch (lastCommand)
428 {
James Feist9eb0b582018-04-27 12:15:46 -0700429 case probe_type_codes::AND:
430 ret = cur && ret;
431 break;
432 case probe_type_codes::OR:
433 ret = cur || ret;
434 break;
435 default:
436 ret = cur;
437 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800438 }
439 lastCommand = probeType != PROBE_TYPES.end()
440 ? probeType->second
441 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800442 }
443
444 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800445 if (ret && foundDevs.size() == 0)
446 {
James Feistf1b14142019-04-10 15:22:09 -0700447 foundDevs.emplace_back(std::nullopt);
James Feist3cb5fec2018-01-23 14:41:51 -0800448 }
James Feist0eb40352019-04-09 14:44:04 -0700449 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700450 {
James Feist0eb40352019-04-09 14:44:04 -0700451 // match one could match multiple dbus values, which means we don't care
452 // what one we found so we shouldn't be using template replace. return
453 // an empty one
454 foundDevs.clear();
James Feistf1b14142019-04-10 15:22:09 -0700455 foundDevs.emplace_back(std::nullopt);
James Feist6bd2a022018-03-13 12:30:58 -0700456 }
James Feist3cb5fec2018-01-23 14:41:51 -0800457 return ret;
458}
James Feist8f2710a2018-05-09 17:18:55 -0700459// this class finds the needed dbus fields and on destruction runs the probe
460struct PerformProbe : std::enable_shared_from_this<PerformProbe>
461{
James Feist3cb5fec2018-01-23 14:41:51 -0800462
James Feist8f2710a2018-05-09 17:18:55 -0700463 PerformProbe(
James Feista465ccc2019-02-08 12:51:01 -0800464 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700465 std::function<void(std::vector<std::optional<boost::container::flat_map<
466 std::string, BasicVariantType>>>&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700467 _probeCommand(probeCommand),
468 _callback(std::move(callback))
469 {
470 }
471 ~PerformProbe()
472 {
James Feistf1b14142019-04-10 15:22:09 -0700473 std::vector<std::optional<
474 boost::container::flat_map<std::string, BasicVariantType>>>
James Feist0eb40352019-04-09 14:44:04 -0700475 foundDevs;
476 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700477 {
James Feist0eb40352019-04-09 14:44:04 -0700478 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700479 }
480 }
481 void run()
482 {
483 // parse out dbus probes by discarding other probe types
James Feista465ccc2019-02-08 12:51:01 -0800484 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700485 cmp_str>::const_iterator probeType;
486
James Feista465ccc2019-02-08 12:51:01 -0800487 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700488 {
489 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800490 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700491 cmp_str>::const_iterator probeType;
492 for (probeType = PROBE_TYPES.begin();
493 probeType != PROBE_TYPES.end(); probeType++)
494 {
495 if (probe.find(probeType->first) != std::string::npos)
496 {
497 found = true;
498 break;
499 }
500 }
501 if (found)
502 {
503 continue;
504 }
505 // syntax requires probe before first open brace
506 auto findStart = probe.find("(");
507 std::string interface = probe.substr(0, findStart);
508
509 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
510 }
511 }
512 std::vector<std::string> _probeCommand;
James Feistf1b14142019-04-10 15:22:09 -0700513 std::function<void(std::vector<std::optional<boost::container::flat_map<
514 std::string, BasicVariantType>>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700515 _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700516};
517
518// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800519bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800520{
James Feist1df06a42019-04-11 14:23:04 -0700521 std::filesystem::create_directory(configurationOutDir);
522 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700523 if (!output.good())
524 {
525 return false;
526 }
James Feist1b2e2242018-01-30 13:45:19 -0800527 output << systemConfiguration.dump(4);
528 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700529 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700530}
James Feist1b2e2242018-01-30 13:45:19 -0800531
James Feist97a63f12018-05-17 13:50:57 -0700532template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800533bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
534 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700535{
536 try
537 {
538 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800539 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700540 ref = value;
541 return true;
542 }
543 catch (const std::out_of_range)
544 {
545 return false;
546 }
547}
James Feistbb43d022018-06-12 15:44:33 -0700548
James Feistebcc26b2019-03-22 12:30:43 -0700549// template function to add array as dbus property
550template <typename PropertyType>
551void addArrayToDbus(const std::string& name, const nlohmann::json& array,
552 sdbusplus::asio::dbus_interface* iface,
553 sdbusplus::asio::PropertyPermission permission,
554 nlohmann::json& systemConfiguration,
555 const std::string& jsonPointerString)
556{
557 std::vector<PropertyType> values;
558 for (const auto& property : array)
559 {
560 auto ptr = property.get_ptr<const PropertyType*>();
561 if (ptr != nullptr)
562 {
563 values.emplace_back(*ptr);
564 }
565 }
566
567 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
568 {
569 iface->register_property(name, values);
570 }
571 else
572 {
573 iface->register_property(
574 name, values,
575 [&systemConfiguration,
576 jsonPointerString{std::string(jsonPointerString)}](
577 const std::vector<PropertyType>& newVal,
578 std::vector<PropertyType>& val) {
579 val = newVal;
580 if (!setJsonFromPointer(jsonPointerString, val,
581 systemConfiguration))
582 {
583 std::cerr << "error setting json field\n";
584 return -1;
585 }
586 if (!writeJsonFiles(systemConfiguration))
587 {
588 std::cerr << "error setting json file\n";
589 return -1;
590 }
591 return 1;
592 });
593 }
594}
595
James Feistbb43d022018-06-12 15:44:33 -0700596template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800597void addProperty(const std::string& propertyName, const PropertyType& value,
598 sdbusplus::asio::dbus_interface* iface,
599 nlohmann::json& systemConfiguration,
600 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700601 sdbusplus::asio::PropertyPermission permission)
602{
603 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
604 {
605 iface->register_property(propertyName, value);
606 return;
607 }
James Feist68500ff2018-08-08 15:40:42 -0700608 iface->register_property(
609 propertyName, value,
610 [&systemConfiguration,
611 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800612 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700613 val = newVal;
614 if (!setJsonFromPointer(jsonPointerString, val,
615 systemConfiguration))
616 {
617 std::cerr << "error setting json field\n";
618 return -1;
619 }
James Feistc6248a52018-08-14 10:09:45 -0700620 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700621 {
622 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700623 return -1;
624 }
625 return 1;
626 });
627}
628
629void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800630 const std::string& jsonPointerPath,
631 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
632 sdbusplus::asio::object_server& objServer,
633 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700634{
635 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
636 iface->register_method(
637 "Delete", [&objServer, &systemConfiguration, interface,
638 jsonPointerPath{std::string(jsonPointerPath)}]() {
639 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
640 interface.lock();
641 if (!iface)
642 {
643 // this technically can't happen as the pointer is pointing to
644 // us
645 throw DBusInternalError();
646 }
647 nlohmann::json::json_pointer ptr(jsonPointerPath);
648 if (!objServer.remove_interface(iface))
649 {
650 std::cerr << "Can't delete interface " << jsonPointerPath
651 << "\n";
652 throw DBusInternalError();
653 }
654 systemConfiguration[ptr] = nullptr;
655
656 if (!writeJsonFiles(systemConfiguration))
657 {
658 std::cerr << "error setting json file\n";
659 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700660 }
James Feistbb43d022018-06-12 15:44:33 -0700661 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700662 });
James Feistbb43d022018-06-12 15:44:33 -0700663}
664
James Feist1b2e2242018-01-30 13:45:19 -0800665// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700666void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800667 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
668 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
669 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700670 sdbusplus::asio::PropertyPermission permission =
671 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800672{
James Feista465ccc2019-02-08 12:51:01 -0800673 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800674 {
James Feist8f2710a2018-05-09 17:18:55 -0700675 auto type = dictPair.value().type();
676 bool array = false;
677 if (dictPair.value().type() == nlohmann::json::value_t::array)
678 {
679 array = true;
680 if (!dictPair.value().size())
681 {
682 continue;
683 }
684 type = dictPair.value()[0].type();
685 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800686 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700687 {
688 if (arrayItem.type() != type)
689 {
690 isLegal = false;
691 break;
692 }
693 }
694 if (!isLegal)
695 {
696 std::cerr << "dbus format error" << dictPair.value() << "\n";
697 continue;
698 }
James Feista218ddb2019-04-11 14:01:31 -0700699 }
700 if (type == nlohmann::json::value_t::object)
701 {
702 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700703 }
James Feist97a63f12018-05-17 13:50:57 -0700704 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700705 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
706 {
707 // all setable numbers are doubles as it is difficult to always
708 // create a configuration file with all whole numbers as decimals
709 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700710 if (array)
711 {
712 if (dictPair.value()[0].is_number())
713 {
714 type = nlohmann::json::value_t::number_float;
715 }
716 }
717 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700718 {
719 type = nlohmann::json::value_t::number_float;
720 }
721 }
722
James Feist8f2710a2018-05-09 17:18:55 -0700723 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800724 {
James Feist9eb0b582018-04-27 12:15:46 -0700725 case (nlohmann::json::value_t::boolean):
726 {
James Feist8f2710a2018-05-09 17:18:55 -0700727 if (array)
728 {
729 // todo: array of bool isn't detected correctly by
730 // sdbusplus, change it to numbers
731 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700732 iface.get(), permission,
733 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700734 }
James Feistbb43d022018-06-12 15:44:33 -0700735
James Feist97a63f12018-05-17 13:50:57 -0700736 else
737 {
James Feistbb43d022018-06-12 15:44:33 -0700738 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700739 iface.get(), systemConfiguration, key,
740 permission);
James Feist97a63f12018-05-17 13:50:57 -0700741 }
James Feist9eb0b582018-04-27 12:15:46 -0700742 break;
743 }
744 case (nlohmann::json::value_t::number_integer):
745 {
James Feist8f2710a2018-05-09 17:18:55 -0700746 if (array)
747 {
748 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700749 iface.get(), permission,
750 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700751 }
752 else
753 {
James Feistbb43d022018-06-12 15:44:33 -0700754 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700755 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700756 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700757 }
James Feist9eb0b582018-04-27 12:15:46 -0700758 break;
759 }
760 case (nlohmann::json::value_t::number_unsigned):
761 {
James Feist8f2710a2018-05-09 17:18:55 -0700762 if (array)
763 {
764 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700765 iface.get(), permission,
766 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700767 }
768 else
769 {
James Feistbb43d022018-06-12 15:44:33 -0700770 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700771 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700772 systemConfiguration, key,
773 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700774 }
James Feist9eb0b582018-04-27 12:15:46 -0700775 break;
776 }
777 case (nlohmann::json::value_t::number_float):
778 {
James Feist8f2710a2018-05-09 17:18:55 -0700779 if (array)
780 {
781 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700782 iface.get(), permission,
783 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700784 }
James Feistbb43d022018-06-12 15:44:33 -0700785
James Feist97a63f12018-05-17 13:50:57 -0700786 else
787 {
James Feistbb43d022018-06-12 15:44:33 -0700788 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700789 iface.get(), systemConfiguration, key,
790 permission);
James Feist97a63f12018-05-17 13:50:57 -0700791 }
James Feist9eb0b582018-04-27 12:15:46 -0700792 break;
793 }
794 case (nlohmann::json::value_t::string):
795 {
James Feist8f2710a2018-05-09 17:18:55 -0700796 if (array)
797 {
James Feistebcc26b2019-03-22 12:30:43 -0700798 addArrayToDbus<std::string>(
799 dictPair.key(), dictPair.value(), iface.get(),
800 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700801 }
802 else
803 {
James Feistc6248a52018-08-14 10:09:45 -0700804 addProperty(
805 dictPair.key(), dictPair.value().get<std::string>(),
806 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700807 }
James Feist9eb0b582018-04-27 12:15:46 -0700808 break;
809 }
James Feist0eb40352019-04-09 14:44:04 -0700810 default:
811 {
James Feista218ddb2019-04-11 14:01:31 -0700812 std::cerr << "Unexpected json type in system configuration "
813 << dictPair.key() << ": "
814 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700815 break;
816 }
James Feist1b2e2242018-01-30 13:45:19 -0800817 }
818 }
James Feistc6248a52018-08-14 10:09:45 -0700819 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
820 {
821 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
822 systemConfiguration);
823 }
James Feist8f2710a2018-05-09 17:18:55 -0700824 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800825}
826
James Feista465ccc2019-02-08 12:51:01 -0800827sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700828{
829 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
830 interface) != settableInterfaces.end()
831 ? sdbusplus::asio::PropertyPermission::readWrite
832 : sdbusplus::asio::PropertyPermission::readOnly;
833}
834
James Feista465ccc2019-02-08 12:51:01 -0800835void createAddObjectMethod(const std::string& jsonPointerPath,
836 const std::string& path,
837 nlohmann::json& systemConfiguration,
838 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700839{
840 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
841
842 iface->register_method(
843 "AddObject",
844 [&systemConfiguration, &objServer,
845 jsonPointerPath{std::string(jsonPointerPath)},
846 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800847 const boost::container::flat_map<std::string, JsonVariantType>&
848 data) {
James Feist68500ff2018-08-08 15:40:42 -0700849 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800850 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700851 auto findExposes = base.find("Exposes");
852
853 if (findExposes == base.end())
854 {
855 throw std::invalid_argument("Entity must have children.");
856 }
857
858 // this will throw invalid-argument to sdbusplus if invalid json
859 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800860 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700861 {
James Feista465ccc2019-02-08 12:51:01 -0800862 nlohmann::json& newJson = newData[item.first];
863 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
864 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700865 }
866
867 auto findName = newData.find("Name");
868 auto findType = newData.find("Type");
869 if (findName == newData.end() || findType == newData.end())
870 {
871 throw std::invalid_argument("AddObject missing Name or Type");
872 }
James Feista465ccc2019-02-08 12:51:01 -0800873 const std::string* type = findType->get_ptr<const std::string*>();
874 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700875 if (type == nullptr || name == nullptr)
876 {
877 throw std::invalid_argument("Type and Name must be a string.");
878 }
879
880 size_t lastIndex = 0;
881 // we add in the "exposes"
882 for (; lastIndex < findExposes->size(); lastIndex++)
883 {
884 if (findExposes->at(lastIndex)["Name"] == *name &&
885 findExposes->at(lastIndex)["Type"] == *type)
886 {
887 throw std::invalid_argument(
888 "Field already in JSON, not adding");
889 }
890 lastIndex++;
891 }
892
893 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
894 *type + ".json");
895 // todo(james) we might want to also make a list of 'can add'
896 // interfaces but for now I think the assumption if there is a
897 // schema avaliable that it is allowed to update is fine
898 if (!schemaFile.good())
899 {
900 throw std::invalid_argument(
901 "No schema avaliable, cannot validate.");
902 }
903 nlohmann::json schema =
904 nlohmann::json::parse(schemaFile, nullptr, false);
905 if (schema.is_discarded())
906 {
907 std::cerr << "Schema not legal" << *type << ".json\n";
908 throw DBusInternalError();
909 }
910 if (!validateJson(schema, newData))
911 {
912 throw std::invalid_argument("Data does not match schema");
913 }
914
915 if (!writeJsonFiles(systemConfiguration))
916 {
917 std::cerr << "Error writing json files\n";
918 throw DBusInternalError();
919 }
920 std::string dbusName = *name;
921
922 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700923 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist68500ff2018-08-08 15:40:42 -0700924 auto iface = objServer.add_interface(
925 path + "/" + dbusName,
926 "xyz.openbmc_project.Configuration." + *type);
927 // permission is read-write, as since we just created it, must be
928 // runtime modifiable
929 populateInterfaceFromJson(
930 systemConfiguration,
James Feistc6248a52018-08-14 10:09:45 -0700931 jsonPointerPath + "/" + std::to_string(lastIndex), iface,
James Feist68500ff2018-08-08 15:40:42 -0700932 newData, objServer,
933 sdbusplus::asio::PropertyPermission::readWrite);
934 // todo(james) generate patch
935 findExposes->push_back(newData);
936 });
937 iface->initialize();
938}
939
James Feista465ccc2019-02-08 12:51:01 -0800940void postToDbus(const nlohmann::json& newConfiguration,
941 nlohmann::json& systemConfiguration,
942 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800943
James Feist1b2e2242018-01-30 13:45:19 -0800944{
James Feist97a63f12018-05-17 13:50:57 -0700945 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800946 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800947 {
James Feistf1b14142019-04-10 15:22:09 -0700948 std::string boardKey = boardPair.value()["Name"];
949 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700950 // loop through newConfiguration, but use values from system
951 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700952 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700953 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800954 std::string boardType;
955 if (findBoardType != boardValues.end() &&
956 findBoardType->type() == nlohmann::json::value_t::string)
957 {
958 boardType = findBoardType->get<std::string>();
959 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700960 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800961 }
962 else
963 {
964 std::cerr << "Unable to find type for " << boardKey
965 << " reverting to Chassis.\n";
966 boardType = "Chassis";
967 }
James Feist11be6672018-04-06 14:05:32 -0700968 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800969
970 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700971 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700972 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
973 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800974
James Feist8f2710a2018-05-09 17:18:55 -0700975 auto inventoryIface = objServer.add_interface(
976 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700977
James Feist8f2710a2018-05-09 17:18:55 -0700978 auto boardIface = objServer.add_interface(
979 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700980
James Feist68500ff2018-08-08 15:40:42 -0700981 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
982 objServer);
983
James Feist97a63f12018-05-17 13:50:57 -0700984 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700985 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700986 jsonPointerPath += "/";
987 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800988 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700989 {
990 if (boardField.value().type() == nlohmann::json::value_t::object)
991 {
James Feist8f2710a2018-05-09 17:18:55 -0700992 auto iface =
993 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700994 populateInterfaceFromJson(systemConfiguration,
995 jsonPointerPath + boardField.key(),
996 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700997 }
998 }
James Feist97a63f12018-05-17 13:50:57 -0700999
James Feist1e3e6982018-08-03 16:09:28 -07001000 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001001 if (exposes == boardValues.end())
1002 {
1003 continue;
1004 }
James Feist97a63f12018-05-17 13:50:57 -07001005 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001006 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001007
1008 // store the board level pointer so we can modify it on the way down
1009 std::string jsonPointerPathBoard = jsonPointerPath;
1010 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001011 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001012 {
James Feist97a63f12018-05-17 13:50:57 -07001013 exposesIndex++;
1014 jsonPointerPath = jsonPointerPathBoard;
1015 jsonPointerPath += std::to_string(exposesIndex);
1016
James Feistd63d18a2018-07-19 15:23:45 -07001017 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001018 if (findName == item.end())
1019 {
1020 std::cerr << "cannot find name in field " << item << "\n";
1021 continue;
1022 }
James Feist1e3e6982018-08-03 16:09:28 -07001023 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001024 // if status is not found it is assumed to be status = 'okay'
1025 if (findStatus != item.end())
1026 {
1027 if (*findStatus == "disabled")
1028 {
1029 continue;
1030 }
1031 }
James Feistd63d18a2018-07-19 15:23:45 -07001032 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001033 std::string itemType;
1034 if (findType != item.end())
1035 {
1036 itemType = findType->get<std::string>();
1037 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001038 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1039 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001040 }
1041 else
1042 {
1043 itemType = "unknown";
1044 }
1045 std::string itemName = findName->get<std::string>();
1046 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001047 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001048
James Feist8f2710a2018-05-09 17:18:55 -07001049 auto itemIface = objServer.add_interface(
1050 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001051 "xyz.openbmc_project.Configuration." + itemType);
1052
James Feist97a63f12018-05-17 13:50:57 -07001053 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001054 itemIface, item, objServer,
1055 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001056
James Feista465ccc2019-02-08 12:51:01 -08001057 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001058 {
James Feist97a63f12018-05-17 13:50:57 -07001059 jsonPointerPath = jsonPointerPathBoard +
1060 std::to_string(exposesIndex) + "/" +
1061 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001062 if (objectPair.value().type() ==
1063 nlohmann::json::value_t::object)
1064 {
James Feist8f2710a2018-05-09 17:18:55 -07001065 auto objectIface = objServer.add_interface(
1066 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001067 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001068 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001069
1070 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001071 systemConfiguration, jsonPointerPath, objectIface,
1072 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001073 }
1074 else if (objectPair.value().type() ==
1075 nlohmann::json::value_t::array)
1076 {
1077 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001078 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001079 {
James Feist8f2710a2018-05-09 17:18:55 -07001080 continue;
1081 }
1082 bool isLegal = true;
1083 auto type = objectPair.value()[0].type();
1084 if (type != nlohmann::json::value_t::object)
1085 {
1086 continue;
1087 }
1088
1089 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001090 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001091 {
1092 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001093 {
James Feist8f2710a2018-05-09 17:18:55 -07001094 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001095 break;
1096 }
James Feist8f2710a2018-05-09 17:18:55 -07001097 }
1098 if (!isLegal)
1099 {
1100 std::cerr << "dbus format error" << objectPair.value()
1101 << "\n";
1102 break;
1103 }
1104
James Feista465ccc2019-02-08 12:51:01 -08001105 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001106 {
James Feist97a63f12018-05-17 13:50:57 -07001107
James Feist8f2710a2018-05-09 17:18:55 -07001108 auto objectIface = objServer.add_interface(
1109 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001110 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001111 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001112 populateInterfaceFromJson(
1113 systemConfiguration,
1114 jsonPointerPath + "/" + std::to_string(index),
1115 objectIface, arrayItem, objServer,
1116 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001117 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001118 }
1119 }
1120 }
1121 }
1122 }
1123}
1124
1125// finds the template character (currently set to $) and replaces the value with
1126// the field found in a dbus object i.e. $ADDRESS would get populated with the
1127// ADDRESS field from a object on dbus
1128void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001129 nlohmann::json::iterator& keyPair,
1130 const boost::container::flat_map<std::string, BasicVariantType>&
1131 foundDevice,
1132 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001133{
James Feist11be6672018-04-06 14:05:32 -07001134 if (keyPair.value().type() == nlohmann::json::value_t::object)
1135 {
1136 for (auto nextLayer = keyPair.value().begin();
1137 nextLayer != keyPair.value().end(); nextLayer++)
1138 {
1139 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1140 }
1141 return;
1142 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001143
1144 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1145 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001146 {
1147 return;
1148 }
1149
Ed Tanous12bc7932019-02-26 14:36:20 -08001150 boost::replace_all(*strPtr, "$index", std::to_string(foundDeviceIdx));
1151
Ed Tanous12bc7932019-02-26 14:36:20 -08001152 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001153 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001154 std::string templateName = "$" + foundDevicePair.first;
1155 if (boost::iequals(*strPtr, templateName))
James Feist1b2e2242018-01-30 13:45:19 -08001156 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001157 std::visit([&](auto&& val) { keyPair.value() = val; },
1158 foundDevicePair.second);
1159 // We probably just invalidated the pointer above, so set it to null
1160 strPtr = nullptr;
1161 break;
James Feist1b2e2242018-01-30 13:45:19 -08001162 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001163
1164 std::string probeValue =
1165 std::visit(VariantToStringVisitor(), foundDevicePair.second);
1166 boost::replace_all(*strPtr, templateName, probeValue);
1167 }
1168
1169 strPtr = keyPair.value().get_ptr<std::string*>();
1170 if (strPtr == nullptr)
1171 {
1172 return;
James Feist1b2e2242018-01-30 13:45:19 -08001173 }
James Feistc6090822019-01-04 16:02:48 -08001174
1175 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001176 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001177 {
1178 try
1179 {
James Feistc6090822019-01-04 16:02:48 -08001180 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001181 int64_t temp = std::stoul(*strPtr, &pos, 0);
1182 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001183 {
1184 keyPair.value() = static_cast<uint64_t>(temp);
1185 }
James Feist28dc2da2018-10-15 14:47:42 -07001186 }
1187 catch (std::invalid_argument)
1188 {
1189 }
James Feistc6090822019-01-04 16:02:48 -08001190 catch (std::out_of_range)
1191 {
1192 }
James Feist28dc2da2018-10-15 14:47:42 -07001193 }
James Feist1b2e2242018-01-30 13:45:19 -08001194}
1195
James Feist8f2710a2018-05-09 17:18:55 -07001196// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001197bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001198{
1199 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001200 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001201 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1202 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001203 {
1204 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001205 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001206 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001207 }
James Feistb4383f42018-08-06 16:54:10 -07001208
1209 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1210 globalSchema);
1211 if (!schemaStream.good())
1212 {
1213 std::cerr
1214 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1215 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001216 return false;
James Feistb4383f42018-08-06 16:54:10 -07001217 }
1218 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1219 if (schema.is_discarded())
1220 {
1221 std::cerr
1222 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1223 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001224 return false;
James Feistb4383f42018-08-06 16:54:10 -07001225 }
1226
James Feista465ccc2019-02-08 12:51:01 -08001227 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001228 {
1229 std::ifstream jsonStream(jsonPath.c_str());
1230 if (!jsonStream.good())
1231 {
1232 std::cerr << "unable to open " << jsonPath.string() << "\n";
1233 continue;
1234 }
1235 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1236 if (data.is_discarded())
1237 {
1238 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1239 continue;
1240 }
James Feist8da99192019-01-24 08:20:16 -08001241 /*
1242 * todo(james): reenable this once less things are in flight
1243 *
James Feistb4383f42018-08-06 16:54:10 -07001244 if (!validateJson(schema, data))
1245 {
1246 std::cerr << "Error validating " << jsonPath.string() << "\n";
1247 continue;
1248 }
James Feist8da99192019-01-24 08:20:16 -08001249 */
James Feistb4383f42018-08-06 16:54:10 -07001250
James Feist3cb5fec2018-01-23 14:41:51 -08001251 if (data.type() == nlohmann::json::value_t::array)
1252 {
James Feista465ccc2019-02-08 12:51:01 -08001253 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001254 {
1255 configurations.emplace_back(d);
1256 }
1257 }
1258 else
1259 {
1260 configurations.emplace_back(data);
1261 }
1262 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001263 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001264}
James Feist3cb5fec2018-01-23 14:41:51 -08001265
James Feist8f2710a2018-05-09 17:18:55 -07001266struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001267{
James Feist75fdeeb2018-02-20 14:26:16 -08001268
James Feista465ccc2019-02-08 12:51:01 -08001269 PerformScan(nlohmann::json& systemConfiguration,
1270 std::list<nlohmann::json>& configurations,
1271 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001272 _systemConfiguration(systemConfiguration),
1273 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001274 {
James Feist8f2710a2018-05-09 17:18:55 -07001275 }
1276 void run()
1277 {
1278 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001279 {
James Feist1e3e6982018-08-03 16:09:28 -07001280 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001281 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001282
James Feist1b2e2242018-01-30 13:45:19 -08001283 nlohmann::json probeCommand;
1284 // check for poorly formatted fields, probe must be an array
1285 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001286 {
1287 std::cerr << "configuration file missing probe:\n " << *it
1288 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001289 it = _configurations.erase(it);
1290 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001291 }
James Feist1b2e2242018-01-30 13:45:19 -08001292 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001293 {
1294 probeCommand = nlohmann::json::array();
1295 probeCommand.push_back(*findProbe);
1296 }
1297 else
1298 {
1299 probeCommand = *findProbe;
1300 }
James Feist1b2e2242018-01-30 13:45:19 -08001301
1302 if (findName == it->end())
1303 {
1304 std::cerr << "configuration file missing name:\n " << *it
1305 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001306 it = _configurations.erase(it);
1307 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001308 }
James Feistf1b14142019-04-10 15:22:09 -07001309 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001310
James Feistf1b14142019-04-10 15:22:09 -07001311 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1312 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001313 {
James Feist8f2710a2018-05-09 17:18:55 -07001314 it = _configurations.erase(it);
1315 continue;
1316 }
James Feistf1b14142019-04-10 15:22:09 -07001317 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001318
James Feist8f2710a2018-05-09 17:18:55 -07001319 // store reference to this to children to makes sure we don't get
1320 // destroyed too early
1321 auto thisRef = shared_from_this();
1322 auto p = std::make_shared<PerformProbe>(
1323 probeCommand,
James Feistf1b14142019-04-10 15:22:09 -07001324 [&, recordPtr, probeName,
1325 thisRef](std::vector<std::optional<boost::container::flat_map<
1326 std::string, BasicVariantType>>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001327 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001328
James Feistf1b14142019-04-10 15:22:09 -07001329 PASSED_PROBES.push_back(probeName);
James Feist8f2710a2018-05-09 17:18:55 -07001330 size_t foundDeviceIdx = 0;
1331
James Feistf1b14142019-04-10 15:22:09 -07001332 nlohmann::json record = *recordPtr;
James Feistbe5425f2018-06-08 10:30:55 -07001333
James Feista465ccc2019-02-08 12:51:01 -08001334 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001335 {
James Feistf1b14142019-04-10 15:22:09 -07001336 std::string recordName;
1337 size_t hash = 0;
1338 if (foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001339 {
James Feistf1b14142019-04-10 15:22:09 -07001340 // use an array so alphabetical order from the
1341 // flat_map is maintained
1342 auto device = nlohmann::json::array();
1343 for (auto& devPair : *foundDevice)
1344 {
1345 device.push_back(devPair.first);
1346 std::visit(
1347 [&device](auto&& v) {
1348 device.push_back(v);
1349 },
1350 devPair.second);
1351 }
1352 hash = std::hash<std::string>{}(probeName +
1353 device.dump());
1354 // hashes are hard to distinguish, use the
1355 // non-hashed version if we want debug
1356 if constexpr (DEBUG)
1357 {
1358 recordName = probeName + device.dump();
1359 }
1360 else
1361 {
1362 recordName = std::to_string(hash);
1363 }
James Feist8f2710a2018-05-09 17:18:55 -07001364 }
James Feistf1b14142019-04-10 15:22:09 -07001365 else
1366 {
1367 recordName = probeName;
1368 }
1369
James Feist1df06a42019-04-11 14:23:04 -07001370 auto fromLastJson = lastJson.find(recordName);
1371 if (fromLastJson != lastJson.end())
1372 {
1373 // keep user changes
1374 _systemConfiguration[recordName] = *fromLastJson;
1375 continue;
1376 }
1377
James Feistf1b14142019-04-10 15:22:09 -07001378 // insert into configuration temporarily to be able to
1379 // reference ourselves
1380
1381 _systemConfiguration[recordName] = record;
1382
1383 if (foundDevice)
1384 {
1385 for (auto keyPair = record.begin();
1386 keyPair != record.end(); keyPair++)
1387 {
1388 templateCharReplace(keyPair, *foundDevice,
1389 foundDeviceIdx);
1390 }
1391 }
1392 auto findExpose = record.find("Exposes");
1393 if (findExpose == record.end())
James Feist8f2710a2018-05-09 17:18:55 -07001394 {
1395 continue;
1396 }
James Feista465ccc2019-02-08 12:51:01 -08001397 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001398 {
1399 for (auto keyPair = expose.begin();
1400 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001401 {
James Feist1b2e2242018-01-30 13:45:19 -08001402
James Feist8f2710a2018-05-09 17:18:55 -07001403 // fill in template characters with devices
1404 // found
James Feistf1b14142019-04-10 15:22:09 -07001405 if (foundDevice)
1406 {
1407 templateCharReplace(keyPair, *foundDevice,
1408 foundDeviceIdx);
1409 }
James Feist8f2710a2018-05-09 17:18:55 -07001410 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001411 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001412 {
1413 if (keyPair.value().type() !=
1414 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001415 {
James Feist8f2710a2018-05-09 17:18:55 -07001416 std::cerr << "bind_ value must be of "
1417 "type string "
1418 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001419 continue;
1420 }
James Feist8f2710a2018-05-09 17:18:55 -07001421 bool foundBind = false;
1422 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001423 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001424
James Feista465ccc2019-02-08 12:51:01 -08001425 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001426 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001427 {
James Feist1b2e2242018-01-30 13:45:19 -08001428
James Feist8f2710a2018-05-09 17:18:55 -07001429 auto configListFind =
1430 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001431 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001432
1433 if (configListFind ==
1434 configurationPair.value()
1435 .end() ||
1436 configListFind->type() !=
1437 nlohmann::json::value_t::array)
1438 {
1439 continue;
1440 }
James Feista465ccc2019-02-08 12:51:01 -08001441 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001442 *configListFind)
1443 {
1444 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001445 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001446 if (boost::iequals(
1447 foundObjectName,
1448 keyPair.value()
1449 .get<std::string>()))
1450 {
James Feist1e3e6982018-08-03 16:09:28 -07001451 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001452 "okay";
1453 expose[bind] = exposedObject;
1454
1455 foundBind = true;
1456 break;
1457 }
1458 }
1459 if (foundBind)
1460 {
James Feist3cb5fec2018-01-23 14:41:51 -08001461 break;
1462 }
1463 }
James Feist8f2710a2018-05-09 17:18:55 -07001464 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001465 {
James Feist8f2710a2018-05-09 17:18:55 -07001466 std::cerr << "configuration file "
1467 "dependency error, "
1468 "could not find bind "
1469 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001470 }
1471 }
1472 }
1473 }
James Feistf1b14142019-04-10 15:22:09 -07001474 // overwrite ourselves with cleaned up version
1475 _systemConfiguration[recordName] = record;
James Feist1df06a42019-04-11 14:23:04 -07001476 logDeviceAdded(record.at("Name").get<std::string>());
1477
James Feistf1b14142019-04-10 15:22:09 -07001478 foundDeviceIdx++;
James Feist3cb5fec2018-01-23 14:41:51 -08001479 }
James Feist8f2710a2018-05-09 17:18:55 -07001480 });
1481 p->run();
1482 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001483 }
1484 }
James Feist75fdeeb2018-02-20 14:26:16 -08001485
James Feist8f2710a2018-05-09 17:18:55 -07001486 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001487 {
James Feist8f2710a2018-05-09 17:18:55 -07001488 if (_passed)
1489 {
1490 auto nextScan = std::make_shared<PerformScan>(
1491 _systemConfiguration, _configurations, std::move(_callback));
1492 nextScan->run();
1493 }
1494 else
1495 {
1496 _callback();
1497 }
1498 }
James Feista465ccc2019-02-08 12:51:01 -08001499 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001500 std::list<nlohmann::json> _configurations;
1501 std::function<void(void)> _callback;
1502 std::vector<std::shared_ptr<PerformProbe>> _probes;
1503 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001504 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001505};
James Feistc95cb142018-02-26 10:41:42 -08001506
James Feist1df06a42019-04-11 14:23:04 -07001507void startRemovedTimer(boost::asio::deadline_timer& timer,
1508 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1509 nlohmann::json& systemConfiguration)
1510{
1511 static bool scannedPowerOff = false;
1512 static bool scannedPowerOn = false;
1513
1514 if (scannedPowerOn)
1515 {
1516 return;
1517 }
1518
1519 if (!isPowerOn() && scannedPowerOff)
1520 {
1521 return;
1522 }
1523
1524 timer.expires_from_now(boost::posix_time::seconds(10));
1525 timer.async_wait([&systemConfiguration](
1526 const boost::system::error_code& ec) {
1527 if (ec == boost::asio::error::operation_aborted)
1528 {
1529 // we were cancelled
1530 return;
1531 }
1532
1533 bool powerOff = !isPowerOn();
1534 for (const auto& item : lastJson.items())
1535 {
1536 if (systemConfiguration.find(item.key()) ==
1537 systemConfiguration.end())
1538 {
1539 bool isDetectedPowerOn = false;
1540 auto powerState = item.value().find("PowerState");
1541 if (powerState != item.value().end())
1542 {
1543 auto ptr = powerState->get_ptr<const std::string*>();
1544 if (ptr)
1545 {
1546 if (*ptr == "On" || *ptr == "BiosPost")
1547 {
1548 isDetectedPowerOn = true;
1549 }
1550 }
1551 }
1552 if (powerOff && isDetectedPowerOn)
1553 {
1554 // power not on yet, don't know if it's there or not
1555 continue;
1556 }
1557 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1558 {
1559 // already logged it when power was off
1560 continue;
1561 }
1562
1563 logDeviceRemoved(item.value().at("Name").get<std::string>());
1564 }
1565 }
1566 scannedPowerOff = true;
1567 if (!powerOff)
1568 {
1569 scannedPowerOn = true;
1570 }
1571 });
1572}
1573
James Feist8f2710a2018-05-09 17:18:55 -07001574// main properties changed entry
1575void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001576 boost::asio::io_service& io,
1577 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1578 nlohmann::json& systemConfiguration,
1579 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001580{
1581 static boost::asio::deadline_timer timer(io);
James Feist1df06a42019-04-11 14:23:04 -07001582 static bool timerRunning;
1583
1584 timerRunning = true;
James Feist8f2710a2018-05-09 17:18:55 -07001585 timer.expires_from_now(boost::posix_time::seconds(1));
1586
1587 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001588 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001589 if (ec == boost::asio::error::operation_aborted)
1590 {
1591 // we were cancelled
1592 return;
1593 }
1594 else if (ec)
1595 {
1596 std::cerr << "async wait error " << ec << "\n";
1597 return;
1598 }
James Feist1df06a42019-04-11 14:23:04 -07001599 timerRunning = false;
James Feist8f2710a2018-05-09 17:18:55 -07001600
1601 nlohmann::json oldConfiguration = systemConfiguration;
1602 DBUS_PROBE_OBJECTS.clear();
1603
1604 std::list<nlohmann::json> configurations;
1605 if (!findJsonFiles(configurations))
1606 {
1607 std::cerr << "cannot find json files\n";
1608 return;
1609 }
1610
1611 auto perfScan = std::make_shared<PerformScan>(
1612 systemConfiguration, configurations, [&, oldConfiguration]() {
1613 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001614 for (auto it = newConfiguration.begin();
1615 it != newConfiguration.end();)
1616 {
1617 auto findKey = oldConfiguration.find(it.key());
1618 if (findKey != oldConfiguration.end())
1619 {
1620 it = newConfiguration.erase(it);
1621 }
1622 else
1623 {
1624 it++;
1625 }
1626 }
James Feist8f2710a2018-05-09 17:18:55 -07001627 registerCallbacks(io, dbusMatches, systemConfiguration,
1628 objServer);
1629 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001630 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001631
James Feistbb43d022018-06-12 15:44:33 -07001632 io.post([&]() {
1633 if (!writeJsonFiles(systemConfiguration))
1634 {
1635 std::cerr << "Error writing json files\n";
1636 }
1637 });
James Feist8f2710a2018-05-09 17:18:55 -07001638 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001639 postToDbus(newConfiguration, systemConfiguration,
1640 objServer);
James Feist1df06a42019-04-11 14:23:04 -07001641 if (!timerRunning)
1642 {
1643 startRemovedTimer(timer, dbusMatches,
1644 systemConfiguration);
1645 }
James Feist8f2710a2018-05-09 17:18:55 -07001646 });
1647 });
1648 });
1649 perfScan->run();
1650 });
James Feist75fdeeb2018-02-20 14:26:16 -08001651}
1652
James Feista465ccc2019-02-08 12:51:01 -08001653void registerCallbacks(boost::asio::io_service& io,
1654 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1655 nlohmann::json& systemConfiguration,
1656 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001657{
1658 static boost::container::flat_set<std::string> watchedObjects;
1659
James Feista465ccc2019-02-08 12:51:01 -08001660 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001661 {
1662 auto findObject = watchedObjects.find(objectMap.first);
1663 if (findObject != watchedObjects.end())
1664 {
1665 continue;
1666 }
James Feist8f2710a2018-05-09 17:18:55 -07001667 std::function<void(sdbusplus::message::message & message)>
1668 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001669
James Feista465ccc2019-02-08 12:51:01 -08001670 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001671 propertiesChangedCallback(io, dbusMatches,
1672 systemConfiguration, objServer);
1673 };
1674
1675 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001676 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001677 "type='signal',member='PropertiesChanged',arg0='" +
1678 objectMap.first + "'",
1679 eventHandler);
1680 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001681 }
1682}
1683
James Feista465ccc2019-02-08 12:51:01 -08001684int main(int argc, char** argv)
James Feist75fdeeb2018-02-20 14:26:16 -08001685{
1686 // setup connection to dbus
1687 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001688 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001689 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001690
James Feist8f2710a2018-05-09 17:18:55 -07001691 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001692
James Feist8f2710a2018-05-09 17:18:55 -07001693 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1694 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1695 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001696
James Feist8f2710a2018-05-09 17:18:55 -07001697 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1698 objServer.add_interface("/xyz/openbmc_project/inventory",
1699 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001700
1701 // to keep reference to the match / filter objects so they don't get
1702 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001703 std::vector<sdbusplus::bus::match::match> dbusMatches;
1704
1705 nlohmann::json systemConfiguration = nlohmann::json::object();
1706
1707 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001708 "Notify",
1709 [](const boost::container::flat_map<
1710 std::string,
1711 boost::container::flat_map<std::string, BasicVariantType>>&
1712 object) { return; });
James Feist8f2710a2018-05-09 17:18:55 -07001713 inventoryIface->initialize();
1714
1715 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001716#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001717 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001718#endif
James Feist8f2710a2018-05-09 17:18:55 -07001719 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1720 objServer);
1721 });
James Feist4131aea2018-03-09 09:47:30 -08001722
James Feistfd1264a2018-05-03 12:10:00 -07001723 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001724 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1725 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001726 });
James Feist8f2710a2018-05-09 17:18:55 -07001727 entityIface->initialize();
1728
James Feist1df06a42019-04-11 14:23:04 -07001729 if (fwVersionIsSame())
1730 {
1731 if (std::filesystem::is_regular_file(currentConfiguration))
1732 {
1733 // this file could just be deleted, but it's nice for debug
1734 std::filesystem::create_directory(tempConfigDir);
1735 std::filesystem::remove(lastConfiguration);
1736 std::filesystem::copy(currentConfiguration, lastConfiguration);
1737 std::filesystem::remove(currentConfiguration);
1738
1739 std::ifstream jsonStream(lastConfiguration);
1740 if (jsonStream.good())
1741 {
1742 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1743 if (data.is_discarded())
1744 {
1745 std::cerr << "syntax error in " << lastConfiguration
1746 << "\n";
1747 }
1748 else
1749 {
1750 lastJson = std::move(data);
1751 }
1752 }
1753 else
1754 {
1755 std::cerr << "unable to open " << lastConfiguration << "\n";
1756 }
1757 }
1758 }
1759 else
1760 {
1761 // not an error, just logging at this level to make it in the journal
1762 std::cerr << "Clearing previous configuration\n";
1763 std::filesystem::remove(currentConfiguration);
1764 }
1765
1766 // some boards only show up after power is on, we want to not say they are
1767 // removed until the same state happens
1768 setupPowerMatch(SYSTEM_BUS);
1769
James Feist1b2e2242018-01-30 13:45:19 -08001770 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001771
1772 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001773}