blob: 4f3709a4f510a76ffa59a9a66afed37b337c0356 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
James Feist1df06a42019-04-11 14:23:04 -070017#include "EntityManager.hpp"
18
Patrick Venturea49dc332019-10-26 08:32:02 -070019#include "Overlay.hpp"
20#include "Utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070021#include "VariantVisitors.hpp"
22
James Feist11be6672018-04-06 14:05:32 -070023#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070024#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070027#include <boost/algorithm/string/split.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080028#include <boost/container/flat_map.hpp>
29#include <boost/container/flat_set.hpp>
James Feistf5125b02019-06-06 11:27:43 -070030#include <boost/range/iterator_range.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070031#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080032#include <fstream>
33#include <iostream>
34#include <nlohmann/json.hpp>
35#include <regex>
36#include <sdbusplus/asio/connection.hpp>
37#include <sdbusplus/asio/object_server.hpp>
38#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080039
James Feista465ccc2019-02-08 12:51:01 -080040constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
41constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070042constexpr const char* tempConfigDir = "/tmp/configuration/";
43constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
44constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080045constexpr const char* globalSchema = "global.json";
James Feist8f2710a2018-05-09 17:18:55 -070046constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080047
James Feistf1b14142019-04-10 15:22:09 -070048constexpr const bool DEBUG = false;
49
James Feist3cb5fec2018-01-23 14:41:51 -080050struct cmp_str
51{
James Feista465ccc2019-02-08 12:51:01 -080052 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080053 {
54 return std::strcmp(a, b) < 0;
55 }
56};
57
58// underscore T for collison with dbus c api
59enum class probe_type_codes
60{
61 FALSE_T,
62 TRUE_T,
63 AND,
64 OR,
James Feist6bd2a022018-03-13 12:30:58 -070065 FOUND,
66 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080067};
James Feista465ccc2019-02-08 12:51:01 -080068const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080069 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
70 {"TRUE", probe_type_codes::TRUE_T},
71 {"AND", probe_type_codes::AND},
72 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070073 {"FOUND", probe_type_codes::FOUND},
74 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080075
James Feist41334262019-03-25 13:30:20 -070076static constexpr std::array<const char*, 5> settableInterfaces = {
77 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070078using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080079 std::variant<std::vector<std::string>, std::vector<double>, std::string,
80 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
81 uint16_t, uint8_t, bool>;
James Feist3cb5fec2018-01-23 14:41:51 -080082using GetSubTreeType = std::vector<
83 std::pair<std::string,
84 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
85
86using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070087 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080088 boost::container::flat_map<
89 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070090 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080091
James Feistd58879a2019-09-11 11:26:07 -070092// store reference to all interfaces so we can destroy them later
93boost::container::flat_map<
94 std::string, std::vector<std::shared_ptr<sdbusplus::asio::dbus_interface>>>
95 inventory;
96
James Feist3cb5fec2018-01-23 14:41:51 -080097// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070098std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -070099static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800100
Johnathan Mantey2015f752019-03-26 15:22:31 -0700101const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
102const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800103
James Feista465ccc2019-02-08 12:51:01 -0800104void registerCallbacks(boost::asio::io_service& io,
105 std::vector<sdbusplus::bus::match::match>& dbusMatches,
106 nlohmann::json& systemConfiguration,
James Feist733f7652019-11-13 14:32:29 -0800107 sdbusplus::asio::object_server& objServer,
108 const DBusProbeObjectT& dbusProbeObjects);
James Feist75fdeeb2018-02-20 14:26:16 -0800109
James Feistd58879a2019-09-11 11:26:07 -0700110static std::shared_ptr<sdbusplus::asio::dbus_interface>
111 createInterface(sdbusplus::asio::object_server& objServer,
112 const std::string& path, const std::string& interface,
113 const std::string& parent)
114{
115 return inventory[parent].emplace_back(
116 objServer.add_interface(path, interface));
117}
118
James Feist3cb5fec2018-01-23 14:41:51 -0800119// calls the mapper to find all exposed objects of an interface type
120// and creates a vector<flat_map> that contains all the key value pairs
121// getManagedObjects
James Feist787c3c32019-11-07 14:42:58 -0800122void findDbusObjects(std::vector<std::shared_ptr<PerformProbe>>& probeVector,
James Feist8f2710a2018-05-09 17:18:55 -0700123 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feist733f7652019-11-13 14:32:29 -0800124 boost::container::flat_set<std::string>& interfaces,
125 std::shared_ptr<PerformScan> scan)
James Feist3cb5fec2018-01-23 14:41:51 -0800126{
James Feist8f2710a2018-05-09 17:18:55 -0700127
James Feist733f7652019-11-13 14:32:29 -0800128 for (const auto& [interface, _] : scan->dbusProbeObjects)
129 {
130 interfaces.erase(interface);
131 }
132 if (interfaces.empty())
133 {
134 return;
135 }
136
James Feist3cb5fec2018-01-23 14:41:51 -0800137 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700138 connection->async_method_call(
James Feist733f7652019-11-13 14:32:29 -0800139 [connection, interfaces, probeVector,
140 scan](boost::system::error_code& ec,
141 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700142 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700143 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700144 {
James Feist0de40152018-07-25 11:56:12 -0700145 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700146 {
James Feist0de40152018-07-25 11:56:12 -0700147 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700148 }
James Feist0de40152018-07-25 11:56:12 -0700149 std::cerr << "Error communicating to mapper.\n";
150
151 // if we can't communicate to the mapper something is very wrong
152 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700153 }
James Feist787c3c32019-11-07 14:42:58 -0800154
155 for (auto& object : interfaceSubtree)
James Feist3cb5fec2018-01-23 14:41:51 -0800156 {
James Feist787c3c32019-11-07 14:42:58 -0800157 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700158 {
James Feist787c3c32019-11-07 14:42:58 -0800159 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700160 }
James Feist3cb5fec2018-01-23 14:41:51 -0800161 }
James Feist787c3c32019-11-07 14:42:58 -0800162
James Feist63845bf2019-01-24 12:19:51 -0800163 if (interfaceConnections.empty())
164 {
James Feist63845bf2019-01-24 12:19:51 -0800165 return;
166 }
James Feist8f2710a2018-05-09 17:18:55 -0700167 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800168 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700169 {
170 connection->async_method_call(
James Feist733f7652019-11-13 14:32:29 -0800171 [conn, interfaces, probeVector,
172 scan](boost::system::error_code& errc,
173 const ManagedObjectType& managedInterface) {
James Feist98132792019-07-09 13:29:09 -0700174 if (errc)
James Feist8f2710a2018-05-09 17:18:55 -0700175 {
176 std::cerr
177 << "error getting managed object for device "
178 << conn << "\n";
James Feist8f2710a2018-05-09 17:18:55 -0700179 return;
180 }
James Feista465ccc2019-02-08 12:51:01 -0800181 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700182 {
James Feist787c3c32019-11-07 14:42:58 -0800183 // we could match multiple interfaces with one owner
184 for (auto& [interface, object] :
185 interfaceManagedObj.second)
James Feist8f2710a2018-05-09 17:18:55 -0700186 {
James Feist787c3c32019-11-07 14:42:58 -0800187 auto ifaceObjFind = interfaces.find(interface);
188
189 if (ifaceObjFind != interfaces.end())
190 {
James Feist733f7652019-11-13 14:32:29 -0800191 scan->dbusProbeObjects[interface]
192 .emplace_back(object);
James Feist787c3c32019-11-07 14:42:58 -0800193 }
James Feist8f2710a2018-05-09 17:18:55 -0700194 }
195 }
James Feist8f2710a2018-05-09 17:18:55 -0700196 },
James Feist787c3c32019-11-07 14:42:58 -0800197 conn, "/", "org.freedesktop.DBus.ObjectManager",
James Feist8f2710a2018-05-09 17:18:55 -0700198 "GetManagedObjects");
199 }
200 },
201 "xyz.openbmc_project.ObjectMapper",
202 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700203 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist787c3c32019-11-07 14:42:58 -0800204 interfaces);
James Feist3cb5fec2018-01-23 14:41:51 -0800205}
James Feist8f2710a2018-05-09 17:18:55 -0700206// probes dbus interface dictionary for a key with a value that matches a regex
James Feist08a5b172019-08-28 14:47:47 -0700207bool probeDbus(const std::string& interface,
208 const std::map<std::string, nlohmann::json>& matches,
James Feist733f7652019-11-13 14:32:29 -0800209 FoundDeviceT& devices, std::shared_ptr<PerformScan> scan,
210 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800211{
James Feista465ccc2019-02-08 12:51:01 -0800212 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
James Feist733f7652019-11-13 14:32:29 -0800213 dbusObject = scan->dbusProbeObjects[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800214 if (dbusObject.empty())
215 {
James Feist8f2710a2018-05-09 17:18:55 -0700216 foundProbe = false;
217 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800218 }
219 foundProbe = true;
220
221 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800222 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800223 {
224 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800225 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800226 {
227 auto deviceValue = device.find(match.first);
228 if (deviceValue != device.end())
229 {
230 switch (match.second.type())
231 {
James Feist9eb0b582018-04-27 12:15:46 -0700232 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800233 {
James Feist9eb0b582018-04-27 12:15:46 -0700234 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700235 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700236
237 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800238 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700239 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700240 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700241 {
242 deviceMatches = false;
243 break;
244 }
James Feist3cb5fec2018-01-23 14:41:51 -0800245 break;
246 }
James Feist9eb0b582018-04-27 12:15:46 -0700247 case nlohmann::json::value_t::boolean:
248 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800249 {
James Feista465ccc2019-02-08 12:51:01 -0800250 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700251 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800252
James Feist9eb0b582018-04-27 12:15:46 -0700253 if (probeValue != match.second.get<unsigned int>())
254 {
255 deviceMatches = false;
256 }
257 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800258 }
James Feist9eb0b582018-04-27 12:15:46 -0700259 case nlohmann::json::value_t::number_integer:
260 {
James Feista465ccc2019-02-08 12:51:01 -0800261 int probeValue = std::visit(VariantToIntVisitor(),
262 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800263
James Feist9eb0b582018-04-27 12:15:46 -0700264 if (probeValue != match.second.get<int>())
265 {
266 deviceMatches = false;
267 }
268 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800269 }
James Feist9eb0b582018-04-27 12:15:46 -0700270 case nlohmann::json::value_t::number_float:
271 {
James Feista465ccc2019-02-08 12:51:01 -0800272 float probeValue = std::visit(VariantToFloatVisitor(),
273 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700274
275 if (probeValue != match.second.get<float>())
276 {
277 deviceMatches = false;
278 }
279 break;
280 }
James Feist0eb40352019-04-09 14:44:04 -0700281 default:
282 {
283 std::cerr << "unexpected dbus probe type "
284 << match.second.type_name() << "\n";
285 }
James Feist3cb5fec2018-01-23 14:41:51 -0800286 }
287 }
288 else
289 {
290 deviceMatches = false;
291 break;
292 }
293 }
294 if (deviceMatches)
295 {
James Feistf5125b02019-06-06 11:27:43 -0700296 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800297 foundMatch = true;
298 deviceMatches = false; // for next iteration
299 }
300 }
301 return foundMatch;
302}
303
304// default probe entry point, iterates a list looking for specific types to
305// call specific probe functions
306bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800307 const std::vector<std::string>& probeCommand,
James Feist733f7652019-11-13 14:32:29 -0800308 std::shared_ptr<PerformScan> scan,
James Feist08a5b172019-08-28 14:47:47 -0700309 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
310 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800311{
312 const static std::regex command(R"(\((.*)\))");
313 std::smatch match;
314 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700315 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800316 bool cur = true;
317 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700318 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800319
James Feista465ccc2019-02-08 12:51:01 -0800320 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800321 {
322 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800323 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800324 cmp_str>::const_iterator probeType;
325
326 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
Patrick Venture4b7a7452019-10-28 08:41:02 -0700327 ++probeType)
James Feist3cb5fec2018-01-23 14:41:51 -0800328 {
329 if (probe.find(probeType->first) != std::string::npos)
330 {
331 foundProbe = true;
332 break;
333 }
334 }
335 if (foundProbe)
336 {
337 switch (probeType->second)
338 {
James Feist9eb0b582018-04-27 12:15:46 -0700339 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800340 {
James Feiste31e00a2019-07-24 10:45:43 -0700341 cur = false;
342 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800343 }
James Feist9eb0b582018-04-27 12:15:46 -0700344 case probe_type_codes::TRUE_T:
345 {
James Feiste31e00a2019-07-24 10:45:43 -0700346 cur = true;
347 break;
James Feist9eb0b582018-04-27 12:15:46 -0700348 }
349 case probe_type_codes::MATCH_ONE:
350 {
351 // set current value to last, this probe type shouldn't
352 // affect the outcome
353 cur = ret;
354 matchOne = true;
355 break;
356 }
357 /*case probe_type_codes::AND:
358 break;
359 case probe_type_codes::OR:
360 break;
361 // these are no-ops until the last command switch
362 */
363 case probe_type_codes::FOUND:
364 {
365 if (!std::regex_search(probe, match, command))
366 {
James Feist0eb40352019-04-09 14:44:04 -0700367 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700368 << "\n";
369 return false;
370 }
371 std::string commandStr = *(match.begin() + 1);
372 boost::replace_all(commandStr, "'", "");
James Feist733f7652019-11-13 14:32:29 -0800373 cur = (std::find(scan->passedProbes.begin(),
374 scan->passedProbes.end(),
375 commandStr) != scan->passedProbes.end());
James Feist9eb0b582018-04-27 12:15:46 -0700376 break;
377 }
James Feist0eb40352019-04-09 14:44:04 -0700378 default:
379 {
380 break;
381 }
James Feist3cb5fec2018-01-23 14:41:51 -0800382 }
383 }
384 // look on dbus for object
385 else
386 {
387 if (!std::regex_search(probe, match, command))
388 {
James Feist0eb40352019-04-09 14:44:04 -0700389 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800390 return false;
391 }
392 std::string commandStr = *(match.begin() + 1);
393 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700394 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800395 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800396 auto json = nlohmann::json::parse(commandStr, nullptr, false);
397 if (json.is_discarded())
398 {
James Feist0eb40352019-04-09 14:44:04 -0700399 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800400 return false;
401 }
402 // we can match any (string, variant) property. (string, string)
403 // does a regex
404 std::map<std::string, nlohmann::json> dbusProbeMap =
405 json.get<std::map<std::string, nlohmann::json>>();
406 auto findStart = probe.find("(");
407 if (findStart == std::string::npos)
408 {
409 return false;
410 }
411 std::string probeInterface = probe.substr(0, findStart);
James Feist733f7652019-11-13 14:32:29 -0800412 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan,
413 foundProbe);
James Feist3cb5fec2018-01-23 14:41:51 -0800414 }
415
416 // some functions like AND and OR only take affect after the
417 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700418 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800419 {
James Feist54a0dca2019-06-26 10:34:54 -0700420 ret = cur && ret;
421 }
422 else if (lastCommand == probe_type_codes::OR)
423 {
424 ret = cur || ret;
425 }
426
427 if (first)
428 {
429 ret = cur;
430 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800431 }
432 lastCommand = probeType != PROBE_TYPES.end()
433 ? probeType->second
434 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800435 }
436
437 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800438 if (ret && foundDevs.size() == 0)
439 {
James Feist08a5b172019-08-28 14:47:47 -0700440 foundDevs.emplace_back(
441 boost::container::flat_map<std::string, BasicVariantType>{});
James Feist3cb5fec2018-01-23 14:41:51 -0800442 }
James Feist0eb40352019-04-09 14:44:04 -0700443 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700444 {
James Feist71f295f2019-06-20 13:35:12 -0700445 // match the last one
446 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700447 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700448
449 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700450 }
James Feist3cb5fec2018-01-23 14:41:51 -0800451 return ret;
452}
453
James Feist7d807752019-11-13 14:47:57 -0800454PerformProbe::PerformProbe(const std::vector<std::string>& probeCommand,
455 std::shared_ptr<PerformScan>& scanPtr,
456 std::function<void(FoundDeviceT&)>&& callback) :
457 _probeCommand(probeCommand),
458 scan(scanPtr), _callback(std::move(callback))
459{
460}
461PerformProbe::~PerformProbe()
462{
463 FoundDeviceT foundDevs;
464 if (probe(_probeCommand, scan, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700465 {
James Feist7d807752019-11-13 14:47:57 -0800466 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700467 }
James Feist7d807752019-11-13 14:47:57 -0800468}
James Feist8f2710a2018-05-09 17:18:55 -0700469
470// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800471bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800472{
James Feist1df06a42019-04-11 14:23:04 -0700473 std::filesystem::create_directory(configurationOutDir);
474 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700475 if (!output.good())
476 {
477 return false;
478 }
James Feist1b2e2242018-01-30 13:45:19 -0800479 output << systemConfiguration.dump(4);
480 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700481 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700482}
James Feist1b2e2242018-01-30 13:45:19 -0800483
James Feist97a63f12018-05-17 13:50:57 -0700484template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800485bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
486 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700487{
488 try
489 {
490 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800491 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700492 ref = value;
493 return true;
494 }
James Feist98132792019-07-09 13:29:09 -0700495 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700496 {
497 return false;
498 }
499}
James Feistbb43d022018-06-12 15:44:33 -0700500
James Feistebcc26b2019-03-22 12:30:43 -0700501// template function to add array as dbus property
502template <typename PropertyType>
503void addArrayToDbus(const std::string& name, const nlohmann::json& array,
504 sdbusplus::asio::dbus_interface* iface,
505 sdbusplus::asio::PropertyPermission permission,
506 nlohmann::json& systemConfiguration,
507 const std::string& jsonPointerString)
508{
509 std::vector<PropertyType> values;
510 for (const auto& property : array)
511 {
512 auto ptr = property.get_ptr<const PropertyType*>();
513 if (ptr != nullptr)
514 {
515 values.emplace_back(*ptr);
516 }
517 }
518
519 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
520 {
521 iface->register_property(name, values);
522 }
523 else
524 {
525 iface->register_property(
526 name, values,
527 [&systemConfiguration,
528 jsonPointerString{std::string(jsonPointerString)}](
529 const std::vector<PropertyType>& newVal,
530 std::vector<PropertyType>& val) {
531 val = newVal;
532 if (!setJsonFromPointer(jsonPointerString, val,
533 systemConfiguration))
534 {
535 std::cerr << "error setting json field\n";
536 return -1;
537 }
538 if (!writeJsonFiles(systemConfiguration))
539 {
540 std::cerr << "error setting json file\n";
541 return -1;
542 }
543 return 1;
544 });
545 }
546}
547
James Feistbb43d022018-06-12 15:44:33 -0700548template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800549void addProperty(const std::string& propertyName, const PropertyType& value,
550 sdbusplus::asio::dbus_interface* iface,
551 nlohmann::json& systemConfiguration,
552 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700553 sdbusplus::asio::PropertyPermission permission)
554{
555 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
556 {
557 iface->register_property(propertyName, value);
558 return;
559 }
James Feist68500ff2018-08-08 15:40:42 -0700560 iface->register_property(
561 propertyName, value,
562 [&systemConfiguration,
563 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800564 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700565 val = newVal;
566 if (!setJsonFromPointer(jsonPointerString, val,
567 systemConfiguration))
568 {
569 std::cerr << "error setting json field\n";
570 return -1;
571 }
James Feistc6248a52018-08-14 10:09:45 -0700572 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700573 {
574 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700575 return -1;
576 }
577 return 1;
578 });
579}
580
581void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800582 const std::string& jsonPointerPath,
583 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
584 sdbusplus::asio::object_server& objServer,
585 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700586{
587 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
588 iface->register_method(
589 "Delete", [&objServer, &systemConfiguration, interface,
590 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700591 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700592 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700593 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700594 {
595 // this technically can't happen as the pointer is pointing to
596 // us
597 throw DBusInternalError();
598 }
599 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feist98132792019-07-09 13:29:09 -0700600 if (!objServer.remove_interface(dbusInterface))
James Feistc6248a52018-08-14 10:09:45 -0700601 {
602 std::cerr << "Can't delete interface " << jsonPointerPath
603 << "\n";
604 throw DBusInternalError();
605 }
606 systemConfiguration[ptr] = nullptr;
607
608 if (!writeJsonFiles(systemConfiguration))
609 {
610 std::cerr << "error setting json file\n";
611 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700612 }
James Feistbb43d022018-06-12 15:44:33 -0700613 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700614 });
James Feistbb43d022018-06-12 15:44:33 -0700615}
616
James Feist1b2e2242018-01-30 13:45:19 -0800617// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700618void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800619 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
620 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
621 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700622 sdbusplus::asio::PropertyPermission permission =
623 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800624{
James Feista465ccc2019-02-08 12:51:01 -0800625 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800626 {
James Feist8f2710a2018-05-09 17:18:55 -0700627 auto type = dictPair.value().type();
628 bool array = false;
629 if (dictPair.value().type() == nlohmann::json::value_t::array)
630 {
631 array = true;
632 if (!dictPair.value().size())
633 {
634 continue;
635 }
636 type = dictPair.value()[0].type();
637 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800638 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700639 {
640 if (arrayItem.type() != type)
641 {
642 isLegal = false;
643 break;
644 }
645 }
646 if (!isLegal)
647 {
648 std::cerr << "dbus format error" << dictPair.value() << "\n";
649 continue;
650 }
James Feista218ddb2019-04-11 14:01:31 -0700651 }
652 if (type == nlohmann::json::value_t::object)
653 {
654 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700655 }
James Feist97a63f12018-05-17 13:50:57 -0700656 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700657 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
658 {
659 // all setable numbers are doubles as it is difficult to always
660 // create a configuration file with all whole numbers as decimals
661 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700662 if (array)
663 {
664 if (dictPair.value()[0].is_number())
665 {
666 type = nlohmann::json::value_t::number_float;
667 }
668 }
669 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700670 {
671 type = nlohmann::json::value_t::number_float;
672 }
673 }
674
James Feist8f2710a2018-05-09 17:18:55 -0700675 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800676 {
James Feist9eb0b582018-04-27 12:15:46 -0700677 case (nlohmann::json::value_t::boolean):
678 {
James Feist8f2710a2018-05-09 17:18:55 -0700679 if (array)
680 {
681 // todo: array of bool isn't detected correctly by
682 // sdbusplus, change it to numbers
683 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700684 iface.get(), permission,
685 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700686 }
James Feistbb43d022018-06-12 15:44:33 -0700687
James Feist97a63f12018-05-17 13:50:57 -0700688 else
689 {
James Feistbb43d022018-06-12 15:44:33 -0700690 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700691 iface.get(), systemConfiguration, key,
692 permission);
James Feist97a63f12018-05-17 13:50:57 -0700693 }
James Feist9eb0b582018-04-27 12:15:46 -0700694 break;
695 }
696 case (nlohmann::json::value_t::number_integer):
697 {
James Feist8f2710a2018-05-09 17:18:55 -0700698 if (array)
699 {
700 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700701 iface.get(), permission,
702 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700703 }
704 else
705 {
James Feistbb43d022018-06-12 15:44:33 -0700706 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700707 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700708 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700709 }
James Feist9eb0b582018-04-27 12:15:46 -0700710 break;
711 }
712 case (nlohmann::json::value_t::number_unsigned):
713 {
James Feist8f2710a2018-05-09 17:18:55 -0700714 if (array)
715 {
716 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700717 iface.get(), permission,
718 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700719 }
720 else
721 {
James Feistbb43d022018-06-12 15:44:33 -0700722 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700723 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700724 systemConfiguration, key,
725 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700726 }
James Feist9eb0b582018-04-27 12:15:46 -0700727 break;
728 }
729 case (nlohmann::json::value_t::number_float):
730 {
James Feist8f2710a2018-05-09 17:18:55 -0700731 if (array)
732 {
733 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700734 iface.get(), permission,
735 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700736 }
James Feistbb43d022018-06-12 15:44:33 -0700737
James Feist97a63f12018-05-17 13:50:57 -0700738 else
739 {
James Feistbb43d022018-06-12 15:44:33 -0700740 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700741 iface.get(), systemConfiguration, key,
742 permission);
James Feist97a63f12018-05-17 13:50:57 -0700743 }
James Feist9eb0b582018-04-27 12:15:46 -0700744 break;
745 }
746 case (nlohmann::json::value_t::string):
747 {
James Feist8f2710a2018-05-09 17:18:55 -0700748 if (array)
749 {
James Feistebcc26b2019-03-22 12:30:43 -0700750 addArrayToDbus<std::string>(
751 dictPair.key(), dictPair.value(), iface.get(),
752 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700753 }
754 else
755 {
James Feistc6248a52018-08-14 10:09:45 -0700756 addProperty(
757 dictPair.key(), dictPair.value().get<std::string>(),
758 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700759 }
James Feist9eb0b582018-04-27 12:15:46 -0700760 break;
761 }
James Feist0eb40352019-04-09 14:44:04 -0700762 default:
763 {
James Feista218ddb2019-04-11 14:01:31 -0700764 std::cerr << "Unexpected json type in system configuration "
765 << dictPair.key() << ": "
766 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700767 break;
768 }
James Feist1b2e2242018-01-30 13:45:19 -0800769 }
770 }
James Feistc6248a52018-08-14 10:09:45 -0700771 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
772 {
773 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
774 systemConfiguration);
775 }
James Feist8f2710a2018-05-09 17:18:55 -0700776 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800777}
778
James Feista465ccc2019-02-08 12:51:01 -0800779sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700780{
781 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
782 interface) != settableInterfaces.end()
783 ? sdbusplus::asio::PropertyPermission::readWrite
784 : sdbusplus::asio::PropertyPermission::readOnly;
785}
786
James Feista465ccc2019-02-08 12:51:01 -0800787void createAddObjectMethod(const std::string& jsonPointerPath,
788 const std::string& path,
789 nlohmann::json& systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700790 sdbusplus::asio::object_server& objServer,
791 const std::string& board)
James Feist68500ff2018-08-08 15:40:42 -0700792{
James Feistd58879a2019-09-11 11:26:07 -0700793 std::shared_ptr<sdbusplus::asio::dbus_interface> iface = createInterface(
794 objServer, path, "xyz.openbmc_project.AddObject", board);
James Feist68500ff2018-08-08 15:40:42 -0700795
796 iface->register_method(
797 "AddObject",
798 [&systemConfiguration, &objServer,
James Feistd58879a2019-09-11 11:26:07 -0700799 jsonPointerPath{std::string(jsonPointerPath)}, path{std::string(path)},
800 board](const boost::container::flat_map<std::string, JsonVariantType>&
801 data) {
James Feist68500ff2018-08-08 15:40:42 -0700802 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800803 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700804 auto findExposes = base.find("Exposes");
805
806 if (findExposes == base.end())
807 {
808 throw std::invalid_argument("Entity must have children.");
809 }
810
811 // this will throw invalid-argument to sdbusplus if invalid json
812 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800813 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700814 {
James Feista465ccc2019-02-08 12:51:01 -0800815 nlohmann::json& newJson = newData[item.first];
816 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
817 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700818 }
819
820 auto findName = newData.find("Name");
821 auto findType = newData.find("Type");
822 if (findName == newData.end() || findType == newData.end())
823 {
824 throw std::invalid_argument("AddObject missing Name or Type");
825 }
James Feista465ccc2019-02-08 12:51:01 -0800826 const std::string* type = findType->get_ptr<const std::string*>();
827 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700828 if (type == nullptr || name == nullptr)
829 {
830 throw std::invalid_argument("Type and Name must be a string.");
831 }
832
833 size_t lastIndex = 0;
834 // we add in the "exposes"
835 for (; lastIndex < findExposes->size(); lastIndex++)
836 {
837 if (findExposes->at(lastIndex)["Name"] == *name &&
838 findExposes->at(lastIndex)["Type"] == *type)
839 {
840 throw std::invalid_argument(
841 "Field already in JSON, not adding");
842 }
843 lastIndex++;
844 }
845
846 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
847 *type + ".json");
848 // todo(james) we might want to also make a list of 'can add'
849 // interfaces but for now I think the assumption if there is a
850 // schema avaliable that it is allowed to update is fine
851 if (!schemaFile.good())
852 {
853 throw std::invalid_argument(
854 "No schema avaliable, cannot validate.");
855 }
856 nlohmann::json schema =
857 nlohmann::json::parse(schemaFile, nullptr, false);
858 if (schema.is_discarded())
859 {
860 std::cerr << "Schema not legal" << *type << ".json\n";
861 throw DBusInternalError();
862 }
863 if (!validateJson(schema, newData))
864 {
865 throw std::invalid_argument("Data does not match schema");
866 }
867
James Feist16a02f22019-05-13 15:21:37 -0700868 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700869 if (!writeJsonFiles(systemConfiguration))
870 {
871 std::cerr << "Error writing json files\n";
872 throw DBusInternalError();
873 }
874 std::string dbusName = *name;
875
876 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700877 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistd58879a2019-09-11 11:26:07 -0700878
879 std::shared_ptr<sdbusplus::asio::dbus_interface> interface =
880 createInterface(objServer, path + "/" + dbusName,
881 "xyz.openbmc_project.Configuration." + *type,
882 board);
James Feist68500ff2018-08-08 15:40:42 -0700883 // permission is read-write, as since we just created it, must be
884 // runtime modifiable
885 populateInterfaceFromJson(
886 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700887 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700888 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700889 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700890 });
891 iface->initialize();
892}
893
James Feista465ccc2019-02-08 12:51:01 -0800894void postToDbus(const nlohmann::json& newConfiguration,
895 nlohmann::json& systemConfiguration,
896 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800897
James Feist1b2e2242018-01-30 13:45:19 -0800898{
James Feist97a63f12018-05-17 13:50:57 -0700899 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800900 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800901 {
James Feistf1b14142019-04-10 15:22:09 -0700902 std::string boardKey = boardPair.value()["Name"];
James Feistd58879a2019-09-11 11:26:07 -0700903 std::string boardKeyOrig = boardPair.value()["Name"];
James Feistf1b14142019-04-10 15:22:09 -0700904 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700905 // loop through newConfiguration, but use values from system
906 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700907 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700908 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800909 std::string boardType;
910 if (findBoardType != boardValues.end() &&
911 findBoardType->type() == nlohmann::json::value_t::string)
912 {
913 boardType = findBoardType->get<std::string>();
914 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700915 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800916 }
917 else
918 {
919 std::cerr << "Unable to find type for " << boardKey
920 << " reverting to Chassis.\n";
921 boardType = "Chassis";
922 }
James Feist11be6672018-04-06 14:05:32 -0700923 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800924
925 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700926 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700927 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
928 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800929
James Feistd58879a2019-09-11 11:26:07 -0700930 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
931 createInterface(objServer, boardName,
932 "xyz.openbmc_project.Inventory.Item", boardKey);
James Feist68500ff2018-08-08 15:40:42 -0700933
James Feistd58879a2019-09-11 11:26:07 -0700934 std::shared_ptr<sdbusplus::asio::dbus_interface> boardIface =
935 createInterface(objServer, boardName,
936 "xyz.openbmc_project.Inventory.Item." + boardType,
937 boardKeyOrig);
James Feist11be6672018-04-06 14:05:32 -0700938
James Feist68500ff2018-08-08 15:40:42 -0700939 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
James Feistd58879a2019-09-11 11:26:07 -0700940 objServer, boardKeyOrig);
James Feist68500ff2018-08-08 15:40:42 -0700941
James Feist97a63f12018-05-17 13:50:57 -0700942 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700943 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700944 jsonPointerPath += "/";
945 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800946 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700947 {
948 if (boardField.value().type() == nlohmann::json::value_t::object)
949 {
James Feistd58879a2019-09-11 11:26:07 -0700950 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
951 createInterface(objServer, boardName, boardField.key(),
952 boardKeyOrig);
953
James Feistc6248a52018-08-14 10:09:45 -0700954 populateInterfaceFromJson(systemConfiguration,
955 jsonPointerPath + boardField.key(),
956 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700957 }
958 }
James Feist97a63f12018-05-17 13:50:57 -0700959
James Feist1e3e6982018-08-03 16:09:28 -0700960 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800961 if (exposes == boardValues.end())
962 {
963 continue;
964 }
James Feist97a63f12018-05-17 13:50:57 -0700965 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700966 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700967
968 // store the board level pointer so we can modify it on the way down
969 std::string jsonPointerPathBoard = jsonPointerPath;
970 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800971 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800972 {
James Feist97a63f12018-05-17 13:50:57 -0700973 exposesIndex++;
974 jsonPointerPath = jsonPointerPathBoard;
975 jsonPointerPath += std::to_string(exposesIndex);
976
James Feistd63d18a2018-07-19 15:23:45 -0700977 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800978 if (findName == item.end())
979 {
980 std::cerr << "cannot find name in field " << item << "\n";
981 continue;
982 }
James Feist1e3e6982018-08-03 16:09:28 -0700983 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800984 // if status is not found it is assumed to be status = 'okay'
985 if (findStatus != item.end())
986 {
987 if (*findStatus == "disabled")
988 {
989 continue;
990 }
991 }
James Feistd63d18a2018-07-19 15:23:45 -0700992 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800993 std::string itemType;
994 if (findType != item.end())
995 {
996 itemType = findType->get<std::string>();
997 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700998 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
999 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001000 }
1001 else
1002 {
1003 itemType = "unknown";
1004 }
1005 std::string itemName = findName->get<std::string>();
1006 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001007 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001008
James Feistd58879a2019-09-11 11:26:07 -07001009 std::shared_ptr<sdbusplus::asio::dbus_interface> itemIface =
1010 createInterface(objServer, boardName + "/" + itemName,
1011 "xyz.openbmc_project.Configuration." + itemType,
1012 boardKeyOrig);
James Feist1b2e2242018-01-30 13:45:19 -08001013
James Feist97a63f12018-05-17 13:50:57 -07001014 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001015 itemIface, item, objServer,
1016 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001017
James Feista465ccc2019-02-08 12:51:01 -08001018 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001019 {
James Feist97a63f12018-05-17 13:50:57 -07001020 jsonPointerPath = jsonPointerPathBoard +
1021 std::to_string(exposesIndex) + "/" +
1022 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001023 if (objectPair.value().type() ==
1024 nlohmann::json::value_t::object)
1025 {
James Feistd58879a2019-09-11 11:26:07 -07001026 std::shared_ptr<sdbusplus::asio::dbus_interface>
1027 objectIface = createInterface(
1028 objServer, boardName + "/" + itemName,
1029 "xyz.openbmc_project.Configuration." + itemType +
1030 "." + objectPair.key(),
1031 boardKeyOrig);
James Feist97a63f12018-05-17 13:50:57 -07001032
1033 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001034 systemConfiguration, jsonPointerPath, objectIface,
1035 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001036 }
1037 else if (objectPair.value().type() ==
1038 nlohmann::json::value_t::array)
1039 {
1040 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001041 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001042 {
James Feist8f2710a2018-05-09 17:18:55 -07001043 continue;
1044 }
1045 bool isLegal = true;
1046 auto type = objectPair.value()[0].type();
1047 if (type != nlohmann::json::value_t::object)
1048 {
1049 continue;
1050 }
1051
1052 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001053 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001054 {
1055 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001056 {
James Feist8f2710a2018-05-09 17:18:55 -07001057 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001058 break;
1059 }
James Feist8f2710a2018-05-09 17:18:55 -07001060 }
1061 if (!isLegal)
1062 {
1063 std::cerr << "dbus format error" << objectPair.value()
1064 << "\n";
1065 break;
1066 }
1067
James Feista465ccc2019-02-08 12:51:01 -08001068 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001069 {
James Feist97a63f12018-05-17 13:50:57 -07001070
James Feistd58879a2019-09-11 11:26:07 -07001071 std::shared_ptr<sdbusplus::asio::dbus_interface>
1072 objectIface = createInterface(
1073 objServer, boardName + "/" + itemName,
1074 "xyz.openbmc_project.Configuration." +
1075 itemType + "." + objectPair.key() +
1076 std::to_string(index),
1077 boardKeyOrig);
1078
James Feistc6248a52018-08-14 10:09:45 -07001079 populateInterfaceFromJson(
1080 systemConfiguration,
1081 jsonPointerPath + "/" + std::to_string(index),
1082 objectIface, arrayItem, objServer,
1083 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001084 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001085 }
1086 }
1087 }
1088 }
1089 }
1090}
1091
James Feist8f2710a2018-05-09 17:18:55 -07001092// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001093bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001094{
1095 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001096 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001097 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1098 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001099 {
1100 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001101 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001102 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001103 }
James Feistb4383f42018-08-06 16:54:10 -07001104
1105 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1106 globalSchema);
1107 if (!schemaStream.good())
1108 {
1109 std::cerr
1110 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1111 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001112 return false;
James Feistb4383f42018-08-06 16:54:10 -07001113 }
1114 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1115 if (schema.is_discarded())
1116 {
1117 std::cerr
1118 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1119 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001120 return false;
James Feistb4383f42018-08-06 16:54:10 -07001121 }
1122
James Feista465ccc2019-02-08 12:51:01 -08001123 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001124 {
1125 std::ifstream jsonStream(jsonPath.c_str());
1126 if (!jsonStream.good())
1127 {
1128 std::cerr << "unable to open " << jsonPath.string() << "\n";
1129 continue;
1130 }
1131 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1132 if (data.is_discarded())
1133 {
1134 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1135 continue;
1136 }
James Feist8da99192019-01-24 08:20:16 -08001137 /*
1138 * todo(james): reenable this once less things are in flight
1139 *
James Feistb4383f42018-08-06 16:54:10 -07001140 if (!validateJson(schema, data))
1141 {
1142 std::cerr << "Error validating " << jsonPath.string() << "\n";
1143 continue;
1144 }
James Feist8da99192019-01-24 08:20:16 -08001145 */
James Feistb4383f42018-08-06 16:54:10 -07001146
James Feist3cb5fec2018-01-23 14:41:51 -08001147 if (data.type() == nlohmann::json::value_t::array)
1148 {
James Feista465ccc2019-02-08 12:51:01 -08001149 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001150 {
1151 configurations.emplace_back(d);
1152 }
1153 }
1154 else
1155 {
1156 configurations.emplace_back(data);
1157 }
1158 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001159 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001160}
James Feist3cb5fec2018-01-23 14:41:51 -08001161
James Feist733f7652019-11-13 14:32:29 -08001162PerformScan::PerformScan(
1163 nlohmann::json& systemConfiguration, nlohmann::json& missingConfigurations,
1164 std::list<nlohmann::json>& configurations,
1165 std::function<void(const DBusProbeObjectT&)>&& callback) :
1166 _systemConfiguration(systemConfiguration),
1167 _missingConfigurations(missingConfigurations),
1168 _configurations(configurations), _callback(std::move(callback))
James Feist75fdeeb2018-02-20 14:26:16 -08001169{
James Feist733f7652019-11-13 14:32:29 -08001170}
1171void PerformScan::run()
1172{
1173 boost::container::flat_set<std::string> dbusProbeInterfaces;
1174 std::vector<std::shared_ptr<PerformProbe>> dbusProbePointers;
James Feist75fdeeb2018-02-20 14:26:16 -08001175
James Feist733f7652019-11-13 14:32:29 -08001176 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001177 {
James Feist733f7652019-11-13 14:32:29 -08001178 auto findProbe = it->find("Probe");
1179 auto findName = it->find("Name");
James Feist787c3c32019-11-07 14:42:58 -08001180
James Feist733f7652019-11-13 14:32:29 -08001181 nlohmann::json probeCommand;
1182 // check for poorly formatted fields, probe must be an array
1183 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001184 {
James Feist733f7652019-11-13 14:32:29 -08001185 std::cerr << "configuration file missing probe:\n " << *it << "\n";
1186 it = _configurations.erase(it);
1187 continue;
1188 }
1189 else if ((*findProbe).type() != nlohmann::json::value_t::array)
1190 {
1191 probeCommand = nlohmann::json::array();
1192 probeCommand.push_back(*findProbe);
1193 }
1194 else
1195 {
1196 probeCommand = *findProbe;
1197 }
James Feist3cb5fec2018-01-23 14:41:51 -08001198
James Feist733f7652019-11-13 14:32:29 -08001199 if (findName == it->end())
1200 {
1201 std::cerr << "configuration file missing name:\n " << *it << "\n";
1202 it = _configurations.erase(it);
1203 continue;
1204 }
1205 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001206
James Feist733f7652019-11-13 14:32:29 -08001207 if (std::find(passedProbes.begin(), passedProbes.end(), probeName) !=
1208 passedProbes.end())
1209 {
1210 it = _configurations.erase(it);
1211 continue;
1212 }
1213 nlohmann::json* recordPtr = &(*it);
James Feist1b2e2242018-01-30 13:45:19 -08001214
James Feist733f7652019-11-13 14:32:29 -08001215 // store reference to this to children to makes sure we don't get
1216 // destroyed too early
1217 auto thisRef = shared_from_this();
1218 auto probePointer = std::make_shared<PerformProbe>(
1219 probeCommand, thisRef,
1220 [&, recordPtr, probeName](FoundDeviceT& foundDevices) {
James Feist08a5b172019-08-28 14:47:47 -07001221 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001222
James Feist733f7652019-11-13 14:32:29 -08001223 passedProbes.push_back(probeName);
James Feist08a5b172019-08-28 14:47:47 -07001224 size_t foundDeviceIdx = 1;
James Feist8f2710a2018-05-09 17:18:55 -07001225
James Feist08a5b172019-08-28 14:47:47 -07001226 for (auto& foundDevice : foundDevices)
1227 {
1228 nlohmann::json record = *recordPtr;
1229 std::string recordName;
1230 size_t hash = 0;
1231 if (foundDevice.size())
James Feist3cb5fec2018-01-23 14:41:51 -08001232 {
James Feist08a5b172019-08-28 14:47:47 -07001233 // use an array so alphabetical order from the
1234 // flat_map is maintained
1235 auto device = nlohmann::json::array();
1236 for (auto& devPair : foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001237 {
James Feist08a5b172019-08-28 14:47:47 -07001238 device.push_back(devPair.first);
1239 std::visit(
1240 [&device](auto&& v) { device.push_back(v); },
1241 devPair.second);
1242 }
1243 hash =
1244 std::hash<std::string>{}(probeName + device.dump());
1245 // hashes are hard to distinguish, use the
1246 // non-hashed version if we want debug
1247 if constexpr (DEBUG)
1248 {
1249 recordName = probeName + device.dump();
James Feist8f2710a2018-05-09 17:18:55 -07001250 }
James Feistf1b14142019-04-10 15:22:09 -07001251 else
1252 {
James Feist08a5b172019-08-28 14:47:47 -07001253 recordName = std::to_string(hash);
James Feistf1b14142019-04-10 15:22:09 -07001254 }
James Feist08a5b172019-08-28 14:47:47 -07001255 }
1256 else
1257 {
1258 recordName = probeName;
1259 }
James Feistf1b14142019-04-10 15:22:09 -07001260
James Feist08a5b172019-08-28 14:47:47 -07001261 auto fromLastJson = lastJson.find(recordName);
1262 if (fromLastJson != lastJson.end())
1263 {
1264 // keep user changes
1265 _systemConfiguration[recordName] = *fromLastJson;
James Feist899e17f2019-09-13 11:46:29 -07001266 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001267 continue;
1268 }
James Feist1df06a42019-04-11 14:23:04 -07001269
James Feist08a5b172019-08-28 14:47:47 -07001270 // insert into configuration temporarily to be able to
1271 // reference ourselves
James Feistf1b14142019-04-10 15:22:09 -07001272
James Feist08a5b172019-08-28 14:47:47 -07001273 _systemConfiguration[recordName] = record;
1274
1275 for (auto keyPair = record.begin(); keyPair != record.end();
1276 keyPair++)
1277 {
1278 templateCharReplace(keyPair, foundDevice,
1279 foundDeviceIdx);
1280 }
1281
1282 auto findExpose = record.find("Exposes");
1283 if (findExpose == record.end())
1284 {
James Feistf1b14142019-04-10 15:22:09 -07001285 _systemConfiguration[recordName] = record;
James Feist08a5b172019-08-28 14:47:47 -07001286 foundDeviceIdx++;
1287 continue;
1288 }
James Feistf1b14142019-04-10 15:22:09 -07001289
James Feist08a5b172019-08-28 14:47:47 -07001290 for (auto& expose : *findExpose)
1291 {
1292 for (auto keyPair = expose.begin();
1293 keyPair != expose.end(); keyPair++)
James Feistf1b14142019-04-10 15:22:09 -07001294 {
James Feist08a5b172019-08-28 14:47:47 -07001295
1296 templateCharReplace(keyPair, foundDevice,
1297 foundDeviceIdx);
1298
James Feist668bbb12020-02-05 14:27:26 -08001299 bool isBind =
1300 boost::starts_with(keyPair.key(), "Bind");
1301 bool isDisable = keyPair.key() == "DisableNode";
1302
1303 // special cases
1304 if (!(isBind || isDisable))
James Feistf1b14142019-04-10 15:22:09 -07001305 {
James Feist668bbb12020-02-05 14:27:26 -08001306 continue;
1307 }
1308
1309 if (keyPair.value().type() !=
1310 nlohmann::json::value_t::string &&
1311 keyPair.value().type() !=
1312 nlohmann::json::value_t::array)
1313 {
1314 std::cerr << "Value is invalid type "
1315 << keyPair.key() << "\n";
1316 continue;
1317 }
1318
1319 std::vector<std::string> matches;
1320 if (keyPair.value().type() ==
1321 nlohmann::json::value_t::string)
1322 {
1323 matches.emplace_back(keyPair.value());
1324 }
1325 else
1326 {
1327 for (const auto& value : keyPair.value())
James Feistf1b14142019-04-10 15:22:09 -07001328 {
James Feist668bbb12020-02-05 14:27:26 -08001329 if (value.type() !=
1330 nlohmann::json::value_t::string)
1331 {
1332 std::cerr << "Value is invalid type "
1333 << value << "\n";
1334 break;
1335 }
1336 matches.emplace_back(value);
James Feistf1b14142019-04-10 15:22:09 -07001337 }
James Feist668bbb12020-02-05 14:27:26 -08001338 }
James Feist08a5b172019-08-28 14:47:47 -07001339
James Feist668bbb12020-02-05 14:27:26 -08001340 std::set<std::string> foundMatches;
1341 for (auto& configurationPair :
1342 _systemConfiguration.items())
1343 {
1344 if (isDisable)
James Feist8f2710a2018-05-09 17:18:55 -07001345 {
James Feist668bbb12020-02-05 14:27:26 -08001346 // don't disable ourselves
1347 if (configurationPair.key() == recordName)
James Feist3cb5fec2018-01-23 14:41:51 -08001348 {
James Feist1b2e2242018-01-30 13:45:19 -08001349 continue;
1350 }
James Feist668bbb12020-02-05 14:27:26 -08001351 }
1352 auto configListFind =
1353 configurationPair.value().find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001354
James Feist668bbb12020-02-05 14:27:26 -08001355 if (configListFind ==
1356 configurationPair.value().end() ||
1357 configListFind->type() !=
1358 nlohmann::json::value_t::array)
James Feist08a5b172019-08-28 14:47:47 -07001359 {
James Feist668bbb12020-02-05 14:27:26 -08001360 continue;
James Feist08a5b172019-08-28 14:47:47 -07001361 }
James Feist668bbb12020-02-05 14:27:26 -08001362 for (auto& exposedObject : *configListFind)
1363 {
1364 auto matchIt = std::find_if(
1365 matches.begin(), matches.end(),
1366 [name = (exposedObject)["Name"]
1367 .get<std::string>()](
1368 const std::string& s) {
1369 return s == name;
1370 });
1371 if (matchIt == matches.end())
1372 {
1373 continue;
1374 }
1375 foundMatches.insert(*matchIt);
1376
1377 if (isBind)
1378 {
1379 std::string bind = keyPair.key().substr(
1380 sizeof("Bind") - 1);
1381
1382 exposedObject["Status"] = "okay";
1383 expose[bind] = exposedObject;
1384 }
1385 else if (isDisable)
1386 {
1387 exposedObject["Status"] = "disabled";
1388 }
1389 }
1390 }
1391 if (foundMatches.size() != matches.size())
1392 {
1393 std::cerr << "configuration file "
1394 "dependency error, "
1395 "could not find "
1396 << keyPair.key() << " "
1397 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001398 }
1399 }
1400 }
James Feist08a5b172019-08-28 14:47:47 -07001401 // overwrite ourselves with cleaned up version
1402 _systemConfiguration[recordName] = record;
James Feist899e17f2019-09-13 11:46:29 -07001403 _missingConfigurations.erase(recordName);
James Feist08a5b172019-08-28 14:47:47 -07001404
1405 foundDeviceIdx++;
1406 }
1407 });
James Feist787c3c32019-11-07 14:42:58 -08001408
James Feist733f7652019-11-13 14:32:29 -08001409 // parse out dbus probes by discarding other probe types, store in a
1410 // map
1411 for (const std::string& probe : probeCommand)
1412 {
1413 bool found = false;
1414 boost::container::flat_map<const char*, probe_type_codes,
1415 cmp_str>::const_iterator probeType;
1416 for (probeType = PROBE_TYPES.begin();
1417 probeType != PROBE_TYPES.end(); ++probeType)
James Feist787c3c32019-11-07 14:42:58 -08001418 {
James Feist733f7652019-11-13 14:32:29 -08001419 if (probe.find(probeType->first) != std::string::npos)
James Feist787c3c32019-11-07 14:42:58 -08001420 {
James Feist733f7652019-11-13 14:32:29 -08001421 found = true;
1422 break;
James Feist787c3c32019-11-07 14:42:58 -08001423 }
James Feist787c3c32019-11-07 14:42:58 -08001424 }
James Feist733f7652019-11-13 14:32:29 -08001425 if (found)
1426 {
1427 continue;
1428 }
1429 // syntax requires probe before first open brace
1430 auto findStart = probe.find("(");
1431 std::string interface = probe.substr(0, findStart);
1432 dbusProbeInterfaces.emplace(interface);
1433 dbusProbePointers.emplace_back(probePointer);
James Feist3cb5fec2018-01-23 14:41:51 -08001434 }
James Feist733f7652019-11-13 14:32:29 -08001435 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001436 }
James Feist75fdeeb2018-02-20 14:26:16 -08001437
James Feist733f7652019-11-13 14:32:29 -08001438 // probe vector stores a shared_ptr to each PerformProbe that cares
1439 // about a dbus interface
1440 findDbusObjects(dbusProbePointers, SYSTEM_BUS, dbusProbeInterfaces,
1441 shared_from_this());
1442}
1443
1444PerformScan::~PerformScan()
1445{
1446 if (_passed)
James Feist75fdeeb2018-02-20 14:26:16 -08001447 {
James Feist733f7652019-11-13 14:32:29 -08001448 auto nextScan = std::make_shared<PerformScan>(
1449 _systemConfiguration, _missingConfigurations, _configurations,
1450 std::move(_callback));
1451 nextScan->passedProbes = std::move(passedProbes);
1452 nextScan->dbusProbeObjects = std::move(dbusProbeObjects);
1453 nextScan->run();
James Feist8f2710a2018-05-09 17:18:55 -07001454 }
James Feist733f7652019-11-13 14:32:29 -08001455 else
1456 {
1457 _callback(dbusProbeObjects);
1458 }
1459}
James Feistc95cb142018-02-26 10:41:42 -08001460
James Feist1df06a42019-04-11 14:23:04 -07001461void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001462 nlohmann::json& systemConfiguration)
1463{
1464 static bool scannedPowerOff = false;
1465 static bool scannedPowerOn = false;
1466
James Feistfb00f392019-06-25 14:16:48 -07001467 if (systemConfiguration.empty() || lastJson.empty())
1468 {
1469 return; // not ready yet
1470 }
James Feist1df06a42019-04-11 14:23:04 -07001471 if (scannedPowerOn)
1472 {
1473 return;
1474 }
1475
1476 if (!isPowerOn() && scannedPowerOff)
1477 {
1478 return;
1479 }
1480
1481 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001482 timer.async_wait(
1483 [&systemConfiguration](const boost::system::error_code& ec) {
1484 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001485 {
James Feist1a996582019-05-14 15:10:06 -07001486 // we were cancelled
1487 return;
1488 }
1489
1490 bool powerOff = !isPowerOn();
1491 for (const auto& item : lastJson.items())
1492 {
1493 if (systemConfiguration.find(item.key()) ==
1494 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001495 {
James Feist1a996582019-05-14 15:10:06 -07001496 bool isDetectedPowerOn = false;
1497 auto powerState = item.value().find("PowerState");
1498 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001499 {
James Feist1a996582019-05-14 15:10:06 -07001500 auto ptr = powerState->get_ptr<const std::string*>();
1501 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001502 {
James Feist1a996582019-05-14 15:10:06 -07001503 if (*ptr == "On" || *ptr == "BiosPost")
1504 {
1505 isDetectedPowerOn = true;
1506 }
James Feist1df06a42019-04-11 14:23:04 -07001507 }
1508 }
James Feist1a996582019-05-14 15:10:06 -07001509 if (powerOff && isDetectedPowerOn)
1510 {
1511 // power not on yet, don't know if it's there or not
1512 continue;
1513 }
1514 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1515 {
1516 // already logged it when power was off
1517 continue;
1518 }
James Feist1df06a42019-04-11 14:23:04 -07001519
James Feist1a996582019-05-14 15:10:06 -07001520 logDeviceRemoved(item.value());
1521 }
James Feist1df06a42019-04-11 14:23:04 -07001522 }
James Feist1a996582019-05-14 15:10:06 -07001523 scannedPowerOff = true;
1524 if (!powerOff)
1525 {
1526 scannedPowerOn = true;
1527 }
1528 });
James Feist1df06a42019-04-11 14:23:04 -07001529}
1530
James Feist8f2710a2018-05-09 17:18:55 -07001531// main properties changed entry
1532void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001533 boost::asio::io_service& io,
1534 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1535 nlohmann::json& systemConfiguration,
1536 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001537{
1538 static boost::asio::deadline_timer timer(io);
James Feist899e17f2019-09-13 11:46:29 -07001539 static size_t instance = 0;
1540 instance++;
1541 size_t count = instance;
James Feist1df06a42019-04-11 14:23:04 -07001542
James Feist899e17f2019-09-13 11:46:29 -07001543 timer.expires_from_now(boost::posix_time::seconds(5));
James Feist8f2710a2018-05-09 17:18:55 -07001544
1545 // setup an async wait as we normally get flooded with new requests
James Feist899e17f2019-09-13 11:46:29 -07001546 timer.async_wait([&systemConfiguration, &dbusMatches, &io, &objServer,
1547 count](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001548 if (ec == boost::asio::error::operation_aborted)
1549 {
1550 // we were cancelled
1551 return;
1552 }
1553 else if (ec)
1554 {
1555 std::cerr << "async wait error " << ec << "\n";
1556 return;
1557 }
1558
1559 nlohmann::json oldConfiguration = systemConfiguration;
James Feist899e17f2019-09-13 11:46:29 -07001560 auto missingConfigurations = std::make_shared<nlohmann::json>();
1561 *missingConfigurations = systemConfiguration;
1562
James Feist8f2710a2018-05-09 17:18:55 -07001563 std::list<nlohmann::json> configurations;
1564 if (!findJsonFiles(configurations))
1565 {
1566 std::cerr << "cannot find json files\n";
1567 return;
1568 }
1569
1570 auto perfScan = std::make_shared<PerformScan>(
James Feist899e17f2019-09-13 11:46:29 -07001571 systemConfiguration, *missingConfigurations, configurations,
1572 [&systemConfiguration, &io, &objServer, &dbusMatches, count,
James Feist733f7652019-11-13 14:32:29 -08001573 oldConfiguration,
1574 missingConfigurations](const DBusProbeObjectT& dbusProbeObjects) {
James Feist899e17f2019-09-13 11:46:29 -07001575 // this is something that since ac has been applied to the bmc
1576 // we saw, and we no longer see it
1577 bool powerOff = !isPowerOn();
1578 for (const auto& item : missingConfigurations->items())
1579 {
1580 bool isDetectedPowerOn = false;
1581 auto powerState = item.value().find("PowerState");
1582 if (powerState != item.value().end())
1583 {
1584 auto ptr = powerState->get_ptr<const std::string*>();
1585 if (ptr)
1586 {
1587 if (*ptr == "On" || *ptr == "BiosPost")
1588 {
1589 isDetectedPowerOn = true;
1590 }
1591 }
1592 }
1593 if (powerOff && isDetectedPowerOn)
1594 {
1595 // power not on yet, don't know if it's there or not
1596 continue;
1597 }
1598 std::string name = item.value()["Name"].get<std::string>();
1599 std::vector<
1600 std::shared_ptr<sdbusplus::asio::dbus_interface>>&
1601 ifaces = inventory[name];
1602 for (auto& iface : ifaces)
1603 {
1604 objServer.remove_interface(iface);
1605 }
1606 ifaces.clear();
1607 systemConfiguration.erase(item.key());
1608 logDeviceRemoved(item.value());
1609 }
1610
James Feist8f2710a2018-05-09 17:18:55 -07001611 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001612 for (auto it = newConfiguration.begin();
1613 it != newConfiguration.end();)
1614 {
1615 auto findKey = oldConfiguration.find(it.key());
1616 if (findKey != oldConfiguration.end())
1617 {
1618 it = newConfiguration.erase(it);
1619 }
1620 else
1621 {
1622 it++;
1623 }
1624 }
James Feist899e17f2019-09-13 11:46:29 -07001625 for (const auto& item : newConfiguration.items())
1626 {
1627 logDeviceAdded(item.value());
1628 }
1629
James Feist8f2710a2018-05-09 17:18:55 -07001630 registerCallbacks(io, dbusMatches, systemConfiguration,
James Feist733f7652019-11-13 14:32:29 -08001631 objServer, dbusProbeObjects);
James Feist8f2710a2018-05-09 17:18:55 -07001632 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001633 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001634
James Feistbb43d022018-06-12 15:44:33 -07001635 io.post([&]() {
1636 if (!writeJsonFiles(systemConfiguration))
1637 {
1638 std::cerr << "Error writing json files\n";
1639 }
1640 });
James Feist8f2710a2018-05-09 17:18:55 -07001641 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001642 postToDbus(newConfiguration, systemConfiguration,
1643 objServer);
James Feist899e17f2019-09-13 11:46:29 -07001644 if (count != instance)
James Feist1df06a42019-04-11 14:23:04 -07001645 {
James Feist899e17f2019-09-13 11:46:29 -07001646 return;
James Feist1df06a42019-04-11 14:23:04 -07001647 }
James Feist899e17f2019-09-13 11:46:29 -07001648 startRemovedTimer(timer, systemConfiguration);
James Feist8f2710a2018-05-09 17:18:55 -07001649 });
1650 });
1651 });
1652 perfScan->run();
1653 });
James Feist75fdeeb2018-02-20 14:26:16 -08001654}
1655
James Feista465ccc2019-02-08 12:51:01 -08001656void registerCallbacks(boost::asio::io_service& io,
1657 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1658 nlohmann::json& systemConfiguration,
James Feist733f7652019-11-13 14:32:29 -08001659 sdbusplus::asio::object_server& objServer,
1660 const DBusProbeObjectT& dbusProbeObjects)
James Feist75fdeeb2018-02-20 14:26:16 -08001661{
1662 static boost::container::flat_set<std::string> watchedObjects;
1663
James Feist733f7652019-11-13 14:32:29 -08001664 for (const auto& objectMap : dbusProbeObjects)
James Feist75fdeeb2018-02-20 14:26:16 -08001665 {
James Feist787c3c32019-11-07 14:42:58 -08001666 auto [_, inserted] = watchedObjects.insert(objectMap.first);
1667 if (!inserted)
James Feist75fdeeb2018-02-20 14:26:16 -08001668 {
1669 continue;
1670 }
James Feist8f2710a2018-05-09 17:18:55 -07001671 std::function<void(sdbusplus::message::message & message)>
1672 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001673
James Feista465ccc2019-02-08 12:51:01 -08001674 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001675 propertiesChangedCallback(io, dbusMatches,
1676 systemConfiguration, objServer);
1677 };
1678
1679 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001680 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001681 "type='signal',member='PropertiesChanged',arg0='" +
1682 objectMap.first + "'",
1683 eventHandler);
1684 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001685 }
1686}
1687
James Feist98132792019-07-09 13:29:09 -07001688int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001689{
1690 // setup connection to dbus
1691 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001692 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001693 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001694
James Feist8f2710a2018-05-09 17:18:55 -07001695 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001696
James Feist8f2710a2018-05-09 17:18:55 -07001697 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1698 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1699 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001700
James Feist8f2710a2018-05-09 17:18:55 -07001701 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1702 objServer.add_interface("/xyz/openbmc_project/inventory",
1703 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001704
1705 // to keep reference to the match / filter objects so they don't get
1706 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001707 std::vector<sdbusplus::bus::match::match> dbusMatches;
1708
1709 nlohmann::json systemConfiguration = nlohmann::json::object();
1710
1711 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001712 "Notify",
1713 [](const boost::container::flat_map<
1714 std::string,
James Feist98132792019-07-09 13:29:09 -07001715 boost::container::flat_map<std::string, BasicVariantType>>&) {
1716 return;
1717 });
James Feist8f2710a2018-05-09 17:18:55 -07001718 inventoryIface->initialize();
1719
1720 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001721#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001722 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001723#endif
James Feist8f2710a2018-05-09 17:18:55 -07001724 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1725 objServer);
1726 });
James Feist4131aea2018-03-09 09:47:30 -08001727
James Feistfd1264a2018-05-03 12:10:00 -07001728 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001729 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1730 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001731 });
James Feist8f2710a2018-05-09 17:18:55 -07001732 entityIface->initialize();
1733
James Feist1df06a42019-04-11 14:23:04 -07001734 if (fwVersionIsSame())
1735 {
1736 if (std::filesystem::is_regular_file(currentConfiguration))
1737 {
1738 // this file could just be deleted, but it's nice for debug
1739 std::filesystem::create_directory(tempConfigDir);
1740 std::filesystem::remove(lastConfiguration);
1741 std::filesystem::copy(currentConfiguration, lastConfiguration);
1742 std::filesystem::remove(currentConfiguration);
1743
1744 std::ifstream jsonStream(lastConfiguration);
1745 if (jsonStream.good())
1746 {
1747 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1748 if (data.is_discarded())
1749 {
1750 std::cerr << "syntax error in " << lastConfiguration
1751 << "\n";
1752 }
1753 else
1754 {
1755 lastJson = std::move(data);
1756 }
1757 }
1758 else
1759 {
1760 std::cerr << "unable to open " << lastConfiguration << "\n";
1761 }
1762 }
1763 }
1764 else
1765 {
1766 // not an error, just logging at this level to make it in the journal
1767 std::cerr << "Clearing previous configuration\n";
1768 std::filesystem::remove(currentConfiguration);
1769 }
1770
1771 // some boards only show up after power is on, we want to not say they are
1772 // removed until the same state happens
1773 setupPowerMatch(SYSTEM_BUS);
1774
James Feist1b2e2242018-01-30 13:45:19 -08001775 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001776
1777 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001778}